root/branches/release-38/lib/MT/WeblogPublisher.pm @ 2273

Revision 2273, 75.5 kB (checked in by bchoate, 19 months ago)

Applied patch for start/end variable assignment to avoid unbalanced hash error. BugId:79542 Thanks, Jay.

  • 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 );
11our @EXPORT = qw(ArchiveFileTemplate ArchiveType);
12
13use MT::ArchiveType;
14use File::Basename;
15
16our %ArchiveTypes;
17
18sub ArchiveFileTemplate {
19    my %param = @_;
20    \%param;
21}
22
23sub ArchiveType {
24    new MT::ArchiveType(@_);
25}
26
27sub new {
28    my $class = shift;
29    my $this  = {@_};
30    my $cfg   = MT->config;
31    if ( !exists $this->{start_time} ) {
32        $this->{start_time} = time;
33    }
34    if ( !exists $this->{NoTempFiles} ) {
35        $this->{NoTempFiles} = $cfg->NoTempFiles;
36    }
37    if ( !exists $this->{PublishCommenterIcon} ) {
38        $this->{PublishCommenterIcon} = $cfg->PublishCommenterIcon;
39    }
40    bless $this, $class;
41    $this->init();
42    $this;
43}
44
45sub init_archive_types {
46    my $types = MT->registry("archive_types") || {};
47    my $mt = MT->instance;
48    while (my ($type, $typedata) = each %$types) {
49        if ('HASH' eq ref $typedata) {
50            # $typedata = { %$typedata };  # don't edit registry
51            # $typedata->{$_} = $mt->handler_to_coderef($typedata->{$_})
52            #     for qw( archive_label archive_file archive_title date_range
53            #             archive_group_iter archive_group_entries
54            #             archive_entries_count );
55            # $typedata->{default_archive_templates} = [
56            #     map { { %$_ } }
57            #         @{ $typedata->{default_archive_templates} || [] }
58            # ];
59            # All of the above is useless, since we're replacing $typedate
60            # by this object.
61            $typedata = MT::ArchiveType->new( %$typedata );
62        }
63        $ArchiveTypes{$type} = $typedata;
64    }
65}
66
67sub archive_types {
68    init_archive_types() unless %ArchiveTypes;
69    keys %ArchiveTypes;
70}
71
72sub archiver {
73    my $mt = shift;
74    my ($at) = @_;
75    init_archive_types() unless %ArchiveTypes;
76    my $archiver = $at ? $ArchiveTypes{$at} : undef;
77    if ($archiver && !ref($archiver)) {
78        # A package name-- load package and instantiate Archiver object
79        if ($archiver =~ m/::/) {
80            eval("require $archiver; 1;");
81            die "Invalid archive type package '$archiver': $@"
82                if $@; # fatal error here
83            my $inst = $archiver->new();
84            $archiver = $ArchiveTypes{$at} = $inst;
85        }
86    }
87    return $archiver;
88}
89
90sub init {
91    init_archive_types() unless %ArchiveTypes;
92}
93
94sub core_archive_types {
95    return {
96        'Yearly' => 'MT::ArchiveType::Yearly',
97        'Monthly' => 'MT::ArchiveType::Monthly',
98        'Weekly' => 'MT::ArchiveType::Weekly',
99        'Individual' => 'MT::ArchiveType::Individual',
100        'Page' => 'MT::ArchiveType::Page',
101        'Daily' => 'MT::ArchiveType::Daily',
102        'Category' => 'MT::ArchiveType::Category',
103        'Author' => 'MT::ArchiveType::Author',
104        'Author-Yearly' => 'MT::ArchiveType::AuthorYearly',
105        'Author-Monthly' => 'MT::ArchiveType::AuthorMonthly',
106        'Author-Weekly' => 'MT::ArchiveType::AuthorWeekly',
107        'Author-Daily' => 'MT::ArchiveType::AuthorDaily',
108        'Category-Yearly' => 'MT::ArchiveType::CategoryYearly',
109        'Category-Monthly' => 'MT::ArchiveType::CategoryMonthly',
110        'Category-Daily' => 'MT::ArchiveType::CategoryDaily',
111        'Category-Weekly' => 'MT::ArchiveType::CategoryWeekly',
112    };
113
114}
115
116sub start_time {
117    my $pub = shift;
118    $pub->{start_time} = shift if @_;
119    return $pub->{start_time};
120}
121
122sub rebuild {
123    my $mt    = shift;
124    my %param = @_;
125    my $blog;
126    unless ( $blog = $param{Blog} ) {
127        my $blog_id = $param{BlogID};
128        $blog = MT::Blog->load($blog_id)
129          or return $mt->error(
130            MT->translate(
131                "Load of blog '[_1]' failed: [_2]", $blog_id,
132                MT::Blog->errstr
133            )
134          );
135    }
136    return 1 if $blog->is_dynamic;
137    my $at = $blog->archive_type || '';
138    my @at = split /,/, $at;
139    my $entry_class;
140    if ( my $set_at = $param{ArchiveType} ) {
141        my %at = map { $_ => 1 } @at;
142        return $mt->error(
143            MT->translate(
144                "Archive type '[_1]' is not a chosen archive type", $set_at
145            )
146        ) unless $at{$set_at};
147
148        @at = ($set_at);
149        my $archiver = $mt->archiver($set_at);
150        $entry_class = $archiver->entry_class || "entry";
151    }
152    else {
153        $entry_class = '*';
154    }
155
156    if (   $param{ArchiveType}
157        && ( !$param{Entry} )
158        && ( $param{ArchiveType} eq 'Category' ) )
159    {
160
161        # Pass to full category rebuild
162        return $mt->rebuild_categories(%param);
163    }
164
165    if (   $param{ArchiveType}
166        && ( !$param{Author} )
167        && ( $param{ArchiveType} eq 'Author' ) )
168    {
169        return $mt->rebuild_authors(%param);
170    }
171
172    if (@at) {
173        require MT::Entry;
174        my %arg = ( 'sort' => 'authored_on', direction => 'descend' );
175        $arg{offset} = $param{Offset} if $param{Offset};
176        $arg{limit}  = $param{Limit}  if $param{Limit};
177        my $pre_iter = MT::Entry->load_iter(
178            {
179                blog_id => $blog->id,
180                class   => $entry_class,
181                status  => MT::Entry::RELEASE()
182            },
183            \%arg
184        );
185        my ( $next, $curr );
186        my $prev = $pre_iter->();
187        my $iter = sub {
188            ( $next, $curr ) = ( $curr, $prev );
189            if ($curr) {
190                $prev = $pre_iter->();
191            }
192            $curr;
193        };
194        my $cb  = $param{EntryCallback};
195        my $fcb = $param{FilterCallback};
196        while ( my $entry = $iter->() ) {
197            if ($cb) {
198                $cb->($entry) || $mt->log( $cb->errstr() );
199            }
200            if ($fcb) {
201                $fcb->($entry) or last;
202            }
203            for my $at (@at) {
204                my $archiver = $mt->archiver($at);
205
206                # Skip this archive type if the archive type doesn't
207                # match the kind of entry we've loaded
208                next unless $archiver;
209                next if $entry->class ne $archiver->entry_class;
210                if ( $archiver->category_based ) {
211                    my $cats = $entry->categories;
212                    CATEGORY: for my $cat (@$cats) {
213                        next CATEGORY if $archiver->category_class ne $cat->class_type;
214                        $mt->_rebuild_entry_archive_type(
215                            Entry       => $entry,
216                            Blog        => $blog,
217                            Category    => $cat,
218                            ArchiveType => $at,
219                            NoStatic    => $param{NoStatic},
220                            Force       => ($param{Force} ? 1 : 0),
221                            $param{TemplateMap}
222                            ? ( TemplateMap => $param{TemplateMap} )
223                            : (),
224                            $param{TemplateID}
225                            ? ( TemplateID =>
226                                  $param{TemplateID} )
227                            : (),
228                        ) or return;
229                    }
230                }
231                elsif ( $archiver->author_based ) {
232                    if ( $entry->author ) {
233                        $mt->_rebuild_entry_archive_type(
234                            Entry       => $entry,
235                            Blog        => $blog,
236                            ArchiveType => $at,
237                            $param{TemplateMap}
238                            ? ( TemplateMap => $param{TemplateMap} )
239                            : (),
240                            $param{TemplateID}
241                            ? ( TemplateID =>
242                                  $param{TemplateID} )
243                            : (),
244                            NoStatic => $param{NoStatic},
245                            Force    => ($param{Force} ? 1 : 0),
246                            Author   => $entry->author,
247                        ) or return;
248                    }
249                }
250                else {
251                    $mt->_rebuild_entry_archive_type(
252                        Entry       => $entry,
253                        Blog        => $blog,
254                        ArchiveType => $at,
255                        $param{TemplateMap}
256                        ? ( TemplateMap => $param{TemplateMap} )
257                        : (),
258                        $param{TemplateID}
259                        ? ( TemplateID =>
260                              $param{TemplateID} )
261                        : (),
262                        NoStatic => $param{NoStatic},
263                        Force    => ($param{Force} ? 1 : 0),
264                    ) or return;
265                }
266            }
267        }
268    }
269    unless ( $param{NoIndexes} ) {
270        $mt->rebuild_indexes( Blog => $blog ) or return;
271    }
272    1;
273}
274
275sub rebuild_categories {
276    my $mt    = shift;
277    my %param = @_;
278    my $blog;
279    unless ( $blog = $param{Blog} ) {
280        my $blog_id = $param{BlogID};
281        $blog = MT::Blog->load($blog_id)
282          or return $mt->error(
283            MT->translate(
284                "Load of blog '[_1]' failed: [_2]", $blog_id,
285                MT::Blog->errstr
286            )
287          );
288    }
289    my %arg;
290    $arg{'sort'} = 'id';
291    $arg{direction} = 'ascend';
292    $arg{offset} = $param{Offset} if $param{Offset};
293    $arg{limit}  = $param{Limit}  if $param{Limit};
294    my $cat_iter = MT::Category->load_iter( { blog_id => $blog->id }, \%arg );
295    my $fcb = $param{FilterCallback};
296
297    while ( my $cat = $cat_iter->() ) {
298        if ($fcb) {
299            $fcb->($cat) or last;
300        }
301        $mt->_rebuild_entry_archive_type(
302            Blog        => $blog,
303            Category    => $cat,
304            ArchiveType => 'Category',
305            $param{TemplateMap}
306            ? ( TemplateMap => $param{TemplateMap} )
307            : (),
308            NoStatic => $param{NoStatic},
309            Force    => ($param{Force} ? 1 : 0),
310        ) or return;
311    }
312    1;
313}
314
315sub rebuild_authors {
316    my $mt    = shift;
317    my %param = @_;
318    my $blog;
319    unless ( $blog = $param{Blog} ) {
320        my $blog_id = $param{BlogID};
321        $blog = MT::Blog->load($blog_id)
322          or return $mt->error(
323            MT->translate(
324                "Load of blog '[_1]' failed: [_2]", $blog_id,
325                MT::Blog->errstr
326            )
327          );
328    }
329    my %arg;
330    $arg{'sort'} = 'id';
331    $arg{direction} = 'ascend';
332    $arg{offset} = $param{Offset} if $param{Offset};
333    $arg{limit}  = $param{Limit}  if $param{Limit};
334    $arg{unique} = 1;
335    my %terms;
336    $terms{status} = MT::Author::ACTIVE();
337    require MT::Entry;
338    $arg{join} = MT::Entry->join_on(
339        'author_id',
340        { blog_id => $blog->id, class => 'entry' },
341        { unique  => 1 }
342    );
343    my $auth_iter = MT::Author->load_iter( \%terms, \%arg );
344    my $fcb = $param{FilterCallback};
345
346    while ( my $a = $auth_iter->() ) {
347        if ($fcb) {
348            $fcb->($a) or last;
349        }
350        $mt->_rebuild_entry_archive_type(
351            Blog        => $blog,
352            Author      => $a,
353            ArchiveType => 'Author',
354            $param{TemplateMap}
355            ? ( TemplateMap => $param{TemplateMap} )
356            : (),
357            NoStatic => $param{NoStatic},
358            Force    => ($param{Force} ? 1 : 0),
359        ) or return;
360    }
361    1;
362}
363
364#   rebuild_entry
365#
366# $mt->rebuild_entry(Entry => $entry_id,
367#                    Blog => [ $blog | $blog_id ],
368#                    [ BuildDependencies => (0 | 1), ]
369#                    [ OldPrevious => $old_previous_entry_id,
370#                      OldNext => $old_next_entry_id, ]
371#                    [ NoStatic => (0 | 1), ]
372#                    );
373sub rebuild_entry {
374    my $mt    = shift;
375    my %param = @_;
376    my $entry = $param{Entry}
377      or return $mt->error(
378        MT->translate( "Parameter '[_1]' is required", 'Entry' ) );
379    require MT::Entry;
380    $entry = MT::Entry->load($entry) unless ref $entry;
381    return unless $entry;
382    my $blog;
383    unless ( $blog = $param{Blog} ) {
384        my $blog_id = $entry->blog_id;
385        $blog = MT::Blog->load($blog_id)
386          or return $mt->error(
387            MT->translate(
388                "Load of blog '[_1]' failed: [_2]", $blog_id,
389                MT::Blog->errstr
390            )
391          );
392    }
393    return 1 if $blog->is_dynamic;
394
395    my $at = $param{PreferredArchiveOnly} ? $blog->archive_type_preferred : $blog->archive_type;
396    if ( $at && $at ne 'None' ) {
397        my @at = split /,/, $at;
398        for my $at (@at) {
399            my $archiver = $mt->archiver($at);
400            next unless $archiver;    # invalid archive type
401            next if $entry->class ne $archiver->entry_class;
402            if ( $archiver->category_based ) {
403                my $cats = $entry->categories;    # (ancestors => 1)
404                for my $cat (@$cats) {
405                    $mt->_rebuild_entry_archive_type(
406                        Entry       => $entry,
407                        Blog        => $blog,
408                        ArchiveType => $at,
409                        Category    => $cat,
410                        NoStatic    => $param{NoStatic},
411                        # Force       => ($param{Force} ? 1 : 0),
412                        $param{TemplateMap}
413                        ? ( TemplateMap => $param{TemplateMap} )
414                        : (),
415                    ) or return;
416                }
417            }
418            else {
419                $mt->_rebuild_entry_archive_type(
420                    Entry       => $entry,
421                    Blog        => $blog,
422                    ArchiveType => $at,
423                    $param{TemplateMap}
424                    ? ( TemplateMap => $param{TemplateMap} )
425                    : (),
426                    NoStatic => $param{NoStatic},
427                    # Force    => ($param{Force} ? 1 : 0),
428                    Author   => $entry->author,
429                ) or return;
430            }
431        }
432    }
433
434    ## The above will just rebuild the archive pages for this particular
435    ## entry. If we want to rebuild all of the entries/archives/indexes
436    ## on which this entry could be featured etc., however, we need to
437    ## rebuild all of the entry's dependencies. Note that all of these
438    ## are not *necessarily* dependencies, depending on the usage of tags,
439    ## etc. There is not a good way to determine exact dependencies; it is
440    ## easier to just rebuild, rebuild, rebuild.
441
442    return 1
443      unless $param{BuildDependencies}
444      || $param{BuildIndexes}
445      || $param{BuildArchives};
446
447    if ( $param{BuildDependencies} ) {
448        ## Rebuild previous and next entry archive pages.
449        if ( my $prev = $entry->previous(1) ) {
450            $mt->rebuild_entry( Entry => $prev, PreferredArchiveOnly => 1 ) or return;
451            ## Rebuild the old previous and next entries, if we have some.
452            if ( $param{OldPrevious}
453                && ( $param{OldPrevious} != $prev->id )
454                && ( my $old_prev = MT::Entry->load( $param{OldPrevious} ) ) )
455            {
456                $mt->rebuild_entry( Entry => $old_prev, PreferredArchiveOnly => 1 ) or return;
457            }
458        }
459        if ( my $next = $entry->next(1) ) {
460            $mt->rebuild_entry( Entry => $next, PreferredArchiveOnly => 1 ) or return;
461
462            if ( $param{OldNext}
463                && ( $param{OldNext} != $next->id )
464                && ( my $old_next = MT::Entry->load( $param{OldNext} ) ) )
465            {
466                $mt->rebuild_entry( Entry => $old_next, PreferredArchiveOnly => 1 ) or return;
467            }
468        }
469    }
470
471    if ( $param{BuildDependencies} || $param{BuildIndexes} ) {
472        ## Rebuild all indexes, in case this entry is on an index.
473        if ( !( exists $param{BuildIndexes} ) || $param{BuildIndexes} ) {
474            $mt->rebuild_indexes( Blog => $blog ) or return;
475        }
476    }
477
478    if ( $param{BuildDependencies} || $param{BuildArchives} ) {
479        ## Rebuild previous and next daily, weekly, and monthly archives;
480        ## adding a new entry could cause changes to the intra-archive
481        ## navigation.
482        my %at = map { $_ => 1 } split /,/, $blog->archive_type;
483        my @db_at = grep { my $archiver = $mt->archiver($_); $archiver && $archiver->date_based } $mt->archive_types;
484        for my $at (@db_at) {
485            if ( $at{$at} ) {
486                my $archiver = $mt->archiver($at);
487                if ( $archiver->category_based ) {
488                    my $cats = $entry->categories;
489                    for my $cat (@$cats) {
490                        if ( my $prev_arch = $archiver->previous_archive_entry({
491                            entry    => $entry,
492                            category => $cat,
493                        }) ) {
494                            $mt->_rebuild_entry_archive_type(
495                                NoStatic => $param{NoStatic},
496                                # Force    => ($param{Force} ? 1 : 0),
497                                Entry    => $prev_arch,
498                                Blog     => $blog,
499                                Category => $cat,
500                                $param{TemplateMap}
501                                ? ( TemplateMap => $param{TemplateMap} )
502                                : (),
503                                ArchiveType => $at
504                            ) or return;
505                        }
506                        if ( my $next_arch = $archiver->next_archive_entry({
507                            entry    => $entry,
508                            category => $cat,
509                        }) ) {
510                            $mt->_rebuild_entry_archive_type(
511                                NoStatic => $param{NoStatic},
512                                # Force    => ($param{Force} ? 1 : 0),
513                                Entry    => $next_arch,
514                                Blog     => $blog,
515                                Category => $cat,
516                                $param{TemplateMap}
517                                ? ( TemplateMap => $param{TemplateMap} )
518                                : (),
519                                ArchiveType => $at
520                            ) or return;
521                        }
522                    }
523                } else {
524                    if ( my $prev_arch = $archiver->previous_archive_entry({
525                        entry => $entry,
526                        $archiver->author_based ? (author => $entry->author) : (),
527                    }) ) {
528                        $mt->_rebuild_entry_archive_type(
529                            NoStatic    => $param{NoStatic},
530                            # Force       => ($param{Force} ? 1 : 0),
531                            Entry       => $prev_arch,
532                            Blog        => $blog,
533                            ArchiveType => $at,
534                            $param{TemplateMap}
535                            ? ( TemplateMap => $param{TemplateMap} )
536                            : (),
537                            $archiver->author_based ? (Author => $entry->author) : (),
538                        ) or return;
539                    }
540                    if ( my $next_arch = $archiver->next_archive_entry({
541                        entry => $entry,
542                        $archiver->author_based ? (author => $entry->author) : (),
543                    }) ) {
544                        $mt->_rebuild_entry_archive_type(
545                            NoStatic    => $param{NoStatic},
546                            # Force       => ($param{Force} ? 1 : 0),
547                            Entry       => $next_arch,
548                            Blog        => $blog,
549                            ArchiveType => $at,
550                            $param{TemplateMap}
551                            ? ( TemplateMap => $param{TemplateMap} )
552                            : (),
553                            $archiver->author_based ? (Author => $entry->author) : (),
554                        ) or return;
555                    }
556                }
557            }
558        }
559    }
560
561    1;
562}
563
564### Recip hash
565### {ArchiveType - {Category-id} - {Date key} - {Start}
566###                                           - {End}
567###                                           - {File}
568###                              - {File}
569###              - {Author-id}   - {Date key} - {Start}
570###                                           - {End}
571###                                           - {File}
572###                              - {File}
573###              - {Date key}    - {Start}
574###                              - {End}
575###                              - {File}
576###
577sub rebuild_archives {
578    my $mt = shift;
579    my %param = @_;
580    my $blog = $param{Blog}
581      or return $mt->error(
582        MT->translate( "Parameter '[_1]' is required", 'Blog' ) );
583    return 1 if $blog->is_dynamic;
584
585    my $recip = $param{Recip}
586        or return $mt->error(
587            MT->translate( "Parameter '[_1]' is required", 'Recip' ) );
588
589    for my $at (keys %$recip){
590        my $archiver = $mt->archiver($at);
591        next unless $archiver;
592
593        if ($archiver->category_based()) {
594            require MT::Category;
595            for my $cat_id (keys %{$recip->{$at}}) {
596                my $cat = MT::Category->load($cat_id)
597                    or next;
598                if ($archiver->date_based()) {
599                    for my $key (keys %{$recip->{$at}->{$cat_id}}) {
600                        $mt->_rebuild_entry_archive_type(
601                            NoStatic    => 0,
602                            Force       => ($param{Force} ? 1 : 0),
603                            Blog        => $blog,
604                            Category    => $cat,
605                            ArchiveType => $at,
606                            Start       => $recip->{$at}->{$cat_id}->{$key}->{Start},
607                            End         => $recip->{$at}->{$cat_id}->{$key}->{End},
608                            File        => $recip->{$at}->{$cat_id}->{$key}->{File}
609                        ) or return;
610                    }
611                } else {
612                    $mt->_rebuild_entry_archive_type(
613                        NoStatic    => 0,
614                        Force       => ($param{Force} ? 1 : 0),
615                        Blog        => $blog,
616                        Category    => $cat,
617                        ArchiveType => $at,
618                        File        => $recip->{$at}->{$cat_id}->{File}
619                    ) or return;
620                }
621            }
622        } elsif ($archiver->author_based()) {
623            require MT::Author;
624            for my $auth_id (keys %{$recip->{$at}}) {
625                my $author = MT::Author->load($auth_id)
626                    or next;
627                if ($archiver->date_based()) {
628                    for my $key (keys %{$recip->{$at}->{$auth_id}}) {
629                        $mt->_rebuild_entry_archive_type(
630                            NoStatic    => 0,
631                            Force       => ($param{Force} ? 1 : 0),
632                            Blog        => $blog,
633                            Author      => $author,
634                            ArchiveType => $at,
635                            Start       => $recip->{$at}->{$auth_id}->{$key}->{Start},
636                            End         => $recip->{$at}->{$auth_id}->{$key}->{End},
637                            File        => $recip->{$at}->{$auth_id}->{$key}->{File}
638                        ) or return;
639                    }
640                } else {
641                    $mt->_rebuild_entry_archive_type(
642                        NoStatic    => 0,
643                        Force       => ($param{Force} ? 1 : 0),
644                        Blog        => $blog,
645                        Author      => $author,
646                        ArchiveType => $at,
647                        File        => $recip->{$at}->{$auth_id}->{File}
648                    ) or return;
649                }
650            }
651        } elsif ($archiver->date_based()) {
652            for my $key (keys %{$recip->{$at}}) {
653                $mt->_rebuild_entry_archive_type(
654                    NoStatic    => 0,
655                    Force       => ($param{Force} ? 1 : 0),
656                    Blog        => $blog,
657                    ArchiveType => $at,
658                    Start       => $recip->{$at}->{$key}->{Start},
659                    End         => $recip->{$at}->{$key}->{End},
660                    File        => $recip->{$at}->{$key}->{File}
661                ) or return;
662            }
663        } else {
664            require MT::Entry;
665            for my $entry_id (keys %{$recip->{$at}}) {
666                my $entry = MT::Entry->load($entry_id)
667                    or next;
668                $mt->_rebuild_entry_archive_type(
669                    NoStatic    => 0,
670                    Force       => ($param{Force} ? 1 : 0),
671                    Entry       => $entry,
672                    Blog        => $blog,
673                    ArchiveType => $at,
674                    File        => $recip->{$at}->{$entry_id}->{File}
675                ) or return;
676            }
677        }
678    }
679
680    1;
681}
682
683sub _rebuild_entry_archive_type {
684    my $mt    = shift;
685    my %param = @_;
686
687    my $at    = $param{ArchiveType}
688      or return $mt->error(
689        MT->translate( "Parameter '[_1]' is required", 'ArchiveType' ) );
690    return 1 if $at eq 'None';
691    my $entry =
692      ( $param{ArchiveType} ne 'Category' && $param{ArchiveType} ne 'Author' &&
693        !exists $param{Start} && !exists $param{End} )
694      ? (
695        $param{Entry}
696          or return $mt->error(
697            MT->translate( "Parameter '[_1]' is required", 'Entry' )
698          )
699      )
700      : undef;
701
702    my $blog;
703    unless ( $blog = $param{Blog} ) {
704        my $blog_id = $entry->blog_id;
705        $blog = MT::Blog->load($blog_id)
706          or return $mt->error(
707            MT->translate(
708                "Load of blog '[_1]' failed: [_2]", $blog_id,
709                MT::Blog->errstr
710            )
711          );
712    }
713
714    ## Load the template-archive-type map entries for this blog and
715    ## archive type. We do this before we load the list of entries, because
716    ## we will run through the files and check if we even need to rebuild
717    ## anything. If there is nothing to rebuild at all for this entry,
718    ## we save some time by not loading the list of entries.
719    require MT::TemplateMap;
720    my @map;
721    if ( $param{TemplateMap} ) {
722        @map = ( $param{TemplateMap} );
723    }
724    else {
725        my $cached_maps = MT->instance->request('__cached_maps')
726          || MT->instance->request( '__cached_maps', {} );
727        if ( my $maps = $cached_maps->{ $at . $blog->id } ) {
728            @map = @$maps;
729        }
730        else {
731            @map = MT::TemplateMap->load(
732                {
733                    archive_type => $at,
734                    blog_id      => $blog->id,
735                    $param{TemplateID}
736                    ? ( template_id => $param{TemplateID} )
737                    : ()
738                }
739            );
740            $cached_maps->{ $at . $blog->id } = \@map;
741        }
742    }
743    return 1 unless @map;
744    my @map_build;
745
746    my $done = MT->instance->request('__published:'.$blog->id)
747      || MT->instance->request( '__published:'.$blog->id, {} );
748    for my $map (@map) {
749        my $file = exists $param{File}
750            ? $param{File}
751            : $mt->archive_file_for( $entry, $blog, $at, $param{Category}, $map,
752            undef, $param{Author} );
753        if ( $file eq '' ) {
754
755            # np
756        }
757        elsif ( !defined($file) ) {
758            return $mt->error( MT->translate( $blog->errstr() ) );
759        }
760        else {
761            push @map_build, $map unless $done->{$file};
762            $map->{__saved_output_file} = $file;
763        }
764    }
765    return 1 unless @map_build;
766    @map = @map_build;
767
768    my (%cond);
769    require MT::Template::Context;
770    my $ctx = MT::Template::Context->new;
771    $ctx->{current_archive_type} = $at;
772    $ctx->{archive_type}         = $at;
773    $at ||= "";
774
775    my $archiver = $mt->archiver($at);
776    return unless $archiver;
777
778    # Special handling for pages-- they are always published to the
779    # 'site' path instead of the 'archive' path, which is reserved for blog
780    # content.
781    my $arch_root = ( $at eq 'Page' ) ? $blog->site_path : $blog->archive_path;
782    return $mt->error(
783        MT->translate("You did not set your blog publishing path") )
784      unless $arch_root;
785
786    my ($start, $end);
787    if (exists $param{Start} && exists $param{End}) {
788        $start = $param{Start};
789        $end   = $param{End};
790    } else {
791        if ($archiver->can('date_range')) {
792            ( $start, $end ) = $archiver->date_range( $entry->authored_on );
793        }
794    }
795
796    ## For each mapping, we need to rebuild the entries we loaded above in
797    ## the particular template map, and write it to the specified archive
798    ## file template.
799    require MT::Template;
800    for my $map (@map) {
801        next unless $map->build_type; # ignore disabled template maps
802
803        $mt->rebuild_file(
804            $blog, $arch_root, $map, $at, $ctx, \%cond,
805            !$param{NoStatic},
806            Category  => $param{Category},
807            Entry     => $entry,
808            Author    => $param{Author},
809            StartDate => $start,
810            EndDate   => $end,
811            Force     => $param{Force} ? 1 : 0,
812        ) or return;
813        $done->{ $map->{__saved_output_file} }++;
814    }
815    1;
816}
817
818sub rebuild_file {
819    my $mt = shift;
820    my ( $blog, $root_path, $map, $at, $ctx_, $cond, $build_static, %args )
821      = @_;
822    my $ctx = bless { %$ctx_ }, ref $ctx_;
823
824    my $finfo;
825    my $archiver = $mt->archiver($at);
826    my ( $entry, $start, $end, $category, $author );
827
828    if ( $finfo = $args{FileInfo} ) {
829        $args{Author}   = $finfo->author_id   if $finfo->author_id;
830        $args{Category} = $finfo->category_id if $finfo->category_id;
831        $args{Entry}    = $finfo->entry_id    if $finfo->entry_id;
832        $map ||= MT::TemplateMap->load( $finfo->templatemap_id );
833        $at  ||= $finfo->archive_type;
834        if ( $finfo->startdate ) {
835            if ( ( $start, $end ) = $archiver->date_range($finfo->startdate) ) {
836                $args{StartDate} = $start;
837                $args{EndDate}   = $end;
838            }
839        }
840    }
841
842    # Calculate file path and URL for the new entry.
843    my $file = File::Spec->catfile( $root_path, $map->{__saved_output_file} );
844
845    ## Untaint. We have to assume that we can trust the user's setting of
846    ## the archive_path, and nothing else is based on user input.
847    ($file) = $file =~ /(.+)/s;
848
849    # compare file modification time to start of build process. if it
850    # is greater than the start_time, then we shouldn't need to build this
851    # file again
852    my $fmgr = $blog->file_mgr;
853    if (my $mod_time = $fmgr->file_mod_time($file)) {
854        return 1 if $mod_time >= $mt->start_time;
855    }
856
857    if ( $archiver->category_based ) {
858        $category = $args{Category};
859        die "Category archive type requires Category parameter"
860          unless $args{Category};
861        $category = MT::Category->load($category)
862          unless ref $category;
863        $ctx->var( 'category_archive', 1 );
864        $ctx->{__stash}{archive_category} = $category;
865    }
866    if ( $archiver->entry_based ) {
867        $entry = $args{Entry};
868        die "$at archive type requires Entry parameter"
869          unless $entry;
870        require MT::Entry;
871        $entry = MT::Entry->load($entry) if !ref $entry;
872        $ctx->var( 'entry_archive', 1 );
873        $ctx->{__stash}{entry} = $entry;
874    }
875    if ( $archiver->date_based ) {
876        # Date-based archive type
877        $start = $args{StartDate};
878        $end   = $args{EndDate};
879        Carp::confess("Date-based archive types require StartDate parameter")
880          unless $args{StartDate};
881        $ctx->var( 'datebased_archive', 1 );
882    }
883    if ( $archiver->author_based ) {
884
885        # author based archive type
886        $author = $args{Author};
887        die "Author-based archive type requires Author parameter"
888          unless $args{Author};
889        require MT::Author;
890        $author = MT::Author->load($author)
891          unless ref $author;
892        $ctx->var( 'author_archive', 1 );
893        $ctx->{__stash}{author} = $author;
894    }
895    local $ctx->{current_timestamp}     = $start if $start;
896    local $ctx->{current_timestamp_end} = $end   if $end;
897
898    $ctx->{__stash}{blog} = $blog;
899
900    require MT::FileInfo;
901
902# This kind of testing should be done at the time we save a post,
903# not during publishing!!!
904# if ($archiver->entry_based) {
905#     my $fcount = MT::FileInfo->count({
906#         blog_id => $blog->id,
907#         entry_id => $entry->id,
908#         file_path => $file},
909#         { not => { entry_id => 1 } });
910#     die MT->translate('The same archive file exists. You should change the basename or the archive path. ([_1])', $file) if $fcount > 0;
911# }
912
913    my $url = $blog->archive_url;
914    $url = $blog->site_url
915      if $archiver->entry_based && $archiver->entry_class eq 'page';
916    $url .= '/' unless $url =~ m|/$|;
917    $url .= $map->{__saved_output_file};
918
919    my $cached_tmpl = MT->instance->request('__cached_templates')
920      || MT->instance->request( '__cached_templates', {} );
921    my $tmpl_id = $map->template_id;
922
923    # template specific for this entry (or page, as the case may be)
924    if ( $entry && $entry->template_id ) {
925
926        # allow entry to override *if* we're publishing an individual
927        # page, and this is the 'preferred' one...
928        if ( $archiver->entry_based ) {
929            if ( $map->is_preferred ) {
930                $tmpl_id = $entry->template_id;
931            }
932        }
933    }
934
935    my $tmpl = $cached_tmpl->{$tmpl_id};
936    unless ($tmpl) {
937        $tmpl = MT::Template->load($tmpl_id);
938        if ($cached_tmpl) {
939            $cached_tmpl->{$tmpl_id} = $tmpl;
940        }
941    }
942
943    $tmpl->context($ctx);
944
945    # From Here
946    if ( my $tmpl_param = $archiver->template_params ) {
947        $tmpl->param($tmpl_param);
948    }
949
950    my ($rel_url) = ( $url =~ m|^(?:[^:]*\:\/\/)?[^/]*(.*)| );
951    $rel_url =~ s|//+|/|g;
952
953    # Clear out all the FileInfo records that might point at the page
954    # we're about to create
955    # FYI: if it's an individual entry, we don't use the date as a
956    #      criterion, since this could actually have changed since
957    #      the FileInfo was last built. When the date does change,
958    #      the old date-based archive doesn't necessarily get fixed,
959    #      but if another comes along it will get corrected
960    unless ($finfo) {
961        my %terms;
962        $terms{blog_id}     = $blog->id;
963        $terms{category_id} = $category->id if $archiver->category_based;
964        $terms{author_id}   = $author->id if $archiver->author_based;
965        $terms{entry_id}    = $entry->id if $archiver->entry_based;
966        $terms{startdate}   = $start
967          if $archiver->date_based && ( !$archiver->entry_based );
968        $terms{archive_type}   = $at;
969        $terms{templatemap_id} = $map->id;
970        my @finfos = MT::FileInfo->load( \%terms );
971
972        if (   ( scalar @finfos == 1 )
973            && ( $finfos[0]->file_path eq $file )
974            && ( ( $finfos[0]->url || '' ) eq $rel_url )
975            && ( $finfos[0]->template_id == $tmpl_id ) )
976        {
977
978            # if the shoe fits, wear it
979            $finfo = $finfos[0];
980        }
981        else {
982
983           # if the shoe don't fit, remove all shoes and create the perfect shoe
984            foreach (@finfos) { $_->remove(); }
985
986            $finfo = MT::FileInfo->set_info_for_url(
987                $rel_url, $file, $at,
988                {
989                    Blog        => $blog->id,
990                    TemplateMap => $map->id,
991                    Template    => $tmpl_id,
992                    ( $archiver->entry_based && $entry )
993                    ? ( Entry => $entry->id )
994                    : (),
995                    StartDate => $start,
996                    ( $archiver->category_based && $category )
997                    ? ( Category => $category->id )
998                    : (),
999                    ( $archiver->author_based )
1000                    ? ( Author => $author->id )
1001                    : (),
1002                }
1003              )
1004              || die "Couldn't create FileInfo because "
1005              . MT::FileInfo->errstr();
1006        }
1007    }
1008
1009    # If you rebuild when you've just switched to dynamic pages,
1010    # we move the file that might be there so that the custom
1011    # 404 will be triggered.
1012    require MT::PublishOption;
1013    if ( $tmpl->build_dynamic
1014      || ( $map->build_type == MT::PublishOption::DYNAMIC() ) )
1015    {
1016        rename(
1017            $finfo->file_path,    # is this just $file ?
1018            $finfo->file_path . '.static'
1019        );
1020
1021        ## If the FileInfo is set to static, flip it to virtual.
1022        if ( !$finfo->virtual ) {
1023            $finfo->virtual(1);
1024            $finfo->save();
1025        }
1026    }
1027
1028    return 1 if ( $tmpl->build_dynamic )
1029        || ( $map->build_type == MT::PublishOption::DYNAMIC() );
1030    return 1 if ( $entry && $entry->status != MT::Entry::RELEASE() );
1031    return 1 unless ( $tmpl->build_type );
1032
1033    my $timer = MT->get_timer;
1034    if ($timer) {
1035        $timer->pause_partial;
1036    }
1037    local $timer->{elapsed} = 0 if $timer;
1038
1039    if (
1040        $build_static
1041        && MT->run_callbacks(
1042            'build_file_filter',
1043            Context      => $ctx,
1044            context      => $ctx,
1045            ArchiveType  => $at,
1046            archive_type => $at,
1047            TemplateMap  => $map,
1048            template_map => $map,
1049            Blog         => $blog,
1050            blog         => $blog,
1051            Entry        => $entry,
1052            entry        => $entry,
1053            FileInfo     => $finfo,
1054            file_info    => $finfo,
1055            File         => $file,
1056            file         => $file,
1057            Template     => $tmpl,
1058            template     => $tmpl,
1059            PeriodStart  => $start,
1060            period_start => $start,
1061            Category     => $category,
1062            category     => $category,
1063            force        => ($args{Force} ? 1 : 0),
1064        )
1065      )
1066    {
1067
1068        if ( $archiver->group_based ) {
1069            require MT::Promise;
1070            my $entries = sub { $archiver->archive_group_entries($ctx) };
1071            $ctx->stash( 'entries', MT::Promise::delay($entries) );
1072        }
1073
1074        my $html = undef;
1075        $ctx->stash( 'blog', $blog );
1076        $ctx->stash( 'entry', $entry ) if $entry;
1077
1078        require MT::Request;
1079        MT::Request->instance->cache('build_template', $tmpl);
1080
1081        $html = $tmpl->build( $ctx, $cond );
1082        unless (defined($html)) {
1083            $timer->unpause if $timer;
1084            require MT::I18N;
1085            return $mt->error(
1086            (
1087                $category ? MT->translate(
1088                    "An error occurred publishing [_1] '[_2]': [_3]",
1089                    MT::I18N::lowercase( $category->class_label ),
1090                    $category->id,
1091                    $tmpl->errstr
1092                  )
1093                : $entry ? MT->translate(
1094                    "An error occurred publishing [_1] '[_2]': [_3]",
1095                    MT::I18N::lowercase( $entry->class_label ),
1096                    $entry->title,
1097                    $tmpl->errstr
1098                  )
1099                : MT->translate(
1100"An error occurred publishing date-based archive '[_1]': [_2]",
1101                    $at . $start,
1102                    $tmpl->errstr
1103                )
1104            )
1105          );
1106        }
1107        my $orig_html = $html;
1108        MT->run_callbacks(
1109            'build_page',
1110            Context      => $ctx,
1111            context      => $ctx,
1112            ArchiveType  => $at,
1113            archive_type => $at,
1114            TemplateMap  => $map,
1115            template_map => $map,
1116            Blog         => $blog,
1117            blog         => $blog,
1118            Entry        => $entry,
1119            entry        => $entry,
1120            FileInfo     => $finfo,
1121            file_info    => $finfo,
1122            PeriodStart  => $start,
1123            period_start => $start,
1124            Category     => $category,
1125            category     => $category,
1126            RawContent   => \$orig_html,
1127            raw_content  => \$orig_html,
1128            Content      => \$html,
1129            content      => \$html,
1130            BuildResult  => \$orig_html,
1131            build_result => \$orig_html,
1132            Template     => $tmpl,
1133            template     => $tmpl,
1134            File         => $file,
1135            file         => $file
1136        );
1137        ## First check whether the content is actually
1138        ## changed. If not, we won't update the published
1139        ## file, so as not to modify the mtime.
1140        unless ($fmgr->content_is_updated( $file, \$html )) {
1141            $timer->unpause if $timer;
1142            return 1;
1143        }
1144
1145        ## Determine if we need to build directory structure,
1146        ## and build it if we do. DirUmask determines
1147        ## directory permissions.
1148        require File::Spec;
1149        my $path = dirname($file);
1150        $path =~ s!/$!!
1151          unless $path eq '/'; ## OS X doesn't like / at the end in mkdir().
1152        unless ( $fmgr->exists($path) ) {
1153            if (!$fmgr->mkpath($path)) {
1154                $timer->unpause if $timer;
1155                return $mt->trans_error( "Error making path '[_1]': [_2]",
1156                    $path, $fmgr->errstr );
1157            }
1158        }
1159
1160        ## By default we write all data to temp files, then rename
1161        ## the temp files to the real files (an atomic
1162        ## operation). Some users don't like this (requires too
1163        ## liberal directory permissions). So we have a config
1164        ## option to turn it off (NoTempFiles).
1165        my $use_temp_files = !$mt->{NoTempFiles};
1166        my $temp_file = $use_temp_files ? "$file.new" : $file;
1167        unless ( defined $fmgr->put_data( $html, $temp_file ) ) {
1168            $timer->unpause if $timer;
1169            return $mt->trans_error( "Writing to '[_1]' failed: [_2]",
1170                $temp_file, $fmgr->errstr );
1171        }
1172        if ($use_temp_files) {
1173            if (!$fmgr->rename( $temp_file, $file )) {
1174                $timer->unpause if $timer;
1175                return $mt->trans_error(
1176                    "Renaming tempfile '[_1]' failed: [_2]",
1177                    $temp_file, $fmgr->errstr );
1178            }
1179        }
1180        MT->run_callbacks(
1181            'build_file',
1182            Context      => $ctx,
1183            context      => $ctx,
1184            ArchiveType  => $at,
1185            archive_type => $at,
1186            TemplateMap  => $map,
1187            template_map => $map,
1188            FileInfo     => $finfo,
1189            file_info    => $finfo,
1190            Blog         => $blog,
1191            blog         => $blog,
1192            Entry        => $entry,
1193            entry        => $entry,
1194            PeriodStart  => $start,
1195            period_start => $start,
1196            RawContent   => \$orig_html,
1197            raw_content  => \$orig_html,
1198            Content      => \$html,
1199            content      => \$html,
1200            BuildResult  => \$orig_html,
1201            build_result => \$orig_html,
1202            Template     => $tmpl,
1203            template     => $tmpl,
1204            Category     => $category,
1205            category     => $category,
1206            File         => $file,
1207            file         => $file
1208        );
1209    }
1210    $timer->mark("total:rebuild_file[template_id:" . $tmpl->id . "]")
1211        if $timer;
1212    1;
1213}
1214
1215sub rebuild_indexes {
1216    my $mt    = shift;
1217    my %param = @_;
1218    require MT::Template;
1219    require MT::Template::Context;
1220    require MT::Entry;
1221    my $blog;
1222    unless ( $blog = $param{Blog} ) {
1223        my $blog_id = $param{BlogID};
1224        $blog = MT::Blog->load($blog_id)
1225          or return $mt->error(
1226            MT->translate(
1227                "Load of blog '[_1]' failed: [_2]", $blog_id,
1228                MT::Blog->errstr
1229            )
1230          );
1231    }
1232    my $tmpl = $param{Template};
1233    unless ($blog) {
1234        $blog = MT::Blog->load( $tmpl->blog_id );
1235    }
1236    return 1 if $blog->is_dynamic;
1237    my $iter;
1238    if ($tmpl) {
1239        my $i = 0;
1240        $iter = sub { $i++ < 1 ? $tmpl : undef };
1241    }
1242    else {
1243        $iter = MT::Template->load_iter(
1244            {
1245                type    => 'index',
1246                blog_id => $blog->id
1247            }
1248        );
1249    }
1250    my $force = $param{Force};
1251
1252    local *FH;
1253    my $site_root = $blog->site_path;
1254    return $mt->error(
1255        MT->translate("You did not set your blog publishing path") )
1256      unless $site_root;
1257    my $fmgr = $blog->file_mgr;
1258    while ( my $tmpl = $iter->() ) {
1259        ## Skip index templates that the user has designated not to be
1260        ## rebuilt automatically. We need to do the defined-ness check
1261        ## because we added the flag in 2.01, and for templates saved
1262        ## before that time, the rebuild_me flag will be undefined. But
1263        ## we assume that these templates should be rebuilt, since that
1264        ## was the previous behavior.
1265        ## Note that dynamic templates do need to be "rebuilt"--the
1266        ## FileInfo table needs to be maintained.
1267        if ( !$tmpl->build_dynamic && !$force ) {
1268            next if ( defined $tmpl->rebuild_me && !$tmpl->rebuild_me );
1269        }
1270        next if ( defined $tmpl->build_type && !$tmpl->build_type );
1271
1272        my $file = $tmpl->outfile;
1273        $file = '' unless defined $file;
1274        if ( $tmpl->build_dynamic && ( $file eq '' ) ) {
1275            next;
1276        }
1277        return $mt->error(
1278            MT->translate(
1279                "Template '[_1]' does not have an Output File.",
1280                $tmpl->name
1281            )
1282        ) unless $file ne '';
1283        my $url = join( '/', $blog->site_url, $file );
1284        unless ( File::Spec->file_name_is_absolute($file) ) {
1285            $file = File::Spec->catfile( $site_root, $file );
1286        }
1287
1288        # Everything from here out is identical with rebuild_file
1289        my ($rel_url) = ( $url =~ m|^(?:[^:]*\:\/\/)?[^/]*(.*)| );
1290        $rel_url =~ s|//+|/|g;
1291        ## Untaint. We have to assume that we can trust the user's setting of
1292        ## the site_path and the template outfile.
1293        ($file) = $file =~ /(.+)/s;
1294        my $finfo = $param{FileInfo};  # available for single template calls
1295        unless ( $finfo ) {
1296            require MT::FileInfo;
1297            my @finfos = MT::FileInfo->load(
1298                {
1299                    blog_id     => $tmpl->blog_id,
1300                    template_id => $tmpl->id
1301                }
1302            );
1303            if (   ( scalar @finfos == 1 )
1304                && ( $finfos[0]->file_path eq $file )
1305                && ( ( $finfos[0]->url || '' ) eq $rel_url ) )
1306            {
1307                $finfo = $finfos[0];
1308            }
1309            else {
1310                foreach (@finfos) { $_->remove(); }
1311                $finfo = MT::FileInfo->set_info_for_url(
1312                    $rel_url, $file, 'index',
1313                    {
1314                        Blog     => $tmpl->blog_id,
1315                        Template => $tmpl->id,
1316                    }
1317                  )
1318                  || die "Couldn't create FileInfo because " . MT::FileInfo->errstr;
1319            }
1320        }
1321        if ( $tmpl->build_dynamic ) {
1322            rename( $file, $file . ".static" );
1323
1324            ## If the FileInfo is set to static, flip it to virtual.
1325            if ( !$finfo->virtual ) {
1326                $finfo->virtual(1);
1327                $finfo->save();
1328            }
1329        }
1330
1331        next if ( $tmpl->build_dynamic );
1332        next unless ( $tmpl->build_type );
1333
1334        ## We're not building dynamically, so if the FileInfo is currently
1335        ## set as dynamic (virtual), change it to static.
1336        if ( $finfo && $finfo->virtual ) {
1337            $finfo->virtual(0);
1338            $finfo->save();
1339        }
1340
1341        my $timer = MT->get_timer;
1342        if ($timer) {
1343            $timer->pause_partial;
1344        }
1345        local $timer->{elapsed} = 0 if $timer;
1346
1347        my $ctx = MT::Template::Context->new;
1348        next
1349          unless (
1350            MT->run_callbacks(
1351                'build_file_filter',
1352                Context      => $ctx,
1353                context      => $ctx,
1354                ArchiveType  => 'index',
1355                archive_type => 'index',
1356                Blog         => $blog,
1357                blog         => $blog,
1358                FileInfo     => $finfo,
1359                file_info    => $finfo,
1360                Template     => $tmpl,
1361                template     => $tmpl,
1362                File         => $file,
1363                file         => $file,
1364                force        => $force,
1365            )
1366          );
1367        $ctx->stash( 'blog', $blog );
1368
1369        require MT::Request;
1370        MT::Request->instance->cache('build_template', $tmpl);
1371
1372        my $html = $tmpl->build($ctx);
1373        unless (defined $html) {
1374            $timer->unpause if $timer;
1375            return $mt->error( $tmpl->errstr );
1376        }
1377
1378        my $orig_html = $html;
1379        MT->run_callbacks(
1380            'build_page',
1381            Context      => $ctx,
1382            context      => $ctx,
1383            Blog         => $blog,
1384            blog         => $blog,
1385            FileInfo     => $finfo,
1386            file_info    => $finfo,
1387            ArchiveType  => 'index',
1388            archive_type => 'index',
1389            RawContent   => \$orig_html,
1390            raw_content  => \$orig_html,
1391            Content      => \$html,
1392            content      => \$html,
1393            BuildResult  => \$orig_html,
1394            build_result => \$orig_html,
1395            Template     => $tmpl,
1396            template     => $tmpl,
1397            File         => $file,
1398            file         => $file
1399        );
1400
1401        ## First check whether the content is actually changed. If not,
1402        ## we won't update the published file, so as not to modify the mtime.
1403        next unless $fmgr->content_is_updated( $file, \$html );
1404
1405        ## Determine if we need to build directory structure,
1406        ## and build it if we do. DirUmask determines
1407        ## directory permissions.
1408        require File::Spec;
1409        my $path = dirname($file);
1410        $path =~ s!/$!!
1411          unless $path eq '/';    ## OS X doesn't like / at the end in mkdir().
1412        unless ( $fmgr->exists($path) ) {
1413            if (! $fmgr->mkpath($path) ) {
1414                $timer->unpause if $timer;
1415                return $mt->trans_error( "Error making path '[_1]': [_2]",
1416                    $path, $fmgr->errstr );
1417            }
1418        }
1419
1420        ## Update the published file.
1421        my $use_temp_files = !$mt->{NoTempFiles};
1422        my $temp_file = $use_temp_files ? "$file.new" : $file;
1423        unless (defined( $fmgr->put_data( $html, $temp_file ) )) {
1424            $timer->unpause if $timer;
1425            return $mt->trans_error( "Writing to '[_1]' failed: [_2]",
1426                $temp_file, $fmgr->errstr );
1427        }
1428        if ($use_temp_files) {
1429            if (!$fmgr->rename( $temp_file, $file )) {
1430                $timer->unpause if $timer;
1431                return $mt->trans_error( "Renaming tempfile '[_1]' failed: [_2]",
1432                    $temp_file, $fmgr->errstr );
1433            }
1434        }
1435        MT->run_callbacks(
1436            'build_file',
1437            Context      => $ctx,
1438            context      => $ctx,
1439            ArchiveType  => 'index',
1440            archive_type => 'index',
1441            FileInfo     => $finfo,
1442            file_info    => $finfo,
1443            Blog         => $blog,
1444            blog         => $blog,
1445            RawContent   => \$orig_html,
1446            raw_content  => \$orig_html,
1447            Content      => \$html,
1448            content      => \$html,
1449            BuildResult  => \$orig_html,
1450            build_result => \$orig_html,
1451            Template     => $tmpl,
1452            template     => $tmpl,
1453            File         => $file,
1454            file         => $file
1455        );
1456
1457        $timer->mark("total:rebuild_indexes[template_id:" . $tmpl->id . ";file:$file]")
1458            if $timer;
1459    }
1460    1;
1461}
1462
1463sub rebuild_from_fileinfo {
1464    my $pub = shift;
1465    my ($fi) = @_;
1466
1467    require MT::Blog;
1468    require MT::Entry;
1469    require MT::Category;
1470    require MT::Template;
1471    require MT::TemplateMap;
1472    require MT::Template::Context;
1473
1474    my $at = $fi->archive_type
1475      or return $pub->error(
1476        MT->translate( "Parameter '[_1]' is required", 'ArchiveType' ) );
1477
1478    # callback for custom archive types
1479    return
1480      unless MT->run_callbacks(
1481        'build_archive_filter',
1482        archive_type => $at,
1483        file_info    => $fi
1484      );
1485
1486    if ( $at eq 'index' ) {
1487        $pub->rebuild_indexes(
1488            BlogID   => $fi->blog_id,
1489            Template => MT::Template->load( $fi->template_id ),
1490            FileInfo => $fi,
1491            Force    => 1,
1492        ) or return;
1493        return 1;
1494    }
1495
1496    return 1 if $at eq 'None';
1497
1498    my ( $start, $end );
1499    my $blog = MT::Blog->load( $fi->blog_id )
1500      if $fi->blog_id;
1501    my $entry = MT::Entry->load( $fi->entry_id )
1502      or return $pub->error(
1503        MT->translate( "Parameter '[_1]' is required", 'Entry' ) )
1504      if $fi->entry_id;
1505    if ( $fi->startdate ) {
1506        my $archiver = $pub->archiver($at);
1507
1508        if ( ( $start, $end ) = $archiver->date_range( $fi->startdate ) ) {
1509            $entry = MT::Entry->load( { authored_on => [ $start, $end ] },
1510                { range_incl => { authored_on => 1 }, limit => 1 } )
1511              or return $pub->error(
1512                MT->translate( "Parameter '[_1]' is required", 'Entry' ) );
1513        }
1514    }
1515    my $cat = MT::Category->load( $fi->category_id )
1516      if $fi->category_id;
1517    my $author = MT::Author->load( $fi->author_id )
1518      if $fi->author_id;
1519
1520    ## Load the template-archive-type map entries for this blog and
1521    ## archive type. We do this before we load the list of entries, because
1522    ## we will run through the files and check if we even need to rebuild
1523    ## anything. If there is nothing to rebuild at all for this entry,
1524    ## we save some time by not loading the list of entries.
1525    my $map = MT::TemplateMap->load( $fi->templatemap_id );
1526    my $file = $pub->archive_file_for( $entry, $blog, $at, $cat, $map,
1527        undef, $author );
1528    if ( !defined($file) ) {
1529        return $pub->error( $blog->errstr() );
1530    }
1531    $map->{__saved_output_file} = $file;
1532
1533    my $ctx = MT::Template::Context->new;
1534    $ctx->{current_archive_type} = $at;
1535    if ( $start && $end ) {
1536        $ctx->{current_timestamp} = $start;
1537        $ctx->{current_timestamp_end} = $end;
1538    }
1539
1540    my $arch_root =
1541      ( $at eq 'Page' ) ? $blog->site_path : $blog->archive_path;
1542    return $pub->error(
1543        MT->translate("You did not set your blog publishing path") )
1544      unless $arch_root;
1545
1546    my %cond;
1547    $pub->rebuild_file( $blog, $arch_root, $map, $at, $ctx, \%cond, 1,
1548        FileInfo => $fi, )
1549      or return;
1550
1551    1;
1552}
1553
1554sub trans_error {
1555    my $this = shift;
1556    return $this->error( MT->translate(@_) );
1557}
1558
1559sub publish_future_posts {
1560    my $this = shift;
1561
1562    require MT::Blog;
1563    require MT::Entry;
1564    require MT::Util;
1565    my $mt            = MT->instance;
1566    my $total_changed = 0;
1567    my @blogs = MT::Blog->load(undef, {
1568        join => MT::Entry->join_on('blog_id', {
1569            status => MT::Entry::FUTURE(),
1570        }, { unique => 1 })
1571    });
1572    foreach my $blog (@blogs) {
1573        my @ts = MT::Util::offset_time_list( time, $blog );
1574        my $now = sprintf "%04d%02d%02d%02d%02d%02d", $ts[5] + 1900, $ts[4] + 1,
1575          @ts[ 3, 2, 1, 0 ];
1576        my $iter = MT::Entry->load_iter(
1577            {
1578                blog_id => $blog->id,
1579                status  => MT::Entry::FUTURE(),
1580                class   => '*'
1581            },
1582            {
1583                'sort'    => 'authored_on',
1584                direction => 'descend'
1585            }
1586        );
1587        my @queue;
1588        while ( my $entry = $iter->() ) {
1589            push @queue, $entry->id if $entry->authored_on le $now;
1590        }
1591
1592        my $changed = 0;
1593        my @results;
1594        my %rebuild_queue;
1595        my %ping_queue;
1596        foreach my $entry_id (@queue) {
1597            my $entry = MT::Entry->load($entry_id)
1598                or next;
1599            $entry->status( MT::Entry::RELEASE() );
1600            $entry->save
1601              or die $entry->errstr;
1602
1603            $rebuild_queue{ $entry->id } = $entry;
1604            $ping_queue{ $entry->id }    = 1;
1605            my $n = $entry->next(1);
1606            $rebuild_queue{ $n->id } = $n if $n;
1607            my $p = $entry->previous(1);
1608            $rebuild_queue{ $p->id } = $p if $p;
1609            $changed++;
1610            $total_changed++;
1611        }
1612        if ($changed) {
1613            my %rebuilt_okay;
1614            my $rebuilt;
1615            eval {
1616                foreach my $id ( keys %rebuild_queue )
1617                {
1618                    my $entry = $rebuild_queue{$id};
1619                    $mt->rebuild_entry( Entry => $entry, Blog => $blog )
1620                      or die $mt->errstr;
1621                    $rebuilt_okay{$id} = 1;
1622                    if ( $ping_queue{$id} ) {
1623                        $mt->ping_and_save( Entry => $entry, Blog => $blog );
1624                    }
1625                    $rebuilt++;
1626                }
1627                $mt->rebuild_indexes( Blog => $blog )
1628                  or die $mt->errstr;
1629            };
1630            if ( my $err = $@ ) {
1631
1632                # a fatal error occured while processing the rebuild
1633                # step. LOG the error and revert the entry/entries:
1634                require MT::Log;
1635                $mt->log(
1636                    {
1637                        message => $mt->translate(
1638"An error occurred while publishing scheduled entries: [_1]",
1639                            $err
1640                        ),
1641                        class   => "system",
1642                        blog_id => $blog->id,
1643                        level   => MT::Log::ERROR()
1644                    }
1645                );
1646                foreach my $id (@queue) {
1647                    next if exists $rebuilt_okay{$id};
1648                    my $e = $rebuild_queue{$id};
1649                    next unless $e;
1650                    $e->status( MT::Entry::FUTURE() );
1651                    $e->save or die $e->errstr;
1652                }
1653            }
1654        }
1655    }
1656    $total_changed > 0 ? 1 : 0;
1657}
1658
1659sub remove_entry_archive_file {
1660    my $mt    = shift;
1661    my %param = @_;
1662
1663    my $entry = $param{Entry};
1664    my $at    = $param{ArchiveType} || 'Individual';
1665    my $cat   = $param{Category};
1666    my $auth  = $param{Author};
1667
1668    require MT::TemplateMap;
1669    my $blog = $param{Blog};
1670    unless ($blog) {
1671        if ($entry) {
1672            $blog = $entry->blog;
1673        }
1674        elsif ($cat) {
1675            require MT::Blog;
1676            $blog = MT::Blog->load( $cat->blog_id )
1677                or return;
1678        }
1679    }
1680    my @map = MT::TemplateMap->load(
1681        {
1682            archive_type => $at,
1683            blog_id      => $blog->id,
1684            $param{TemplateID} ? ( template_id => $param{TemplateID} ) : (),
1685        }
1686    );
1687    return 1 unless @map;
1688
1689    my $fmgr = $blog->file_mgr;
1690    my $arch_root = ( $at eq 'Page' ) ? $blog->site_path : $blog->archive_path;
1691
1692    require File::Spec;
1693    for my $map (@map) {
1694        my $file =
1695          $mt->archive_file_for( $entry, $blog, $at, $cat, $map, undef, $auth );
1696        $file = File::Spec->catfile( $arch_root, $file );
1697        if ( !defined($file) ) {
1698            die MT->translate( $blog->errstr() );
1699            return $mt->error( MT->translate( $blog->errstr() ) );
1700        }
1701
1702        # Run callbacks
1703        MT->run_callbacks( 'pre_delete_archive_file', $file, $at, $entry);
1704
1705        $fmgr->delete($file);
1706
1707        # Run callbacks
1708        MT->run_callbacks( 'post_delete_archive_file', $file, $at, $entry);
1709    }
1710    1;
1711}
1712
1713##
1714## archive_file_for takes an entry to determine the timestamps,
1715## but if the entry is not available it uses the time_start
1716## and time_end values
1717##
1718sub archive_file_for {
1719    my $mt = shift;
1720    init_archive_types() unless %ArchiveTypes;
1721
1722    my ( $entry, $blog, $at, $cat, $map, $timestamp, $author ) = @_;
1723    return if $at eq 'None';
1724    my $archiver = $mt->archiver($at);
1725    return '' unless $archiver;
1726
1727    my $file;
1728    if ( $blog->is_dynamic ) {
1729        require MT::TemplateMap;
1730        $map = MT::TemplateMap->new;
1731        $map->file_template( $archiver->dynamic_template );
1732    }
1733    unless ($map) {
1734        my $cache = MT::Request->instance->cache('maps');
1735        unless ($cache) {
1736            MT::Request->instance->cache( 'maps', $cache = {} );
1737        }
1738        unless ( $map = $cache->{ $blog->id . $at } ) {
1739            require MT::TemplateMap;
1740            $map = MT::TemplateMap->load(
1741                {
1742                    blog_id      => $blog->id,
1743                    archive_type => $at,
1744                    is_preferred => 1
1745                }
1746            );
1747            $cache->{ $blog->id . $at } = $map if $map;
1748        }
1749    }
1750    my $file_tmpl = $map->file_template if $map;
1751    unless ($file_tmpl) {
1752        if ( my $tmpls = $archiver->default_archive_templates ) {
1753            my ($default) = grep { $_->{default} } @$tmpls;
1754            $file_tmpl = $default->{template} if $default;
1755        }
1756    }
1757    $file_tmpl ||= '';
1758    my ($ctx);
1759    if ( $file_tmpl =~ m/\%[_-]?[A-Za-z]/ ) {
1760        if ( $file_tmpl =~ m/<\$?MT/ ) {
1761            $file_tmpl =~
1762s!(<\$?MT[^>]+?>)|(%[_-]?[A-Za-z])!$1 ? $1 : '<MTFileTemplate format="'. $2 . '">'!gie;
1763        }
1764        else {
1765            $file_tmpl = qq{<MTFileTemplate format="$file_tmpl">};
1766        }
1767    }
1768    if ($file_tmpl) {
1769        require MT::Template::Context;
1770        $ctx = MT::Template::Context->new;
1771        $ctx->stash( 'blog', $blog );
1772    }
1773    local $ctx->{__stash}{category}         = $cat if $cat;
1774    local $ctx->{__stash}{archive_category} = $cat if $cat;
1775    $timestamp = $entry->authored_on() if $entry;
1776    local $ctx->{__stash}{entry} = $entry if $entry;
1777    local $ctx->{__stash}{author} =
1778      $author ? $author : $entry ? $entry->author : undef;
1779
1780    my %blog_at = map { $_ => 1 } split /,/, $blog->archive_type;
1781    return '' unless $blog_at{$at};
1782
1783    $file = $archiver->archive_file(
1784        $ctx,
1785        Timestamp => $timestamp,
1786        Template  => $file_tmpl
1787    );
1788    if ( $file_tmpl && !$file ) {
1789        local $ctx->{archive_type} = $at;
1790        require MT::Builder;
1791        my $build = MT::Builder->new;
1792        my $tokens = $build->compile( $ctx, $file_tmpl )
1793          or return $blog->error( $build->errstr() );
1794        defined( $file = $build->build( $ctx, $tokens ) )
1795          or return $blog->error( $build->errstr() );
1796    }
1797    else {
1798        my $ext = $blog->file_extension;
1799        $file .= '.' . $ext if $ext;
1800    }
1801    $file;
1802}
1803
1804# Adds an element to the rebuild queue when the plugin is enabled.
1805sub queue_build_file_filter {
1806    my $mt = shift;
1807    my ( $cb, %args ) = @_;
1808
1809    my $fi = $args{file_info};
1810    return 1 if $fi->{from_queue};
1811
1812    require MT::PublishOption;
1813    my $throttle = MT::PublishOption::get_throttle($fi);
1814
1815    # Prevent building of disabled templates if they get this far
1816    return 0 if $throttle->{type} == MT::PublishOption::DISABLED();
1817
1818    # Check for 'force' flag for 'manual' publish option, which
1819    # forces the template to build; used for 'rebuild' list actions
1820    # and publish site operations
1821    if ($throttle->{type} == MT::PublishOption::MANUALLY()) {
1822        return $args{force} ? 1 : 0;
1823    }
1824
1825    # From here on, we're committed to publishing this file via TheSchwartz
1826    return 1 if $throttle->{type} != MT::PublishOption::ASYNC();
1827
1828    require MT::TheSchwartz;
1829    require TheSchwartz::Job;
1830    my $job = TheSchwartz::Job->new();
1831    $job->funcname('MT::Worker::Publish');
1832    $job->uniqkey( $fi->id );
1833
1834    my $priority = 0;
1835
1836    my $at = $fi->archive_type || '';
1837    # Default priority assignment....
1838    if (($at eq 'Individual') || ($at eq 'Page')) {
1839        require MT::TemplateMap;
1840        my $map = MT::TemplateMap->load($fi->templatemap_id);
1841        # Individual/Page archive pages that are the 'permalink' pages
1842        # should have highest build priority.
1843        if ($map && $map->is_preferred) {
1844            $priority = 10;
1845        } else {
1846            $priority = 5;
1847        }
1848    } elsif ($at eq 'index') {
1849        # Index pages are second in priority, if they are named 'index'
1850        # or 'default'
1851        if ($fi->file_path =~ m!/(index|default|atom|feed)!i) {
1852            $priority = 9;
1853        } else {
1854            $priority = 8;
1855        }
1856    } elsif ($at =~ m/Category|Author/) {
1857        $priority = 1;
1858    } elsif ($at =~ m/Yearly/) {
1859        $priority = 1;
1860    } elsif ($at =~ m/Monthly/) {
1861        $priority = 2;
1862    } elsif ($at =~ m/Weekly/) {
1863        $priority = 3;
1864    } elsif ($at =~ m/Daily/) {
1865        $priority = 4;
1866    }
1867
1868    $job->priority( $priority );
1869    $job->coalesce( ( $fi->blog_id || 0 ) . ':' . $$ . ':' . $priority . ':' . ( time - ( time % 10 ) ) );
1870
1871    MT::TheSchwartz->insert($job);
1872
1873    return 0;
1874}
1875
18761;
1877
1878__END__
1879
1880=head1 NAME
1881
1882MT::WeblogPublisher - Express weblog templates and content into a specific URL structure
1883
1884=head1 SYNOPSIS
1885
1886    use MT::WeblogPublisher;
1887    my $pub = MT::WeblogPublisher->new;
1888    $pub->rebuild(Blog => $blog, ArchiveType => "Individual");
1889
1890=head1 METHODS
1891
1892=head2 MT::WeblogPublisher->new()
1893
1894Return a new C<MT::WeblogPublisher>. Additionally, call
1895L<MT::ConfigMgr/instance> and set the I<NoTempFiles> and
1896I<PublishCommenterIcon> attributes, if not already set.
1897
1898=head2 $mt->rebuild( %args )
1899
1900Rebuilds your entire blog, indexes and archives; or some subset of your blog,
1901as specified in the arguments.
1902
1903I<%args> can contain:
1904
1905=over 4
1906
1907=item * Blog
1908
1909An I<MT::Blog> object corresponding to the blog that you would like to
1910rebuild.
1911
1912Either this or C<BlogID> is required.
1913
1914=item * BlogID
1915
1916The ID of the blog that you would like to rebuild.
1917
1918Either this or C<Blog> is required.
1919
1920=item * ArchiveType
1921
1922The archive type that you would like to rebuild. This should be one of
1923the following string values: C<Individual>, C<Daily>, C<Weekly>,
1924C<Monthly>, or C<Category>.
1925
1926This argument is optional; if not provided, all archive types will be rebuilt.
1927
1928=item * EntryCallback
1929
1930A callback that will be called for each entry that is rebuilt. If provided,
1931the value should be a subroutine reference; the subroutine will be handed
1932the I<MT::Entry> object for the entry that is about to be rebuilt. You could
1933use this to keep a running log of which entry is being rebuilt, for example:
1934
1935    $mt->rebuild(
1936              BlogID => $blog_id,
1937              EntryCallback => sub { print $_[0]->title, "\n" },
1938          );
1939
1940Or to provide a status indicator:
1941
1942    use MT::Entry;
1943    my $total = MT::Entry->count({ blog_id => $blog_id });
1944    my $i = 0;
1945    local $| = 1;
1946    $mt->rebuild(
1947              BlogID => $blog_id,
1948              EntryCallback => sub { printf "%d/%d\r", ++$i, $total },
1949          );
1950    print "\n";
1951
1952This argument is optional; by default no callbacks are executed.
1953
1954=item * NoIndexes
1955
1956By default I<rebuild> will rebuild the index templates after rebuilding all
1957of the entries; if you do not want to rebuild the index templates, set the
1958value for this argument to a true value.
1959
1960This argument is optional.
1961
1962=item * Limit
1963
1964Limit the number of entries to be rebuilt to the last C<N> entries in the
1965blog. For example, if you set this to C<20> and do not provide an offset (see
1966L<Offset>, below), the 20 most recent entries in the blog will be rebuilt.
1967
1968This is only useful if you are rebuilding C<Individual> archives.
1969
1970This argument is optional; by default all entries will be rebuilt.
1971
1972=item * Offset
1973
1974When used with C<Limit>, specifies the entry at which to start rebuilding
1975your individual entry archives. For example, if you set this to C<10>, and
1976set a C<Limit> of C<5> (see L<Limit>, above), entries 10-14 (inclusive) will
1977be rebuilt. The offset starts at C<0>, and the ordering is reverse
1978chronological.
1979
1980This is only useful if you are rebuilding C<Individual> archives, and if you
1981are using C<Limit>.
1982
1983This argument is optional; by default all entries will be rebuilt, starting
1984at the first entry.
1985
1986=back
1987
1988=head2 $mt->rebuild_entry( %args )
1989
1990Rebuilds a particular entry in your blog (and its dependencies, if specified).
1991
1992I<%args> can contain:
1993
1994=over 4
1995
1996=item * Entry
1997
1998An I<MT::Entry> object corresponding to the object you would like to rebuild.
1999
2000This argument is required.
2001
2002=item * Blog
2003
2004An I<MT::Blog> object corresponding to the blog to which the I<Entry> belongs.
2005
2006This argument is optional; if not provided, the I<MT::Blog> object will be
2007loaded in I<rebuild_entry> from the I<$entry-E<gt>blog_id> column of the
2008I<MT::Entry> object passed in. If you already have the I<MT::Blog> object
2009loaded, however, it makes sense to pass it in yourself, as it will skip one
2010small step in I<rebuild_entry> (loading the object).
2011
2012=item * BuildDependencies
2013
2014Saving an entry can have effects on other entries; so after saving, it is
2015often necessary to rebuild other entries, to reflect the changes onto all
2016of the affected archive pages, indexes, etc.
2017
2018If you supply this parameter with a true value, I<rebuild_indexes> will
2019rebuild: the archives for the next and previous entries, chronologically;
2020all of the index templates; the archives for the next and previous daily,
2021weekly, and monthly archives.
2022
2023=item * Previous, Next, OldPrevious, OldNext
2024
2025These values identify entries which may need to be updated now that
2026"this" entry has changed. When the authored_on field of an entry is
2027changed, its new neighbors (Previous and Next) need to be rebuilt as
2028well as its former neighbors (OldPrevious and OldNext).
2029
2030=item * NoStatic
2031
2032When this value is true, it acts as a hint to the rebuilding routine
2033that static output files need not be rebuilt; the "rebuild" operation
2034is just to update the bookkeeping that supports dynamic rebuilds.
2035
2036=back
2037
2038=head2 $mt->rebuild_file($blog, $archive_root, $map, $archive_type, $ctx, \%cond, $build_static, %args)
2039
2040Method responsible for building a single archive page from a template and
2041writing it to the file management layer.
2042
2043I<$blog> refers to the target weblog. I<$archive_root> is the target archive
2044path to publish the file. I<$map> is a L<MT::TemplateMap> object that
2045relates to publishing the file. I<$archive_type> is one of "Daily",
2046"Weekly", "Monthly", "Category" or "Individual". I<$ctx> is a handle to
2047the L<MT::Template::Context> object to use to build the file. I<\%cond>
2048is a hashref to conditional arguments used to drive the build process.
2049I<$build_static> is a boolean flag that controls whether static files are
2050created (otherwise, the records necessary for serving dynamic pages are
2051created and that is all).
2052
2053I<%args> is a hash that uniquely identifies the specific instance
2054of the given archive type. That is, for a category archive page it
2055identifies the category; for a date-based archive page it identifies
2056which time period is covered by the page; for an individual archive it
2057identifies the entry. I<%args> should contain just one of these
2058keys:
2059
2060=over 4
2061
2062=item * Category
2063
2064A category ID or L<MT::Category> instance of the category archive page to
2065be built.
2066
2067=item * Entry
2068
2069An entry ID or L<MT::Entry> instance of the entry archive page to be
2070built.
2071
2072=item * StartDate
2073
2074The starting timestamp of the date-based archive to be built.
2075
2076=back
2077
2078=head2 $mt->rebuild_indexes( %args )
2079
2080Rebuilds all of the index templates in your blog, or just one, if you use
2081the I<Template> argument (below). Only rebuilds templates that are set to
2082be rebuilt automatically, unless you use the I<Force> (below).
2083
2084I<%args> can contain:
2085
2086=over 4
2087
2088=item * Blog
2089
2090An I<MT::Blog> object corresponding to the blog whose indexes you would like
2091to rebuild.
2092
2093Either this or C<BlogID> is required.
2094
2095=item * BlogID
2096
2097The ID of the blog whose indexes you would like to rebuild.
2098
2099Either this or C<Blog> is required.
2100
2101=item * Template
2102
2103An I<MT::Template> object specifying the index template to rebuild; if you use
2104this argument, I<only> this index template will be rebuilt.
2105
2106Note that if the template that you specify here is set to not rebuild
2107automatically, you I<must> specify the I<Force> argument in order to force it
2108to be rebuilt.
2109
2110=item * Force
2111
2112A boolean flag specifying whether or not to rebuild index templates who have
2113been marked not to be rebuilt automatically.
2114
2115The default is C<0> (do not rebuild such templates).
2116
2117=back
2118
2119=head2 $mt->trans_error
2120
2121Call L<MT/translate>.
2122
2123=head2 $mt->remove_entry_archive_file(%param)
2124
2125Delete the archive files for an entry based on the following
2126parameters.  One of a I<Blog>, I<Entry> or I<Category> are required.
2127
2128=over 4
2129
2130=item * Blog
2131=item * Entry
2132=item * Category
2133=item * ArchiveType (Default 'Individual')
2134=item * TemplateID
2135
2136=back
2137
2138=head2 $mt->rebuild_categories(%param)
2139
2140Rebuild category archives based on the following parameters:
2141
2142=over 4
2143
2144=item * Blog
2145=item * BlogID
2146=item * Limit
2147=item * Offset
2148=item * Limit
2149=item * NoStatic
2150
2151=back
2152
2153=head2 $mt->publish_future_posts
2154
2155Build and publish all scheduled entries with a I<authored_on> timestamp
2156that is less than the current time.
2157
2158=head1 CALLBACKS
2159
2160=over 4
2161
2162=item BuildFileFilter
2163
2164This filter is called when Movable Type wants to rebuild a file, but
2165before doing so. This gives plugins the chance to determine whether a
2166file should actually be rebuild in particular situations.
2167
2168A BuildFileFilter callback routine is called as follows:
2169
2170    sub build_file_filter($eh, %args)
2171    {
2172        ...
2173        return $boolean;
2174    }
2175
2176As with other callback funcions, the first parameter is an
2177C<MT::ErrorHandler> object. This can be used by the callback to
2178propagate an error message to the surrounding context.
2179
2180The C<%args> parameters identify the page to be built. See
2181L<MT::FileInfo> for more information on how a page is determined by
2182these parameters. Elements in C<%args> are as follows:
2183
2184=over 4
2185
2186=item C<Context>
2187
2188Holds the template context that has been constructed for building (see
2189C<MT::Template::Context>).
2190
2191=item C<ArchiveType>
2192
2193The archive type of the file, usually one of C<'Index'>,
2194C<'Individual'>, C<'Category'>, C<'Daily'>, C<'Monthly'>, or
2195C<'Weekly'>.
2196
2197=item C<Templatemap>
2198
2199An C<MT::TemplateMap> object; this singles out which template is being
2200built, and the filesystem path of the file to be written.
2201
2202=item C<Blog>
2203
2204The C<MT::Blog> object representing the blog whose pages are being
2205rebuilt.
2206
2207=item C<Entry>
2208
2209In the case of an individual archive page, this points to the
2210C<MT::Entry> object whose page is being rebuilt. In the case of an
2211archive page other than an individual page, this parameter is not
2212necessarily undefined. It is best to rely on the C<$at> parameter to
2213determine whether a single entry is on deck to be built.
2214
2215=item C<PeriodStart>
2216
2217In the case of a date-based archive page, this is a timestamp at the
2218beginning of the period from which entries will be included on this
2219page, in Movable Type's standard 14-digit "timestamp" format. For
2220example, if the page is a Daily archive for April 17, 1796, this value
2221would be 17960417000000. If the page were a Monthly archive for March,
22222003, C<$start> would be 20030301000000. Again, this parameter may be
2223defined even when the page on deck is not a date-based archive page.
2224
2225=item C<Category>
2226
2227In the case of a Category archive, this parameter identifies the
2228category which will be built on the page.
2229
2230=item C<FileInfo>
2231
2232If defined, an L<MT::FileInfo> object which contains information about the
2233file. See L<MT::FileInfo> for more information about what a C<MT::FileInfo>
2234contains. Chief amongst all the members of C<MT::FileInfo>, for these
2235purposes, will be the C<virtual> member. This is a boolean value which will be
2236false if a page was actually created on disk for this "page," and false if no
2237page was created (because the corresponding template is set to be
2238built dynamically).
2239
2240It is possible for the FileInfo parameter to be undefined, namely if the blog has not been configured to publish anything dynamically, or if the
2241installation is using a data driver that does not support dynamic publishing.
2242
2243=back
2244
2245=item BuildPage
2246
2247BuildPage callbacks are invoked just after a page has been built, but
2248before the content has been written to the file system.
2249
2250    sub build_page($eh, %args)
2251    {
2252    }
2253
2254The parameters given are include those sent to the BuildFileFilter callback.
2255In addition, the following parameters are also given:
2256
2257=over 4
2258
2259=item C<Content>
2260
2261This is a scalar reference to the content that will eventually be
2262published.
2263
2264=item C<BuildResult> / (or C<RawContent>, deprecated)
2265
2266This is a scalar reference to the content originally produced by building
2267the page. This value is provided mainly for reference; modifications to it
2268will be ignored.
2269
2270=back
2271
2272=item BuildFile
2273
2274BuildFile callbacks are invoked just after a file has been built.
2275
2276    sub build_file($eh, %args)
2277    {
2278    }
2279
2280Parameters in %args are as with BuildPage.
2281
2282=back
2283
2284=head1 AUTHOR & COPYRIGHT
2285
2286Please see L<MT/AUTHOR & COPYRIGHT>.
2287
2288=cut
Note: See TracBrowser for help on using the browser.