root/branches/release-30/lib/MT/WeblogPublisher.pm @ 1394

Revision 1394, 161.2 kB (checked in by bsmith, 21 months ago)

adding variables to standardize amongst other templates

  • Property svn:keywords set to Id Revision
Line 
1# Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd.
2# This program is distributed under the terms of the
3# GNU General Public License, version 2.
4#
5# $Id$
6
7package MT::WeblogPublisher;
8
9use strict;
10use base qw( MT::ErrorHandler Exporter );
11
12use MT::Util qw( start_end_period start_end_day start_end_year
13  start_end_week start_end_month week2ymd
14  dirify );
15use MT::I18N qw( lowercase );
16use File::Basename;
17
18our @EXPORT = qw(ArchiveFileTemplate ArchiveType);
19my %ArchiveTypes;
20
21sub ArchiveFileTemplate {
22    my %param = @_;
23    \%param;
24}
25
26sub ArchiveType {
27    new MT::ArchiveType(@_);
28}
29
30sub new {
31    my $class = shift;
32    my $this  = {@_};
33    my $cfg   = MT->config;
34    if ( !exists $this->{NoTempFiles} ) {
35        $this->{NoTempFiles} = $cfg->NoTempFiles;
36    }
37    if ( !exists $this->{PublishCommenterIcon} ) {
38        $this->{PublishCommenterIcon} = $cfg->PublishCommenterIcon;
39    }
40    bless $this, $class;
41    $this->init();
42    $this;
43}
44
45sub init_archive_types {
46    my $types = MT->registry("archive_types") || {};
47    my $mt = MT->instance;
48    while (my ($type, $typedata) = each %$types) {
49        if ('HASH' eq ref $typedata) {
50            $typedata = { %$typedata };  # don't edit registry
51            $typedata->{$_} = $mt->handler_to_coderef($typedata->{$_})
52                for qw( archive_label archive_file archive_title date_range
53                        archive_group_iter archive_group_entries
54                        archive_entries_count );
55            $typedata->{default_archive_templates} = [
56                map { ArchiveFileTemplate(%$_) }
57                    @{ $typedata->{default_archive_templates} || [] }
58            ];
59            $typedata = ArchiveType(%$typedata);
60        }
61        $ArchiveTypes{$type} = $typedata;
62    }
63}
64
65sub archive_types {
66    init_archive_types() unless %ArchiveTypes;
67    keys %ArchiveTypes;
68}
69
70sub archiver {
71    my $mt = shift;
72    my ($at) = @_;
73    init_archive_types() unless %ArchiveTypes;
74    $at ? $ArchiveTypes{$at} : undef;
75}
76
77sub init {
78    init_archive_types() unless %ArchiveTypes;
79}
80
81sub core_archive_types {
82    return {
83        'Yearly' => ArchiveType(
84            name                  => 'Yearly',
85            archive_label         => \&yearly_archive_label,
86            archive_file          => \&yearly_archive_file,
87            archive_title         => \&yearly_archive_title,
88            date_range            => \&yearly_date_range,
89            archive_group_iter    => \&yearly_group_iter,
90            archive_group_entries => \&yearly_group_entries,
91            archive_entries_count => \&yearly_entries_count,
92            dynamic_template      => 'archives/<$MTArchiveDate format="%Y"$>',
93            default_archive_templates => [
94                ArchiveFileTemplate(
95                    label    => MT->translate('yyyy/index.html'),
96                    template => '%y/%i',
97                    default  => 1
98                ),
99            ],
100            dynamic_support => 1,
101            date_based      => 1,
102            template_params => {
103                datebased_only_archive   => 1,
104                datebased_yearly_archive => 1,
105                module_yearly_archives   => 1,
106                main_template            => 1,
107                archive_template         => 1,
108                archive_listing          => 1,
109                archive_class            => "datebased-yearly-archive",
110            },
111        ),
112        'Monthly' => ArchiveType(
113            name                  => 'Monthly',
114            archive_label         => \&monthly_archive_label,
115            archive_file          => \&monthly_archive_file,
116            archive_title         => \&monthly_archive_title,
117            date_range            => \&monthly_date_range,
118            archive_group_iter    => \&monthly_group_iter,
119            archive_group_entries => \&monthly_group_entries,
120            archive_entries_count => \&monthly_entries_count,
121            dynamic_template      => 'archives/<$MTArchiveDate format="%Y%m"$>',
122            default_archive_templates => [
123                ArchiveFileTemplate(
124                    label    => MT->translate('yyyy/mm/index.html'),
125                    template => '%y/%m/%i',
126                    default  => 1
127                ),
128            ],
129            dynamic_support => 1,
130            date_based      => 1,
131            template_params => {
132                datebased_only_archive    => 1,
133                datebased_monthly_archive => 1,
134                main_template             => 1,
135                archive_template          => 1,
136                archive_listing           => 1,
137                archive_class             => "datebased-monthly-archive",
138            },
139        ),
140        'Weekly' => ArchiveType(
141            name                  => 'Weekly',
142            archive_label         => \&weekly_archive_label,
143            archive_file          => \&weekly_archive_file,
144            archive_title         => \&weekly_archive_title,
145            date_range            => \&weekly_date_range,
146            archive_group_iter    => \&weekly_group_iter,
147            archive_group_entries => \&weekly_group_entries,
148            archive_entries_count => \&weekly_entries_count,
149            dynamic_template =>
150              'archives/week/<$MTArchiveDate format="%Y%m%d"$>',
151            default_archive_templates => [
152                ArchiveFileTemplate(
153                    label    => MT->translate('yyyy/mm/day-week/index.html'),
154                    template => '%y/%m/%d-week/%i',
155                    default  => 1
156                ),
157            ],
158            dynamic_support => 1,
159            date_based      => 1,
160            template_params => {
161                datebased_only_archive   => 1,
162                datebased_weekly_archive => 1,
163                main_template            => 1,
164                archive_template         => 1,
165                archive_listing          => 1,
166                archive_class            => "datebased-weekly-archive",
167            },
168        ),
169        'Individual' => ArchiveType(
170            name                      => 'Individual',
171            archive_label             => \&individual_archive_label,
172            archive_file              => \&individual_archive_file,
173            archive_title             => \&individual_archive_title,
174            archive_group_iter        => \&individual_group_iter,
175            dynamic_template          => 'entry/<$MTEntryID$>',
176            default_archive_templates => [
177                ArchiveFileTemplate(
178                    label    => MT->translate('yyyy/mm/entry-basename.html'),
179                    template => '%y/%m/%-f',
180                    default  => 1
181                ),
182                ArchiveFileTemplate(
183                    label    => MT->translate('yyyy/mm/entry_basename.html'),
184                    template => '%y/%m/%f'
185                ),
186                ArchiveFileTemplate(
187                    label => MT->translate('yyyy/mm/entry-basename/index.html'),
188                    template => '%y/%m/%-b/%i'
189                ),
190                ArchiveFileTemplate(
191                    label => MT->translate('yyyy/mm/entry_basename/index.html'),
192                    template => '%y/%m/%b/%i'
193                ),
194                ArchiveFileTemplate(
195                    label    => MT->translate('yyyy/mm/dd/entry-basename.html'),
196                    template => '%y/%m/%d/%-f'
197                ),
198                ArchiveFileTemplate(
199                    label    => MT->translate('yyyy/mm/dd/entry_basename.html'),
200                    template => '%y/%m/%d/%f'
201                ),
202                ArchiveFileTemplate(
203                    label =>
204                      MT->translate('yyyy/mm/dd/entry-basename/index.html'),
205                    template => '%y/%m/%d/%-b/%i'
206                ),
207                ArchiveFileTemplate(
208                    label =>
209                      MT->translate('yyyy/mm/dd/entry_basename/index.html'),
210                    template => '%y/%m/%d/%b/%i'
211                ),
212                ArchiveFileTemplate(
213                    label => MT->translate(
214                        'category/sub-category/entry-basename.html'),
215                    template => '%-c/%-f'
216                ),
217                ArchiveFileTemplate(
218                    label => MT->translate(
219                        'category/sub-category/entry-basename/index.html'),
220                    template => '%-c/%-b/%i'
221                ),
222                ArchiveFileTemplate(
223                    label => MT->translate(
224                        'category/sub_category/entry_basename.html'),
225                    template => '%c/%f'
226                ),
227                ArchiveFileTemplate(
228                    label => MT->translate(
229                        'category/sub_category/entry_basename/index.html'),
230                    template => '%c/%b/%i'
231                ),
232            ],
233            dynamic_support => 1,
234            entry_based     => 1,
235            template_params => {
236                entry_archive     => 1,
237                main_template     => 1,
238                archive_template  => 1,
239                entry_template    => 1,
240                feedback_template => 1,
241                archive_class     => "entry-archive",
242            },
243        ),
244        'Page' => ArchiveType(
245            name                      => 'Page',
246            archive_label             => \&page_archive_label,
247            archive_file              => \&page_archive_file,
248            archive_title             => \&individual_archive_title,
249            archive_group_iter        => \&page_group_iter,
250            dynamic_template          => 'page/<$MTEntryID$>',
251            default_archive_templates => [
252                ArchiveFileTemplate(
253                    label    => MT->translate('folder-path/page-basename.html'),
254                    template => '%-c/%-f',
255                    default  => 1
256                ),
257                ArchiveFileTemplate(
258                    label =>
259                      MT->translate('folder-path/page-basename/index.html'),
260                    template => '%-c/%-b/%i'
261                ),
262                ArchiveFileTemplate(
263                    label    => MT->translate('folder_path/page_basename.html'),
264                    template => '%c/%f'
265                ),
266                ArchiveFileTemplate(
267                    label =>
268                      MT->translate('folder_path/page_basename/index.html'),
269                    template => '%c/%b/%i'
270                ),
271            ],
272            dynamic_support => 1,
273            entry_class     => 'page',
274            entry_based     => 1,
275            template_params => {
276                archive_class     => "page-archive",
277                page_archive      => 1,
278                main_template     => 1,
279                archive_template  => 1,
280                page_template     => 1,
281                feedback_template => 1,
282            },
283        ),
284
285# 'Folder' =>
286#     ArchiveType( name => 'Folder',
287#         archive_label => \&folder_archive_label,
288#         archive_file => \&folder_archive_file,
289#         archive_title => \&folder_archive_title,
290#         archive_group_iter => \&folder_group_iter,
291#         archive_group_entries => \&folder_group_entries,
292#         dynamic_template => 'folder/<$MTCategoryID$>',
293#         default_archive_templates => [
294#             ArchiveFileTemplate( Label => MT->translate('folder/sub_folder/index.html'),
295#                 template => '%c/%i', Default => 1 ),
296#             ArchiveFileTemplate( Label => MT->translate('folder/sub-folder/index.html'),
297#                 template => '%-c/%i' ),
298#             ],
299#         dynamic_support => 1,
300#         category_based => 1,
301#     ),
302        'Daily' => ArchiveType(
303            name                  => 'Daily',
304            archive_label         => \&daily_archive_label,
305            archive_file          => \&daily_archive_file,
306            archive_title         => \&daily_archive_title,
307            date_range            => \&daily_date_range,
308            archive_group_iter    => \&daily_group_iter,
309            archive_group_entries => \&daily_group_entries,
310            archive_entries_count => \&daily_entries_count,
311            dynamic_template => 'archives/<$MTArchiveDate format="%Y%m%d"$>',
312            default_archive_templates => [
313                ArchiveFileTemplate(
314                    label    => MT->translate('yyyy/mm/dd/index.html'),
315                    template => '%y/%m/%d/%f',
316                    default  => 1
317                ),
318            ],
319            dynamic_support => 1,
320            date_based      => 1,
321            template_params => {
322                archive_class           => "datebased-daily-archive",
323                datebased_only_archive  => 1,
324                datebased_daily_archive => 1,
325                main_template           => 1,
326                archive_template        => 1,
327                archive_listing         => 1,
328            },
329        ),
330        'Category' => ArchiveType(
331            name                      => 'Category',
332            archive_label             => \&category_archive_label,
333            archive_file              => \&category_archive_file,
334            archive_title             => \&category_archive_title,
335            archive_group_iter        => \&category_group_iter,
336            archive_group_entries     => \&category_group_entries,
337            archive_entries_count     => \&category_entries_count,
338            dynamic_template          => 'category/<$MTCategoryID$>',
339            default_archive_templates => [
340                ArchiveFileTemplate(
341                    label => MT->translate('category/sub-category/index.html'),
342                    template => '%-c/%i',
343                    default  => 1
344                ),
345                ArchiveFileTemplate(
346                    label => MT->translate('category/sub_category/index.html'),
347                    template => '%c/%i'
348                ),
349            ],
350            dynamic_support => 1,
351            category_based  => 1,
352            template_params => {
353                archive_class                      => "category-archive",
354                category_archive                   => 1,
355                main_template                      => 1,
356                archive_template                   => 1,
357                archive_listing                    => 1,
358                'module_category-monthly_archives' => 1,
359            },
360        ),
361        'Author' => ArchiveType(
362            name                      => 'Author',
363            archive_label             => \&author_archive_label,
364            archive_file              => \&author_archive_file,
365            archive_title             => \&author_archive_title,
366            archive_group_iter        => \&author_group_iter,
367            archive_group_entries     => \&author_group_entries,
368            archive_entries_count     => \&author_entries_count,
369            default_archive_templates => [
370                ArchiveFileTemplate(
371                    label    => 'author-display-name/index.html',
372                    template => '%-a/%f',
373                    default  => 1
374                ),
375                ArchiveFileTemplate(
376                    label    => 'author_display_name/index.html',
377                    template => '%a/%f'
378                ),
379            ],
380            dynamic_template => 'author/<$MTEntryAuthorID$>/<$MTEntryID$>',
381            dynamic_support  => 1,
382            author_based     => 1,
383            template_params  => {
384                archive_class                    => "author-archive",
385                'module_author-monthly_archives' => 1,
386                main_template                    => 1,
387                author_archive                   => 1,
388                archive_template                 => 1,
389                archive_listing                  => 1,
390            },
391        ),
392        'Author-Yearly' => ArchiveType(
393            name                      => 'Author-Yearly',
394            archive_label             => \&author_yearly_archive_label,
395            archive_file              => \&author_yearly_archive_file,
396            archive_title             => \&author_yearly_archive_title,
397            archive_group_iter        => \&author_yearly_group_iter,
398            archive_group_entries     => \&author_yearly_group_entries,
399            archive_entries_count     => \&author_yearly_entries_count,
400            date_range                => \&author_yearly_date_range,
401            default_archive_templates => [
402                ArchiveFileTemplate(
403                    label    => 'author-display-name/yyyy/index.html',
404                    template => '%-a/%y/%f',
405                    default  => 1
406                ),
407                ArchiveFileTemplate(
408                    label    => 'author_display_name/yyyy/index.html',
409                    template => '%a/%y/%f'
410                ),
411            ],
412            dynamic_template =>
413              'author/<$MTEntryAuthorID$>/<$MTArchiveDate format="%Y"$>',
414            dynamic_support => 1,
415            author_based    => 1,
416            date_based      => 1,
417            template_params => {
418                archive_class         => "author-yearly-archive",
419                author_yearly_archive => 1,
420                main_template         => 1,
421                archive_template      => 1,
422                archive_listing       => 1,
423            },
424        ),
425        'Author-Monthly' => ArchiveType(
426            name                      => 'Author-Monthly',
427            archive_label             => \&author_monthly_archive_label,
428            archive_file              => \&author_monthly_archive_file,
429            archive_title             => \&author_monthly_archive_title,
430            archive_group_iter        => \&author_monthly_group_iter,
431            archive_group_entries     => \&author_monthly_group_entries,
432            archive_entries_count     => \&author_monthly_entries_count,
433            date_range                => \&author_monthly_date_range,
434            default_archive_templates => [
435                ArchiveFileTemplate(
436                    label    => 'author-display-name/yyyy/mm/index.html',
437                    template => '%-a/%y/%m/%f',
438                    default  => 1
439                ),
440                ArchiveFileTemplate(
441                    label    => 'author_display_name/yyyy/mm/index.html',
442                    template => '%a/%y/%m/%f'
443                ),
444            ],
445            dynamic_template =>
446              'author/<$MTEntryAuthorID$>/<$MTArchiveDate format="%Y%m"$>',
447            dynamic_support => 1,
448            author_based    => 1,
449            date_based      => 1,
450            template_params => {
451                archive_class                    => "author-monthly-archive",
452                author_monthly_archive           => 1,
453                'module_author-monthly_archives' => 1,
454                main_template                    => 1,
455                archive_template                 => 1,
456                archive_listing                  => 1,
457            },
458        ),
459        'Author-Weekly' => ArchiveType(
460            name                      => 'Author-Weekly',
461            archive_label             => \&author_weekly_archive_label,
462            archive_file              => \&author_weekly_archive_file,
463            archive_title             => \&author_weekly_archive_title,
464            archive_group_iter        => \&author_weekly_group_iter,
465            archive_group_entries     => \&author_weekly_group_entries,
466            archive_entries_count     => \&author_weekly_entries_count,
467            date_range                => \&author_weekly_date_range,
468            default_archive_templates => [
469                ArchiveFileTemplate(
470                    label => 'author-display-name/yyyy/mm/day-week/index.html',
471                    template => '%-a/%y/%m/%d-week/%f',
472                    default  => 1
473                ),
474                ArchiveFileTemplate(
475                    label => 'author_display_name/yyyy/mm/day-week/index.html',
476                    template => '%a/%y/%m/%d-week/%f'
477                ),
478            ],
479            dynamic_template =>
480'author/<$MTEntryAuthorID$>/week/<$MTArchiveDate format="%Y%m%d"$>',
481            dynamic_support => 1,
482            author_based    => 1,
483            date_based      => 1,
484            template_params => {
485                archive_class         => "author-weekly-archive",
486                author_weekly_archive => 1,
487                main_template         => 1,
488                archive_template      => 1,
489                archive_listing       => 1,
490            },
491        ),
492        'Author-Daily' => ArchiveType(
493            name                      => 'Author-Daily',
494            archive_label             => \&author_daily_archive_label,
495            archive_file              => \&author_daily_archive_file,
496            archive_title             => \&author_daily_archive_title,
497            archive_group_iter        => \&author_daily_group_iter,
498            archive_group_entries     => \&author_daily_group_entries,
499            archive_entries_count     => \&author_daily_entries_count,
500            date_range                => \&author_daily_date_range,
501            default_archive_templates => [
502                ArchiveFileTemplate(
503                    label    => 'author-display-name/yyyy/mm/dd/index.html',
504                    template => '%-a/%y/%m/%d/%f',
505                    default  => 1
506                ),
507                ArchiveFileTemplate(
508                    label    => 'author_display_name/yyyy/mm/dd/index.html',
509                    template => '%a/%y/%m/%d/%f'
510                ),
511            ],
512            dynamic_template =>
513              'author/<$MTEntryAuthorID$>/<$MTArchiveDate format="%Y%m%d"$>',
514            dynamic_support => 1,
515            author_based    => 1,
516            date_based      => 1,
517            template_params => {
518                archive_class        => "author-daily-archive",
519                author_daily_archive => 1,
520                main_template        => 1,
521                archive_template     => 1,
522                archive_listing      => 1,
523            },
524        ),
525        'Category-Yearly' => ArchiveType(
526            name                      => 'Category-Yearly',
527            archive_label             => \&cat_yearly_archive_label,
528            archive_file              => \&cat_yearly_archive_file,
529            archive_title             => \&cat_yearly_archive_title,
530            date_range                => \&cat_yearly_date_range,
531            archive_group_iter        => \&cat_yearly_group_iter,
532            archive_group_entries     => \&cat_yearly_group_entries,
533            archive_entries_count     => \&cat_yearly_entries_count,
534            default_archive_templates => [
535                ArchiveFileTemplate(
536                    label    => 'category/sub-category/yyyy/index.html',
537                    template => '%-c/%y/%i',
538                    default  => 1
539                ),
540                ArchiveFileTemplate(
541                    label    => 'category/sub_category/yyyy/index.html',
542                    template => '%c/%y/%i'
543                ),
544            ],
545            dynamic_template =>
546              'category/<$MTCategoryID$>/<$MTArchiveDate format="%Y"$>',
547            dynamic_support => 1,
548            date_based      => 1,
549            category_based  => 1,
550            template_params => {
551                archive_class           => "category-yearly-archive",
552                category_yearly_archive => 1,
553                main_template           => 1,
554                archive_template        => 1,
555                archive_listing         => 1,
556            },
557        ),
558        'Category-Monthly' => ArchiveType(
559            name                      => 'Category-Monthly',
560            archive_label             => \&cat_monthly_archive_label,
561            archive_file              => \&cat_monthly_archive_file,
562            archive_title             => \&cat_monthly_archive_title,
563            date_range                => \&cat_monthly_date_range,
564            archive_group_iter        => \&cat_monthly_group_iter,
565            archive_group_entries     => \&cat_monthly_group_entries,
566            archive_entries_count     => \&cat_monthly_entries_count,
567            default_archive_templates => [
568                ArchiveFileTemplate(
569                    label    => 'category/sub-category/yyyy/mm/index.html',
570                    template => '%-c/%y/%m/%i',
571                    default  => 1
572                ),
573                ArchiveFileTemplate(
574                    label    => 'category/sub_category/yyyy/mm/index.html',
575                    template => '%c/%y/%m/%i'
576                ),
577            ],
578            dynamic_template =>
579              'category/<$MTCategoryID$>/<$MTArchiveDate format="%Y%m"$>',
580            dynamic_support => 1,
581            date_based      => 1,
582            category_based  => 1,
583            template_params => {
584                archive_class            => "category-monthly-archive",
585                category_monthly_archive => 1,
586                'module_category-monthly_archives' => 1,
587                main_template                      => 1,
588                archive_template                   => 1,
589                archive_listing                    => 1,
590            },
591        ),
592        'Category-Daily' => ArchiveType(
593            name                      => 'Category-Daily',
594            archive_label             => \&cat_daily_archive_label,
595            archive_file              => \&cat_daily_archive_file,
596            archive_title             => \&cat_daily_archive_title,
597            date_range                => \&cat_daily_date_range,
598            archive_group_iter        => \&cat_daily_group_iter,
599            archive_group_entries     => \&cat_daily_group_entries,
600            archive_entries_count     => \&cat_daily_entries_count,
601            default_archive_templates => [
602                ArchiveFileTemplate(
603                    label    => 'category/sub-category/yyyy/mm/dd/index.html',
604                    template => '%-c/%y/%m/%d/%i',
605                    default  => 1
606                ),
607                ArchiveFileTemplate(
608                    label    => 'category/sub_category/yyyy/mm/dd/index.html',
609                    template => '%c/%y/%m/%d/%i'
610                ),
611            ],
612            dynamic_template =>
613              'category/<$MTCategoryID$>/<$MTArchiveDate format="%Y%m%d"$>',
614            dynamic_support => 1,
615            date_based      => 1,
616            category_based  => 1,
617            template_params => {
618                archive_class          => "category-daily-archive",
619                category_daily_archive => 1,
620                main_template          => 1,
621                archive_template       => 1,
622                archive_listing        => 1,
623            },
624        ),
625        'Category-Weekly' => ArchiveType(
626            name                      => 'Category-Weekly',
627            archive_label             => \&cat_weekly_archive_label,
628            archive_file              => \&cat_weekly_archive_file,
629            archive_title             => \&cat_weekly_archive_title,
630            date_range                => \&cat_weekly_date_range,
631            archive_group_iter        => \&cat_weekly_group_iter,
632            archive_group_entries     => \&cat_weekly_group_entries,
633            archive_entries_count     => \&cat_weekly_entries_count,
634            default_archive_templates => [
635                ArchiveFileTemplate(
636                    label =>
637                      'category/sub-category/yyyy/mm/day-week/index.html',
638                    template => '%-c/%y/%m/%d-week/%i',
639                    default  => 1
640                ),
641                ArchiveFileTemplate(
642                    label =>
643                      'category/sub_category/yyyy/mm/day-week/index.html',
644                    template => '%c/%y/%m/%d-week/%i'
645                ),
646            ],
647            dynamic_template =>
648              'section/<$MTCategoryID$>/week/<$MTArchiveDate format="%Y%m%d"$>',
649            dynamic_support => 1,
650            date_based      => 1,
651            category_based  => 1,
652            template_params => {
653                archive_class           => "category-weekly-archive",
654                category_weekly_archive => 1,
655                main_template           => 1,
656                archive_template        => 1,
657                archive_listing         => 1,
658            },
659        )
660    };
661
662}
663
664sub rebuild {
665    my $mt    = shift;
666    my %param = @_;
667    my $blog;
668    unless ( $blog = $param{Blog} ) {
669        my $blog_id = $param{BlogID};
670        $blog = MT::Blog->load($blog_id)
671          or return $mt->error(
672            MT->translate(
673                "Load of blog '[_1]' failed: [_2]", $blog_id,
674                MT::Blog->errstr
675            )
676          );
677    }
678    return 1 if $blog->is_dynamic;
679    my $at = $blog->archive_type || '';
680    my @at = split /,/, $at;
681    my $entry_class;
682    if ( my $set_at = $param{ArchiveType} ) {
683        my %at = map { $_ => 1 } @at;
684        return $mt->error(
685            MT->translate(
686                "Archive type '[_1]' is not a chosen archive type", $set_at
687            )
688        ) unless $at{$set_at};
689
690        @at = ($set_at);
691        my $archiver = $mt->archiver($set_at);
692        $entry_class = $archiver->{entry_class} || "entry";
693    }
694    else {
695        $entry_class = '*';
696    }
697
698    if (   $param{ArchiveType}
699        && ( !$param{Entry} )
700        && ( $param{ArchiveType} eq 'Category' ) )
701    {
702
703        # Pass to full category rebuild
704        return $mt->rebuild_categories(%param);
705    }
706
707    if (   $param{ArchiveType}
708        && ( !$param{Author} )
709        && ( $param{ArchiveType} eq 'Author' ) )
710    {
711        return $mt->rebuild_authors(%param);
712    }
713
714    if (@at) {
715        require MT::Entry;
716        my %arg = ( 'sort' => 'authored_on', direction => 'descend' );
717        $arg{offset} = $param{Offset} if $param{Offset};
718        $arg{limit}  = $param{Limit}  if $param{Limit};
719        my $pre_iter = MT::Entry->load_iter(
720            {
721                blog_id => $blog->id,
722                class   => $entry_class,
723                status  => MT::Entry::RELEASE()
724            },
725            \%arg
726        );
727        my ( $next, $curr );
728        my $prev = $pre_iter->();
729        my $iter = sub {
730            ( $next, $curr ) = ( $curr, $prev );
731            if ($curr) {
732                $prev = $pre_iter->();
733            }
734            $curr;
735        };
736        my $cb  = $param{EntryCallback};
737        my $fcb = $param{FilterCallback};
738        while ( my $entry = $iter->() ) {
739            if ($cb) {
740                $cb->($entry) || $mt->log( $cb->errstr() );
741            }
742            if ($fcb) {
743                $fcb->($entry) or last;
744            }
745            for my $at (@at) {
746                my $archiver = $mt->archiver($at);
747
748                # Skip this archive type if the archive type doesn't
749                # match the kind of entry we've loaded
750                next unless $archiver;
751                next if $entry->class ne $archiver->entry_class;
752                if ( $archiver->category_based ) {
753                    my $cats = $entry->categories;
754                    CATEGORY: for my $cat (@$cats) {
755                        next CATEGORY if $archiver->category_class ne $cat->class_type;
756                        $mt->_rebuild_entry_archive_type(
757                            Entry       => $entry,
758                            Blog        => $blog,
759                            Category    => $cat,
760                            ArchiveType => $at,
761                            NoStatic    => $param{NoStatic},
762                            $param{TemplateMap}
763                            ? ( TemplateMap => $param{TemplateMap} )
764                            : (),
765                            $param{TemplateID}
766                            ? ( TemplateID =>
767                                  $param{TemplateID} )
768                            : (),
769                        ) or return;
770                    }
771                }
772                elsif ( $archiver->author_based ) {
773                    if ( $entry->author ) {
774                        $mt->_rebuild_entry_archive_type(
775                            Entry       => $entry,
776                            Blog        => $blog,
777                            ArchiveType => $at,
778                            $param{TemplateMap}
779                            ? ( TemplateMap => $param{TemplateMap} )
780                            : (),
781                            $param{TemplateID}
782                            ? ( TemplateID =>
783                                  $param{TemplateID} )
784                            : (),
785                            NoStatic => $param{NoStatic},
786                            Author   => $entry->author
787                        ) or return;
788                    }
789                }
790                else {
791                    $mt->_rebuild_entry_archive_type(
792                        Entry       => $entry,
793                        Blog        => $blog,
794                        ArchiveType => $at,
795                        $param{TemplateMap}
796                        ? ( TemplateMap => $param{TemplateMap} )
797                        : (),
798                        $param{TemplateID}
799                        ? ( TemplateID =>
800                              $param{TemplateID} )
801                        : (),
802                        NoStatic => $param{NoStatic}
803                    ) or return;
804                }
805            }
806        }
807    }
808    unless ( $param{NoIndexes} ) {
809        $mt->rebuild_indexes( Blog => $blog ) or return;
810    }
811    1;
812}
813
814sub rebuild_categories {
815    my $mt    = shift;
816    my %param = @_;
817    my $blog;
818    unless ( $blog = $param{Blog} ) {
819        my $blog_id = $param{BlogID};
820        $blog = MT::Blog->load($blog_id)
821          or return $mt->error(
822            MT->translate(
823                "Load of blog '[_1]' failed: [_2]", $blog_id,
824                MT::Blog->errstr
825            )
826          );
827    }
828    my %arg;
829    $arg{'sort'} = 'id';
830    $arg{direction} = 'ascend';
831    $arg{offset} = $param{Offset} if $param{Offset};
832    $arg{limit}  = $param{Limit}  if $param{Limit};
833    my $cat_iter = MT::Category->load_iter( { blog_id => $blog->id }, \%arg );
834    my $fcb = $param{FilterCallback};
835
836    while ( my $cat = $cat_iter->() ) {
837        if ($fcb) {
838            $fcb->($cat) or last;
839        }
840        $mt->_rebuild_entry_archive_type(
841            Blog        => $blog,
842            Category    => $cat,
843            ArchiveType => 'Category',
844            $param{TemplateMap}
845            ? ( TemplateMap => $param{TemplateMap} )
846            : (),
847            NoStatic => $param{NoStatic},
848        ) or return;
849    }
850    1;
851}
852
853sub rebuild_authors {
854    my $mt    = shift;
855    my %param = @_;
856    my $blog;
857    unless ( $blog = $param{Blog} ) {
858        my $blog_id = $param{BlogID};
859        $blog = MT::Blog->load($blog_id)
860          or return $mt->error(
861            MT->translate(
862                "Load of blog '[_1]' failed: [_2]", $blog_id,
863                MT::Blog->errstr
864            )
865          );
866    }
867    my %arg;
868    $arg{'sort'} = 'id';
869    $arg{direction} = 'ascend';
870    $arg{offset} = $param{Offset} if $param{Offset};
871    $arg{limit}  = $param{Limit}  if $param{Limit};
872    require MT::Entry;
873    local $arg{join} = MT::Entry->join_on(
874        'author_id',
875        { blog_id => $blog->id, class => 'entry' },
876        { unique  => 1 }
877    );
878    local $arg{unique} = 1;
879    local $arg{status} = MT::Entry::RELEASE();
880    my $auth_iter = MT::Author->load_iter( undef, \%arg );
881    my $fcb = $param{FilterCallback};
882
883    while ( my $a = $auth_iter->() ) {
884        if ($fcb) {
885            $fcb->($a) or last;
886        }
887        $mt->_rebuild_entry_archive_type(
888            Blog        => $blog,
889            Author      => $a,
890            ArchiveType => 'Author',
891            $param{TemplateMap}
892            ? ( TemplateMap => $param{TemplateMap} )
893            : (),
894            NoStatic => $param{NoStatic},
895        ) or return;
896    }
897    1;
898}
899
900#   rebuild_entry
901#
902# $mt->rebuild_entry(Entry => $entry_id,
903#                    Blog => [ $blog | $blog_id ],
904#                    [ BuildDependencies => (0 | 1), ]
905#                    [ OldPrevious => $old_previous_entry_id,
906#                      OldNext => $old_next_entry_id, ]
907#                    [ NoStatic => (0 | 1), ]
908#                    );
909sub rebuild_entry {
910    my $mt    = shift;
911    my %param = @_;
912    my $entry = $param{Entry}
913      or return $mt->error(
914        MT->translate( "Parameter '[_1]' is required", 'Entry' ) );
915    require MT::Entry;
916    $entry = MT::Entry->load($entry) unless ref $entry;
917    my $blog;
918    unless ( $blog = $param{Blog} ) {
919        my $blog_id = $entry->blog_id;
920        $blog = MT::Blog->load($blog_id)
921          or return $mt->error(
922            MT->translate(
923                "Load of blog '[_1]' failed: [_2]", $blog_id,
924                MT::Blog->errstr
925            )
926          );
927    }
928    return 1 if $blog->is_dynamic;
929
930    my $at = $blog->archive_type;
931    if ( $at && $at ne 'None' ) {
932        my @at = split /,/, $at;
933        for my $at (@at) {
934            my $archiver = $mt->archiver($at);
935            next unless $archiver;    # invalid archive type
936            next if $entry->class ne $archiver->entry_class;
937            if ( $archiver->category_based ) {
938                my $cats = $entry->categories;    # (ancestors => 1)
939                for my $cat (@$cats) {
940                    $mt->_rebuild_entry_archive_type(
941                        Entry       => $entry,
942                        Blog        => $blog,
943                        ArchiveType => $at,
944                        Category    => $cat,
945                        NoStatic    => $param{NoStatic},
946                        $param{TemplateMap}
947                        ? ( TemplateMap => $param{TemplateMap} )
948                        : (),
949                    ) or return;
950                }
951            }
952            else {
953                $mt->_rebuild_entry_archive_type(
954                    Entry       => $entry,
955                    Blog        => $blog,
956                    ArchiveType => $at,
957                    $param{TemplateMap}
958                    ? ( TemplateMap => $param{TemplateMap} )
959                    : (),
960                    NoStatic => $param{NoStatic},
961                    Author   => $entry->author,
962                ) or return;
963            }
964        }
965    }
966
967    ## The above will just rebuild the archive pages for this particular
968    ## entry. If we want to rebuild all of the entries/archives/indexes
969    ## on which this entry could be featured etc., however, we need to
970    ## rebuild all of the entry's dependencies. Note that all of these
971    ## are not *necessarily* dependencies, depending on the usage of tags,
972    ## etc. There is not a good way to determine exact dependencies; it is
973    ## easier to just rebuild, rebuild, rebuild.
974
975    return 1
976      unless $param{BuildDependencies}
977      || $param{BuildIndexes}
978      || $param{BuildArchives};
979
980    if ( $param{BuildDependencies} ) {
981        ## Rebuild previous and next entry archive pages.
982        if ( my $prev = $entry->previous(1) ) {
983            $mt->rebuild_entry( Entry => $prev ) or return;
984
985            ## Rebuild the old previous and next entries, if we have some.
986            if ( $param{OldPrevious}
987                && ( my $old_prev = MT::Entry->load( $param{OldPrevious} ) ) )
988            {
989                $mt->rebuild_entry( Entry => $old_prev ) or return;
990            }
991        }
992        if ( my $next = $entry->next(1) ) {
993            $mt->rebuild_entry( Entry => $next ) or return;
994
995            if ( $param{OldNext}
996                && ( my $old_next = MT::Entry->load( $param{OldNext} ) ) )
997            {
998                $mt->rebuild_entry( Entry => $old_next ) or return;
999            }
1000        }
1001    }
1002
1003    if ( $param{BuildDependencies} || $param{BuildIndexes} ) {
1004        ## Rebuild all indexes, in case this entry is on an index.
1005        if ( !( exists $param{BuildIndexes} ) || $param{BuildIndexes} ) {
1006            $mt->rebuild_indexes( Blog => $blog ) or return;
1007        }
1008    }
1009
1010    if ( $param{BuildDependencies} || $param{BuildArchives} ) {
1011        ## Rebuild previous and next daily, weekly, and monthly archives;
1012        ## adding a new entry could cause changes to the intra-archive
1013        ## navigation.
1014        my %at = map { $_ => 1 } split /,/, $blog->archive_type;
1015        my @db_at = grep { $ArchiveTypes{$_} && $ArchiveTypes{$_}->date_based } $mt->archive_types;
1016        for my $at (@db_at) {
1017            if ( $at{$at} ) {
1018                my @arg = ( $entry->authored_on, $entry->blog_id, $at );
1019                my $archiver = $mt->archiver($at);
1020                if ( my $prev_arch = $mt->get_entry( @arg, 'previous' ) ) {
1021                    if ( $archiver->category_based ) {
1022                        my $cats = $prev_arch->categories;
1023                        for my $cat (@$cats) {
1024                            $mt->_rebuild_entry_archive_type(
1025                                NoStatic => $param{NoStatic},
1026                                Entry    => $prev_arch,
1027                                Blog     => $blog,
1028                                Category => $cat,
1029                                $param{TemplateMap}
1030                                ? ( TemplateMap => $param{TemplateMap} )
1031                                : (),
1032                                ArchiveType => $at
1033                            ) or return;
1034                        }
1035                    }
1036                    else {
1037                        $mt->_rebuild_entry_archive_type(
1038                            NoStatic    => $param{NoStatic},
1039                            Entry       => $prev_arch,
1040                            Blog        => $blog,
1041                            ArchiveType => $at,
1042                            $param{TemplateMap}
1043                            ? ( TemplateMap => $param{TemplateMap} )
1044                            : (),
1045                            Author => $prev_arch->author
1046                        ) or return;
1047                    }
1048                }
1049                if ( my $next_arch = $mt->get_entry( @arg, 'next' ) ) {
1050                    if ( $archiver->category_based ) {
1051                        my $cats = $next_arch->categories;
1052                        for my $cat (@$cats) {
1053                            $mt->_rebuild_entry_archive_type(
1054                                NoStatic => $param{NoStatic},
1055                                Entry    => $next_arch,
1056                                Blog     => $blog,
1057                                Category => $cat,
1058                                $param{TemplateMap}
1059                                ? ( TemplateMap => $param{TemplateMap} )
1060                                : (),
1061                                ArchiveType => $at
1062                            ) or return;
1063                        }
1064                    }
1065                    else {
1066                        $mt->_rebuild_entry_archive_type(
1067                            NoStatic    => $param{NoStatic},
1068                            Entry       => $next_arch,
1069                            Blog        => $blog,
1070                            ArchiveType => $at,
1071                            $param{TemplateMap}
1072                            ? ( TemplateMap => $param{TemplateMap} )
1073                            : (),
1074                            Author => $next_arch->author
1075                        ) or return;
1076                    }
1077                }
1078            }
1079        }
1080    }
1081
1082    1;
1083}
1084
1085sub _rebuild_entry_archive_type {
1086    my $mt    = shift;
1087    my %param = @_;
1088    my $at    = $param{ArchiveType}
1089      or return $mt->error(
1090        MT->translate( "Parameter '[_1]' is required", 'ArchiveType' ) );
1091    return 1 if $at eq 'None';
1092    my $entry =
1093      ( $param{ArchiveType} ne 'Category' && $param{ArchiveType} ne 'Author' )
1094      ? (
1095        $param{Entry}
1096          or return $mt->error(
1097            MT->translate( "Parameter '[_1]' is required", 'Entry' )
1098          )
1099      )
1100      : undef;
1101
1102    my $blog;
1103    unless ( $blog = $param{Blog} ) {
1104        my $blog_id = $entry->blog_id;
1105        $blog = MT::Blog->load($blog_id)
1106          or return $mt->error(
1107            MT->translate(
1108                "Load of blog '[_1]' failed: [_2]", $blog_id,
1109                MT::Blog->errstr
1110            )
1111          );
1112    }
1113
1114    ## Load the template-archive-type map entries for this blog and
1115    ## archive type. We do this before we load the list of entries, because
1116    ## we will run through the files and check if we even need to rebuild
1117    ## anything. If there is nothing to rebuild at all for this entry,
1118    ## we save some time by not loading the list of entries.
1119    require MT::TemplateMap;
1120    my @map;
1121    if ( $param{TemplateMap} ) {
1122        @map = ( $param{TemplateMap} );
1123    }
1124    else {
1125        my $cached_maps = MT->instance->request('__cached_maps')
1126          || MT->instance->request( '__cached_maps', {} );
1127        if ( my $maps = $cached_maps->{ $at . $blog->id } ) {
1128            @map = @$maps;
1129        }
1130        else {
1131            @map = MT::TemplateMap->load(
1132                {
1133                    archive_type => $at,
1134                    blog_id      => $blog->id,
1135                    $param{TemplateID}
1136                    ? ( template_id => $param{TemplateID} )
1137                    : ()
1138                }
1139            );
1140            $cached_maps->{ $at . $blog->id } = \@map;
1141        }
1142    }
1143    return 1 unless @map;
1144    my @map_build;
1145
1146    my $done = MT->instance->request('__published')
1147      || MT->instance->request( '__published', {} );
1148    for my $map (@map) {
1149        my $file =
1150          $mt->archive_file_for( $entry, $blog, $at, $param{Category}, $map,
1151            undef, $param{Author} );
1152        if ( $file eq '' ) {
1153
1154            # np
1155        }
1156        elsif ( !defined($file) ) {
1157            return $mt->error( MT->translate( $blog->errstr() ) );
1158        }
1159        else {
1160            push @map_build, $map unless $done->{$file};
1161            $map->{__saved_output_file} = $file;
1162        }
1163    }
1164    return 1 unless @map_build;
1165    @map = @map_build;
1166
1167    my (%cond);
1168    require MT::Template::Context;
1169    my $ctx = MT::Template::Context->new;
1170    $ctx->{current_archive_type} = $at;
1171    $ctx->{archive_type}         = $at;
1172    $at ||= "";
1173
1174    my $archiver = $mt->archiver($at);
1175    return unless $archiver;
1176
1177    my $fmgr = $blog->file_mgr;
1178
1179    # Special handling for pages-- they are always published to the
1180    # 'site' path instead of the 'archive' path, which is reserved for blog
1181    # content.
1182    my $arch_root = ( $at eq 'Page' ) ? $blog->site_path : $blog->archive_path;
1183    return $mt->error(
1184        MT->translate("You did not set your blog publishing path") )
1185      unless $arch_root;
1186
1187    my $range = $archiver->date_range;
1188    my ( $start, $end ) = $range ? $range->( $entry->authored_on ) : ();
1189
1190    ## For each mapping, we need to rebuild the entries we loaded above in
1191    ## the particular template map, and write it to the specified archive
1192    ## file template.
1193    require MT::Template;
1194    for my $map (@map) {
1195        $mt->rebuild_file(
1196            $blog, $arch_root, $map, $at, $ctx, \%cond,
1197            !$param{NoStatic},
1198            Category  => $param{Category},
1199            Entry     => $entry,
1200            Author    => $param{Author},
1201            StartDate => $start,
1202            EndDate   => $end,
1203        ) or return;
1204        $done->{ $map->{__saved_output_file} }++;
1205    }
1206    1;
1207}
1208
1209sub rebuild_file {
1210    my $mt = shift;
1211    my ( $blog, $root_path, $map, $at, $ctx, $cond, $build_static, %specifier )
1212      = @_;
1213
1214    my $finfo;
1215    my $archiver = $mt->archiver($at);
1216    my ( $entry, $start, $end, $category, $author );
1217
1218    if ( $finfo = $specifier{FileInfo} ) {
1219        $specifier{Author}   = $finfo->author_id   if $finfo->author_id;
1220        $specifier{Category} = $finfo->category_id if $finfo->category_id;
1221        $specifier{Entry}    = $finfo->entry_id    if $finfo->entry_id;
1222        $map ||= MT::TemplateMap->load( $finfo->templatemap_id );
1223        $at  ||= $finfo->archive_type;
1224        if ( $finfo->startdate ) {
1225            if ( my $range = $archiver->date_range ) {
1226                my ( $start, $end ) =
1227                  $range ? $range->( $finfo->startdate ) : ();
1228                $specifier{StartDate} = $start;
1229                $specifier{EndDate}   = $end;
1230            }
1231        }
1232    }
1233
1234    if ( $archiver->category_based ) {
1235        $category = $specifier{Category};
1236        die "Category archive type requires Category parameter"
1237          unless $specifier{Category};
1238        $category = MT::Category->load($category)
1239          unless ref $category;
1240        $ctx->var( 'category_archive', 1 );
1241        $ctx->{__stash}{archive_category} = $category;
1242    }
1243    if ( $archiver->entry_based ) {
1244        $entry = $specifier{Entry};
1245        die "$at archive type requires Entry parameter"
1246          unless $entry;
1247        require MT::Entry;
1248        $entry = MT::Entry->load($entry) if !ref $entry;
1249        $ctx->var( 'entry_archive', 1 );
1250        $ctx->{__stash}{entry} = $entry;
1251    }
1252    if ( $archiver->date_based ) {
1253
1254        # Date-based archive type
1255        $start = $specifier{StartDate};
1256        $end   = $specifier{EndDate};
1257        die "Date-based archive types require StartDate parameter"
1258          unless $specifier{StartDate};
1259        $ctx->var( 'datebased_archive', 1 );
1260    }
1261    if ( $archiver->author_based ) {
1262
1263        # author based archive type
1264        $author = $specifier{Author};
1265        die "Author-based archive type requires Author parameter"
1266          unless $specifier{Author};
1267        require MT::Author;
1268        $author = MT::Author->load($author)
1269          unless ref $author;
1270        $ctx->var( 'author_archive', 1 );
1271        $ctx->{__stash}{author} = $author;
1272    }
1273    local $ctx->{current_timestamp}     = $start if $start;
1274    local $ctx->{current_timestamp_end} = $end   if $end;
1275
1276    my $fmgr = $blog->file_mgr;
1277    $ctx->{__stash}{blog} = $blog;
1278
1279    # Calculate file path and URL for the new entry.
1280    my $file = File::Spec->catfile( $root_path, $map->{__saved_output_file} );
1281    require MT::FileInfo;
1282
1283# This kind of testing should be done at the time we save a post,
1284# not during publishing!!!
1285# if ($archiver->entry_based) {
1286#     my $fcount = MT::FileInfo->count({
1287#         blog_id => $blog->id,
1288#         entry_id => $entry->id,
1289#         file_path => $file},
1290#         { not => { entry_id => 1 } });
1291#     die MT->translate('The same archive file exists. You should change the basename or the archive path. ([_1])', $file) if $fcount > 0;
1292# }
1293
1294    my $url = $blog->archive_url;
1295    $url = $blog->site_url
1296      if $archiver->entry_based && $archiver->entry_class eq 'page';
1297    $url .= '/' unless $url =~ m|/$|;
1298    $url .= $map->{__saved_output_file};
1299
1300    my $cached_tmpl = MT->instance->request('__cached_templates')
1301      || MT->instance->request( '__cached_templates', {} );
1302    my $tmpl_id = $map->template_id;
1303
1304    # template specific for this entry (or page, as the case may be)
1305    if ( $entry && $entry->template_id ) {
1306
1307        # allow entry to override *if* we're publishing an individual
1308        # page, and this is the 'preferred' one...
1309        if ( $archiver->entry_based ) {
1310            if ( $map->is_preferred ) {
1311                $tmpl_id = $entry->template_id;
1312            }
1313        }
1314    }
1315
1316    my $tmpl = $cached_tmpl->{$tmpl_id};
1317    unless ($tmpl) {
1318        $tmpl = MT::Template->load($tmpl_id);
1319        if ($cached_tmpl) {
1320            $cached_tmpl->{$tmpl_id} = $tmpl;
1321        }
1322    }
1323
1324    $tmpl->context($ctx);
1325
1326    # From Here
1327    if ( my $tmpl_param = $archiver->template_params ) {
1328        $tmpl->param($tmpl_param);
1329    }
1330
1331    my ($rel_url) = ( $url =~ m|^(?:[^:]*\:\/\/)?[^/]*(.*)| );
1332    $rel_url =~ s|//+|/|g;
1333
1334    ## Untaint. We have to assume that we can trust the user's setting of
1335    ## the archive_path, and nothing else is based on user input.
1336    ($file) = $file =~ /(.+)/s;
1337
1338    # Clear out all the FileInfo records that might point at the page
1339    # we're about to create
1340    # FYI: if it's an individual entry, we don't use the date as a
1341    #      criterion, since this could actually have changed since
1342    #      the FileInfo was last built. When the date does change,
1343    #      the old date-based archive doesn't necessarily get fixed,
1344    #      but if another comes along it will get corrected
1345    unless ($finfo) {
1346        my %terms;
1347        $terms{blog_id}     = $blog->id;
1348        $terms{category_id} = $category->id if $archiver->category_based;
1349        $terms{author_id}   = $author->id if $archiver->author_based;
1350        $terms{entry_id}    = $entry->id if $archiver->entry_based;
1351        $terms{startdate}   = $start
1352          if $archiver->date_based && ( !$archiver->entry_based );
1353        $terms{archive_type}   = $at;
1354        $terms{templatemap_id} = $map->id;
1355        my @finfos = MT::FileInfo->load( \%terms );
1356
1357        if (   ( scalar @finfos == 1 )
1358            && ( $finfos[0]->file_path eq $file )
1359            && ( ( $finfos[0]->url || '' ) eq $rel_url )
1360            && ( $finfos[0]->template_id == $tmpl_id ) )
1361        {
1362
1363            # if the shoe fits, wear it
1364            $finfo = $finfos[0];
1365        }
1366        else {
1367
1368           # if the shoe don't fit, remove all shoes and create the perfect shoe
1369            foreach (@finfos) { $_->remove(); }
1370
1371            $finfo = MT::FileInfo->set_info_for_url(
1372                $rel_url, $file, $at,
1373                {
1374                    Blog        => $blog->id,
1375                    TemplateMap => $map->id,
1376                    Template    => $tmpl_id,
1377                    ( $archiver->entry_based && $entry )
1378                    ? ( Entry => $entry->id )
1379                    : (),
1380                    StartDate => $start,
1381                    ( $archiver->category_based && $category )
1382                    ? ( Category => $category->id )
1383                    : (),
1384                    ( $archiver->author_based )
1385                    ? ( Author => $author->id )
1386                    : (),
1387                }
1388              )
1389              || die "Couldn't create FileInfo because "
1390              . MT::FileInfo->errstr();
1391        }
1392    }
1393
1394    # If you rebuild when you've just switched to dynamic pages,
1395    # we move the file that might be there so that the custom
1396    # 404 will be triggered.
1397    if ( $tmpl->build_dynamic ) {
1398        rename(
1399            $finfo->file_path,    # is this just $file ?
1400            $finfo->file_path . '.static'
1401        );
1402
1403        ## If the FileInfo is set to static, flip it to virtual.
1404        if ( !$finfo->virtual ) {
1405            $finfo->virtual(1);
1406            $finfo->save();
1407        }
1408    }
1409
1410    return 1 if ( $tmpl->build_dynamic );
1411    return 1 if ( $entry && $entry->status != MT::Entry::RELEASE() );
1412
1413    my $timer = MT->get_timer;
1414    if ($timer) {
1415        $timer->pause_partial;
1416    }
1417    local $timer->{elapsed} = 0 if $timer;
1418
1419    if (
1420        $build_static
1421        && MT->run_callbacks(
1422            'build_file_filter',
1423            Context      => $ctx,
1424            context      => $ctx,
1425            ArchiveType  => $at,
1426            archive_type => $at,
1427            TemplateMap  => $map,
1428            template_map => $map,
1429            Blog         => $blog,
1430            blog         => $blog,
1431            Entry        => $entry,
1432            entry        => $entry,
1433            FileInfo     => $finfo,
1434            file_info    => $finfo,
1435            File         => $file,
1436            file         => $file,
1437            Template     => $tmpl,
1438            template     => $tmpl,
1439            PeriodStart  => $start,
1440            period_start => $start,
1441            Category     => $category,
1442            category     => $category,
1443        )
1444      )
1445    {
1446        if ( $archiver->archive_group_entries ) {
1447            require MT::Promise;
1448            my $entries = sub { $archiver->archive_group_entries->($ctx) };
1449            $ctx->stash( 'entries', MT::Promise::delay($entries) );
1450        }
1451
1452        my $html = undef;
1453        $ctx->stash( 'blog', $blog );
1454        $ctx->stash( 'entry', $entry ) if $entry;
1455
1456        require MT::Request;
1457        MT::Request->instance->cache('build_template', $tmpl);
1458
1459        $html = $tmpl->build( $ctx, $cond );
1460        unless (defined($html)) {
1461            $timer->unpause if $timer;
1462            return $mt->error(
1463            (
1464                $category ? MT->translate(
1465                    "An error occurred publishing [_1] '[_2]': [_3]",
1466                    lowercase( $category->class_label ),
1467                    $category->id,
1468                    $tmpl->errstr
1469                  )
1470                : $entry ? MT->translate(
1471                    "An error occurred publishing [_1] '[_2]': [_3]",
1472                    lowercase( $entry->class_label ),
1473                    $entry->title,
1474                    $tmpl->errstr
1475                  )
1476                : MT->translate(
1477"An error occurred publishing date-based archive '[_1]': [_2]",
1478                    $at . $start,
1479                    $tmpl->errstr
1480                )
1481            )
1482          );
1483        }
1484        my $orig_html = $html;
1485        MT->run_callbacks(
1486            'build_page',
1487            Context      => $ctx,
1488            context      => $ctx,
1489            ArchiveType  => $at,
1490            archive_type => $at,
1491            TemplateMap  => $map,
1492            template_map => $map,
1493            Blog         => $blog,
1494            blog         => $blog,
1495            Entry        => $entry,
1496            entry        => $entry,
1497            FileInfo     => $finfo,
1498            file_info    => $finfo,
1499            PeriodStart  => $start,
1500            period_start => $start,
1501            Category     => $category,
1502            category     => $category,
1503            RawContent   => \$orig_html,
1504            raw_content  => \$orig_html,
1505            Content      => \$html,
1506            content      => \$html,
1507            BuildResult  => \$orig_html,
1508            build_result => \$orig_html,
1509            Template     => $tmpl,
1510            template     => $tmpl,
1511            File         => $file,
1512            file         => $file
1513        );
1514        ## First check whether the content is actually
1515        ## changed. If not, we won't update the published
1516        ## file, so as not to modify the mtime.
1517        unless ($fmgr->content_is_updated( $file, \$html )) {
1518            $timer->unpause if $timer;
1519            return 1;
1520        }
1521
1522        ## Determine if we need to build directory structure,
1523        ## and build it if we do. DirUmask determines
1524        ## directory permissions.
1525        require File::Spec;
1526        my $path = dirname($file);
1527        $path =~ s!/$!!
1528          unless $path eq '/'; ## OS X doesn't like / at the end in mkdir().
1529        unless ( $fmgr->exists($path) ) {
1530            if (!$fmgr->mkpath($path)) {
1531                $timer->unpause if $timer;
1532                return $mt->trans_error( "Error making path '[_1]': [_2]",
1533                    $path, $fmgr->errstr );
1534            }
1535        }
1536
1537        ## By default we write all data to temp files, then rename
1538        ## the temp files to the real files (an atomic
1539        ## operation). Some users don't like this (requires too
1540        ## liberal directory permissions). So we have a config
1541        ## option to turn it off (NoTempFiles).
1542        my $use_temp_files = !$mt->{NoTempFiles};
1543        my $temp_file = $use_temp_files ? "$file.new" : $file;
1544        unless ( defined $fmgr->put_data( $html, $temp_file ) ) {
1545            $timer->unpause if $timer;
1546            return $mt->trans_error( "Writing to '[_1]' failed: [_2]",
1547                $temp_file, $fmgr->errstr );
1548        }
1549        if ($use_temp_files) {
1550            if (!$fmgr->rename( $temp_file, $file )) {
1551                $timer->unpause if $timer;
1552                return $mt->trans_error(
1553                    "Renaming tempfile '[_1]' failed: [_2]",
1554                    $temp_file, $fmgr->errstr );
1555            }
1556        }
1557        MT->run_callbacks(
1558            'build_file',
1559            Context      => $ctx,
1560            context      => $ctx,
1561            ArchiveType  => $at,
1562            archive_type => $at,
1563            TemplateMap  => $map,
1564            template_map => $map,
1565            FileInfo     => $finfo,
1566            file_info    => $finfo,
1567            Blog         => $blog,
1568            blog         => $blog,
1569            Entry        => $entry,
1570            entry        => $entry,
1571            PeriodStart  => $start,
1572            period_start => $start,
1573            RawContent   => \$orig_html,
1574            raw_content  => \$orig_html,
1575            Content      => \$html,
1576            content      => \$html,
1577            BuildResult  => \$orig_html,
1578            build_result => \$orig_html,
1579            Template     => $tmpl,
1580            template     => $tmpl,
1581            Category     => $category,
1582            category     => $category,
1583            File         => $file,
1584            file         => $file
1585        );
1586
1587    }
1588    $timer->mark("total:rebuild_file[template_id:" . $tmpl->id . "]")
1589        if $timer;
1590    1;
1591}
1592
1593sub rebuild_indexes {
1594    my $mt    = shift;
1595    my %param = @_;
1596    require MT::Template;
1597    require MT::Template::Context;
1598    require MT::Entry;
1599    my $blog;
1600    unless ( $blog = $param{Blog} ) {
1601        my $blog_id = $param{BlogID};
1602        $blog = MT::Blog->load($blog_id)
1603          or return $mt->error(
1604            MT->translate(
1605                "Load of blog '[_1]' failed: [_2]", $blog_id,
1606                MT::Blog->errstr
1607            )
1608          );
1609    }
1610    my $tmpl = $param{Template};
1611    unless ($blog) {
1612        $blog = MT::Blog->load( $tmpl->blog_id );
1613    }
1614    return 1 if $blog->is_dynamic;
1615    my $iter;
1616    if ($tmpl) {
1617        my $i = 0;
1618        $iter = sub { $i++ < 1 ? $tmpl : undef };
1619    }
1620    else {
1621        $iter = MT::Template->load_iter(
1622            {
1623                type    => 'index',
1624                blog_id => $blog->id
1625            }
1626        );
1627    }
1628    local *FH;
1629    my $site_root = $blog->site_path;
1630    return $mt->error(
1631        MT->translate("You did not set your blog publishing path") )
1632      unless $site_root;
1633    my $fmgr = $blog->file_mgr;
1634    while ( my $tmpl = $iter->() ) {
1635        ## Skip index templates that the user has designated not to be
1636        ## rebuilt automatically. We need to do the defined-ness check
1637        ## because we added the flag in 2.01, and for templates saved
1638        ## before that time, the rebuild_me flag will be undefined. But
1639        ## we assume that these templates should be rebuilt, since that
1640        ## was the previous behavior.
1641        ## Note that dynamic templates do need to be "rebuilt"--the
1642        ## FileInfo table needs to be maintained.
1643        if ( !$tmpl->build_dynamic && !$param{Force} ) {
1644            next if ( defined $tmpl->rebuild_me && !$tmpl->rebuild_me );
1645        }
1646        my $file = $tmpl->outfile;
1647        $file = '' unless defined $file;
1648        if ( $tmpl->build_dynamic && ( $file eq '' ) ) {
1649            next;
1650        }
1651        return $mt->error(
1652            MT->translate(
1653                "Template '[_1]' does not have an Output File.",
1654                $tmpl->name
1655            )
1656        ) unless $file ne '';
1657        my $url = join( '/', $blog->site_url, $file );
1658        unless ( File::Spec->file_name_is_absolute($file) ) {
1659            $file = File::Spec->catfile( $site_root, $file );
1660        }
1661
1662        # Everything from here out is identical with rebuild_file
1663        my ($rel_url) = ( $url =~ m|^(?:[^:]*\:\/\/)?[^/]*(.*)| );
1664        $rel_url =~ s|//+|/|g;
1665        ## Untaint. We have to assume that we can trust the user's setting of
1666        ## the site_path and the template outfile.
1667        ($file) = $file =~ /(.+)/s;
1668        my $finfo;
1669        require MT::FileInfo;
1670        my @finfos = MT::FileInfo->load(
1671            {
1672                blog_id     => $tmpl->blog_id,
1673                template_id => $tmpl->id
1674            }
1675        );
1676        if (   ( scalar @finfos == 1 )
1677            && ( $finfos[0]->file_path eq $file )
1678            && ( ( $finfos[0]->url || '' ) eq $rel_url ) )
1679        {
1680            $finfo = $finfos[0];
1681        }
1682        else {
1683            foreach (@finfos) { $_->remove(); }
1684            $finfo = MT::FileInfo->set_info_for_url(
1685                $rel_url, $file, 'index',
1686                {
1687                    Blog     => $tmpl->blog_id,
1688                    Template => $tmpl->id,
1689                }
1690              )
1691              || die "Couldn't create FileInfo because " . MT::FileInfo->errstr;
1692        }
1693        if ( $tmpl->build_dynamic ) {
1694            rename( $file, $file . ".static" );
1695
1696            ## If the FileInfo is set to static, flip it to virtual.
1697            if ( !$finfo->virtual ) {
1698                $finfo->virtual(1);
1699                $finfo->save();
1700            }
1701        }
1702
1703        next if ( $tmpl->build_dynamic );
1704
1705        ## We're not building dynamically, so if the FileInfo is currently
1706        ## set as dynamic (virtual), change it to static.
1707        if ( $finfo && $finfo->virtual ) {
1708            $finfo->virtual(0);
1709            $finfo->save();
1710        }
1711
1712        my $timer = MT->get_timer;
1713        if ($timer) {
1714            $timer->pause_partial;
1715        }
1716        local $timer->{elapsed} = 0 if $timer;
1717
1718        my $ctx = MT::Template::Context->new;
1719        next
1720          unless (
1721            MT->run_callbacks(
1722                'build_file_filter',
1723                Context      => $ctx,
1724                context      => $ctx,
1725                ArchiveType  => 'index',
1726                archive_type => 'index',
1727                Blog         => $blog,
1728                blog         => $blog,
1729                FileInfo     => $finfo,
1730                file_info    => $finfo,
1731                Template     => $tmpl,
1732                template     => $tmpl,
1733                File         => $file,
1734                file         => $file
1735            )
1736          );
1737        $ctx->stash( 'blog', $blog );
1738
1739        require MT::Request;
1740        MT::Request->instance->cache('build_template', $tmpl);
1741
1742        my $html = $tmpl->build($ctx);
1743        unless (defined $html) {
1744            $timer->unpause if $timer;
1745            return $mt->error( $tmpl->errstr );
1746        }
1747
1748        my $orig_html = $html;
1749        MT->run_callbacks(
1750            'build_page',
1751            Context      => $ctx,
1752            context      => $ctx,
1753            Blog         => $blog,
1754            blog         => $blog,
1755            FileInfo     => $finfo,
1756            file_info    => $finfo,
1757            ArchiveType  => 'index',
1758            archive_type => 'index',
1759            RawContent   => \$orig_html,
1760            raw_content  => \$orig_html,
1761            Content      => \$html,
1762            content      => \$html,
1763            BuildResult  => \$orig_html,
1764            build_result => \$orig_html,
1765            Template     => $tmpl,
1766            template     => $tmpl,
1767            File         => $file,
1768            file         => $file
1769        );
1770
1771        ## First check whether the content is actually changed. If not,
1772        ## we won't update the published file, so as not to modify the mtime.
1773        next unless $fmgr->content_is_updated( $file, \$html );
1774
1775        ## Determine if we need to build directory structure,
1776        ## and build it if we do. DirUmask determines
1777        ## directory permissions.
1778        require File::Spec;
1779        my $path = dirname($file);
1780        $path =~ s!/$!!
1781          unless $path eq '/';    ## OS X doesn't like / at the end in mkdir().
1782        unless ( $fmgr->exists($path) ) {
1783            if (! $fmgr->mkpath($path) ) {
1784                $timer->unpause if $timer;
1785                return $mt->trans_error( "Error making path '[_1]': [_2]",
1786                    $path, $fmgr->errstr );
1787            }
1788        }
1789
1790        ## Update the published file.
1791        my $use_temp_files = !$mt->{NoTempFiles};
1792        my $temp_file = $use_temp_files ? "$file.new" : $file;
1793        unless (defined( $fmgr->put_data( $html, $temp_file ) )) {
1794            $timer->unpause if $timer;
1795            return $mt->trans_error( "Writing to '[_1]' failed: [_2]",
1796                $temp_file, $fmgr->errstr );
1797        }
1798        if ($use_temp_files) {
1799            if (!$fmgr->rename( $temp_file, $file )) {
1800                $timer->unpause if $timer;
1801                return $mt->trans_error( "Renaming tempfile '[_1]' failed: [_2]",
1802                    $temp_file, $fmgr->errstr );
1803            }
1804        }
1805        MT->run_callbacks(
1806            'build_file',
1807            Context      => $ctx,
1808            context      => $ctx,
1809            ArchiveType  => 'index',
1810            archive_type => 'index',
1811            FileInfo     => $finfo,
1812            file_info    => $finfo,
1813            Blog         => $blog,
1814            blog         => $blog,
1815            RawContent   => \$orig_html,
1816            raw_content  => \$orig_html,
1817            Content      => \$html,
1818            content      => \$html,
1819            BuildResult  => \$orig_html,
1820            build_result => \$orig_html,
1821            Template     => $tmpl,
1822            template     => $tmpl,
1823            File         => $file,
1824            file         => $file
1825        );
1826
1827        $timer->mark("total:rebuild_indexes[template_id:" . $tmpl->id . ";file:$file]")
1828            if $timer;
1829    }
1830    1;
1831}
1832
1833sub rebuild_from_fileinfo {
1834    my $pub = shift;
1835    my ($fi) = @_;
1836
1837    require MT::Blog;
1838    require MT::Entry;
1839    require MT::Category;
1840    require MT::Template;
1841    require MT::TemplateMap;
1842    require MT::Template::Context;
1843
1844    my $at = $fi->archive_type
1845      or return $pub->error(
1846        MT->translate( "Parameter '[_1]' is required", 'ArchiveType' ) );
1847
1848    # callback for custom archive types
1849    return
1850      unless MT->run_callbacks(
1851        'build_archive_filter',
1852        archive_type => $at,
1853        file_info    => $fi
1854      );
1855
1856    if ( $at eq 'index' ) {
1857        $pub->rebuild_indexes(
1858            BlogID   => $fi->blog_id,
1859            Template => MT::Template->load( $fi->template_id ),
1860            Force    => 1,
1861        ) or return;
1862        return 1;
1863    }
1864
1865    return 1 if $at eq 'None';
1866
1867    my ( $start, $end );
1868    my $blog = MT::Blog->load( $fi->blog_id )
1869      if $fi->blog_id;
1870    my $entry = MT::Entry->load( $fi->entry_id )
1871      or return $pub->error(
1872        MT->translate( "Parameter '[_1]' is required", 'Entry' ) )
1873      if $fi->entry_id;
1874    if ( $fi->startdate ) {
1875        my $archiver = $pub->archiver($at);
1876
1877        if ( my $range = $archiver->date_range ) {
1878            ( $start, $end ) = $range->( $fi->startdate );
1879            $entry = MT::Entry->load( { authored_on => [ $start, $end ] },
1880                { range_incl => { authored_on => 1 }, limit => 1 } )
1881              or return $pub->error(
1882                MT->translate( "Parameter '[_1]' is required", 'Entry' ) );
1883        }
1884    }
1885    my $cat = MT::Category->load( $fi->category_id )
1886      if $fi->category_id;
1887    my $author = MT::Author->load( $fi->author_id )
1888      if $fi->author_id;
1889
1890    ## Load the template-archive-type map entries for this blog and
1891    ## archive type. We do this before we load the list of entries, because
1892    ## we will run through the files and check if we even need to rebuild
1893    ## anything. If there is nothing to rebuild at all for this entry,
1894    ## we save some time by not loading the list of entries.
1895    my $map = MT::TemplateMap->load( $fi->templatemap_id );
1896    my $file = $pub->archive_file_for( $entry, $blog, $at, $cat, $map,
1897        undef, $author );
1898    if ( !defined($file) ) {
1899        return $pub->error( $blog->errstr() );
1900    }
1901    $map->{__saved_output_file} = $file;
1902
1903    my $ctx = MT::Template::Context->new;
1904    $ctx->{current_archive_type} = $at;
1905    if ( $start && $end ) {
1906        $ctx->{current_timestamp} = $start;
1907        $ctx->{current_timestamp_end} = $end;
1908    }
1909
1910    my $arch_root =
1911      ( $at eq 'Page' ) ? $blog->site_path : $blog->archive_path;
1912    return $pub->error(
1913        MT->translate("You did not set your blog publishing path") )
1914      unless $arch_root;
1915
1916    my %cond;
1917    $pub->rebuild_file( $blog, $arch_root, $map, $at, $ctx, \%cond, 1,
1918        FileInfo => $fi, )
1919      or return;
1920
1921    1;
1922}
1923
1924sub trans_error {
1925    my $this = shift;
1926    return $this->error( MT->translate(@_) );
1927}
1928
1929sub publish_future_posts {
1930    my $this = shift;
1931
1932    require MT::Blog;
1933    require MT::Entry;
1934    require MT::Util;
1935    my $mt            = MT->instance;
1936    my $total_changed = 0;
1937    my @blogs = MT::Blog->load(undef, {
1938        join => MT::Entry->join_on('blog_id', {
1939            status => MT::Entry::FUTURE(),
1940        }, { unique => 1 })
1941    });
1942    foreach my $blog (@blogs) {
1943        my @ts = MT::Util::offset_time_list( time, $blog );
1944        my $now = sprintf "%04d%02d%02d%02d%02d%02d", $ts[5] + 1900, $ts[4] + 1,
1945          @ts[ 3, 2, 1, 0 ];
1946        my $iter = MT::Entry->load_iter(
1947            {
1948                blog_id => $blog->id,
1949                status  => MT::Entry::FUTURE(),
1950                class   => '*'
1951            },
1952            {
1953                'sort'    => 'authored_on',
1954                direction => 'descend'
1955            }
1956        );
1957        my @queue;
1958        while ( my $entry = $iter->() ) {
1959            push @queue, $entry->id if $entry->authored_on le $now;
1960        }
1961
1962        my $changed = 0;
1963        my @results;
1964        my %rebuild_queue;
1965        my %ping_queue;
1966        foreach my $entry_id (@queue) {
1967            my $entry = MT::Entry->load($entry_id);
1968            $entry->status( MT::Entry::RELEASE() );
1969            $entry->save
1970              or die $entry->errstr;
1971
1972            $rebuild_queue{ $entry->id } = $entry;
1973            $ping_queue{ $entry->id }    = 1;
1974            my $n = $entry->next(1);
1975            $rebuild_queue{ $n->id } = $n if $n;
1976            my $p = $entry->previous(1);
1977            $rebuild_queue{ $p->id } = $p if $p;
1978            $changed++;
1979            $total_changed++;
1980        }
1981        if ($changed) {
1982            my %rebuilt_okay;
1983            my $rebuilt;
1984            eval {
1985                foreach my $id ( keys %rebuild_queue )
1986                {
1987                    my $entry = $rebuild_queue{$id};
1988                    $mt->rebuild_entry( Entry => $entry, Blog => $blog )
1989                      or die $mt->errstr;
1990                    $rebuilt_okay{$id} = 1;
1991                    if ( $ping_queue{$id} ) {
1992                        $mt->ping_and_save( Entry => $entry, Blog => $blog );
1993                    }
1994                    $rebuilt++;
1995                }
1996                $mt->rebuild_indexes( Blog => $blog )
1997                  or die $mt->errstr;
1998            };
1999            if ( my $err = $@ ) {
2000
2001                # a fatal error occured while processing the rebuild
2002                # step. LOG the error and revert the entry/entries:
2003                require MT::Log;
2004                $mt->log(
2005                    {
2006                        message => $mt->translate(
2007"An error occurred while publishing scheduled entries: [_1]",
2008                            $err
2009                        ),
2010                        class   => "system",
2011                        blog_id => $blog->id,
2012                        level   => MT::Log::ERROR()
2013                    }
2014                );
2015                foreach my $id (@queue) {
2016                    next if exists $rebuilt_okay{$id};
2017                    my $e = $rebuild_queue{$id};
2018                    next unless $e;
2019                    $e->status( MT::Entry::FUTURE() );
2020                    $e->save or die $e->errstr;
2021                }
2022            }
2023        }
2024    }
2025    $total_changed > 0 ? 1 : 0;
2026}
2027
2028sub remove_entry_archive_file {
2029    my $mt    = shift;
2030    my %param = @_;
2031
2032    my $entry = $param{Entry};
2033    my $at    = $param{ArchiveType} || 'Individual';
2034    my $cat   = $param{Category};
2035    my $auth  = $param{Author};
2036
2037    require MT::TemplateMap;
2038    my $blog = $param{Blog};
2039    unless ($blog) {
2040        if ($entry) {
2041            $blog = $entry->blog;
2042        }
2043        elsif ($cat) {
2044            require MT::Blog;
2045            $blog = MT::Blog->load( $cat->blog_id );
2046        }
2047    }
2048    my @map = MT::TemplateMap->load(
2049        {
2050            archive_type => $at,
2051            blog_id      => $blog->id,
2052            $param{TemplateID} ? ( template_id => $param{TemplateID} ) : (),
2053        }
2054    );
2055    return 1 unless @map;
2056
2057    my $fmgr = $blog->file_mgr;
2058    my $arch_root = ( $at eq 'Page' ) ? $blog->site_path : $blog->archive_path;
2059
2060    require File::Spec;
2061    for my $map (@map) {
2062        my $file =
2063          $mt->archive_file_for( $entry, $blog, $at, $cat, $map, undef, $auth );
2064        $file = File::Spec->catfile( $arch_root, $file );
2065        if ( !defined($file) ) {
2066            die MT->translate( $blog->errstr() );
2067            return $mt->error( MT->translate( $blog->errstr() ) );
2068        }
2069
2070        # Run callbacks
2071        MT->run_callbacks( 'pre_delete_archive_file', $file, $at, $entry);
2072
2073        $fmgr->delete($file);
2074
2075        # Run callbacks
2076        MT->run_callbacks( 'post_delete_archive_file', $file, $at, $entry);
2077    }
2078    1;
2079}
2080
2081##
2082## archive_file_for takes an entry to determine the timestamps,
2083## but if the entry is not available it uses the time_start
2084## and time_end values
2085##
2086sub archive_file_for {
2087    my $mt = shift;
2088
2089    init_archive_types() unless %ArchiveTypes;
2090
2091    my ( $entry, $blog, $at, $cat, $map, $timestamp, $author ) = @_;
2092    return if $at eq 'None';
2093    my $archiver = $ArchiveTypes{$at};
2094    return '' unless $archiver;
2095
2096    my $file;
2097    if ( $blog->is_dynamic ) {
2098        require MT::TemplateMap;
2099                my $archiver = $ArchiveTypes{$at};
2100                if ($archiver) {
2101                $map = MT::TemplateMap->new;
2102                $map->file_template( $archiver->dynamic_template );
2103                }
2104    }
2105    unless ($map) {
2106        my $cache = MT::Request->instance->cache('maps');
2107        unless ($cache) {
2108            MT::Request->instance->cache( 'maps', $cache = {} );
2109        }
2110        unless ( $map = $cache->{ $blog->id . $at } ) {
2111            require MT::TemplateMap;
2112            $map = MT::TemplateMap->load(
2113                {
2114                    blog_id      => $blog->id,
2115                    archive_type => $at,
2116                    is_preferred => 1
2117                }
2118            );
2119            $cache->{ $blog->id . $at } = $map if $map;
2120        }
2121    }
2122    my $file_tmpl = $map->file_template if $map;
2123    unless ($file_tmpl) {
2124        if ( my $tmpls = $archiver->default_archive_templates ) {
2125            my ($default) = grep { $_->{default} } @$tmpls;
2126            $file_tmpl = $default->{template} if $default;
2127        }
2128    }
2129    $file_tmpl ||= '';
2130    my ($ctx);
2131    if ( $file_tmpl =~ m/\%[_-]?[A-Za-z]/ ) {
2132        if ( $file_tmpl =~ m/<\$?MT/ ) {
2133            $file_tmpl =~
2134s!(<\$?MT[^>]+?>)|(%[_-]?[A-Za-z])!$1 ? $1 : '<MTFileTemplate format="'. $2 . '">'!gie;
2135        }
2136        else {
2137            $file_tmpl = qq{<MTFileTemplate format="$file_tmpl">};
2138        }
2139    }
2140    if ($file_tmpl) {
2141        require MT::Template::Context;
2142        $ctx = MT::Template::Context->new;
2143        $ctx->stash( 'blog', $blog );
2144    }
2145    local $ctx->{__stash}{category}         = $cat if $cat;
2146    local $ctx->{__stash}{archive_category} = $cat if $cat;
2147    $timestamp = $entry->authored_on() if $entry;
2148    local $ctx->{__stash}{entry} = $entry if $entry;
2149    local $ctx->{__stash}{author} =
2150      $author ? $author : $entry ? $entry->author : undef;
2151
2152    my %blog_at = map { $_ => 1 } split /,/, $blog->archive_type;
2153    return '' unless $blog_at{$at};
2154
2155    $file = $archiver->archive_file->(
2156        $ctx,
2157        Timestamp => $timestamp,
2158        Template  => $file_tmpl
2159    );
2160    if ( $file_tmpl && !$file ) {
2161        local $ctx->{archive_type} = $at;
2162        require MT::Builder;
2163        my $build = MT::Builder->new;
2164        my $tokens = $build->compile( $ctx, $file_tmpl )
2165          or return $blog->error( $build->errstr() );
2166        defined( $file = $build->build( $ctx, $tokens ) )
2167          or return $blog->error( $build->errstr() );
2168    }
2169    else {
2170        my $ext = $blog->file_extension;
2171        $file .= '.' . $ext if $ext;
2172    }
2173    $file;
2174}
2175
2176sub get_entry {
2177    my $mt = shift;
2178    my ( $ts, $blog_id, $at, $order ) = @_;
2179    my $range = $mt->archiver($at)->date_range;
2180    my ( $start, $end ) = $range->($ts);
2181    if ( $order eq 'previous' ) {
2182        $order = 'descend';
2183        $ts    = $start;
2184    }
2185    else {
2186        $order = 'ascend';
2187        $ts    = $end;
2188    }
2189    require MT::Entry;
2190    my $entry = MT::Entry->load(
2191        {
2192            blog_id => $blog_id,
2193            status  => MT::Entry::RELEASE()
2194        },
2195        {
2196            limit     => 1,
2197            'sort'    => 'authored_on',
2198            direction => $order,
2199            start_val => $ts
2200        }
2201    );
2202    $entry;
2203}
2204
2205# Adds an element to the rebuild queue when the plugin is enabled.
2206sub queue_build_file_filter {
2207    my $mt = shift;
2208    my ( $cb, %args ) = @_;
2209
2210    my $blog = $args{blog};
2211    return 1 unless $blog && $blog->publish_queue;
2212
2213    my $fi = $args{file_info};
2214    return 1 if $fi->{from_queue};
2215
2216    require MT::TheSchwartz;
2217    require TheSchwartz::Job;
2218    my $job = TheSchwartz::Job->new();
2219    $job->funcname('MT::Worker::Publish');
2220    $job->uniqkey( $fi->id );
2221    $job->coalesce( $$ . ':' . ( time - ( time % 100 ) ) );
2222
2223    # my $at = $fi->archive_type || '';
2224    #
2225    # Default priority assignment....
2226    # if ($at eq 'Individual') {
2227    #     require MT::TemplateMap;
2228    #     my $map = MT::TemplateMap->load($fi->templatemap_id);
2229    #     # Individual archive pages that are the 'permalink' pages should
2230    #     # have highest build priority.
2231    #     if ($map && $map->is_preferred) {
2232    #         $rqf->priority(1);
2233    #     } else {
2234    #         $rqf->priority(9);
2235    #     }
2236    # } elsif ($at eq 'index') {
2237    #     # Index pages are second in priority, if they are named 'index'
2238    #     # or 'default'
2239    #     if ($fi->file_path =~ m!/(index|default|atom|feed)!i) {
2240    #         $rqf->priority(3);
2241    #     } else {
2242    #         $rqf->priority(9);
2243    #     }
2244    # } elsif (($at eq 'Monthly') || ($at eq 'Weekly') || ($at eq 'Daily')) {
2245    #     $rqf->priority(5);
2246    # } elsif ($at eq 'Category') {
2247    #     $rqf->priority(7);
2248    # }
2249    #
2250    # $rqf->save;
2251
2252    MT::TheSchwartz->insert($job);
2253
2254    return 0;
2255}
2256
2257
2258# ----- ARCHIVING -----
2259
2260# Prepare sets up a context with the data necessary to build that page
2261# Archiver returns a filename for the given archive
2262
2263sub yearly_archive_label {
2264    MT->translate("YEARLY_ADV");
2265}
2266
2267sub yearly_archive_file {
2268    my ( $ctx, %param ) = @_;
2269    my $timestamp = $param{Timestamp};
2270    my $file_tmpl = $param{Template};
2271    my $blog      = $ctx->{__stash}{blog};
2272
2273    my $file;
2274    if ($file_tmpl) {
2275        ( $ctx->{current_timestamp}, $ctx->{current_timestamp_end} ) =
2276          start_end_month( $timestamp, $blog );
2277    }
2278    else {
2279        my $start = start_end_month( $timestamp, $blog );
2280        my ( $year, $mon ) = unpack 'A4', $start;
2281        $file = sprintf( "%04d/index", $year, $mon );
2282    }
2283
2284    $file;
2285}
2286
2287sub yearly_archive_title {
2288    my ( $ctx, $entry_or_ts ) = @_;
2289    my $stamp = ref $entry_or_ts ? $entry_or_ts->authored_on : $entry_or_ts;
2290    my $start = start_end_year( $stamp, $ctx->stash('blog') );
2291    require MT::Template::Context;
2292    my $year =
2293      MT::Template::Context::_hdlr_date( $ctx,
2294        { ts => $start, 'format' => "%Y" } );
2295    my $lang = lc MT->current_language || 'en_us';
2296    $lang = 'ja' if lc($lang) eq 'jp';
2297
2298    sprintf( "%s%s", $year, ( $lang eq 'ja' ? '&#24180;' : '' ) );
2299}
2300
2301sub yearly_date_range { start_end_year(@_) }
2302
2303sub yearly_group_iter {
2304    my ( $ctx, $args ) = @_;
2305    my $blog = $ctx->stash('blog');
2306    my $iter;
2307    my $sort_order =
2308      ( $args->{sort_order} || '' ) eq 'ascend' ? 'ascend' : 'descend';
2309    my $order = ( $sort_order eq 'ascend' ) ? 'asc' : 'desc';
2310
2311    require MT::Entry;
2312    $iter = MT::Entry->count_group_by(
2313        {
2314            blog_id => $blog->id,
2315            status  => MT::Entry::RELEASE()
2316        },
2317        {
2318            group => ["extract(year from authored_on)"],
2319            $args->{lastn} ? ( limit => $args->{lastn} ) : (),
2320            sort => "extract(year from authored_on) $order"
2321        }
2322    ) or return $ctx->error("Couldn't get yearly archive list");
2323
2324    return sub {
2325        while ( my @row = $iter->() ) {
2326            my $date = sprintf( "%04d%02d%02d000000", $row[1], 1, 1 );
2327            my ( $start, $end ) = start_end_year($date);
2328            return ( $row[0], year => $row[1], start => $start, end => $end );
2329        }
2330        undef;
2331    };
2332}
2333
2334sub yearly_group_entries {
2335    my ( $ctx, %param ) = @_;
2336    my $ts =
2337        $param{year}
2338    ? sprintf( "%04d%02d%02d000000", $param{year}, 1, 1 )
2339        : undef;
2340    my $limit = $param{limit};
2341    date_based_group_entries( $ctx, 'Yearly', $ts, $limit );
2342}
2343
2344sub yearly_entries_count {
2345    my ( $blog, $at, $entry ) = @_;
2346    return _archive_entries_count(
2347        {
2348            Blog        => $blog,
2349            ArchiveType => $at,
2350            Timestamp   => $entry->authored_on
2351        }
2352    );
2353}
2354
2355sub monthly_archive_label {
2356    MT->translate("MONTHLY_ADV");
2357}
2358
2359sub monthly_archive_file {
2360    my ( $ctx, %param ) = @_;
2361    my $timestamp = $param{Timestamp};
2362    my $file_tmpl = $param{Template};
2363    my $blog      = $ctx->{__stash}{blog};
2364
2365    my $file;
2366    if ($file_tmpl) {
2367        ( $ctx->{current_timestamp}, $ctx->{current_timestamp_end} ) =
2368          start_end_month( $timestamp, $blog );
2369    }
2370    else {
2371        my $start = start_end_month( $timestamp, $blog );
2372        my ( $year, $mon ) = unpack 'A4A2', $start;
2373        $file = sprintf( "%04d/%02d/index", $year, $mon );
2374    }
2375
2376    $file;
2377}
2378
2379sub monthly_archive_title {
2380    my ( $ctx, $entry_or_ts ) = @_;
2381    my $stamp = ref $entry_or_ts ? $entry_or_ts->authored_on : $entry_or_ts;
2382    my $start = start_end_month( $stamp, $ctx->stash('blog') );
2383    MT::Template::Context::_hdlr_date( $ctx,
2384        { ts => $start, 'format' => "%B %Y" } );
2385}
2386
2387sub monthly_date_range { start_end_month(@_) }
2388
2389sub monthly_group_iter {
2390    my ( $ctx, $args ) = @_;
2391    my $blog = $ctx->stash('blog');
2392    my $iter;
2393    my $sort_order =
2394      ( $args->{sort_order} || '' ) eq 'ascend' ? 'ascend' : 'descend';
2395    my $order = ( $sort_order eq 'ascend' ) ? 'asc' : 'desc';
2396
2397    my $ts    = $ctx->{current_timestamp};
2398    my $tsend = $ctx->{current_timestamp_end};
2399
2400    require MT::Entry;
2401    $iter = MT::Entry->count_group_by(
2402        {
2403            blog_id => $blog->id,
2404            status  => MT::Entry::RELEASE(),
2405            ( $ts && $tsend ? ( authored_on => [ $ts, $tsend ] ) : () ),
2406        },
2407        {
2408            ( $ts && $tsend ? ( range_incl => { authored_on => 1 } ) : () ),
2409            group => [
2410                "extract(year from authored_on)",
2411                "extract(month from authored_on)"
2412            ],
2413            $args->{lastn} ? ( limit => $args->{lastn} ) : (),
2414            sort => "extract(year from authored_on) $order,
2415                       extract(month from authored_on) $order"
2416        }
2417    ) or return $ctx->error("Couldn't get monthly archive list");
2418
2419    return sub {
2420        while ( my @row = $iter->() ) {
2421            my $date = sprintf( "%04d%02d%02d000000", $row[1], $row[2], 1 );
2422            my ( $start, $end ) = start_end_month($date);
2423            return (
2424                $row[0],
2425                year  => $row[1],
2426                month => $row[2],
2427                start => $start,
2428                end   => $end
2429            );
2430        }
2431        undef;
2432    };
2433}
2434
2435sub monthly_group_entries {
2436    my ( $ctx, %param ) = @_;
2437    my $ts =
2438        $param{year}
2439    ? sprintf( "%04d%02d%02d000000", $param{year}, $param{month}, 1 )
2440        : undef;
2441    my $limit = $param{limit};
2442    date_based_group_entries( $ctx, 'Monthly', $ts, $limit );
2443}
2444
2445sub monthly_entries_count {
2446    my ( $blog, $at, $entry ) = @_;
2447    return _archive_entries_count(
2448        {
2449            Blog        => $blog,
2450            ArchiveType => $at,
2451            Timestamp   => $entry->authored_on
2452        }
2453    );
2454}
2455
2456sub category_archive_label {
2457    MT->translate("CATEGORY_ADV");
2458}
2459
2460sub category_archive_title {
2461    my ($ctx) = @_;
2462    my $c = $ctx->stash('category');
2463    $c ? $c->label : '';
2464}
2465
2466sub category_archive_file {
2467    my ( $ctx, %param ) = @_;
2468    my $timestamp = $param{Timestamp};
2469    my $file_tmpl = $param{Template};
2470    my $blog      = $ctx->{__stash}{blog};
2471    my $cat       = $ctx->{__stash}{category};
2472    my $entry     = $ctx->{__stash}{entry};
2473    my $file;
2474
2475    my $this_cat = $cat ? $cat : ( $entry ? $entry->category : undef );
2476    if ($file_tmpl) {
2477        $ctx->stash( 'archive_category', $this_cat );
2478        $ctx->{inside_mt_categories} = 1;
2479        $ctx->{__stash}{category} = $this_cat;
2480    }
2481    else {
2482        if ( !$this_cat ) {
2483            return "";
2484        }
2485        my $label = '';
2486        $label = dirify( $this_cat->label );
2487        if ( $label !~ /\w/ ) {
2488            $label = $this_cat ? "cat" . $this_cat->id : "";
2489        }
2490        $file = sprintf( "%s/index", $this_cat->category_path );
2491    }
2492    $file;
2493}
2494
2495sub category_group_iter {
2496    my ( $ctx, $args ) = @_;
2497
2498    my $blog_id = $ctx->stash('blog')->id;
2499    require MT::Category;
2500    my $iter = MT::Category->load_iter( { blog_id => $blog_id },
2501        { 'sort' => 'label', direction => 'ascend' } );
2502    require MT::Placement;
2503    require MT::Entry;
2504
2505    return sub {
2506        while ( my $c = $iter->() ) {
2507            my @arguments = (
2508                {
2509                    blog_id => $blog_id,
2510                    status  => MT::Entry::RELEASE()
2511                },
2512                {
2513                    'join' => [
2514                        'MT::Placement', 'entry_id', { category_id => $c->id }
2515                    ]
2516                }
2517            );
2518            my $count = MT::Entry->count( @arguments );
2519            next unless $count || $args->{show_empty};
2520            return ( $count, category => $c );
2521        }
2522        undef;
2523      }
2524}
2525
2526sub category_group_entries {
2527    my ( $ctx, %param ) = @_;
2528    my $limit = $param{limit};
2529    if ( $limit eq 'auto' ) {
2530        my $blog = $ctx->stash('blog');
2531        $limit = $blog->entries_on_index if $blog;
2532    }
2533    my $c = $ctx->stash('archive_category') || $ctx->stash('category');
2534    require MT::Entry;
2535    my @entries = MT::Entry->load(
2536        { status => MT::Entry::RELEASE() },
2537        {
2538            join => [
2539                'MT::Placement', 'entry_id',
2540                { category_id => $c->id }, { unqiue => 1 }
2541            ],
2542            'sort'      => 'authored_on',
2543            'direction' => 'descend',
2544            ( $limit ? ( 'limit' => $limit ) : () ),
2545        }
2546    );
2547    \@entries;
2548}
2549
2550sub category_entries_count {
2551    my ( $blog, $at, $entry ) = @_;
2552    my $cat = $entry->category;
2553    return 0 unless $cat;
2554    return _archive_entries_count(
2555        {
2556            Blog        => $blog,
2557            ArchiveType => $at,
2558            Category    => $cat
2559        }
2560    );
2561}
2562
2563sub page_archive_label {
2564    MT->translate("PAGE_ADV");
2565}
2566
2567sub page_archive_file {
2568    my ( $ctx, %param ) = @_;
2569    my $timestamp = $param{Timestamp};
2570    my $file_tmpl = $param{Template};
2571    my $blog      = $ctx->{__stash}{blog};
2572    my $page      = $ctx->{__stash}{entry};
2573
2574    my $file;
2575    Carp::croak "archive_file_for Page archive needs a page"
2576      unless $page && $page->isa('MT::Page');
2577    unless ($file_tmpl) {
2578        my $basename = $page->basename();
2579        my $folder   = $page->folder;
2580        my $folder_path;
2581        if ($folder) {
2582            $folder_path = $folder->publish_path || '';
2583            $file =
2584              $folder_path ne '' ? $folder_path . '/' . $basename : $basename;
2585        }
2586        else {
2587            $file = $basename;
2588        }
2589    }
2590    return $file;
2591}
2592
2593sub page_group_iter {
2594    my ( $ctx, $args ) = @_;
2595
2596    require MT::Page;
2597    my $blog_id = $ctx->stash('blog')->id;
2598    my $iter    = MT::Page->load_iter(
2599        {
2600            blog_id => $blog_id,
2601            status  => MT::Entry::RELEASE()
2602        },
2603        { sort => 'title', direction => 'ascend' }
2604    );
2605    return sub {
2606        while ( my $entry = $iter->() ) {
2607            return ( 1, entries => [$entry], entry => $entry );
2608        }
2609        undef;
2610      }
2611}
2612
2613sub individual_archive_label {
2614    MT->translate("INDIVIDUAL_ADV");
2615}
2616
2617sub individual_archive_file {
2618    my ( $ctx, %param ) = @_;
2619    my $timestamp = $param{Timestamp};
2620    my $file_tmpl = $param{Template};
2621    my $blog      = $ctx->{__stash}{blog};
2622    my $entry     = $ctx->{__stash}{entry};
2623
2624    my $file;
2625    Carp::confess "archive_file_for Individual archive needs an entry"
2626      unless $entry;
2627    if ($file_tmpl) {
2628        $ctx->{current_timestamp} = $entry->authored_on;
2629    }
2630    else {
2631        my $basename = $entry->basename();
2632        $basename ||= dirify( $entry->title() );
2633        $file = sprintf( "%04d/%02d/%s",
2634            unpack( 'A4A2', $entry->authored_on ), $basename );
2635    }
2636    $file;
2637}
2638
2639sub individual_archive_title { $_[1]->title }
2640
2641sub individual_group_iter {
2642    my ( $ctx, $args ) = @_;
2643
2644    my $order =
2645      ( $args->{sort_order} || '' ) eq 'ascend' ? 'ascend' : 'descend';
2646
2647    my $blog_id = $ctx->stash('blog')->id;
2648    require MT::Entry;
2649    my $iter = MT::Entry->load_iter(
2650        {
2651            blog_id => $blog_id,
2652            status  => MT::Entry::RELEASE()
2653        },
2654        {
2655            'sort'    => 'authored_on',
2656            direction => $order,
2657            $args->{lastn} ? ( limit => $args->{lastn} ) : ()
2658        }
2659    );
2660    return sub {
2661        while ( my $entry = $iter->() ) {
2662            return ( 1, entries => [$entry], entry => $entry );
2663        }
2664        undef;
2665      }
2666}
2667
2668sub daily_archive_label {
2669    MT->translate("DAILY_ADV");
2670}
2671
2672sub daily_archive_file {
2673    my ( $ctx, %param ) = @_;
2674    my $timestamp = $param{Timestamp};
2675    my $file_tmpl = $param{Template};
2676    my $blog      = $ctx->{__stash}{blog};
2677
2678    my $file;
2679    if ($file_tmpl) {
2680        ( $ctx->{current_timestamp}, $ctx->{current_timestamp_end} ) =
2681          start_end_day($timestamp);
2682    }
2683    else {
2684        my $start = start_end_day($timestamp);
2685        my ( $year, $mon, $mday ) = unpack 'A4A2A2', $start;
2686        $file = sprintf( "%04d/%02d/%02d/index", $year, $mon, $mday );
2687    }
2688    $file;
2689}
2690
2691sub daily_archive_title {
2692    my $stamp = ref $_[1] ? $_[1]->authored_on : $_[1];
2693    my $start = start_end_day( $stamp, $_[0]->stash('blog') );
2694    MT::Template::Context::_hdlr_date( $_[0],
2695        { ts => $start, 'format' => "%x" } );
2696}
2697
2698sub daily_date_range { start_end_day(