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

Revision 1372, 160.1 kB (checked in by bchoate, 22 months ago)

Initial work for performance logging.

  • 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                main_template     => 1,
237                archive_template  => 1,
238                entry_template    => 1,
239                feedback_template => 1,
240                archive_class     => "entry-archive",
241            },
242        ),
243        'Page' => ArchiveType(
244            name                      => 'Page',
245            archive_label             => \&page_archive_label,
246            archive_file              => \&page_archive_file,
247            archive_title             => \&individual_archive_title,
248            archive_group_iter        => \&page_group_iter,
249            dynamic_template          => 'page/<$MTEntryID$>',
250            default_archive_templates => [
251                ArchiveFileTemplate(
252                    label    => MT->translate('folder-path/page-basename.html'),
253                    template => '%-c/%-f',
254                    default  => 1
255                ),
256                ArchiveFileTemplate(
257                    label =>
258                      MT->translate('folder-path/page-basename/index.html'),
259                    template => '%-c/%-b/%i'
260                ),
261                ArchiveFileTemplate(
262                    label    => MT->translate('folder_path/page_basename.html'),
263                    template => '%c/%f'
264                ),
265                ArchiveFileTemplate(
266                    label =>
267                      MT->translate('folder_path/page_basename/index.html'),
268                    template => '%c/%b/%i'
269                ),
270            ],
271            dynamic_support => 1,
272            entry_class     => 'page',
273            entry_based     => 1,
274            template_params => {
275                archive_class     => "page-archive",
276                page_archive      => 1,
277                main_template     => 1,
278                archive_template  => 1,
279                page_template     => 1,
280                feedback_template => 1,
281            },
282        ),
283
284# 'Folder' =>
285#     ArchiveType( name => 'Folder',
286#         archive_label => \&folder_archive_label,
287#         archive_file => \&folder_archive_file,
288#         archive_title => \&folder_archive_title,
289#         archive_group_iter => \&folder_group_iter,
290#         archive_group_entries => \&folder_group_entries,
291#         dynamic_template => 'folder/<$MTCategoryID$>',
292#         default_archive_templates => [
293#             ArchiveFileTemplate( Label => MT->translate('folder/sub_folder/index.html'),
294#                 template => '%c/%i', Default => 1 ),
295#             ArchiveFileTemplate( Label => MT->translate('folder/sub-folder/index.html'),
296#                 template => '%-c/%i' ),
297#             ],
298#         dynamic_support => 1,
299#         category_based => 1,
300#     ),
301        'Daily' => ArchiveType(
302            name                  => 'Daily',
303            archive_label         => \&daily_archive_label,
304            archive_file          => \&daily_archive_file,
305            archive_title         => \&daily_archive_title,
306            date_range            => \&daily_date_range,
307            archive_group_iter    => \&daily_group_iter,
308            archive_group_entries => \&daily_group_entries,
309            archive_entries_count => \&daily_entries_count,
310            dynamic_template => 'archives/<$MTArchiveDate format="%Y%m%d"$>',
311            default_archive_templates => [
312                ArchiveFileTemplate(
313                    label    => MT->translate('yyyy/mm/dd/index.html'),
314                    template => '%y/%m/%d/%f',
315                    default  => 1
316                ),
317            ],
318            dynamic_support => 1,
319            date_based      => 1,
320            template_params => {
321                archive_class           => "datebased-daily-archive",
322                datebased_only_archive  => 1,
323                datebased_daily_archive => 1,
324                main_template           => 1,
325                archive_template        => 1,
326                archive_listing         => 1,
327            },
328        ),
329        'Category' => ArchiveType(
330            name                      => 'Category',
331            archive_label             => \&category_archive_label,
332            archive_file              => \&category_archive_file,
333            archive_title             => \&category_archive_title,
334            archive_group_iter        => \&category_group_iter,
335            archive_group_entries     => \&category_group_entries,
336            archive_entries_count     => \&category_entries_count,
337            dynamic_template          => 'category/<$MTCategoryID$>',
338            default_archive_templates => [
339                ArchiveFileTemplate(
340                    label => MT->translate('category/sub-category/index.html'),
341                    template => '%-c/%i',
342                    default  => 1
343                ),
344                ArchiveFileTemplate(
345                    label => MT->translate('category/sub_category/index.html'),
346                    template => '%c/%i'
347                ),
348            ],
349            dynamic_support => 1,
350            category_based  => 1,
351            template_params => {
352                archive_class                      => "category-archive",
353                category_archive                   => 1,
354                main_template                      => 1,
355                archive_template                   => 1,
356                archive_listing                    => 1,
357                'module_category-monthly_archives' => 1,
358            },
359        ),
360        'Author' => ArchiveType(
361            name                      => 'Author',
362            archive_label             => \&author_archive_label,
363            archive_file              => \&author_archive_file,
364            archive_title             => \&author_archive_title,
365            archive_group_iter        => \&author_group_iter,
366            archive_group_entries     => \&author_group_entries,
367            archive_entries_count     => \&author_entries_count,
368            default_archive_templates => [
369                ArchiveFileTemplate(
370                    label    => 'author-display-name/index.html',
371                    template => '%-a/%f',
372                    default  => 1
373                ),
374                ArchiveFileTemplate(
375                    label    => 'author_display_name/index.html',
376                    template => '%a/%f'
377                ),
378            ],
379            dynamic_template => 'author/<$MTEntryAuthorID$>/<$MTEntryID$>',
380            dynamic_support  => 1,
381            author_based     => 1,
382            template_params  => {
383                archive_class                    => "author-archive",
384                'module_author-monthly_archives' => 1,
385                main_template                    => 1,
386                author_archive                   => 1,
387                archive_template                 => 1,
388                archive_listing                  => 1,
389            },
390        ),
391        'Author-Yearly' => ArchiveType(
392            name                      => 'Author-Yearly',
393            archive_label             => \&author_yearly_archive_label,
394            archive_file              => \&author_yearly_archive_file,
395            archive_title             => \&author_yearly_archive_title,
396            archive_group_iter        => \&author_yearly_group_iter,
397            archive_group_entries     => \&author_yearly_group_entries,
398            archive_entries_count     => \&author_yearly_entries_count,
399            date_range                => \&author_yearly_date_range,
400            default_archive_templates => [
401                ArchiveFileTemplate(
402                    label    => 'author-display-name/yyyy/index.html',
403                    template => '%-a/%y/%f',
404                    default  => 1
405                ),
406                ArchiveFileTemplate(
407                    label    => 'author_display_name/yyyy/index.html',
408                    template => '%a/%y/%f'
409                ),
410            ],
411            dynamic_template =>
412              'author/<$MTEntryAuthorID$>/<$MTArchiveDate format="%Y"$>',
413            dynamic_support => 1,
414            author_based    => 1,
415            date_based      => 1,
416            template_params => {
417                archive_class         => "author-yearly-archive",
418                author_yearly_archive => 1,
419                main_template         => 1,
420                archive_template      => 1,
421                archive_listing       => 1,
422            },
423        ),
424        'Author-Monthly' => ArchiveType(
425            name                      => 'Author-Monthly',
426            archive_label             => \&author_monthly_archive_label,
427            archive_file              => \&author_monthly_archive_file,
428            archive_title             => \&author_monthly_archive_title,
429            archive_group_iter        => \&author_monthly_group_iter,
430            archive_group_entries     => \&author_monthly_group_entries,
431            archive_entries_count     => \&author_monthly_entries_count,
432            date_range                => \&author_monthly_date_range,
433            default_archive_templates => [
434                ArchiveFileTemplate(
435                    label    => 'author-display-name/yyyy/mm/index.html',
436                    template => '%-a/%y/%m/%f',
437                    default  => 1
438                ),
439                ArchiveFileTemplate(
440                    label    => 'author_display_name/yyyy/mm/index.html',
441                    template => '%a/%y/%m/%f'
442                ),
443            ],
444            dynamic_template =>
445              'author/<$MTEntryAuthorID$>/<$MTArchiveDate format="%Y%m"$>',
446            dynamic_support => 1,
447            author_based    => 1,
448            date_based      => 1,
449            template_params => {
450                archive_class                    => "author-monthly-archive",
451                author_monthly_archive           => 1,
452                'module_author-monthly_archives' => 1,
453                main_template                    => 1,
454                archive_template                 => 1,
455                archive_listing                  => 1,
456            },
457        ),
458        'Author-Weekly' => ArchiveType(
459            name                      => 'Author-Weekly',
460            archive_label             => \&author_weekly_archive_label,
461            archive_file              => \&author_weekly_archive_file,
462            archive_title             => \&author_weekly_archive_title,
463            archive_group_iter        => \&author_weekly_group_iter,
464            archive_group_entries     => \&author_weekly_group_entries,
465            archive_entries_count     => \&author_weekly_entries_count,
466            date_range                => \&author_weekly_date_range,
467            default_archive_templates => [
468                ArchiveFileTemplate(
469                    label => 'author-display-name/yyyy/mm/day-week/index.html',
470                    template => '%-a/%y/%m/%d-week/%f',
471                    default  => 1
472                ),
473                ArchiveFileTemplate(
474                    label => 'author_display_name/yyyy/mm/day-week/index.html',
475                    template => '%a/%y/%m/%d-week/%f'
476                ),
477            ],
478            dynamic_template =>
479'author/<$MTEntryAuthorID$>/week/<$MTArchiveDate format="%Y%m%d"$>',
480            dynamic_support => 1,
481            author_based    => 1,
482            date_based      => 1,
483            template_params => {
484                archive_class         => "author-weekly-archive",
485                author_weekly_archive => 1,
486                main_template         => 1,
487                archive_template      => 1,
488                archive_listing       => 1,
489            },
490        ),
491        'Author-Daily' => ArchiveType(
492            name                      => 'Author-Daily',
493            archive_label             => \&author_daily_archive_label,
494            archive_file              => \&author_daily_archive_file,
495            archive_title             => \&author_daily_archive_title,
496            archive_group_iter        => \&author_daily_group_iter,
497            archive_group_entries     => \&author_daily_group_entries,
498            archive_entries_count     => \&author_daily_entries_count,
499            date_range                => \&author_daily_date_range,
500            default_archive_templates => [
501                ArchiveFileTemplate(
502                    label    => 'author-display-name/yyyy/mm/dd/index.html',
503                    template => '%-a/%y/%m/%d/%f',
504                    default  => 1
505                ),
506                ArchiveFileTemplate(
507                    label    => 'author_display_name/yyyy/mm/dd/index.html',
508                    template => '%a/%y/%m/%d/%f'
509                ),
510            ],
511            dynamic_template =>
512              'author/<$MTEntryAuthorID$>/<$MTArchiveDate format="%Y%m%d"$>',
513            dynamic_support => 1,
514            author_based    => 1,
515            date_based      => 1,
516            template_params => {
517                archive_class        => "author-daily-archive",
518                author_daily_archive => 1,
519                main_template        => 1,
520                archive_template     => 1,
521                archive_listing      => 1,
522            },
523        ),
524        'Category-Yearly' => ArchiveType(
525            name                      => 'Category-Yearly',
526            archive_label             => \&cat_yearly_archive_label,
527            archive_file              => \&cat_yearly_archive_file,
528            archive_title             => \&cat_yearly_archive_title,
529            date_range                => \&cat_yearly_date_range,
530            archive_group_iter        => \&cat_yearly_group_iter,
531            archive_group_entries     => \&cat_yearly_group_entries,
532            archive_entries_count     => \&cat_yearly_entries_count,
533            default_archive_templates => [
534                ArchiveFileTemplate(
535                    label    => 'category/sub-category/yyyy/index.html',
536                    template => '%-c/%y/%i',
537                    default  => 1
538                ),
539                ArchiveFileTemplate(
540                    label    => 'category/sub_category/yyyy/index.html',
541                    template => '%c/%y/%i'
542                ),
543            ],
544            dynamic_template =>
545              'category/<$MTCategoryID$>/<$MTArchiveDate format="%Y"$>',
546            dynamic_support => 1,
547            date_based      => 1,
548            category_based  => 1,
549            template_params => {
550                archive_class           => "category-yearly-archive",
551                category_yearly_archive => 1,
552                main_template           => 1,
553                archive_template        => 1,
554                archive_listing         => 1,
555            },
556        ),
557        'Category-Monthly' => ArchiveType(
558            name                      => 'Category-Monthly',
559            archive_label             => \&cat_monthly_archive_label,
560            archive_file              => \&cat_monthly_archive_file,
561            archive_title             => \&cat_monthly_archive_title,
562            date_range                => \&cat_monthly_date_range,
563            archive_group_iter        => \&cat_monthly_group_iter,
564            archive_group_entries     => \&cat_monthly_group_entries,
565            archive_entries_count     => \&cat_monthly_entries_count,
566            default_archive_templates => [
567                ArchiveFileTemplate(
568                    label    => 'category/sub-category/yyyy/mm/index.html',
569                    template => '%-c/%y/%m/%i',
570                    default  => 1
571                ),
572                ArchiveFileTemplate(
573                    label    => 'category/sub_category/yyyy/mm/index.html',
574                    template => '%c/%y/%m/%i'
575                ),
576            ],
577            dynamic_template =>
578              'category/<$MTCategoryID$>/<$MTArchiveDate format="%Y%m"$>',
579            dynamic_support => 1,
580            date_based      => 1,
581            category_based  => 1,
582            template_params => {
583                archive_class            => "category-monthly-archive",
584                category_monthly_archive => 1,
585                'module_category-monthly_archives' => 1,
586                main_template                      => 1,
587                archive_template                   => 1,
588                archive_listing                    => 1,
589            },
590        ),
591        'Category-Daily' => ArchiveType(
592            name                      => 'Category-Daily',
593            archive_label             => \&cat_daily_archive_label,
594            archive_file              => \&cat_daily_archive_file,
595            archive_title             => \&cat_daily_archive_title,
596            date_range                => \&cat_daily_date_range,
597            archive_group_iter        => \&cat_daily_group_iter,
598            archive_group_entries     => \&cat_daily_group_entries,
599            archive_entries_count     => \&cat_daily_entries_count,
600            default_archive_templates => [
601                ArchiveFileTemplate(
602                    label    => 'category/sub-category/yyyy/mm/dd/index.html',
603                    template => '%-c/%y/%m/%d/%i',
604                    default  => 1
605                ),
606                ArchiveFileTemplate(
607                    label    => 'category/sub_category/yyyy/mm/dd/index.html',
608                    template => '%c/%y/%m/%d/%i'
609                ),
610            ],
611            dynamic_template =>
612              'category/<$MTCategoryID$>/<$MTArchiveDate format="%Y%m%d"$>',
613            dynamic_support => 1,
614            date_based      => 1,
615            category_based  => 1,
616            template_params => {
617                archive_class          => "category-daily-archive",
618                category_daily_archive => 1,
619                main_template          => 1,
620                archive_template       => 1,
621                archive_listing        => 1,
622            },
623        ),
624        'Category-Weekly' => ArchiveType(
625            name                      => 'Category-Weekly',
626            archive_label             => \&cat_weekly_archive_label,
627            archive_file              => \&cat_weekly_archive_file,
628            archive_title             => \&cat_weekly_archive_title,
629            date_range                => \&cat_weekly_date_range,
630            archive_group_iter        => \&cat_weekly_group_iter,
631            archive_group_entries     => \&cat_weekly_group_entries,
632            archive_entries_count     => \&cat_weekly_entries_count,
633            default_archive_templates => [
634                ArchiveFileTemplate(
635                    label =>
636                      'category/sub-category/yyyy/mm/day-week/index.html',
637                    template => '%-c/%y/%m/%d-week/%i',
638                    default  => 1
639                ),
640                ArchiveFileTemplate(
641                    label =>
642                      'category/sub_category/yyyy/mm/day-week/index.html',
643                    template => '%c/%y/%m/%d-week/%i'
644                ),
645            ],
646            dynamic_template =>
647              'section/<$MTCategoryID$>/week/<$MTArchiveDate format="%Y%m%d"$>',
648            dynamic_support => 1,
649            date_based      => 1,
650            category_based  => 1,
651            template_params => {
652                archive_class           => "category-weekly-archive",
653                category_weekly_archive => 1,
654                main_template           => 1,
655                archive_template        => 1,
656                archive_listing         => 1,
657            },
658        )
659    };
660
661}
662
663sub rebuild {
664    my $mt    = shift;
665    my %param = @_;
666    my $blog;
667    unless ( $blog = $param{Blog} ) {
668        my $blog_id = $param{BlogID};
669        $blog = MT::Blog->load($blog_id)
670          or return $mt->error(
671            MT->translate(
672                "Load of blog '[_1]' failed: [_2]", $blog_id,
673                MT::Blog->errstr
674            )
675          );
676    }
677    return 1 if $blog->is_dynamic;
678    my $at = $blog->archive_type || '';
679    my @at = split /,/, $at;
680    my $entry_class;
681    if ( my $set_at = $param{ArchiveType} ) {
682        my %at = map { $_ => 1 } @at;
683        return $mt->error(
684            MT->translate(
685                "Archive type '[_1]' is not a chosen archive type", $set_at
686            )
687        ) unless $at{$set_at};
688
689        @at = ($set_at);
690        my $archiver = $mt->archiver($set_at);
691        $entry_class = $archiver->{entry_class} || "entry";
692    }
693    else {
694        $entry_class = '*';
695    }
696
697    if (   $param{ArchiveType}
698        && ( !$param{Entry} )
699        && ( $param{ArchiveType} eq 'Category' ) )
700    {
701
702        # Pass to full category rebuild
703        return $mt->rebuild_categories(%param);
704    }
705
706    if (   $param{ArchiveType}
707        && ( !$param{Author} )
708        && ( $param{ArchiveType} eq 'Author' ) )
709    {
710        return $mt->rebuild_authors(%param);
711    }
712
713    if (@at) {
714        require MT::Entry;
715        my %arg = ( 'sort' => 'authored_on', direction => 'descend' );
716        $arg{offset} = $param{Offset} if $param{Offset};
717        $arg{limit}  = $param{Limit}  if $param{Limit};
718        my $pre_iter = MT::Entry->load_iter(
719            {
720                blog_id => $blog->id,
721                class   => $entry_class,
722                status  => MT::Entry::RELEASE()
723            },
724            \%arg
725        );
726        my ( $next, $curr );
727        my $prev = $pre_iter->();
728        my $iter = sub {
729            ( $next, $curr ) = ( $curr, $prev );
730            if ($curr) {
731                $prev = $pre_iter->();
732            }
733            $curr;
734        };
735        my $cb  = $param{EntryCallback};
736        my $fcb = $param{FilterCallback};
737        while ( my $entry = $iter->() ) {
738            if ($cb) {
739                $cb->($entry) || $mt->log( $cb->errstr() );
740            }
741            if ($fcb) {
742                $fcb->($entry) or last;
743            }
744            for my $at (@at) {
745                my $archiver = $mt->archiver($at);
746
747                # Skip this archive type if the archive type doesn't
748                # match the kind of entry we've loaded
749                next unless $archiver;
750                next if $entry->class ne $archiver->entry_class;
751                if ( $archiver->category_based ) {
752                    my $cats = $entry->categories;
753                    CATEGORY: for my $cat (@$cats) {
754                        next CATEGORY if $archiver->category_class ne $cat->class_type;
755                        $mt->_rebuild_entry_archive_type(
756                            Entry       => $entry,
757                            Blog        => $blog,
758                            Category    => $cat,
759                            ArchiveType => $at,
760                            NoStatic    => $param{NoStatic},
761                            $param{TemplateMap}
762                            ? ( TemplateMap => $param{TemplateMap} )
763                            : (),
764                            $param{TemplateID}
765                            ? ( TemplateID =>
766                                  $param{TemplateID} )
767                            : (),
768                        ) or return;
769                    }
770                }
771                elsif ( $archiver->author_based ) {
772                    if ( $entry->author ) {
773                        $mt->_rebuild_entry_archive_type(
774                            Entry       => $entry,
775                            Blog        => $blog,
776                            ArchiveType => $at,
777                            $param{TemplateMap}
778                            ? ( TemplateMap => $param{TemplateMap} )
779                            : (),
780                            $param{TemplateID}
781                            ? ( TemplateID =>
782                                  $param{TemplateID} )
783                            : (),
784                            NoStatic => $param{NoStatic},
785                            Author   => $entry->author
786                        ) or return;
787                    }
788                }
789                else {
790                    $mt->_rebuild_entry_archive_type(
791                        Entry       => $entry,
792                        Blog        => $blog,
793                        ArchiveType => $at,
794                        $param{TemplateMap}
795                        ? ( TemplateMap => $param{TemplateMap} )
796                        : (),
797                        $param{TemplateID}
798                        ? ( TemplateID =>
799                              $param{TemplateID} )
800                        : (),
801                        NoStatic => $param{NoStatic}
802                    ) or return;
803                }
804            }
805        }
806    }
807    unless ( $param{NoIndexes} ) {
808        $mt->rebuild_indexes( Blog => $blog ) or return;
809    }
810    1;
811}
812
813sub rebuild_categories {
814    my $mt    = shift;
815    my %param = @_;
816    my $blog;
817    unless ( $blog = $param{Blog} ) {
818        my $blog_id = $param{BlogID};
819        $blog = MT::Blog->load($blog_id)
820          or return $mt->error(
821            MT->translate(
822                "Load of blog '[_1]' failed: [_2]", $blog_id,
823                MT::Blog->errstr
824            )
825          );
826    }
827    my %arg;
828    $arg{'sort'} = 'id';
829    $arg{direction} = 'ascend';
830    $arg{offset} = $param{Offset} if $param{Offset};
831    $arg{limit}  = $param{Limit}  if $param{Limit};
832    my $cat_iter = MT::Category->load_iter( { blog_id => $blog->id }, \%arg );
833    my $fcb = $param{FilterCallback};
834
835    while ( my $cat = $cat_iter->() ) {
836        if ($fcb) {
837            $fcb->($cat) or last;
838        }
839        $mt->_rebuild_entry_archive_type(
840            Blog        => $blog,
841            Category    => $cat,
842            ArchiveType => 'Category',
843            $param{TemplateMap}
844            ? ( TemplateMap => $param{TemplateMap} )
845            : (),
846            NoStatic => $param{NoStatic},
847        ) or return;
848    }
849    1;
850}
851
852sub rebuild_authors {
853    my $mt    = shift;
854    my %param = @_;
855    my $blog;
856    unless ( $blog = $param{Blog} ) {
857        my $blog_id = $param{BlogID};
858        $blog = MT::Blog->load($blog_id)
859          or return $mt->error(
860            MT->translate(
861                "Load of blog '[_1]' failed: [_2]", $blog_id,
862                MT::Blog->errstr
863            )
864          );
865    }
866    my %arg;
867    $arg{'sort'} = 'id';
868    $arg{direction} = 'ascend';
869    $arg{offset} = $param{Offset} if $param{Offset};
870    $arg{limit}  = $param{Limit}  if $param{Limit};
871    require MT::Entry;
872    local $arg{join} = MT::Entry->join_on(
873        'author_id',
874        { blog_id => $blog->id, class => 'entry' },
875        { unique  => 1 }
876    );
877    local $arg{unique} = 1;
878    local $arg{status} = MT::Entry::RELEASE();
879    my $auth_iter = MT::Author->load_iter( undef, \%arg );
880    my $fcb = $param{FilterCallback};
881
882    while ( my $a = $auth_iter->() ) {
883        if ($fcb) {
884            $fcb->($a) or last;
885        }
886        $mt->_rebuild_entry_archive_type(
887            Blog        => $blog,
888            Author      => $a,
889            ArchiveType => 'Author',
890            $param{TemplateMap}
891            ? ( TemplateMap => $param{TemplateMap} )
892            : (),
893            NoStatic => $param{NoStatic},
894        ) or return;
895    }
896    1;
897}
898
899#   rebuild_entry
900#
901# $mt->rebuild_entry(Entry => $entry_id,
902#                    Blog => [ $blog | $blog_id ],
903#                    [ BuildDependencies => (0 | 1), ]
904#                    [ OldPrevious => $old_previous_entry_id,
905#                      OldNext => $old_next_entry_id, ]
906#                    [ NoStatic => (0 | 1), ]
907#                    );
908sub rebuild_entry {
909    my $mt    = shift;
910    my %param = @_;
911    my $entry = $param{Entry}
912      or return $mt->error(
913        MT->translate( "Parameter '[_1]' is required", 'Entry' ) );
914    require MT::Entry;
915    $entry = MT::Entry->load($entry) unless ref $entry;
916    my $blog;
917    unless ( $blog = $param{Blog} ) {
918        my $blog_id = $entry->blog_id;
919        $blog = MT::Blog->load($blog_id)
920          or return $mt->error(
921            MT->translate(
922                "Load of blog '[_1]' failed: [_2]", $blog_id,
923                MT::Blog->errstr
924            )
925          );
926    }
927    return 1 if $blog->is_dynamic;
928
929    my $at = $blog->archive_type;
930    if ( $at && $at ne 'None' ) {
931        my @at = split /,/, $at;
932        for my $at (@at) {
933            my $archiver = $mt->archiver($at);
934            next unless $archiver;    # invalid archive type
935            next if $entry->class ne $archiver->entry_class;
936            if ( $archiver->category_based ) {
937                my $cats = $entry->categories;    # (ancestors => 1)
938                for my $cat (@$cats) {
939                    $mt->_rebuild_entry_archive_type(
940                        Entry       => $entry,
941                        Blog        => $blog,
942                        ArchiveType => $at,
943                        Category    => $cat,
944                        NoStatic    => $param{NoStatic},
945                        $param{TemplateMap}
946                        ? ( TemplateMap => $param{TemplateMap} )
947                        : (),
948                    ) or return;
949                }
950            }
951            else {
952                $mt->_rebuild_entry_archive_type(
953                    Entry       => $entry,
954                    Blog        => $blog,
955                    ArchiveType => $at,
956                    $param{TemplateMap}
957                    ? ( TemplateMap => $param{TemplateMap} )
958                    : (),
959                    NoStatic => $param{NoStatic},
960                    Author   => $entry->author,
961                ) or return;
962            }
963        }
964    }
965
966    ## The above will just rebuild the archive pages for this particular
967    ## entry. If we want to rebuild all of the entries/archives/indexes
968    ## on which this entry could be featured etc., however, we need to
969    ## rebuild all of the entry's dependencies. Note that all of these
970    ## are not *necessarily* dependencies, depending on the usage of tags,
971    ## etc. There is not a good way to determine exact dependencies; it is
972    ## easier to just rebuild, rebuild, rebuild.
973
974    return 1
975      unless $param{BuildDependencies}
976      || $param{BuildIndexes}
977      || $param{BuildArchives};
978
979    if ( $param{BuildDependencies} ) {
980        ## Rebuild previous and next entry archive pages.
981        if ( my $prev = $entry->previous(1) ) {
982            $mt->rebuild_entry( Entry => $prev ) or return;
983
984            ## Rebuild the old previous and next entries, if we have some.
985            if ( $param{OldPrevious}
986                && ( my $old_prev = MT::Entry->load( $param{OldPrevious} ) ) )
987            {
988                $mt->rebuild_entry( Entry => $old_prev ) or return;
989            }
990        }
991        if ( my $next = $entry->next(1) ) {
992            $mt->rebuild_entry( Entry => $next ) or return;
993
994            if ( $param{OldNext}
995                && ( my $old_next = MT::Entry->load( $param{OldNext} ) ) )
996            {
997                $mt->rebuild_entry( Entry => $old_next ) or return;
998            }
999        }
1000    }
1001
1002    if ( $param{BuildDependencies} || $param{BuildIndexes} ) {
1003        ## Rebuild all indexes, in case this entry is on an index.
1004        if ( !( exists $param{BuildIndexes} ) || $param{BuildIndexes} ) {
1005            $mt->rebuild_indexes( Blog => $blog ) or return;
1006        }
1007    }
1008
1009    if ( $param{BuildDependencies} || $param{BuildArchives} ) {
1010        ## Rebuild previous and next daily, weekly, and monthly archives;
1011        ## adding a new entry could cause changes to the intra-archive
1012        ## navigation.
1013        my %at = map { $_ => 1 } split /,/, $blog->archive_type;
1014        my @db_at = grep { $ArchiveTypes{$_} && $ArchiveTypes{$_}->date_based } $mt->archive_types;
1015        for my $at (@db_at) {
1016            if ( $at{$at} ) {
1017                my @arg = ( $entry->authored_on, $entry->blog_id, $at );
1018                my $archiver = $mt->archiver($at);
1019                if ( my $prev_arch = $mt->get_entry( @arg, 'previous' ) ) {
1020                    if ( $archiver->category_based ) {
1021                        my $cats = $prev_arch->categories;
1022                        for my $cat (@$cats) {
1023                            $mt->_rebuild_entry_archive_type(
1024                                NoStatic => $param{NoStatic},
1025                                Entry    => $prev_arch,
1026                                Blog     => $blog,
1027                                Category => $cat,
1028                                $param{TemplateMap}
1029                                ? ( TemplateMap => $param{TemplateMap} )
1030                                : (),
1031                                ArchiveType => $at
1032                            ) or return;
1033                        }
1034                    }
1035                    else {
1036                        $mt->_rebuild_entry_archive_type(
1037                            NoStatic    => $param{NoStatic},
1038                            Entry       => $prev_arch,
1039                            Blog        => $blog,
1040                            ArchiveType => $at,
1041                            $param{TemplateMap}
1042                            ? ( TemplateMap => $param{TemplateMap} )
1043                            : (),
1044                            Author => $prev_arch->author
1045                        ) or return;
1046                    }
1047                }
1048                if ( my $next_arch = $mt->get_entry( @arg, 'next' ) ) {
1049                    if ( $archiver->category_based ) {
1050                        my $cats = $next_arch->categories;
1051                        for my $cat (@$cats) {
1052                            $mt->_rebuild_entry_archive_type(
1053                                NoStatic => $param{NoStatic},
1054                                Entry    => $next_arch,
1055                                Blog     => $blog,
1056                                Category => $cat,
1057                                $param{TemplateMap}
1058                                ? ( TemplateMap => $param{TemplateMap} )
1059                                : (),
1060                                ArchiveType => $at
1061                            ) or return;
1062                        }
1063                    }
1064                    else {
1065                        $mt->_rebuild_entry_archive_type(
1066                            NoStatic    => $param{NoStatic},
1067                            Entry       => $next_arch,
1068                            Blog        => $blog,
1069                            ArchiveType => $at,
1070                            $param{TemplateMap}
1071                            ? ( TemplateMap => $param{TemplateMap} )
1072                            : (),
1073                            Author => $next_arch->author
1074                        ) or return;
1075                    }
1076                }
1077            }
1078        }
1079    }
1080
1081    1;
1082}
1083
1084sub _rebuild_entry_archive_type {
1085    my $mt    = shift;
1086    my %param = @_;
1087    my $at    = $param{ArchiveType}
1088      or return $mt->error(
1089        MT->translate( "Parameter '[_1]' is required", 'ArchiveType' ) );
1090    return 1 if $at eq 'None';
1091    my $entry =
1092      ( $param{ArchiveType} ne 'Category' && $param{ArchiveType} ne 'Author' )
1093      ? (
1094        $param{Entry}
1095          or return $mt->error(
1096            MT->translate( "Parameter '[_1]' is required", 'Entry' )
1097          )
1098      )
1099      : undef;
1100
1101    my $blog;
1102    unless ( $blog = $param{Blog} ) {
1103        my $blog_id = $entry->blog_id;
1104        $blog = MT::Blog->load($blog_id)
1105          or return $mt->error(
1106            MT->translate(
1107                "Load of blog '[_1]' failed: [_2]", $blog_id,
1108                MT::Blog->errstr
1109            )
1110          );
1111    }
1112
1113    ## Load the template-archive-type map entries for this blog and
1114    ## archive type. We do this before we load the list of entries, because
1115    ## we will run through the files and check if we even need to rebuild
1116    ## anything. If there is nothing to rebuild at all for this entry,
1117    ## we save some time by not loading the list of entries.
1118    require MT::TemplateMap;
1119    my @map;
1120    if ( $param{TemplateMap} ) {
1121        @map = ( $param{TemplateMap} );
1122    }
1123    else {
1124        my $cached_maps = MT->instance->request('__cached_maps')
1125          || MT->instance->request( '__cached_maps', {} );
1126        if ( my $maps = $cached_maps->{ $at . $blog->id } ) {
1127            @map = @$maps;
1128        }
1129        else {
1130            @map = MT::TemplateMap->load(
1131                {
1132                    archive_type => $at,
1133                    blog_id      => $blog->id,
1134                    $param{TemplateID}
1135                    ? ( template_id => $param{TemplateID} )
1136                    : ()
1137                }
1138            );
1139            $cached_maps->{ $at . $blog->id } = \@map;
1140        }
1141    }
1142    return 1 unless @map;
1143    my @map_build;
1144
1145    my $done = MT->instance->request('__published')
1146      || MT->instance->request( '__published', {} );
1147    for my $map (@map) {
1148        my $file =
1149          $mt->archive_file_for( $entry, $blog, $at, $param{Category}, $map,
1150            undef, $param{Author} );
1151        if ( $file eq '' ) {
1152
1153            # np
1154        }
1155        elsif ( !defined($file) ) {
1156            return $mt->error( MT->translate( $blog->errstr() ) );
1157        }
1158        else {
1159            push @map_build, $map unless $done->{$file};
1160            $map->{__saved_output_file} = $file;
1161        }
1162    }
1163    return 1 unless @map_build;
1164    @map = @map_build;
1165
1166    my (%cond);
1167    require MT::Template::Context;
1168    my $ctx = MT::Template::Context->new;
1169    $ctx->{current_archive_type} = $at;
1170    $ctx->{archive_type}         = $at;
1171    $at ||= "";
1172
1173    my $archiver = $mt->archiver($at);
1174    return unless $archiver;
1175
1176    my $fmgr = $blog->file_mgr;
1177
1178    # Special handling for pages-- they are always published to the
1179    # 'site' path instead of the 'archive' path, which is reserved for blog
1180    # content.
1181    my $arch_root = ( $at eq 'Page' ) ? $blog->site_path : $blog->archive_path;
1182    return $mt->error(
1183        MT->translate("You did not set your blog publishing path") )
1184      unless $arch_root;
1185
1186    my $range = $archiver->date_range;
1187    my ( $start, $end ) = $range ? $range->( $entry->authored_on ) : ();
1188
1189    ## For each mapping, we need to rebuild the entries we loaded above in
1190    ## the particular template map, and write it to the specified archive
1191    ## file template.
1192    require MT::Template;
1193    for my $map (@map) {
1194        $mt->rebuild_file(
1195            $blog, $arch_root, $map, $at, $ctx, \%cond,
1196            !$param{NoStatic},
1197            Category  => $param{Category},
1198            Entry     => $entry,
1199            Author    => $param{Author},
1200            StartDate => $start,
1201            EndDate   => $end,
1202        ) or return;
1203        $done->{ $map->{__saved_output_file} }++;
1204    }
1205    1;
1206}
1207
1208sub rebuild_file {
1209    my $mt = shift;
1210    my ( $blog, $root_path, $map, $at, $ctx, $cond, $build_static, %specifier )
1211      = @_;
1212
1213    my $finfo;
1214    my $archiver = $mt->archiver($at);
1215    my ( $entry, $start, $end, $category, $author );
1216
1217    if ( $finfo = $specifier{FileInfo} ) {
1218        $specifier{Author}   = $finfo->author_id   if $finfo->author_id;
1219        $specifier{Category} = $finfo->category_id if $finfo->category_id;
1220        $specifier{Entry}    = $finfo->entry_id    if $finfo->entry_id;
1221        $map ||= MT::TemplateMap->load( $finfo->templatemap_id );
1222        $at  ||= $finfo->archive_type;
1223        if ( $finfo->startdate ) {
1224            if ( my $range = $archiver->date_range ) {
1225                my ( $start, $end ) =
1226                  $range ? $range->( $finfo->startdate ) : ();
1227                $specifier{StartDate} = $start;
1228                $specifier{EndDate}   = $end;
1229            }
1230        }
1231    }
1232
1233    if ( $archiver->category_based ) {
1234        $category = $specifier{Category};
1235        die "Category archive type requires Category parameter"
1236          unless $specifier{Category};
1237        $category = MT::Category->load($category)
1238          unless ref $category;
1239        $ctx->var( 'category_archive', 1 );
1240        $ctx->{__stash}{archive_category} = $category;
1241    }
1242    if ( $archiver->entry_based ) {
1243        $entry = $specifier{Entry};
1244        die "$at archive type requires Entry parameter"
1245          unless $entry;
1246        require MT::Entry;
1247        $entry = MT::Entry->load($entry) if !ref $entry;
1248        $ctx->var( 'entry_archive', 1 );
1249        $ctx->{__stash}{entry} = $entry;
1250    }
1251    if ( $archiver->date_based ) {
1252
1253        # Date-based archive type
1254        $start = $specifier{StartDate};
1255        $end   = $specifier{EndDate};
1256        die "Date-based archive types require StartDate parameter"
1257          unless $specifier{StartDate};
1258        $ctx->var( 'datebased_archive', 1 );
1259    }
1260    if ( $archiver->author_based ) {
1261
1262        # author based archive type
1263        $author = $specifier{Author};
1264        die "Author-based archive type requires Author parameter"
1265          unless $specifier{Author};
1266        require MT::Author;
1267        $author = MT::Author->load($author)
1268          unless ref $author;
1269        $ctx->var( 'author_archive', 1 );
1270        $ctx->{__stash}{author} = $author;
1271    }
1272    local $ctx->{current_timestamp}     = $start if $start;
1273    local $ctx->{current_timestamp_end} = $end   if $end;
1274
1275    my $fmgr = $blog->file_mgr;
1276    $ctx->{__stash}{blog} = $blog;
1277
1278    # Calculate file path and URL for the new entry.
1279    my $file = File::Spec->catfile( $root_path, $map->{__saved_output_file} );
1280    require MT::FileInfo;
1281
1282# This kind of testing should be done at the time we save a post,
1283# not during publishing!!!
1284# if ($archiver->entry_based) {
1285#     my $fcount = MT::FileInfo->count({
1286#         blog_id => $blog->id,
1287#         entry_id => $entry->id,
1288#         file_path => $file},
1289#         { not => { entry_id => 1 } });
1290#     die MT->translate('The same archive file exists. You should change the basename or the archive path. ([_1])', $file) if $fcount > 0;
1291# }
1292
1293    my $url = $blog->archive_url;
1294    $url = $blog->site_url
1295      if $archiver->entry_based && $archiver->entry_class eq 'page';
1296    $url .= '/' unless $url =~ m|/$|;
1297    $url .= $map->{__saved_output_file};
1298
1299    my $cached_tmpl = MT->instance->request('__cached_templates')
1300      || MT->instance->request( '__cached_templates', {} );
1301    my $tmpl_id = $map->template_id;
1302
1303    # template specific for this entry (or page, as the case may be)
1304    if ( $entry && $entry->template_id ) {
1305
1306        # allow entry to override *if* we're publishing an individual
1307        # page, and this is the 'preferred' one...
1308        if ( $archiver->entry_based ) {
1309            if ( $map->is_preferred ) {
1310                $tmpl_id = $entry->template_id;
1311            }
1312        }
1313    }
1314
1315    my $tmpl = $cached_tmpl->{$tmpl_id};
1316    unless ($tmpl) {
1317        $tmpl = MT::Template->load($tmpl_id);
1318        if ($cached_tmpl) {
1319            $cached_tmpl->{$tmpl_id} = $tmpl;
1320        }
1321    }
1322
1323    $tmpl->context($ctx);
1324
1325    # From Here
1326    if ( my $tmpl_param = $archiver->template_params ) {
1327        $tmpl->param($tmpl_param);
1328    }
1329
1330    my ($rel_url) = ( $url =~ m|^(?:[^:]*\:\/\/)?[^/]*(.*)| );
1331    $rel_url =~ s|//+|/|g;
1332
1333    ## Untaint. We have to assume that we can trust the user's setting of
1334    ## the archive_path, and nothing else is based on user input.
1335    ($file) = $file =~ /(.+)/s;
1336
1337    # Clear out all the FileInfo records that might point at the page
1338    # we're about to create
1339    # FYI: if it's an individual entry, we don't use the date as a
1340    #      criterion, since this could actually have changed since
1341    #      the FileInfo was last built. When the date does change,
1342    #      the old date-based archive doesn't necessarily get fixed,
1343    #      but if another comes along it will get corrected
1344    unless ($finfo) {
1345        my %terms;
1346        $terms{blog_id}     = $blog->id;
1347        $terms{category_id} = $category->id if $archiver->category_based;
1348        $terms{author_id}   = $author->id if $archiver->author_based;
1349        $terms{entry_id}    = $entry->id if $archiver->entry_based;
1350        $terms{startdate}   = $start
1351          if $archiver->date_based && ( !$archiver->entry_based );
1352        $terms{archive_type}   = $at;
1353        $terms{templatemap_id} = $map->id;
1354        my @finfos = MT::FileInfo->load( \%terms );
1355
1356        if (   ( scalar @finfos == 1 )
1357            && ( $finfos[0]->file_path eq $file )
1358            && ( ( $finfos[0]->url || '' ) eq $rel_url )
1359            && ( $finfos[0]->template_id == $tmpl_id ) )
1360        {
1361
1362            # if the shoe fits, wear it
1363            $finfo = $finfos[0];
1364        }
1365        else {
1366
1367           # if the shoe don't fit, remove all shoes and create the perfect shoe
1368            foreach (@finfos) { $_->remove(); }
1369
1370            $finfo = MT::FileInfo->set_info_for_url(
1371                $rel_url, $file, $at,
1372                {
1373                    Blog        => $blog->id,
1374                    TemplateMap => $map->id,
1375                    Template    => $tmpl_id,
1376                    ( $archiver->entry_based && $entry )
1377                    ? ( Entry => $entry->id )
1378                    : (),
1379                    StartDate => $start,
1380                    ( $archiver->category_based && $category )
1381                    ? ( Category => $category->id )
1382                    : (),
1383                    ( $archiver->author_based )
1384                    ? ( Author => $author->id )
1385                    : (),
1386                }
1387              )
1388              || die "Couldn't create FileInfo because "
1389              . MT::FileInfo->errstr();
1390        }
1391    }
1392
1393    # If you rebuild when you've just switched to dynamic pages,
1394    # we move the file that might be there so that the custom
1395    # 404 will be triggered.
1396    if ( $tmpl->build_dynamic ) {
1397        rename(
1398            $finfo->file_path,    # is this just $file ?
1399            $finfo->file_path . '.static'
1400        );
1401
1402        ## If the FileInfo is set to static, flip it to virtual.
1403        if ( !$finfo->virtual ) {
1404            $finfo->virtual(1);
1405            $finfo->save();
1406        }
1407    }
1408
1409    return 1 if ( $tmpl->build_dynamic );
1410    return 1 if ( $entry && $entry->status != MT::Entry::RELEASE() );
1411
1412    my $timer = MT->get_timer;
1413    if ($timer) {
1414        $timer->pause_partial;
1415    }
1416    local $timer->{elapsed} = 0 if $timer;
1417
1418    if (
1419        $build_static
1420        && MT->run_callbacks(
1421            'build_file_filter',
1422            Context      => $ctx,
1423            context      => $ctx,
1424            ArchiveType  => $at,
1425            archive_type => $at,
1426            TemplateMap  => $map,
1427            template_map => $map,
1428            Blog         => $blog,
1429            blog         => $blog,
1430            Entry        => $entry,
1431            entry        => $entry,
1432            FileInfo     => $finfo,
1433            file_info    => $finfo,
1434            File         => $file,
1435            file         => $file,
1436            Template     => $tmpl,
1437            template     => $tmpl,
1438            PeriodStart  => $start,
1439            period_start => $start,
1440            Category     => $category,
1441            category     => $category,
1442        )
1443      )
1444    {
1445        if ( $archiver->archive_group_entries ) {
1446
1447            # TBD: Would it help to use MT::Promise here?
1448            my $entries = $archiver->archive_group_entries->($ctx);
1449            $ctx->stash( 'entries', $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    date_based_group_entries( $ctx, 'Yearly',
2337        %param ? sprintf( "%04d%02d%02d000000", $param{year}, 1, 1 ) : undef );
2338}
2339
2340sub yearly_entries_count {
2341    my ( $blog, $at, $entry ) = @_;
2342    return _archive_entries_count(
2343        {
2344            Blog        => $blog,
2345            ArchiveType => $at,
2346            Timestamp   => $entry->authored_on
2347        }
2348    );
2349}
2350
2351sub monthly_archive_label {
2352    MT->translate("MONTHLY_ADV");
2353}
2354
2355sub monthly_archive_file {
2356    my ( $ctx, %param ) = @_;
2357    my $timestamp = $param{Timestamp};
2358    my $file_tmpl = $param{Template};
2359    my $blog      = $ctx->{__stash}{blog};
2360
2361    my $file;
2362    if ($file_tmpl) {
2363        ( $ctx->{current_timestamp}, $ctx->{current_timestamp_end} ) =
2364          start_end_month( $timestamp, $blog );
2365    }
2366    else {
2367        my $start = start_end_month( $timestamp, $blog );
2368        my ( $year, $mon ) = unpack 'A4A2', $start;
2369        $file = sprintf( "%04d/%02d/index", $year, $mon );
2370    }
2371
2372    $file;
2373}
2374
2375sub monthly_archive_title {
2376    my ( $ctx, $entry_or_ts ) = @_;
2377    my $stamp = ref $entry_or_ts ? $entry_or_ts->authored_on : $entry_or_ts;
2378    my $start = start_end_month( $stamp, $ctx->stash('blog') );
2379    MT::Template::Context::_hdlr_date( $ctx,
2380        { ts => $start, 'format' => "%B %Y" } );
2381}
2382
2383sub monthly_date_range { start_end_month(@_) }
2384
2385sub monthly_group_iter {
2386    my ( $ctx, $args ) = @_;
2387    my $blog = $ctx->stash('blog');
2388    my $iter;
2389    my $sort_order =
2390      ( $args->{sort_order} || '' ) eq 'ascend' ? 'ascend' : 'descend';
2391    my $order = ( $sort_order eq 'ascend' ) ? 'asc' : 'desc';
2392
2393    my $ts    = $ctx->{current_timestamp};
2394    my $tsend = $ctx->{current_timestamp_end};
2395
2396    require MT::Entry;
2397    $iter = MT::Entry->count_group_by(
2398        {
2399            blog_id => $blog->id,
2400            status  => MT::Entry::RELEASE(),
2401            ( $ts && $tsend ? ( authored_on => [ $ts, $tsend ] ) : () ),
2402        },
2403        {
2404            ( $ts && $tsend ? ( range_incl => { authored_on => 1 } ) : () ),
2405            group => [
2406                "extract(year from authored_on)",
2407                "extract(month from authored_on)"
2408            ],
2409            $args->{lastn} ? ( limit => $args->{lastn} ) : (),
2410            sort => "extract(year from authored_on) $order,
2411                       extract(month from authored_on) $order"
2412        }
2413    ) or return $ctx->error("Couldn't get monthly archive list");
2414
2415    return sub {
2416        while ( my @row = $iter->() ) {
2417            my $date = sprintf( "%04d%02d%02d000000", $row[1], $row[2], 1 );
2418            my ( $start, $end ) = start_end_month($date);
2419            return (
2420                $row[0],
2421                year  => $row[1],
2422                month => $row[2],
2423                start => $start,
2424                end   => $end
2425            );
2426        }
2427        undef;
2428    };
2429}
2430
2431sub monthly_group_entries {
2432    my ( $ctx, %param ) = @_;
2433    date_based_group_entries( $ctx, 'Monthly',
2434        %param
2435        ? sprintf( "%04d%02d%02d000000", $param{year}, $param{month}, 1 )
2436        : undef );
2437}
2438
2439sub monthly_entries_count {
2440    my ( $blog, $at, $entry ) = @_;
2441    return _archive_entries_count(
2442        {
2443            Blog        => $blog,
2444            ArchiveType => $at,
2445            Timestamp   => $entry->authored_on
2446        }
2447    );
2448}
2449
2450sub category_archive_label {
2451    MT->translate("CATEGORY_ADV");
2452}
2453
2454sub category_archive_title {
2455    my ($ctx) = @_;
2456    my $c = $ctx->stash('category');
2457    $c ? $c->label : '';
2458}
2459
2460sub category_archive_file {
2461    my ( $ctx, %param ) = @_;
2462    my $timestamp = $param{Timestamp};
2463    my $file_tmpl = $param{Template};
2464    my $blog      = $ctx->{__stash}{blog};
2465    my $cat       = $ctx->{__stash}{category};
2466    my $entry     = $ctx->{__stash}{entry};
2467    my $file;
2468
2469    my $this_cat = $cat ? $cat : ( $entry ? $entry->category : undef );
2470    if ($file_tmpl) {
2471        $ctx->stash( 'archive_category', $this_cat );
2472        $ctx->{inside_mt_categories} = 1;
2473        $ctx->{__stash}{category} = $this_cat;
2474    }
2475    else {
2476        if ( !$this_cat ) {
2477            return "";
2478        }
2479        my $label = '';
2480        $label = dirify( $this_cat->label );
2481        if ( $label !~ /\w/ ) {
2482            $label = $this_cat ? "cat" . $this_cat->id : "";
2483        }
2484        $file = sprintf( "%s/index", $this_cat->category_path );
2485    }
2486    $file;
2487}
2488
2489sub category_group_iter {
2490    my ( $ctx, $args ) = @_;
2491
2492    my $blog_id = $ctx->stash('blog')->id;
2493    require MT::Category;
2494    my $iter = MT::Category->load_iter( { blog_id => $blog_id },
2495        { 'sort' => 'label', direction => 'ascend' } );
2496    require MT::Placement;
2497    require MT::Entry;
2498
2499    return sub {
2500        while ( my $c = $iter->() ) {
2501            my @arguments = (
2502                {
2503                    blog_id => $blog_id,
2504                    status  => MT::Entry::RELEASE()
2505                },
2506                {
2507                    'join' => [
2508                        'MT::Placement', 'entry_id', { category_id => $c->id }
2509                    ]
2510                }
2511            );
2512            my $count = MT::Entry->count( @arguments );
2513            next unless $count || $args->{show_empty};
2514            return ( $count, category => $c );
2515        }
2516        undef;
2517      }
2518}
2519
2520sub category_group_entries {
2521    my ( $ctx, %param ) = @_;
2522
2523    my $c = $ctx->stash('archive_category') || $ctx->stash('category');
2524    require MT::Entry;
2525    my @entries = MT::Entry->load(
2526        { status => MT::Entry::RELEASE() },
2527        {
2528            join => [
2529                'MT::Placement', 'entry_id',
2530                { category_id => $c->id }, { unqiue => 1 }
2531            ],
2532            'sort' => 'authored_on',
2533            'direction' => 'descend',
2534        }
2535    );
2536    \@entries;
2537}
2538
2539sub category_entries_count {
2540    my ( $blog, $at, $entry ) = @_;
2541    my $cat = $entry->category;
2542    return 0 unless $cat;
2543    return _archive_entries_count(
2544        {
2545            Blog        => $blog,
2546            ArchiveType => $at,
2547            Category    => $cat
2548        }
2549    );
2550}
2551
2552sub page_archive_label {
2553    MT->translate("PAGE_ADV");
2554}
2555
2556sub page_archive_file {
2557    my ( $ctx, %param ) = @_;
2558    my $timestamp = $param{Timestamp};
2559    my $file_tmpl = $param{Template};
2560    my $blog      = $ctx->{__stash}{blog};
2561    my $page      = $ctx->{__stash}{entry};
2562
2563    my $file;
2564    Carp::croak "archive_file_for Page archive needs a page"
2565      unless $page && $page->isa('MT::Page');
2566    unless ($file_tmpl) {
2567        my $basename = $page->basename();
2568        my $folder   = $page->folder;
2569        my $folder_path;
2570        if ($folder) {
2571            $folder_path = $folder->publish_path || '';
2572            $file =
2573              $folder_path ne '' ? $folder_path . '/' . $basename : $basename;
2574        }
2575        else {
2576            $file = $basename;
2577        }
2578    }
2579    return $file;
2580}
2581
2582sub page_group_iter {
2583    my ( $ctx, $args ) = @_;
2584
2585    require MT::Page;
2586    my $blog_id = $ctx->stash('blog')->id;
2587    my $iter    = MT::Page->load_iter(
2588        {
2589            blog_id => $blog_id,
2590            status  => MT::Entry::RELEASE()
2591        },
2592        { sort => 'title', direction => 'ascend' }
2593    );
2594    return sub {
2595        while ( my $entry = $iter->() ) {
2596            return ( 1, entries => [$entry], entry => $entry );
2597        }
2598        undef;
2599      }
2600}
2601
2602sub individual_archive_label {
2603    MT->translate("INDIVIDUAL_ADV");
2604}
2605
2606sub individual_archive_file {
2607    my ( $ctx, %param ) = @_;
2608    my $timestamp = $param{Timestamp};
2609    my $file_tmpl = $param{Template};
2610    my $blog      = $ctx->{__stash}{blog};
2611    my $entry     = $ctx->{__stash}{entry};
2612
2613    my $file;
2614    Carp::confess "archive_file_for Individual archive needs an entry"
2615      unless $entry;
2616    if ($file_tmpl) {
2617        $ctx->{current_timestamp} = $entry->authored_on;
2618    }
2619    else {
2620        my $basename = $entry->basename();
2621        $basename ||= dirify( $entry->title() );
2622        $file = sprintf( "%04d/%02d/%s",
2623            unpack( 'A4A2', $entry->authored_on ), $basename );
2624    }
2625    $file;
2626}
2627
2628sub individual_archive_title { $_[1]->title }
2629
2630sub individual_group_iter {
2631    my ( $ctx, $args ) = @_;
2632
2633    my $order =
2634      ( $args->{sort_order} || '' ) eq 'ascend' ? 'ascend' : 'descend';
2635
2636    my $blog_id = $ctx->stash('blog')->id;
2637    require MT::Entry;
2638    my $iter = MT::Entry->load_iter(
2639        {
2640            blog_id => $blog_id,
2641            status  => MT::Entry::RELEASE()
2642        },
2643        {
2644            'sort'    => 'authored_on',
2645            direction => $order,
2646            $args->{lastn} ? ( limit => $args->{lastn} ) : ()
2647        }
2648    );
2649    return sub {
2650        while ( my $entry = $iter->() ) {
2651            return ( 1, entries => [$entry], entry => $entry );
2652        }
2653        undef;
2654      }
2655}
2656
2657sub daily_archive_label {
2658    MT->translate("DAILY_ADV");
2659}
2660
2661sub daily_archive_file {
2662    my ( $ctx, %param ) = @_;
2663    my $timestamp = $param{Timestamp};
2664    my $file_tmpl = $param{Template};
2665    my $blog      = $ctx->{__stash}{blog};
2666
2667    my $file;
2668    if ($file_tmpl) {
2669        ( $ctx->{current_timestamp}, $ctx->{current_timestamp_end} ) =
2670          start_end_day($timestamp);
2671    }
2672    else {
2673        my $start = start_end_day($timestamp);
2674        my ( $year, $mon, $mday ) = unpack 'A4A2A2', $start;
2675        $file = sprintf( "%04d/%02d/%02d/index", $year, $mon, $mday );
2676    }
2677    $file;
2678}
2679
2680sub daily_archive_title {
2681    my $stamp = ref $_[1] ? $_[1]->authored_on : $_[1];
2682    my $start = start_end_day( $stamp, $_[0]->stash('blog') );
2683    MT::Template::Context::_hdlr_date( $_[0],
2684        { ts => $start, 'format' => "%x" } );
2685}
2686
2687sub daily_date_range { start_end_day(@_) }
2688
2689sub daily_group_iter {
2690    my ( $ctx, $args ) = @_;
2691    my $blog = $ctx->stash('blog');
2692    my $iter;
2693    my $sort_order =
2694      ( $args->{sort_order} || '' ) eq 'ascend' ? 'ascend' : 'descend';
2695    my $order = ( $sort_order eq 'ascend' ) ? 'asc' : 'desc';
2696
2697    my $ts    = $ctx->{current_timestamp};
2698    my $tsend = $ctx->{current_timestamp_end};
2699
2700    require MT::Entry;
2701    $iter