root/branches/release-29/lib/MT/WeblogPublisher.pm @ 1333

Revision 1333, 158.5 kB (checked in by takayama, 22 months ago)

Fixed BugId:65812
* Changed scheme_version to 4.0037
* Added basename column to MT_Author

  • Assigning basename when author saved

* Changed to use MTAuthorBasename instead of MTAuthorDisplayName

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