root/branches/mt4.11/lib/MT/WeblogPublisher.pm @ 1362

Revision 1362, 159.6 kB (checked in by bchoate, 22 months ago)

Fixes for timer bug affecting MT-Wizard. BugId:68176

  • 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
1198    my $finfo;
1199    my $archiver = $mt->archiver($at);
1200    my ( $entry, $start, $end, $category, $author );
1201
1202    if ( $finfo = $specifier{FileInfo} ) {
1203        $specifier{Author}   = $finfo->author_id   if $finfo->author_id;
1204        $specifier{Category} = $finfo->category_id if $finfo->category_id;
1205        $specifier{Entry}    = $finfo->entry_id    if $finfo->entry_id;
1206        $map ||= MT::TemplateMap->load( $finfo->templatemap_id );
1207        $at  ||= $finfo->archive_type;
1208        if ( $finfo->startdate ) {
1209            if ( my $range = $archiver->date_range ) {
1210                my ( $start, $end ) =
1211                  $range ? $range->( $finfo->startdate ) : ();
1212                $specifier{StartDate} = $start;
1213                $specifier{EndDate}   = $end;
1214            }
1215        }
1216    }
1217
1218    if ( $archiver->category_based ) {
1219        $category = $specifier{Category};
1220        die "Category archive type requires Category parameter"
1221          unless $specifier{Category};
1222        $category = MT::Category->load($category)
1223          unless ref $category;
1224        $ctx->var( 'category_archive', 1 );
1225        $ctx->{__stash}{archive_category} = $category;
1226    }
1227    if ( $archiver->entry_based ) {
1228        $entry = $specifier{Entry};
1229        die "$at archive type requires Entry parameter"
1230          unless $entry;
1231        require MT::Entry;
1232        $entry = MT::Entry->load($entry) if !ref $entry;
1233        $ctx->var( 'entry_archive', 1 );
1234        $ctx->{__stash}{entry} = $entry;
1235    }
1236    if ( $archiver->date_based ) {
1237
1238        # Date-based archive type
1239        $start = $specifier{StartDate};
1240        $end   = $specifier{EndDate};
1241        die "Date-based archive types require StartDate parameter"
1242          unless $specifier{StartDate};
1243        $ctx->var( 'datebased_archive', 1 );
1244    }
1245    if ( $archiver->author_based ) {
1246
1247        # author based archive type
1248        $author = $specifier{Author};
1249        die "Author-based archive type requires Author parameter"
1250          unless $specifier{Author};
1251        require MT::Author;
1252        $author = MT::Author->load($author)
1253          unless ref $author;
1254        $ctx->var( 'author_archive', 1 );
1255        $ctx->{__stash}{author} = $author;
1256    }
1257    local $ctx->{current_timestamp}     = $start if $start;
1258    local $ctx->{current_timestamp_end} = $end   if $end;
1259
1260    my $fmgr = $blog->file_mgr;
1261    $ctx->{__stash}{blog} = $blog;
1262
1263    # Calculate file path and URL for the new entry.
1264    my $file = File::Spec->catfile( $root_path, $map->{__saved_output_file} );
1265    require MT::FileInfo;
1266
1267# This kind of testing should be done at the time we save a post,
1268# not during publishing!!!
1269# if ($archiver->entry_based) {
1270#     my $fcount = MT::FileInfo->count({
1271#         blog_id => $blog->id,
1272#         entry_id => $entry->id,
1273#         file_path => $file},
1274#         { not => { entry_id => 1 } });
1275#     die MT->translate('The same archive file exists. You should change the basename or the archive path. ([_1])', $file) if $fcount > 0;
1276# }
1277
1278    my $url = $blog->archive_url;
1279    $url = $blog->site_url
1280      if $archiver->entry_based && $archiver->entry_class eq 'page';
1281    $url .= '/' unless $url =~ m|/$|;
1282    $url .= $map->{__saved_output_file};
1283
1284    my $cached_tmpl = MT->instance->request('__cached_templates')
1285      || MT->instance->request( '__cached_templates', {} );
1286    my $tmpl_id = $map->template_id;
1287
1288    # template specific for this entry (or page, as the case may be)
1289    if ( $entry && $entry->template_id ) {
1290
1291        # allow entry to override *if* we're publishing an individual
1292        # page, and this is the 'preferred' one...
1293        if ( $archiver->entry_based ) {
1294            if ( $map->is_preferred ) {
1295                $tmpl_id = $entry->template_id;
1296            }
1297        }
1298    }
1299
1300    my $tmpl = $cached_tmpl->{$tmpl_id};
1301    unless ($tmpl) {
1302        $tmpl = MT::Template->load($tmpl_id);
1303        if ($cached_tmpl) {
1304            $cached_tmpl->{$tmpl_id} = $tmpl;
1305        }
1306    }
1307
1308    $tmpl->context($ctx);
1309
1310    # From Here
1311    if ( my $tmpl_param = $archiver->template_params ) {
1312        $tmpl->param($tmpl_param);
1313    }
1314
1315    my ($rel_url) = ( $url =~ m|^(?:[^:]*\:\/\/)?[^/]*(.*)| );
1316    $rel_url =~ s|//+|/|g;
1317
1318    ## Untaint. We have to assume that we can trust the user's setting of
1319    ## the archive_path, and nothing else is based on user input.
1320    ($file) = $file =~ /(.+)/s;
1321
1322    # Clear out all the FileInfo records that might point at the page
1323    # we're about to create
1324    # FYI: if it's an individual entry, we don't use the date as a
1325    #      criterion, since this could actually have changed since
1326    #      the FileInfo was last built. When the date does change,
1327    #      the old date-based archive doesn't necessarily get fixed,
1328    #      but if another comes along it will get corrected
1329    unless ($finfo) {
1330        my %terms;
1331        $terms{blog_id}     = $blog->id;
1332        $terms{category_id} = $category->id if $archiver->category_based;
1333        $terms{author_id}   = $author->id if $archiver->author_based;
1334        $terms{entry_id}    = $entry->id if $archiver->entry_based;
1335        $terms{startdate}   = $start
1336          if $archiver->date_based && ( !$archiver->entry_based );
1337        $terms{archive_type}   = $at;
1338        $terms{templatemap_id} = $map->id;
1339        my @finfos = MT::FileInfo->load( \%terms );
1340
1341        if (   ( scalar @finfos == 1 )
1342            && ( $finfos[0]->file_path eq $file )
1343            && ( ( $finfos[0]->url || '' ) eq $rel_url )
1344            && ( $finfos[0]->template_id == $tmpl_id ) )
1345        {
1346
1347            # if the shoe fits, wear it
1348            $finfo = $finfos[0];
1349        }
1350        else {
1351
1352           # if the shoe don't fit, remove all shoes and create the perfect shoe
1353            foreach (@finfos) { $_->remove(); }
1354
1355            $finfo = MT::FileInfo->set_info_for_url(
1356                $rel_url, $file, $at,
1357                {
1358                    Blog        => $blog->id,
1359                    TemplateMap => $map->id,
1360                    Template    => $tmpl_id,
1361                    ( $archiver->entry_based && $entry )
1362                    ? ( Entry => $entry->id )
1363                    : (),
1364                    StartDate => $start,
1365                    ( $archiver->category_based && $category )
1366                    ? ( Category => $category->id )
1367                    : (),
1368                    ( $archiver->author_based )
1369                    ? ( Author => $author->id )
1370                    : (),
1371                }
1372              )
1373              || die "Couldn't create FileInfo because "
1374              . MT::FileInfo->errstr();
1375        }
1376    }
1377
1378    # If you rebuild when you've just switched to dynamic pages,
1379    # we move the file that might be there so that the custom
1380    # 404 will be triggered.
1381    if ( $tmpl->build_dynamic ) {
1382        rename(
1383            $finfo->file_path,    # is this just $file ?
1384            $finfo->file_path . '.static'
1385        );
1386
1387        ## If the FileInfo is set to static, flip it to virtual.
1388        if ( !$finfo->virtual ) {
1389            $finfo->virtual(1);
1390            $finfo->save();
1391        }
1392    }
1393
1394    return 1 if ( $tmpl->build_dynamic );
1395    return 1 if ( $entry && $entry->status != MT::Entry::RELEASE() );
1396
1397    my $timer = MT->get_timer;
1398    if ($timer) {
1399        $timer->pause_partial;
1400    }
1401    local $timer->{elapsed} = 0 if $timer;
1402
1403    if (
1404        $build_static
1405        && MT->run_callbacks(
1406            'build_file_filter',
1407            Context      => $ctx,
1408            context      => $ctx,
1409            ArchiveType  => $at,
1410            archive_type => $at,
1411            TemplateMap  => $map,
1412            template_map => $map,
1413            Blog         => $blog,
1414            blog         => $blog,
1415            Entry        => $entry,
1416            entry        => $entry,
1417            FileInfo     => $finfo,
1418            file_info    => $finfo,
1419            File         => $file,
1420            file         => $file,
1421            Template     => $tmpl,
1422            template     => $tmpl,
1423            PeriodStart  => $start,
1424            period_start => $start,
1425            Category     => $category,
1426            category     => $category,
1427        )
1428      )
1429    {
1430        if ( $archiver->archive_group_entries ) {
1431
1432            # TBD: Would it help to use MT::Promise here?
1433            my $entries = $archiver->archive_group_entries->($ctx);
1434            $ctx->stash( 'entries', $entries );
1435        }
1436
1437        my $html = undef;
1438        $ctx->stash( 'blog', $blog );
1439        $ctx->stash( 'entry', $entry ) if $entry;
1440
1441        require MT::Request;
1442        MT::Request->instance->cache('build_template', $tmpl);
1443
1444        $html = $tmpl->build( $ctx, $cond );
1445        unless (defined($html)) {
1446            $timer->unpause if $timer;
1447            return $mt->error(
1448            (
1449                $category ? MT->translate(
1450                    "An error occurred publishing [_1] '[_2]': [_3]",
1451                    lowercase( $category->class_label ),
1452                    $category->id,
1453                    $tmpl->errstr
1454                  )
1455                : $entry ? MT->translate(
1456                    "An error occurred publishing [_1] '[_2]': [_3]",
1457                    lowercase( $entry->class_label ),
1458                    $entry->title,
1459                    $tmpl->errstr
1460                  )
1461                : MT->translate(
1462"An error occurred publishing date-based archive '[_1]': [_2]",
1463                    $at . $start,
1464                    $tmpl->errstr
1465                )
1466            )
1467          );
1468        }
1469        my $orig_html = $html;
1470        MT->run_callbacks(
1471            'build_page',
1472            Context      => $ctx,
1473            context      => $ctx,
1474            ArchiveType  => $at,
1475            archive_type => $at,
1476            TemplateMap  => $map,
1477            template_map => $map,
1478            Blog         => $blog,
1479            blog         => $blog,
1480            Entry        => $entry,
1481            entry        => $entry,
1482            FileInfo     => $finfo,
1483            file_info    => $finfo,
1484            PeriodStart  => $start,
1485            period_start => $start,
1486            Category     => $category,
1487            category     => $category,
1488            RawContent   => \$orig_html,
1489            raw_content  => \$orig_html,
1490            Content      => \$html,
1491            content      => \$html,
1492            BuildResult  => \$orig_html,
1493            build_result => \$orig_html,
1494            Template     => $tmpl,
1495            template     => $tmpl,
1496            File         => $file,
1497            file         => $file
1498        );
1499        ## First check whether the content is actually
1500        ## changed. If not, we won't update the published
1501        ## file, so as not to modify the mtime.
1502        unless ($fmgr->content_is_updated( $file, \$html )) {
1503            $timer->unpause if $timer;
1504            return 1;
1505        }
1506
1507        ## Determine if we need to build directory structure,
1508        ## and build it if we do. DirUmask determines
1509        ## directory permissions.
1510        require File::Spec;
1511        my $path = dirname($file);
1512        $path =~ s!/$!!
1513          unless $path eq '/'; ## OS X doesn't like / at the end in mkdir().
1514        unless ( $fmgr->exists($path) ) {
1515            if (!$fmgr->mkpath($path)) {
1516                $timer->unpause if $timer;
1517                return $mt->trans_error( "Error making path '[_1]': [_2]",
1518                    $path, $fmgr->errstr );
1519            }
1520        }
1521
1522        ## By default we write all data to temp files, then rename
1523        ## the temp files to the real files (an atomic
1524        ## operation). Some users don't like this (requires too
1525        ## liberal directory permissions). So we have a config
1526        ## option to turn it off (NoTempFiles).
1527        my $use_temp_files = !$mt->{NoTempFiles};
1528        my $temp_file = $use_temp_files ? "$file.new" : $file;
1529        unless ( defined $fmgr->put_data( $html, $temp_file ) ) {
1530            $timer->unpause if $timer;
1531            return $mt->trans_error( "Writing to '[_1]' failed: [_2]",
1532                $temp_file, $fmgr->errstr );
1533        }
1534        if ($use_temp_files) {
1535            if (!$fmgr->rename( $temp_file, $file )) {
1536                $timer->unpause if $timer;
1537                return $mt->trans_error(
1538                    "Renaming tempfile '[_1]' failed: [_2]",
1539                    $temp_file, $fmgr->errstr );
1540            }
1541        }
1542        MT->run_callbacks(
1543            'build_file',
1544            Context      => $ctx,
1545            context      => $ctx,
1546            ArchiveType  => $at,
1547            archive_type => $at,
1548            TemplateMap  => $map,
1549            template_map => $map,
1550            FileInfo     => $finfo,
1551            file_info    => $finfo,
1552            Blog         => $blog,
1553            blog         => $blog,
1554            Entry        => $entry,
1555            entry        => $entry,
1556            PeriodStart  => $start,
1557            period_start => $start,
1558            RawContent   => \$orig_html,
1559            raw_content  => \$orig_html,
1560            Content      => \$html,
1561            content      => \$html,
1562            BuildResult  => \$orig_html,
1563            build_result => \$orig_html,
1564            Template     => $tmpl,
1565            template     => $tmpl,
1566            Category     => $category,
1567            category     => $category,
1568            File         => $file,
1569            file         => $file
1570        );
1571
1572    }
1573    $timer->mark("total:rebuild_file[template_id:" . $tmpl->id . "]")
1574        if $timer;
1575    1;
1576}
1577
1578sub rebuild_indexes {
1579    my $mt    = shift;
1580    my %param = @_;
1581    require MT::Template;
1582    require MT::Template::Context;
1583    require MT::Entry;
1584    my $blog;
1585    unless ( $blog = $param{Blog} ) {
1586        my $blog_id = $param{BlogID};
1587        $blog = MT::Blog->load($blog_id)
1588          or return $mt->error(
1589            MT->translate(
1590                "Load of blog '[_1]' failed: [_2]", $blog_id,
1591                MT::Blog->errstr
1592            )
1593          );
1594    }
1595    my $tmpl = $param{Template};
1596    unless ($blog) {
1597        $blog = MT::Blog->load( $tmpl->blog_id );
1598    }
1599    return 1 if $blog->is_dynamic;
1600    my $iter;
1601    if ($tmpl) {
1602        my $i = 0;
1603        $iter = sub { $i++ < 1 ? $tmpl : undef };
1604    }
1605    else {
1606        $iter = MT::Template->load_iter(
1607            {
1608                type    => 'index',
1609                blog_id => $blog->id
1610            }
1611        );
1612    }
1613    local *FH;
1614    my $site_root = $blog->site_path;
1615    return $mt->error(
1616        MT->translate("You did not set your blog publishing path") )
1617      unless $site_root;
1618    my $fmgr = $blog->file_mgr;
1619    while ( my $tmpl = $iter->() ) {
1620        ## Skip index templates that the user has designated not to be
1621        ## rebuilt automatically. We need to do the defined-ness check
1622        ## because we added the flag in 2.01, and for templates saved
1623        ## before that time, the rebuild_me flag will be undefined. But
1624        ## we assume that these templates should be rebuilt, since that
1625        ## was the previous behavior.
1626        ## Note that dynamic templates do need to be "rebuilt"--the
1627        ## FileInfo table needs to be maintained.
1628        if ( !$tmpl->build_dynamic && !$param{Force} ) {
1629            next if ( defined $tmpl->rebuild_me && !$tmpl->rebuild_me );
1630        }
1631        my $file = $tmpl->outfile;
1632        $file = '' unless defined $file;
1633        if ( $tmpl->build_dynamic && ( $file eq '' ) ) {
1634            next;
1635        }
1636        return $mt->error(
1637            MT->translate(
1638                "Template '[_1]' does not have an Output File.",
1639                $tmpl->name
1640            )
1641        ) unless $file ne '';
1642        my $url = join( '/', $blog->site_url, $file );
1643        unless ( File::Spec->file_name_is_absolute($file) ) {
1644            $file = File::Spec->catfile( $site_root, $file );
1645        }
1646
1647        # Everything from here out is identical with rebuild_file
1648        my ($rel_url) = ( $url =~ m|^(?:[^:]*\:\/\/)?[^/]*(.*)| );
1649        $rel_url =~ s|//+|/|g;
1650        ## Untaint. We have to assume that we can trust the user's setting of
1651        ## the site_path and the template outfile.
1652        ($file) = $file =~ /(.+)/s;
1653        my $finfo;
1654        require MT::FileInfo;
1655        my @finfos = MT::FileInfo->load(
1656            {
1657                blog_id     => $tmpl->blog_id,
1658                template_id => $tmpl->id
1659            }
1660        );
1661        if (   ( scalar @finfos == 1 )
1662            && ( $finfos[0]->file_path eq $file )
1663            && ( ( $finfos[0]->url || '' ) eq $rel_url ) )
1664        {
1665            $finfo = $finfos[0];
1666        }
1667        else {
1668            foreach (@finfos) { $_->remove(); }
1669            $finfo = MT::FileInfo->set_info_for_url(
1670                $rel_url, $file, 'index',
1671                {
1672                    Blog     => $tmpl->blog_id,
1673                    Template => $tmpl->id,
1674                }
1675              )
1676              || die "Couldn't create FileInfo because " . MT::FileInfo->errstr;
1677        }
1678        if ( $tmpl->build_dynamic ) {
1679            rename( $file, $file . ".static" );
1680
1681            ## If the FileInfo is set to static, flip it to virtual.
1682            if ( !$finfo->virtual ) {
1683                $finfo->virtual(1);
1684                $finfo->save();
1685            }
1686        }
1687
1688        next if ( $tmpl->build_dynamic );
1689
1690        ## We're not building dynamically, so if the FileInfo is currently
1691        ## set as dynamic (virtual), change it to static.
1692        if ( $finfo && $finfo->virtual ) {
1693            $finfo->virtual(0);
1694            $finfo->save();
1695        }
1696
1697        my $timer = MT->get_timer;
1698        if ($timer) {
1699            $timer->pause_partial;
1700        }
1701        local $timer->{elapsed} = 0 if $timer;
1702
1703        my $ctx = MT::Template::Context->new;
1704        next
1705          unless (
1706            MT->run_callbacks(
1707                'build_file_filter',
1708                Context      => $ctx,
1709                context      => $ctx,
1710                ArchiveType  => 'index',
1711                archive_type => 'index',
1712                Blog         => $blog,
1713                blog         => $blog,
1714                FileInfo     => $finfo,
1715                file_info    => $finfo,
1716                Template     => $tmpl,
1717                template     => $tmpl,
1718                File         => $file,
1719                file         => $file
1720            )
1721          );
1722        $ctx->stash( 'blog', $blog );
1723
1724        require MT::Request;
1725        MT::Request->instance->cache('build_template', $tmpl);
1726
1727        my $html = $tmpl->build($ctx);
1728        unless (defined $html) {
1729            $timer->unpause if $timer;
1730            return $mt->error( $tmpl->errstr );
1731        }
1732
1733        my $orig_html = $html;
1734        MT->run_callbacks(
1735            'build_page',
1736            Context      => $ctx,
1737            context      => $ctx,
1738            Blog         => $blog,
1739            blog         => $blog,
1740            FileInfo     => $finfo,
1741            file_info    => $finfo,
1742            ArchiveType  => 'index',
1743            archive_type => 'index',
1744            RawContent   => \$orig_html,
1745            raw_content  => \$orig_html,
1746            Content      => \$html,
1747            content      => \$html,
1748            BuildResult  => \$orig_html,
1749            build_result => \$orig_html,
1750            Template     => $tmpl,
1751            template     => $tmpl,
1752            File         => $file,
1753            file         => $file
1754        );
1755
1756        ## First check whether the content is actually changed. If not,
1757        ## we won't update the published file, so as not to modify the mtime.
1758        next unless $fmgr->content_is_updated( $file, \$html );
1759
1760        ## Determine if we need to build directory structure,
1761        ## and build it if we do. DirUmask determines
1762        ## directory permissions.
1763        require File::Spec;
1764        my $path = dirname($file);
1765        $path =~ s!/$!!
1766          unless $path eq '/';    ## OS X doesn't like / at the end in mkdir().
1767        unless ( $fmgr->exists($path) ) {
1768            if (! $fmgr->mkpath($path) ) {
1769                $timer->unpause if $timer;
1770                return $mt->trans_error( "Error making path '[_1]': [_2]",
1771                    $path, $fmgr->errstr );
1772            }
1773        }
1774
1775        ## Update the published file.
1776        my $use_temp_files = !$mt->{NoTempFiles};
1777        my $temp_file = $use_temp_files ? "$file.new" : $file;
1778        unless (defined( $fmgr->put_data( $html, $temp_file ) )) {
1779            $timer->unpause if $timer;
1780            return $mt->trans_error( "Writing to '[_1]' failed: [_2]",
1781                $temp_file, $fmgr->errstr );
1782        }
1783        if ($use_temp_files) {
1784            if (!$fmgr->rename( $temp_file, $file )) {
1785                $timer->unpause if $timer;
1786                return $mt->trans_error( "Renaming tempfile '[_1]' failed: [_2]",
1787                    $temp_file, $fmgr->errstr );
1788            }
1789        }
1790        MT->run_callbacks(
1791            'build_file',
1792            Context      => $ctx,
1793            context      => $ctx,
1794            ArchiveType  => 'index',
1795            archive_type => 'index',
1796            FileInfo     => $finfo,
1797            file_info    => $finfo,
1798            Blog         => $blog,
1799            blog         => $blog,
1800            RawContent   => \$orig_html,
1801            raw_content  => \$orig_html,
1802            Content      => \$html,
1803            content      => \$html,
1804            BuildResult  => \$orig_html,
1805            build_result => \$orig_html,
1806            Template     => $tmpl,
1807            template     => $tmpl,
1808            File         => $file,
1809            file         => $file
1810        );
1811
1812        $timer->mark("total:rebuild_indexes[template_id:" . $tmpl->id . ";file:$file]")
1813            if $timer;
1814    }
1815    1;
1816}
1817
1818sub rebuild_from_fileinfo {
1819    my $pub = shift;
1820    my ($fi) = @_;
1821
1822    require MT::Blog;
1823    require MT::Entry;
1824    require MT::Category;
1825    require MT::Template;
1826    require MT::TemplateMap;
1827    require MT::Template::Context;
1828
1829    my $at = $fi->archive_type
1830      or return $pub->error(
1831        MT->translate( "Parameter '[_1]' is required", 'ArchiveType' ) );
1832
1833    # callback for custom archive types
1834    return
1835      unless MT->run_callbacks(
1836        'build_archive_filter',
1837        archive_type => $at,
1838        file_info    => $fi
1839      );
1840
1841    if ( $at eq 'index' ) {
1842        $pub->rebuild_indexes(
1843            BlogID   => $fi->blog_id,
1844            Template => MT::Template->load( $fi->template_id ),
1845            Force    => 1,
1846        ) or return;
1847        return 1;
1848    }
1849
1850    return 1 if $at eq 'None';
1851
1852    my ( $start, $end );
1853    my $blog = MT::Blog->load( $fi->blog_id )
1854      if $fi->blog_id;
1855    my $entry = MT::Entry->load( $fi->entry_id )
1856      or return $pub->error(
1857        MT->translate( "Parameter '[_1]' is required", 'Entry' ) )
1858      if $fi->entry_id;
1859    if ( $fi->startdate ) {
1860        my $archiver = $pub->archiver($at);
1861
1862        if ( my $range = $archiver->date_range ) {
1863            ( $start, $end ) = $range->( $fi->startdate );
1864            $entry = MT::Entry->load( { authored_on => [ $start, $end ] },
1865                { range_incl => { authored_on => 1 }, limit => 1 } )
1866              or return $pub->error(
1867                MT->translate( "Parameter '[_1]' is required", 'Entry' ) );
1868        }
1869    }
1870    my $cat = MT::Category->load( $fi->category_id )
1871      if $fi->category_id;
1872    my $author = MT::Author->load( $fi->author_id )
1873      if $fi->author_id;
1874
1875    ## Load the template-archive-type map entries for this blog and
1876    ## archive type. We do this before we load the list of entries, because
1877    ## we will run through the files and check if we even need to rebuild
1878    ## anything. If there is nothing to rebuild at all for this entry,
1879    ## we save some time by not loading the list of entries.
1880    my $map = MT::TemplateMap->load( $fi->templatemap_id );
1881    my $file = $pub->archive_file_for( $entry, $blog, $at, $cat, $map,
1882        undef, $author );
1883    if ( !defined($file) ) {
1884        return $pub->error( $blog->errstr() );
1885    }
1886    $map->{__saved_output_file} = $file;
1887
1888    my $ctx = MT::Template::Context->new;
1889    $ctx->{current_archive_type} = $at;
1890    if ( $start && $end ) {
1891        $ctx->{current_timestamp} = $start;
1892        $ctx->{current_timestamp_end} = $end;
1893    }
1894
1895    my $arch_root =
1896      ( $at eq 'Page' ) ? $blog->site_path : $blog->archive_path;
1897    return $pub->error(
1898        MT->translate("You did not set your blog publishing path") )
1899      unless $arch_root;
1900
1901    my %cond;
1902    $pub->rebuild_file( $blog, $arch_root, $map, $at, $ctx, \%cond, 1,
1903        FileInfo => $fi, )
1904      or return;
1905
1906    1;
1907}
1908
1909sub trans_error {
1910    my $this = shift;
1911    return $this->error( MT->translate(@_) );
1912}
1913
1914sub publish_future_posts {
1915    my $this = shift;
1916
1917    require MT::Blog;
1918    require MT::Entry;
1919    require MT::Util;
1920    my $mt            = MT->instance;
1921    my $total_changed = 0;
1922    my @blogs = MT::Blog->load(undef, {
1923        join => MT::Entry->join_on('blog_id', {
1924            status => MT::Entry::FUTURE(),
1925        }, { unique => 1 })
1926    });
1927    foreach my $blog (@blogs) {
1928        my @ts = MT::Util::offset_time_list( time, $blog );
1929        my $now = sprintf "%04d%02d%02d%02d%02d%02d", $ts[5] + 1900, $ts[4] + 1,
1930          @ts[ 3, 2, 1, 0 ];
1931        my $iter = MT::Entry->load_iter(
1932            {
1933                blog_id => $blog->id,
1934                status  => MT::Entry::FUTURE(),
1935                class   => '*'
1936            },
1937            {
1938                'sort'    => 'authored_on',
1939                direction => 'descend'
1940            }
1941        );
1942        my @queue;
1943        while ( my $entry = $iter->() ) {
1944            push @queue, $entry->id if $entry->authored_on le $now;
1945        }
1946
1947        my $changed = 0;
1948        my @results;
1949        my %rebuild_queue;
1950        my %ping_queue;
1951        foreach my $entry_id (@queue) {
1952            my $entry = MT::Entry->load($entry_id);
1953            $entry->status( MT::Entry::RELEASE() );
1954            $entry->save
1955              or die $entry->errstr;
1956
1957            $rebuild_queue{ $entry->id } = $entry;
1958            $ping_queue{ $entry->id }    = 1;
1959            my $n = $entry->next(1);
1960            $rebuild_queue{ $n->id } = $n if $n;
1961            my $p = $entry->previous(1);
1962            $rebuild_queue{ $p->id } = $p if $p;
1963            $changed++;
1964            $total_changed++;
1965        }
1966        if ($changed) {
1967            my %rebuilt_okay;
1968            my $rebuilt;
1969            eval {
1970                foreach my $id ( keys %rebuild_queue )
1971                {
1972                    my $entry = $rebuild_queue{$id};
1973                    $mt->rebuild_entry( Entry => $entry, Blog => $blog )
1974                      or die $mt->errstr;
1975                    $rebuilt_okay{$id} = 1;
1976                    if ( $ping_queue{$id} ) {
1977                        $mt->ping_and_save( Entry => $entry, Blog => $blog );
1978                    }
1979                    $rebuilt++;
1980                }
1981                $mt->rebuild_indexes( Blog => $blog )
1982                  or die $mt->errstr;
1983            };
1984            if ( my $err = $@ ) {
1985
1986                # a fatal error occured while processing the rebuild
1987                # step. LOG the error and revert the entry/entries:
1988                require MT::Log;
1989                $mt->log(
1990                    {
1991                        message => $mt->translate(
1992"An error occurred while publishing scheduled entries: [_1]",
1993                            $err
1994                        ),
1995                        class   => "system",
1996                        blog_id => $blog->id,
1997                        level   => MT::Log::ERROR()
1998                    }
1999                );
2000                foreach my $id (@queue) {
2001                    next if exists $rebuilt_okay{$id};
2002                    my $e = $rebuild_queue{$id};
2003                    next unless $e;
2004                    $e->status( MT::Entry::FUTURE() );
2005                    $e->save or die $e->errstr;
2006                }
2007            }
2008        }
2009    }
2010    $total_changed > 0 ? 1 : 0;
2011}
2012
2013sub remove_entry_archive_file {
2014    my $mt    = shift;
2015    my %param = @_;
2016
2017    my $entry = $param{Entry};
2018    my $at    = $param{ArchiveType} || 'Individual';
2019    my $cat   = $param{Category};
2020    my $auth  = $param{Author};
2021
2022    require MT::TemplateMap;
2023    my $blog = $param{Blog};
2024    unless ($blog) {
2025        if ($entry) {
2026            $blog = $entry->blog;
2027        }
2028        elsif ($cat) {
2029            require MT::Blog;
2030            $blog = MT::Blog->load( $cat->blog_id );
2031        }
2032    }
2033    my @map = MT::TemplateMap->load(
2034        {
2035            archive_type => $at,
2036            blog_id      => $blog->id,
2037            $param{TemplateID} ? ( template_id => $param{TemplateID} ) : (),
2038        }
2039    );
2040    return 1 unless @map;
2041
2042    my $fmgr = $blog->file_mgr;
2043    my $arch_root = ( $at eq 'Page' ) ? $blog->site_path : $blog->archive_path;
2044
2045    require File::Spec;
2046    for my $map (@map) {
2047        my $file =
2048          $mt->archive_file_for( $entry, $blog, $at, $cat, $map, undef, $auth );
2049        $file = File::Spec->catfile( $arch_root, $file );
2050        if ( !defined($file) ) {
2051            die MT->translate( $blog->errstr() );
2052            return $mt->error( MT->translate( $blog->errstr() ) );
2053        }
2054
2055        # Run callbacks
2056        MT->run_callbacks( 'pre_delete_archive_file', $file, $at, $entry);
2057
2058        $fmgr->delete($file);
2059
2060        # Run callbacks
2061        MT->run_callbacks( 'post_delete_archive_file', $file, $at, $entry);
2062    }
2063    1;
2064}
2065
2066##
2067## archive_file_for takes an entry to determine the timestamps,
2068## but if the entry is not available it uses the time_start
2069## and time_end values
2070##
2071sub archive_file_for {
2072    my $mt = shift;
2073
2074    init_archive_types() unless %ArchiveTypes;
2075
2076    my ( $entry, $blog, $at, $cat, $map, $timestamp, $author ) = @_;
2077    return if $at eq 'None';
2078    my $archiver = $ArchiveTypes{$at};
2079    return '' unless $archiver;
2080
2081    my $file;
2082    if ( $blog->is_dynamic ) {
2083        require MT::TemplateMap;
2084                my $archiver = $ArchiveTypes{$at};
2085                if ($archiver) {
2086                $map = MT::TemplateMap->new;
2087                $map->file_template( $archiver->dynamic_template );
2088                }
2089    }
2090    unless ($map) {
2091        my $cache = MT::Request->instance->cache('maps');
2092        unless ($cache) {
2093            MT::Request->instance->cache( 'maps', $cache = {} );
2094        }
2095        unless ( $map = $cache->{ $blog->id . $at } ) {
2096            require MT::TemplateMap;
2097            $map = MT::TemplateMap->load(
2098                {
2099                    blog_id      => $blog->id,
2100                    archive_type => $at,
2101                    is_preferred => 1
2102                }
2103            );
2104            $cache->{ $blog->id . $at } = $map if $map;
2105        }
2106    }
2107    my $file_tmpl = $map->file_template if $map;
2108    unless ($file_tmpl) {
2109        if ( my $tmpls = $archiver->default_archive_templates ) {
2110            my ($default) = grep { $_->{default} } @$tmpls;
2111            $file_tmpl = $default->{template} if $default;
2112        }
2113    }
2114    $file_tmpl ||= '';
2115    my ($ctx);
2116    if ( $file_tmpl =~ m/\%[_-]?[A-Za-z]/ ) {
2117        if ( $file_tmpl =~ m/<\$?MT/ ) {
2118            $file_tmpl =~
2119s!(<\$?MT[^>]+?>)|(%[_-]?[A-Za-z])!$1 ? $1 : '<MTFileTemplate format="'. $2 . '">'!gie;
2120        }
2121        else {
2122            $file_tmpl = qq{<MTFileTemplate format="$file_tmpl">};
2123        }
2124    }
2125    if ($file_tmpl) {
2126        require MT::Template::Context;
2127        $ctx = MT::Template::Context->new;
2128        $ctx->stash( 'blog', $blog );
2129    }
2130    local $ctx->{__stash}{category}         = $cat if $cat;
2131    local $ctx->{__stash}{archive_category} = $cat if $cat;
2132    $timestamp = $entry->authored_on() if $entry;
2133    local $ctx->{__stash}{entry} = $entry if $entry;
2134    local $ctx->{__stash}{author} =
2135      $author ? $author : $entry ? $entry->author : undef;
2136
2137    my %blog_at = map { $_ => 1 } split /,/, $blog->archive_type;
2138    return '' unless $blog_at{$at};
2139
2140    $file = $archiver->archive_file->(
2141        $ctx,
2142        Timestamp => $timestamp,
2143        Template  => $file_tmpl
2144    );
2145    if ( $file_tmpl && !$file ) {
2146        local $ctx->{archive_type} = $at;
2147        require MT::Builder;
2148        my $build = MT::Builder->new;
2149        my $tokens = $build->compile( $ctx, $file_tmpl )
2150          or return $blog->error( $build->errstr() );
2151        defined( $file = $build->build( $ctx, $tokens ) )
2152          or return $blog->error( $build->errstr() );
2153    }
2154    else {
2155        my $ext = $blog->file_extension;
2156        $file .= '.' . $ext if $ext;
2157    }
2158    $file;
2159}
2160
2161sub get_entry {
2162    my $mt = shift;
2163    my ( $ts, $blog_id, $at, $order ) = @_;
2164    my $range = $mt->archiver($at)->date_range;
2165    my ( $start, $end ) = $range->($ts);
2166    if ( $order eq 'previous' ) {
2167        $order = 'descend';
2168        $ts    = $start;
2169    }
2170    else {
2171        $order = 'ascend';
2172        $ts    = $end;
2173    }
2174    require MT::Entry;
2175    my $entry = MT::Entry->load(
2176        {
2177            blog_id => $blog_id,
2178            status  => MT::Entry::RELEASE()
2179        },
2180        {
2181            limit     => 1,
2182            'sort'    => 'authored_on',
2183            direction => $order,
2184            start_val => $ts
2185        }
2186    );
2187    $entry;
2188}
2189
2190# Adds an element to the rebuild queue when the plugin is enabled.
2191sub queue_build_file_filter {
2192    my $mt = shift;
2193    my ( $cb, %args ) = @_;
2194
2195    my $blog = $args{blog};
2196    return 1 unless $blog && $blog->publish_queue;
2197
2198    my $fi = $args{file_info};
2199    return 1 if $fi->{from_queue};
2200
2201    require MT::TheSchwartz;
2202    require TheSchwartz::Job;
2203    my $job = TheSchwartz::Job->new();
2204    $job->funcname('MT::Worker::Publish');
2205    $job->uniqkey( $fi->id );
2206    $job->coalesce( $$ . ':' . ( time - ( time % 100 ) ) );
2207
2208    # my $at = $fi->archive_type || '';
2209    #
2210    # Default priority assignment....
2211    # if ($at eq 'Individual') {
2212    #     require MT::TemplateMap;
2213    #     my $map = MT::TemplateMap->load($fi->templatemap_id);
2214    #     # Individual archive pages that are the 'permalink' pages should
2215    #     # have highest build priority.
2216    #     if ($map && $map->is_preferred) {
2217    #         $rqf->priority(1);
2218    #     } else {
2219    #         $rqf->priority(9);
2220    #     }
2221    # } elsif ($at eq 'index') {
2222    #     # Index pages are second in priority, if they are named 'index'
2223    #     # or 'default'
2224    #     if ($fi->file_path =~ m!/(index|default|atom|feed)!i) {
2225    #         $rqf->priority(3);
2226    #     } else {
2227    #         $rqf->priority(9);
2228    #     }
2229    # } elsif (($at eq 'Monthly') || ($at eq 'Weekly') || ($at eq 'Daily')) {
2230    #     $rqf->priority(5);
2231    # } elsif ($at eq 'Category') {
2232    #     $rqf->priority(7);
2233    # }
2234    #
2235    # $rqf->save;
2236
2237    MT::TheSchwartz->insert($job);
2238
2239    return 0;
2240}
2241
2242
2243# ----- ARCHIVING -----
2244
2245# Prepare sets up a context with the data necessary to build that page
2246# Archiver returns a filename for the given archive
2247
2248sub yearly_archive_label {
2249    MT->translate("YEARLY_ADV");
2250}
2251
2252sub yearly_archive_file {
2253    my ( $ctx, %param ) = @_;
2254    my $timestamp = $param{Timestamp};
2255    my $file_tmpl = $param{Template};
2256    my $blog      = $ctx->{__stash}{blog};
2257
2258    my $file;
2259    if ($file_tmpl) {
2260        ( $ctx->{current_timestamp}, $ctx->{current_timestamp_end} ) =
2261          start_end_month( $timestamp, $blog );
2262    }
2263    else {
2264        my $start = start_end_month( $timestamp, $blog );
2265        my ( $year, $mon ) = unpack 'A4', $start;
2266        $file = sprintf( "%04d/index", $year, $mon );
2267    }
2268
2269    $file;
2270}
2271
2272sub yearly_archive_title {
2273    my ( $ctx, $entry_or_ts ) = @_;
2274    my $stamp = ref $entry_or_ts ? $entry_or_ts->authored_on : $entry_or_ts;
2275    my $start = start_end_year( $stamp, $ctx->stash('blog') );
2276    require MT::Template::Context;
2277    my $year =
2278      MT::Template::Context::_hdlr_date( $ctx,
2279        { ts => $start, 'format' => "%Y" } );
2280    my $lang = lc MT->current_language || 'en_us';
2281    $lang = 'ja' if lc($lang) eq 'jp';
2282
2283    sprintf( "%s%s", $year, ( $lang eq 'ja' ? '&#24180;' : '' ) );
2284}
2285
2286sub yearly_date_range { start_end_year(@_) }
2287
2288sub yearly_group_iter {
2289    my ( $ctx, $args ) = @_;
2290    my $blog = $ctx->stash('blog');
2291    my $iter;
2292    my $sort_order =
2293      ( $args->{sort_order} || '' ) eq 'ascend' ? 'ascend' : 'descend';
2294    my $order = ( $sort_order eq 'ascend' ) ? 'asc' : 'desc';
2295
2296    require MT::Entry;
2297    $iter = MT::Entry->count_group_by(
2298        {
2299            blog_id => $blog->id,
2300            status  => MT::Entry::RELEASE()
2301        },
2302        {
2303            group => ["extract(year from authored_on)"],
2304            $args->{lastn} ? ( limit => $args->{lastn} ) : (),
2305            sort => "extract(year from authored_on) $order"
2306        }
2307    ) or return $ctx->error("Couldn't get yearly archive list");
2308
2309    return sub {
2310        while ( my @row = $iter->() ) {
2311            my $date = sprintf( "%04d%02d%02d000000", $row[1], 1, 1 );
2312            my ( $start, $end ) = start_end_year($date);
2313            return ( $row[0], year => $row[1], start => $start, end => $end );
2314        }
2315        undef;
2316    };
2317}
2318
2319sub yearly_group_entries {
2320    my ( $ctx, %param ) = @_;
2321    date_based_group_entries( $ctx, 'Yearly',
2322        %param ? sprintf( "%04d%02d%02d000000", $param{year}, 1, 1 ) : undef );
2323}
2324
2325sub yearly_entries_count {
2326    my ( $blog, $at, $entry ) = @_;
2327    return _archive_entries_count(
2328        {
2329            Blog        => $blog,
2330            ArchiveType => $at,
2331            Timestamp   => $entry->authored_on
2332        }
2333    );
2334}
2335
2336sub monthly_archive_label {
2337    MT->translate("MONTHLY_ADV");
2338}
2339
2340sub monthly_archive_file {
2341    my ( $ctx, %param ) = @_;
2342    my $timestamp = $param{Timestamp};
2343    my $file_tmpl = $param{Template};
2344    my $blog      = $ctx->{__stash}{blog};
2345
2346    my $file;
2347    if ($file_tmpl) {
2348        ( $ctx->{current_timestamp}, $ctx->{current_timestamp_end} ) =
2349          start_end_month( $timestamp, $blog );
2350    }
2351    else {
2352        my $start = start_end_month( $timestamp, $blog );
2353        my ( $year, $mon ) = unpack 'A4A2', $start;
2354        $file = sprintf( "%04d/%02d/index", $year, $mon );
2355    }
2356
2357    $file;
2358}
2359
2360sub monthly_archive_title {
2361    my ( $ctx, $entry_or_ts ) = @_;
2362    my $stamp = ref $entry_or_ts ? $entry_or_ts->authored_on : $entry_or_ts;
2363    my $start = start_end_month( $stamp, $ctx->stash('blog') );
2364    MT::Template::Context::_hdlr_date( $ctx,
2365        { ts => $start, 'format' => "%B %Y" } );
2366}
2367
2368sub monthly_date_range { start_end_month(@_) }
2369
2370sub monthly_group_iter {
2371    my ( $ctx, $args ) = @_;
2372    my $blog = $ctx->stash('blog');
2373    my $iter;
2374    my $sort_order =
2375      ( $args->{sort_order} || '' ) eq 'ascend' ? 'ascend' : 'descend';
2376    my $order = ( $sort_order eq 'ascend' ) ? 'asc' : 'desc';
2377
2378    my $ts    = $ctx->{current_timestamp};
2379    my $tsend = $ctx->{current_timestamp_end};
2380
2381    require MT::Entry;
2382    $iter = MT::Entry->count_group_by(
2383        {
2384            blog_id => $blog->id,
2385            status  => MT::Entry::RELEASE(),
2386            ( $ts && $tsend ? ( authored_on => [ $ts, $tsend ] ) : () ),
2387        },
2388        {
2389            ( $ts && $tsend ? ( range_incl => { authored_on => 1 } ) : () ),
2390            group => [
2391                "extract(year from authored_on)",
2392                "extract(month from authored_on)"
2393            ],
2394            $args->{lastn} ? ( limit => $args->{lastn} ) : (),
2395            sort => "extract(year from authored_on) $order,
2396                       extract(month from authored_on) $order"
2397        }
2398    ) or return $ctx->error("Couldn't get monthly archive list");
2399
2400    return sub {
2401        while ( my @row = $iter->() ) {
2402            my $date = sprintf( "%04d%02d%02d000000", $row[1], $row[2], 1 );
2403            my ( $start, $end ) = start_end_month($date);
2404            return (
2405                $row[0],
2406                year  => $row[1],
2407                month => $row[2],
2408                start => $start,
2409                end   => $end
2410            );
2411        }
2412        undef;
2413    };
2414}
2415
2416sub monthly_group_entries {
2417    my ( $ctx, %param ) = @_;
2418    date_based_group_entries( $ctx, 'Monthly',
2419        %param
2420        ? sprintf( "%04d%02d%02d000000", $param{year}, $param{month}, 1 )
2421        : undef );
2422}
2423
2424sub monthly_entries_count {
2425    my ( $blog, $at, $entry ) = @_;
2426    return _archive_entries_count(
2427        {
2428            Blog        => $blog,
2429            ArchiveType => $at,
2430            Timestamp   => $entry->authored_on
2431        }
2432    );
2433}
2434
2435sub category_archive_label {
2436    MT->translate("CATEGORY_ADV");
2437}
2438
2439sub category_archive_title {
2440    my ($ctx) = @_;
2441    my $c = $ctx->stash('category');
2442    $c ? $c->label : '';
2443}
2444
2445sub category_archive_file {
2446    my ( $ctx, %param ) = @_;
2447    my $timestamp = $param{Timestamp};
2448    my $file_tmpl = $param{Template};
2449    my $blog      = $ctx->{__stash}{blog};
2450    my $cat       = $ctx->{__stash}{category};
2451    my $entry     = $ctx->{__stash}{entry};
2452    my $file;
2453
2454    my $this_cat = $cat ? $cat : ( $entry ? $entry->category : undef );
2455    if ($file_tmpl) {
2456        $ctx->stash( 'archive_category', $this_cat );
2457        $ctx->{inside_mt_categories} = 1;
2458        $ctx->{__stash}{category} = $this_cat;
2459    }
2460    else {
2461        if ( !$this_cat ) {
2462            return "";
2463        }
2464        my $label = '';
2465        $label = dirify( $this_cat->label );
2466        if ( $label !~ /\w/ ) {
2467            $label = $this_cat ? "cat" . $this_cat->id : "";
2468        }
2469        $file = sprintf( "%s/index", $this_cat->category_path );
2470    }
2471    $file;
2472}
2473
2474sub category_group_iter {
2475    my ( $ctx, $args ) = @_;
2476
2477    my $blog_id = $ctx->stash('blog')->id;
2478    require MT::Category;
2479    my $iter = MT::Category->load_iter( { blog_id => $blog_id },
2480        { 'sort' => 'label', direction => 'ascend' } );
2481    require MT::Placement;
2482    require MT::Entry;
2483
2484    return sub {
2485        while ( my $c = $iter->() ) {
2486            my @arguments = (
2487                {
2488                    blog_id => $blog_id,
2489                    status  => MT::Entry::RELEASE()
2490                },
2491                {
2492                    'join' => [
2493                        'MT::Placement', 'entry_id', { category_id => $c->id }
2494                    ]
2495                }
2496            );
2497            my $count = MT::Entry->count( @arguments );
2498            next unless $count || $args->{show_empty};
2499            return ( $count, category => $c );
2500        }
2501        undef;
2502      }
2503}
2504
2505sub category_group_entries {
2506    my ( $ctx, %param ) = @_;
2507
2508    my $c = $ctx->stash('archive_category') || $ctx->stash('category');
2509    require MT::Entry;
2510    my @entries = MT::Entry->load(
2511        { status => MT::Entry::RELEASE() },
2512        {
2513            join => [
2514                'MT::Placement', 'entry_id',
2515                { category_id => $c->id }, { unqiue => 1 }
2516            ],
2517            'sort' => 'authored_on',
2518            'direction' => 'descend',
2519        }
2520    );
2521    \@entries;
2522}
2523
2524sub category_entries_count {
2525    my ( $blog, $at, $entry ) = @_;
2526    my $cat = $entry->category;
2527    return 0 unless $cat;
2528    return _archive_entries_count(
2529        {
2530            Blog        => $blog,
2531            ArchiveType => $at,
2532            Category    => $cat
2533        }
2534    );
2535}
2536
2537sub page_archive_label {
2538    MT->translate("PAGE_ADV");
2539}
2540
2541sub page_archive_file {
2542    my ( $ctx, %param ) = @_;
2543    my $timestamp = $param{Timestamp};
2544    my $file_tmpl = $param{Template};
2545    my $blog      = $ctx->{__stash}{blog};
2546    my $page      = $ctx->{__stash}{entry};
2547
2548    my $file;
2549    Carp::croak "archive_file_for Page archive needs a page"
2550      unless $page && $page->isa('MT::Page');
2551    unless ($file_tmpl) {
2552        my $basename = $page->basename();
2553        my $folder   = $page->folder;
2554        my $folder_path;
2555        if ($folder) {
2556            $folder_path = $folder->publish_path || '';
2557            $file =
2558              $folder_path ne '' ? $folder_path . '/' . $basename : $basename;
2559        }
2560        else {
2561            $file = $basename;
2562        }
2563    }
2564    return $file;
2565}
2566
2567sub page_group_iter {
2568    my ( $ctx, $args ) = @_;
2569
2570    require MT::Page;
2571    my $blog_id = $ctx->stash('blog')->id;
2572    my $iter    = MT::Page->load_iter(
2573        {
2574            blog_id => $blog_id,
2575            status  => MT::Entry::RELEASE()
2576        },
2577        { sort => 'title', direction => 'ascend' }
2578    );
2579    return sub {
2580        while ( my $entry = $iter->() ) {
2581            return ( 1, entries => [$entry], entry => $entry );
2582        }
2583        undef;
2584      }
2585}
2586
2587sub individual_archive_label {
2588    MT->translate("INDIVIDUAL_ADV");
2589}
2590
2591sub individual_archive_file {
2592    my ( $ctx, %param ) = @_;
2593    my $timestamp = $param{Timestamp};
2594    my $file_tmpl = $param{Template};
2595    my $blog      = $ctx->{__stash}{blog};
2596    my $entry     = $ctx->{__stash}{entry};
2597
2598    my $file;
2599    Carp::confess "archive_file_for Individual archive needs an entry"
2600      unless $entry;
2601    if ($file_tmpl) {
2602        $ctx->{current_timestamp} = $entry->authored_on;
2603    }
2604    else {
2605        my $basename = $entry->basename();
2606        $basename ||= dirify( $entry->title() );
2607        $file = sprintf( "%04d/%02d/%s",
2608            unpack( 'A4A2', $entry->authored_on ), $basename );
2609    }
2610    $file;
2611}
2612
2613sub individual_archive_title { $_[1]->title }
2614
2615sub individual_group_iter {
2616    my ( $ctx, $args ) = @_;
2617
2618    my $order =
2619      ( $args->{sort_order} || '' ) eq 'ascend' ? 'ascend' : 'descend';
2620
2621    my $blog_id = $ctx->stash('blog')->id;
2622    require MT::Entry;
2623    my $iter = MT::Entry->load_iter(
2624        {
2625            blog_id => $blog_id,
2626            status  => MT::Entry::RELEASE()
2627        },
2628        {
2629            'sort'    => 'authored_on',
2630            direction => $order,
2631            $args->{lastn} ? ( limit => $args->{lastn} ) : ()
2632        }
2633    );
2634    return sub {
2635        while ( my $entry = $iter->() ) {
2636            return ( 1, entries => [$entry], entry => $entry );
2637        }
2638        undef;
2639      }
2640}
2641
2642sub daily_archive_label {
2643    MT->translate("DAILY_ADV");
2644}
2645
2646sub daily_archive_file {
2647    my ( $ctx, %param ) = @_;
2648    my $timestamp = $param{Timestamp};
2649    my $file_tmpl = $param{Template};
2650    my $blog      = $ctx->{__stash}{blog};
2651
2652    my $file;
2653    if ($file_tmpl) {
2654        ( $ctx->{current_timestamp}, $ctx->{current_timestamp_end} ) =
2655          start_end_day($timestamp);
2656    }
2657    else {
2658        my $start = start_end_day($timestamp);
2659        my ( $year, $mon, $mday ) = unpack 'A4A2A2', $start;
2660        $file = sprintf( "%04d/%02d/%02d/index", $year, $mon, $mday );
2661    }
2662    $file;
2663}
2664
2665sub daily_archive_title {
2666    my $stamp = ref $_[1] ? $_[1]->authored_on : $_[1];
2667    my $start = start_end_day( $stamp, $_[0]->stash('blog') );
2668    MT::Template::Context::_hdlr_date( $_[0],
2669        { ts => $start, 'format' => "%x" } );
2670}
2671
2672sub daily_date_range { start_end_day(@_) }
2673
2674sub daily_group_iter {
2675    my ( $ctx, $args ) = @_;
2676    my $blog = $ctx->stash('blog');
2677    my $iter;
2678    my $sort_order =
2679      ( $args->{sort_order} || '' ) eq 'ascend' ? 'ascend' : 'descend';
2680    my $order = ( $sort_order eq 'ascend' ) ? 'asc' : 'desc';
2681
2682    my $ts    = $ctx->{current_timestamp};
2683    my $tsend = $ctx->{current_timestamp_end};
2684
2685    require MT::Entry;
2686    $iter = MT::Entry->count_group_by(
2687        {
2688            blog_id => $blog->id,
2689            status  => MT::Entry::RELEASE(),
2690            ( $ts && $tsend ? ( authored_on => [ $ts, $tsend ] ) : () ),
2691        },
2692        {
2693            ( $ts && $tsend ? ( range_incl => { authored_on => 1 } ) : () ),
2694            group => [
2695                "extract(year from authored_on)",
2696                "extract(month from authored_on)",
2697                "extract(day from authored_on)"
2698            ],
2699            $args->{lastn} ? ( limit => $args->{lastn} ) : (),
2700            sort => "extract(year from authored_on) $order,
2701                        extract(month from authored