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

Revision 2299, 75.4 kB (checked in by takayama, 19 months ago)

Fixed BugId:79630
* Applied patch. thank you ogawa-san

  • 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    $at ||= "";
769
770    my $archiver = $mt->archiver($at);
771    return unless $archiver;
772
773    # Special handling for pages-- they are always published to the
774    # 'site' path instead of the 'archive' path, which is reserved for blog
775    # content.
776    my $arch_root = ( $at eq 'Page' ) ? $blog->site_path : $blog->archive_path;
777    return $mt->error(
778        MT->translate("You did not set your blog publishing path") )
779      unless $arch_root;
780
781    my ($start, $end);
782    if (exists $param{Start} && exists $param{End}) {
783        $start = $param{Start};
784        $end   = $param{End};
785    } else {
786        if ($archiver->can('date_range')) {
787            ( $start, $end ) = $archiver->date_range( $entry->authored_on );
788        }
789    }
790
791    ## For each mapping, we need to rebuild the entries we loaded above in
792    ## the particular template map, and write it to the specified archive
793    ## file template.
794    require MT::Template;
795    require MT::Template::Context;
796    for my $map (@map) {
797        next unless $map->build_type; # ignore disabled template maps
798
799        my $ctx = MT::Template::Context->new;
800        $ctx->{current_archive_type} = $at;
801        $ctx->{archive_type}         = $at;
802        $mt->rebuild_file(
803            $blog, $arch_root, $map, $at, $ctx, \my %cond,
804            !$param{NoStatic},
805            Category  => $param{Category},
806            Entry     => $entry,
807            Author    => $param{Author},
808            StartDate => $start,
809            EndDate   => $end,
810            Force     => $param{Force} ? 1 : 0,
811        ) or return;
812        $done->{ $map->{__saved_output_file} }++;
813    }
814    1;
815}
816
817sub rebuild_file {
818    my $mt = shift;
819    my ( $blog, $root_path, $map, $at, $ctx, $cond, $build_static, %args )
820      = @_;
821    my $finfo;
822    my $archiver = $mt->archiver($at);
823    my ( $entry, $start, $end, $category, $author );
824
825    if ( $finfo = $args{FileInfo} ) {
826        $args{Author}   = $finfo->author_id   if $finfo->author_id;
827        $args{Category} = $finfo->category_id if $finfo->category_id;
828        $args{Entry}    = $finfo->entry_id    if $finfo->entry_id;
829        $map ||= MT::TemplateMap->load( $finfo->templatemap_id );
830        $at  ||= $finfo->archive_type;
831        if ( $finfo->startdate ) {
832            if ( ( $start, $end ) = $archiver->date_range($finfo->startdate) ) {
833                $args{StartDate} = $start;
834                $args{EndDate}   = $end;
835            }
836        }
837    }
838
839    # Calculate file path and URL for the new entry.
840    my $file = File::Spec->catfile( $root_path, $map->{__saved_output_file} );
841
842    ## Untaint. We have to assume that we can trust the user's setting of
843    ## the archive_path, and nothing else is based on user input.
844    ($file) = $file =~ /(.+)/s;
845
846    # compare file modification time to start of build process. if it
847    # is greater than the start_time, then we shouldn't need to build this
848    # file again
849    my $fmgr = $blog->file_mgr;
850    if (my $mod_time = $fmgr->file_mod_time($file)) {
851        return 1 if $mod_time >= $mt->start_time;
852    }
853
854    if ( $archiver->category_based ) {
855        $category = $args{Category};
856        die "Category archive type requires Category parameter"
857          unless $args{Category};
858        $category = MT::Category->load($category)
859          unless ref $category;
860        $ctx->var( 'category_archive', 1 );
861        $ctx->{__stash}{archive_category} = $category;
862    }
863    if ( $archiver->entry_based ) {
864        $entry = $args{Entry};
865        die "$at archive type requires Entry parameter"
866          unless $entry;
867        require MT::Entry;
868        $entry = MT::Entry->load($entry) if !ref $entry;
869        $ctx->var( 'entry_archive', 1 );
870        $ctx->{__stash}{entry} = $entry;
871    }
872    if ( $archiver->date_based ) {
873        # Date-based archive type
874        $start = $args{StartDate};
875        $end   = $args{EndDate};
876        Carp::confess("Date-based archive types require StartDate parameter")
877          unless $args{StartDate};
878        $ctx->var( 'datebased_archive', 1 );
879    }
880    if ( $archiver->author_based ) {
881
882        # author based archive type
883        $author = $args{Author};
884        die "Author-based archive type requires Author parameter"
885          unless $args{Author};
886        require MT::Author;
887        $author = MT::Author->load($author)
888          unless ref $author;
889        $ctx->var( 'author_archive', 1 );
890        $ctx->{__stash}{author} = $author;
891    }
892    local $ctx->{current_timestamp}     = $start if $start;
893    local $ctx->{current_timestamp_end} = $end   if $end;
894
895    $ctx->{__stash}{blog} = $blog;
896
897    require MT::FileInfo;
898
899# This kind of testing should be done at the time we save a post,
900# not during publishing!!!
901# if ($archiver->entry_based) {
902#     my $fcount = MT::FileInfo->count({
903#         blog_id => $blog->id,
904#         entry_id => $entry->id,
905#         file_path => $file},
906#         { not => { entry_id => 1 } });
907#     die MT->translate('The same archive file exists. You should change the basename or the archive path. ([_1])', $file) if $fcount > 0;
908# }
909
910    my $url = $blog->archive_url;
911    $url = $blog->site_url
912      if $archiver->entry_based && $archiver->entry_class eq 'page';
913    $url .= '/' unless $url =~ m|/$|;
914    $url .= $map->{__saved_output_file};
915
916    my $cached_tmpl = MT->instance->request('__cached_templates')
917      || MT->instance->request( '__cached_templates', {} );
918    my $tmpl_id = $map->template_id;
919
920    # template specific for this entry (or page, as the case may be)
921    if ( $entry && $entry->template_id ) {
922
923        # allow entry to override *if* we're publishing an individual
924        # page, and this is the 'preferred' one...
925        if ( $archiver->entry_based ) {
926            if ( $map->is_preferred ) {
927                $tmpl_id = $entry->template_id;
928            }
929        }
930    }
931
932    my $tmpl = $cached_tmpl->{$tmpl_id};
933    unless ($tmpl) {
934        $tmpl = MT::Template->load($tmpl_id);
935        if ($cached_tmpl) {
936            $cached_tmpl->{$tmpl_id} = $tmpl;
937        }
938    }
939
940    $tmpl->context($ctx);
941
942    # From Here
943    if ( my $tmpl_param = $archiver->template_params ) {
944        $tmpl->param($tmpl_param);
945    }
946
947    my ($rel_url) = ( $url =~ m|^(?:[^:]*\:\/\/)?[^/]*(.*)| );
948    $rel_url =~ s|//+|/|g;
949
950    # Clear out all the FileInfo records that might point at the page
951    # we're about to create
952    # FYI: if it's an individual entry, we don't use the date as a
953    #      criterion, since this could actually have changed since
954    #      the FileInfo was last built. When the date does change,
955    #      the old date-based archive doesn't necessarily get fixed,
956    #      but if another comes along it will get corrected
957    unless ($finfo) {
958        my %terms;
959        $terms{blog_id}     = $blog->id;
960        $terms{category_id} = $category->id if $archiver->category_based;
961        $terms{author_id}   = $author->id if $archiver->author_based;
962        $terms{entry_id}    = $entry->id if $archiver->entry_based;
963        $terms{startdate}   = $start
964          if $archiver->date_based && ( !$archiver->entry_based );
965        $terms{archive_type}   = $at;
966        $terms{templatemap_id} = $map->id;
967        my @finfos = MT::FileInfo->load( \%terms );
968
969        if (   ( scalar @finfos == 1 )
970            && ( $finfos[0]->file_path eq $file )
971            && ( ( $finfos[0]->url || '' ) eq $rel_url )
972            && ( $finfos[0]->template_id == $tmpl_id ) )
973        {
974
975            # if the shoe fits, wear it
976            $finfo = $finfos[0];
977        }
978        else {
979
980           # if the shoe don't fit, remove all shoes and create the perfect shoe
981            foreach (@finfos) { $_->remove(); }
982
983            $finfo = MT::FileInfo->set_info_for_url(
984                $rel_url, $file, $at,
985                {
986                    Blog        => $blog->id,
987                    TemplateMap => $map->id,
988                    Template    => $tmpl_id,
989                    ( $archiver->entry_based && $entry )
990                    ? ( Entry => $entry->id )
991                    : (),
992                    StartDate => $start,
993                    ( $archiver->category_based && $category )
994                    ? ( Category => $category->id )
995                    : (),
996                    ( $archiver->author_based )
997                    ? ( Author => $author->id )
998                    : (),
999                }
1000              )
1001              || die "Couldn't create FileInfo because "
1002              . MT::FileInfo->errstr();
1003        }
1004    }
1005
1006    # If you rebuild when you've just switched to dynamic pages,
1007    # we move the file that might be there so that the custom
1008    # 404 will be triggered.
1009    require MT::PublishOption;
1010    if ( $tmpl->build_dynamic
1011      || ( $map->build_type == MT::PublishOption::DYNAMIC() ) )
1012    {
1013        rename(
1014            $finfo->file_path,    # is this just $file ?
1015            $finfo->file_path . '.static'
1016        );
1017
1018        ## If the FileInfo is set to static, flip it to virtual.
1019        if ( !$finfo->virtual ) {
1020            $finfo->virtual(1);
1021            $finfo->save();
1022        }
1023    }
1024
1025    return 1 if ( $tmpl->build_dynamic )
1026        || ( $map->build_type == MT::PublishOption::DYNAMIC() );
1027    return 1 if ( $entry && $entry->status != MT::Entry::RELEASE() );
1028    return 1 unless ( $tmpl->build_type );
1029
1030    my $timer = MT->get_timer;
1031    if ($timer) {
1032        $timer->pause_partial;
1033    }
1034    local $timer->{elapsed} = 0 if $timer;
1035
1036    if (
1037        $build_static
1038        && MT->run_callbacks(
1039            'build_file_filter',
1040            Context      => $ctx,
1041            context      => $ctx,
1042            ArchiveType  => $at,
1043            archive_type => $at,
1044            TemplateMap  => $map,
1045            template_map => $map,
1046            Blog         => $blog,
1047            blog         => $blog,
1048            Entry        => $entry,
1049            entry        => $entry,
1050            FileInfo     => $finfo,
1051            file_info    => $finfo,
1052            File         => $file,
1053            file         => $file,
1054            Template     => $tmpl,
1055            template     => $tmpl,
1056            PeriodStart  => $start,
1057            period_start => $start,
1058            Category     => $category,
1059            category     => $category,
1060            force        => ($args{Force} ? 1 : 0),
1061        )
1062      )
1063    {
1064
1065        if ( $archiver->group_based ) {
1066            require MT::Promise;
1067            my $entries = sub { $archiver->archive_group_entries($ctx) };
1068            $ctx->stash( 'entries', MT::Promise::delay($entries) );
1069        }
1070
1071        my $html = undef;
1072        $ctx->stash( 'blog', $blog );
1073        $ctx->stash( 'entry', $entry ) if $entry;
1074
1075        require MT::Request;
1076        MT::Request->instance->cache('build_template', $tmpl);
1077
1078        $html = $tmpl->build( $ctx, $cond );
1079        unless (defined($html)) {
1080            $timer->unpause if $timer;
1081            require MT::I18N;
1082            return $mt->error(
1083            (
1084                $category ? MT->translate(
1085                    "An error occurred publishing [_1] '[_2]': [_3]",
1086                    MT::I18N::lowercase( $category->class_label ),
1087                    $category->id,
1088                    $tmpl->errstr
1089                  )
1090                : $entry ? MT->translate(
1091                    "An error occurred publishing [_1] '[_2]': [_3]",
1092                    MT::I18N::lowercase( $entry->class_label ),
1093                    $entry->title,
1094                    $tmpl->errstr
1095                  )
1096                : MT->translate(
1097"An error occurred publishing date-based archive '[_1]': [_2]",
1098                    $at . $start,
1099                    $tmpl->errstr
1100                )
1101            )
1102          );
1103        }
1104        my $orig_html = $html;
1105        MT->run_callbacks(
1106            'build_page',
1107            Context      => $ctx,
1108            context      => $ctx,
1109            ArchiveType  => $at,
1110            archive_type => $at,
1111            TemplateMap  => $map,
1112            template_map => $map,
1113            Blog         => $blog,
1114            blog         => $blog,
1115            Entry        => $entry,
1116            entry        => $entry,
1117            FileInfo     => $finfo,
1118            file_info    => $finfo,
1119            PeriodStart  => $start,
1120            period_start => $start,
1121            Category     => $category,
1122            category     => $category,
1123            RawContent   => \$orig_html,
1124            raw_content  => \$orig_html,
1125            Content      => \$html,
1126            content      => \$html,
1127            BuildResult  => \$orig_html,
1128            build_result => \$orig_html,
1129            Template     => $tmpl,
1130            template     => $tmpl,
1131            File         => $file,
1132            file         => $file
1133        );
1134        ## First check whether the content is actually
1135        ## changed. If not, we won't update the published
1136        ## file, so as not to modify the mtime.
1137        unless ($fmgr->content_is_updated( $file, \$html )) {
1138            $timer->unpause if $timer;
1139            return 1;
1140        }
1141
1142        ## Determine if we need to build directory structure,
1143        ## and build it if we do. DirUmask determines
1144        ## directory permissions.
1145        require File::Spec;
1146        my $path = dirname($file);
1147        $path =~ s!/$!!
1148          unless $path eq '/'; ## OS X doesn't like / at the end in mkdir().
1149        unless ( $fmgr->exists($path) ) {
1150            if (!$fmgr->mkpath($path)) {
1151                $timer->unpause if $timer;
1152                return $mt->trans_error( "Error making path '[_1]': [_2]",
1153                    $path, $fmgr->errstr );
1154            }
1155        }
1156
1157        ## By default we write all data to temp files, then rename
1158        ## the temp files to the real files (an atomic
1159        ## operation). Some users don't like this (requires too
1160        ## liberal directory permissions). So we have a config
1161        ## option to turn it off (NoTempFiles).
1162        my $use_temp_files = !$mt->{NoTempFiles};
1163        my $temp_file = $use_temp_files ? "$file.new" : $file;
1164        unless ( defined $fmgr->put_data( $html, $temp_file ) ) {
1165            $timer->unpause if $timer;
1166            return $mt->trans_error( "Writing to '[_1]' failed: [_2]",
1167                $temp_file, $fmgr->errstr );
1168        }
1169        if ($use_temp_files) {
1170            if (!$fmgr->rename( $temp_file, $file )) {
1171                $timer->unpause if $timer;
1172                return $mt->trans_error(
1173                    "Renaming tempfile '[_1]' failed: [_2]",
1174                    $temp_file, $fmgr->errstr );
1175            }
1176        }
1177        MT->run_callbacks(
1178            'build_file',
1179            Context      => $ctx,
1180            context      => $ctx,
1181            ArchiveType  => $at,
1182            archive_type => $at,
1183            TemplateMap  => $map,
1184            template_map => $map,
1185            FileInfo     => $finfo,
1186            file_info    => $finfo,
1187            Blog         => $blog,
1188            blog         => $blog,
1189            Entry        => $entry,
1190            entry        => $entry,
1191            PeriodStart  => $start,
1192            period_start => $start,
1193            RawContent   => \$orig_html,
1194            raw_content  => \$orig_html,
1195            Content      => \$html,
1196            content      => \$html,
1197            BuildResult  => \$orig_html,
1198            build_result => \$orig_html,
1199            Template     => $tmpl,
1200            template     => $tmpl,
1201            Category     => $category,
1202            category     => $category,
1203            File         => $file,
1204            file         => $file
1205        );
1206    }
1207    $timer->mark("total:rebuild_file[template_id:" . $tmpl->id . "]")
1208        if $timer;
1209    1;
1210}
1211
1212sub rebuild_indexes {
1213    my $mt    = shift;
1214    my %param = @_;
1215    require MT::Template;
1216    require MT::Template::Context;
1217    require MT::Entry;
1218    my $blog;
1219    unless ( $blog = $param{Blog} ) {
1220        my $blog_id = $param{BlogID};
1221        $blog = MT::Blog->load($blog_id)
1222          or return $mt->error(
1223            MT->translate(
1224                "Load of blog '[_1]' failed: [_2]", $blog_id,
1225                MT::Blog->errstr
1226            )
1227          );
1228    }
1229    my $tmpl = $param{Template};
1230    unless ($blog) {
1231        $blog = MT::Blog->load( $tmpl->blog_id );
1232    }
1233    return 1 if $blog->is_dynamic;
1234    my $iter;
1235    if ($tmpl) {
1236        my $i = 0;
1237        $iter = sub { $i++ < 1 ? $tmpl : undef };
1238    }
1239    else {
1240        $iter = MT::Template->load_iter(
1241            {
1242                type    => 'index',
1243                blog_id => $blog->id
1244            }
1245        );
1246    }
1247    my $force = $param{Force};
1248
1249    local *FH;
1250    my $site_root = $blog->site_path;
1251    return $mt->error(
1252        MT->translate("You did not set your blog publishing path") )
1253      unless $site_root;
1254    my $fmgr = $blog->file_mgr;
1255    while ( my $tmpl = $iter->() ) {
1256        ## Skip index templates that the user has designated not to be
1257        ## rebuilt automatically. We need to do the defined-ness check
1258        ## because we added the flag in 2.01, and for templates saved
1259        ## before that time, the rebuild_me flag will be undefined. But
1260        ## we assume that these templates should be rebuilt, since that
1261        ## was the previous behavior.
1262        ## Note that dynamic templates do need to be "rebuilt"--the
1263        ## FileInfo table needs to be maintained.
1264        if ( !$tmpl->build_dynamic && !$force ) {
1265            next if ( defined $tmpl->rebuild_me && !$tmpl->rebuild_me );
1266        }
1267        next if ( defined $tmpl->build_type && !$tmpl->build_type );
1268
1269        my $file = $tmpl->outfile;
1270        $file = '' unless defined $file;
1271        if ( $tmpl->build_dynamic && ( $file eq '' ) ) {
1272            next;
1273        }
1274        return $mt->error(
1275            MT->translate(
1276                "Template '[_1]' does not have an Output File.",
1277                $tmpl->name
1278            )
1279        ) unless $file ne '';
1280        my $url = join( '/', $blog->site_url, $file );
1281        unless ( File::Spec->file_name_is_absolute($file) ) {
1282            $file = File::Spec->catfile( $site_root, $file );
1283        }
1284
1285        # Everything from here out is identical with rebuild_file
1286        my ($rel_url) = ( $url =~ m|^(?:[^:]*\:\/\/)?[^/]*(.*)| );
1287        $rel_url =~ s|//+|/|g;
1288        ## Untaint. We have to assume that we can trust the user's setting of
1289        ## the site_path and the template outfile.
1290        ($file) = $file =~ /(.+)/s;
1291        my $finfo = $param{FileInfo};  # available for single template calls
1292        unless ( $finfo ) {
1293            require MT::FileInfo;
1294            my @finfos = MT::FileInfo->load(
1295                {
1296                    blog_id     => $tmpl->blog_id,
1297                    template_id => $tmpl->id
1298                }
1299            );
1300            if (   ( scalar @finfos == 1 )
1301                && ( $finfos[0]->file_path eq $file )
1302                && ( ( $finfos[0]->url || '' ) eq $rel_url ) )
1303            {
1304                $finfo = $finfos[0];
1305            }
1306            else {
1307                foreach (@finfos) { $_->remove(); }
1308                $finfo = MT::FileInfo->set_info_for_url(
1309                    $rel_url, $file, 'index',
1310                    {
1311                        Blog     => $tmpl->blog_id,
1312                        Template => $tmpl->id,
1313                    }
1314                  )
1315                  || die "Couldn't create FileInfo because " . MT::FileInfo->errstr;
1316            }
1317        }
1318        if ( $tmpl->build_dynamic ) {
1319            rename( $file, $file . ".static" );
1320
1321            ## If the FileInfo is set to static, flip it to virtual.
1322            if ( !$finfo->virtual ) {
1323                $finfo->virtual(1);
1324                $finfo->save();
1325            }
1326        }
1327
1328        next if ( $tmpl->build_dynamic );
1329        next unless ( $tmpl->build_type );
1330
1331        ## We're not building dynamically, so if the FileInfo is currently
1332        ## set as dynamic (virtual), change it to static.
1333        if ( $finfo && $finfo->virtual ) {
1334            $finfo->virtual(0);
1335            $finfo->save();
1336        }
1337
1338        my $timer = MT->get_timer;
1339        if ($timer) {
1340            $timer->pause_partial;
1341        }
1342        local $timer->{elapsed} = 0 if $timer;
1343
1344        my $ctx = MT::Template::Context->new;
1345        next
1346          unless (
1347            MT->run_callbacks(
1348                'build_file_filter',
1349                Context      => $ctx,
1350                context      => $ctx,
1351                ArchiveType  => 'index',
1352                archive_type => 'index',
1353                Blog         => $blog,
1354                blog         => $blog,
1355                FileInfo     => $finfo,
1356                file_info    => $finfo,
1357                Template     => $tmpl,
1358                template     => $tmpl,
1359                File         => $file,
1360                file         => $file,
1361                force        => $force,
1362            )
1363          );
1364        $ctx->stash( 'blog', $blog );
1365
1366        require MT::Request;
1367        MT::Request->instance->cache('build_template', $tmpl);
1368
1369        my $html = $tmpl->build($ctx);
1370        unless (defined $html) {
1371            $timer->unpause if $timer;
1372            return $mt->error( $tmpl->errstr );
1373        }
1374
1375        my $orig_html = $html;
1376        MT->run_callbacks(
1377            'build_page',
1378            Context      => $ctx,
1379            context      => $ctx,
1380            Blog         => $blog,
1381            blog         => $blog,
1382            FileInfo     => $finfo,
1383            file_info    => $finfo,
1384            ArchiveType  => 'index',
1385            archive_type => 'index',
1386            RawContent   => \$orig_html,
1387            raw_content  => \$orig_html,
1388            Content      => \$html,
1389            content      => \$html,
1390            BuildResult  => \$orig_html,
1391            build_result => \$orig_html,
1392            Template     => $tmpl,
1393            template     => $tmpl,
1394            File         => $file,
1395            file         => $file
1396        );
1397
1398        ## First check whether the content is actually changed. If not,
1399        ## we won't update the published file, so as not to modify the mtime.
1400        next unless $fmgr->content_is_updated( $file, \$html );
1401
1402        ## Determine if we need to build directory structure,
1403        ## and build it if we do. DirUmask determines
1404        ## directory permissions.
1405        require File::Spec;
1406        my $path = dirname($file);
1407        $path =~ s!/$!!
1408          unless $path eq '/';    ## OS X doesn't like / at the end in mkdir().
1409        unless ( $fmgr->exists($path) ) {
1410            if (! $fmgr->mkpath($path) ) {
1411                $timer->unpause if $timer;
1412                return $mt->trans_error( "Error making path '[_1]': [_2]",
1413                    $path, $fmgr->errstr );
1414            }
1415        }
1416
1417        ## Update the published file.
1418        my $use_temp_files = !$mt->{NoTempFiles};
1419        my $temp_file = $use_temp_files ? "$file.new" : $file;
1420        unless (defined( $fmgr->put_data( $html, $temp_file ) )) {
1421            $timer->unpause if $timer;
1422            return $mt->trans_error( "Writing to '[_1]' failed: [_2]",
1423                $temp_file, $fmgr->errstr );
1424        }
1425        if ($use_temp_files) {
1426            if (!$fmgr->rename( $temp_file, $file )) {
1427                $timer->unpause if $timer;
1428                return $mt->trans_error( "Renaming tempfile '[_1]' failed: [_2]",
1429                    $temp_file, $fmgr->errstr );
1430            }
1431        }
1432        MT->run_callbacks(
1433            'build_file',
1434            Context      => $ctx,
1435            context      => $ctx,
1436            ArchiveType  => 'index',
1437            archive_type => 'index',
1438            FileInfo     => $finfo,
1439            file_info    => $finfo,
1440            Blog         => $blog,
1441            blog         => $blog,
1442            RawContent   => \$orig_html,
1443            raw_content  => \$orig_html,
1444            Content      => \$html,
1445            content      => \$html,
1446            BuildResult  => \$orig_html,
1447            build_result => \$orig_html,
1448            Template     => $tmpl,
1449            template     => $tmpl,
1450            File         => $file,
1451            file         => $file
1452        );
1453
1454        $timer->mark("total:rebuild_indexes[template_id:" . $tmpl->id . ";file:$file]")
1455            if $timer;
1456    }
1457    1;
1458}
1459
1460sub rebuild_from_fileinfo {
1461    my $pub = shift;
1462    my ($fi) = @_;
1463
1464    require MT::Blog;
1465    require MT::Entry;
1466    require MT::Category;
1467    require MT::Template;
1468    require MT::TemplateMap;
1469    require MT::Template::Context;
1470
1471    my $at = $fi->archive_type
1472      or return $pub->error(
1473        MT->translate( "Parameter '[_1]' is required", 'ArchiveType' ) );
1474
1475    # callback for custom archive types
1476    return
1477      unless MT->run_callbacks(
1478        'build_archive_filter',
1479        archive_type => $at,
1480        file_info    => $fi
1481      );
1482
1483    if ( $at eq 'index' ) {
1484        $pub->rebuild_indexes(
1485            BlogID   => $fi->blog_id,
1486            Template => MT::Template->load( $fi->template_id ),
1487            FileInfo => $fi,
1488            Force    => 1,
1489        ) or return;
1490        return 1;
1491    }
1492
1493    return 1 if $at eq 'None';
1494
1495    my ( $start, $end );
1496    my $blog = MT::Blog->load( $fi->blog_id )
1497      if $fi->blog_id;
1498    my $entry = MT::Entry->load( $fi->entry_id )
1499      or return $pub->error(
1500        MT->translate( "Parameter '[_1]' is required", 'Entry' ) )
1501      if $fi->entry_id;
1502    if ( $fi->startdate ) {
1503        my $archiver = $pub->archiver($at);
1504
1505        if ( ( $start, $end ) = $archiver->date_range( $fi->startdate ) ) {
1506            $entry = MT::Entry->load( { authored_on => [ $start, $end ] },
1507                { range_incl => { authored_on => 1 }, limit => 1 } )
1508              or return $pub->error(
1509                MT->translate( "Parameter '[_1]' is required", 'Entry' ) );
1510        }
1511    }
1512    my $cat = MT::Category->load( $fi->category_id )
1513      if $fi->category_id;
1514    my $author = MT::Author->load( $fi->author_id )
1515      if $fi->author_id;
1516
1517    ## Load the template-archive-type map entries for this blog and
1518    ## archive type. We do this before we load the list of entries, because
1519    ## we will run through the files and check if we even need to rebuild
1520    ## anything. If there is nothing to rebuild at all for this entry,
1521    ## we save some time by not loading the list of entries.
1522    my $map = MT::TemplateMap->load( $fi->templatemap_id );
1523    my $file = $pub->archive_file_for( $entry, $blog, $at, $cat, $map,
1524        undef, $author );
1525    if ( !defined($file) ) {
1526        return $pub->error( $blog->errstr() );
1527    }
1528    $map->{__saved_output_file} = $file;
1529
1530    my $ctx = MT::Template::Context->new;
1531    $ctx->{current_archive_type} = $at;
1532    if ( $start && $end ) {
1533        $ctx->{current_timestamp} = $start;
1534        $ctx->{current_timestamp_end} = $end;
1535    }
1536
1537    my $arch_root =
1538      ( $at eq 'Page' ) ? $blog->site_path : $blog->archive_path;
1539    return $pub->error(
1540        MT->translate("You did not set your blog publishing path") )
1541      unless $arch_root;
1542
1543    my %cond;
1544    $pub->rebuild_file( $blog, $arch_root, $map, $at, $ctx, \%cond, 1,
1545        FileInfo => $fi, )
1546      or return;
1547
1548    1;
1549}
1550
1551sub trans_error {
1552    my $this = shift;
1553    return $this->error( MT->translate(@_) );
1554}
1555
1556sub publish_future_posts {
1557    my $this = shift;
1558
1559    require MT::Blog;
1560    require MT::Entry;
1561    require MT::Util;
1562    my $mt            = MT->instance;
1563    my $total_changed = 0;
1564    my @blogs = MT::Blog->load(undef, {
1565        join => MT::Entry->join_on('blog_id', {
1566            status => MT::Entry::FUTURE(),
1567        }, { unique => 1 })
1568    });
1569    foreach my $blog (@blogs) {
1570        my @ts = MT::Util::offset_time_list( time, $blog );
1571        my $now = sprintf "%04d%02d%02d%02d%02d%02d", $ts[5] + 1900, $ts[4] + 1,
1572          @ts[ 3, 2, 1, 0 ];
1573        my $iter = MT::Entry->load_iter(
1574            {
1575                blog_id => $blog->id,
1576                status  => MT::Entry::FUTURE(),
1577                class   => '*'
1578            },
1579            {
1580                'sort'    => 'authored_on',
1581                direction => 'descend'
1582            }
1583        );
1584        my @queue;
1585        while ( my $entry = $iter->() ) {
1586            push @queue, $entry->id if $entry->authored_on le $now;
1587        }
1588
1589        my $changed = 0;
1590        my @results;
1591        my %rebuild_queue;
1592        my %ping_queue;
1593        foreach my $entry_id (@queue) {
1594            my $entry = MT::Entry->load($entry_id)
1595                or next;
1596            $entry->status( MT::Entry::RELEASE() );
1597            $entry->save
1598              or die $entry->errstr;
1599
1600            $rebuild_queue{ $entry->id } = $entry;
1601            $ping_queue{ $entry->id }    = 1;
1602            my $n = $entry->next(1);
1603            $rebuild_queue{ $n->id } = $n if $n;
1604            my $p = $entry->previous(1);
1605            $rebuild_queue{ $p->id } = $p if $p;
1606            $changed++;
1607            $total_changed++;
1608        }
1609        if ($changed) {
1610            my %rebuilt_okay;
1611            my $rebuilt;
1612            eval {
1613                foreach my $id ( keys %rebuild_queue )
1614                {
1615                    my $entry = $rebuild_queue{$id};
1616                    $mt->rebuild_entry( Entry => $entry, Blog => $blog )
1617                      or die $mt->errstr;
1618                    $rebuilt_okay{$id} = 1;
1619                    if ( $ping_queue{$id} ) {
1620                        $mt->ping_and_save( Entry => $entry, Blog => $blog );
1621                    }
1622                    $rebuilt++;
1623                }
1624                $mt->rebuild_indexes( Blog => $blog )
1625                  or die $mt->errstr;
1626            };
1627            if ( my $err = $@ ) {
1628
1629                # a fatal error occured while processing the rebuild
1630                # step. LOG the error and revert the entry/entries:
1631                require MT::Log;
1632                $mt->log(
1633                    {
1634                        message => $mt->translate(
1635"An error occurred while publishing scheduled entries: [_1]",
1636                            $err
1637                        ),
1638                        class   => "system",
1639                        blog_id => $blog->id,
1640                        level   => MT::Log::ERROR()
1641                    }
1642                );
1643                foreach my $id (@queue) {
1644                    next if exists $rebuilt_okay{$id};
1645                    my $e = $rebuild_queue{$id};
1646                    next unless $e;
1647                    $e->status( MT::Entry::FUTURE() );
1648                    $e->save or die $e->errstr;
1649                }
1650            }
1651        }
1652    }
1653    $total_changed > 0 ? 1 : 0;
1654}
1655
1656sub remove_entry_archive_file {
1657    my $mt    = shift;
1658    my %param = @_;
1659
1660    my $entry = $param{Entry};
1661    my $at    = $param{ArchiveType} || 'Individual';
1662    my $cat   = $param{Category};
1663    my $auth  = $param{Author};
1664
1665    require MT::TemplateMap;
1666    my $blog = $param{Blog};
1667    unless ($blog) {
1668        if ($entry) {
1669            $blog = $entry->blog;
1670        }
1671        elsif ($cat) {
1672            require MT::Blog;
1673            $blog = MT::Blog->load( $cat->blog_id )
1674                or return;
1675        }
1676    }
1677    my @map = MT::TemplateMap->load(
1678        {
1679            archive_type => $at,
1680            blog_id      => $blog->id,
1681            $param{TemplateID} ? ( template_id => $param{TemplateID} ) : (),
1682        }
1683    );
1684    return 1 unless @map;
1685
1686    my $fmgr = $blog->file_mgr;
1687    my $arch_root = ( $at eq 'Page' ) ? $blog->site_path : $blog->archive_path;
1688
1689    require File::Spec;
1690    for my $map (@map) {
1691        my $file =
1692          $mt->archive_file_for( $entry, $blog, $at, $cat, $map, undef, $auth );
1693        $file = File::Spec->catfile( $arch_root, $file );
1694        if ( !defined($file) ) {
1695            die MT->translate( $blog->errstr() );
1696            return $mt->error( MT->translate( $blog->errstr() ) );
1697        }
1698
1699        # Run callbacks
1700        MT->run_callbacks( 'pre_delete_archive_file', $file, $at, $entry);
1701
1702        $fmgr->delete($file);
1703
1704        # Run callbacks
1705        MT->run_callbacks( 'post_delete_archive_file', $file, $at, $entry);
1706    }
1707    1;
1708}
1709
1710##
1711## archive_file_for takes an entry to determine the timestamps,
1712## but if the entry is not available it uses the time_start
1713## and time_end values
1714##
1715sub archive_file_for {
1716    my $mt = shift;
1717    init_archive_types() unless %ArchiveTypes;
1718
1719    my ( $entry, $blog, $at, $cat, $map, $timestamp, $author ) = @_;
1720    return if $at eq 'None';
1721    my $archiver = $mt->archiver($at);
1722    return '' unless $archiver;
1723
1724    my $file;
1725    if ( $blog->is_dynamic ) {
1726        require MT::TemplateMap;
1727        $map = MT::TemplateMap->new;
1728        $map->file_template( $archiver->dynamic_template );
1729    }
1730    unless ($map) {
1731        my $cache = MT::Request->instance->cache('maps');
1732        unless ($cache) {
1733            MT::Request->instance->cache( 'maps', $cache = {} );
1734        }
1735        unless ( $map = $cache->{ $blog->id . $at } ) {
1736            require MT::TemplateMap;
1737            $map = MT::TemplateMap->load(
1738                {
1739                    blog_id      => $blog->id,
1740                    archive_type => $at,
1741                    is_preferred => 1
1742                }
1743            );
1744            $cache->{ $blog->id . $at } = $map if $map;
1745        }
1746    }
1747    my $file_tmpl = $map->file_template if $map;
1748    unless ($file_tmpl) {
1749        if ( my $tmpls = $archiver->default_archive_templates ) {
1750            my ($default) = grep { $_->{default} } @$tmpls;
1751            $file_tmpl = $default->{template} if $default;
1752        }
1753    }
1754    $file_tmpl ||= '';
1755    my ($ctx);
1756    if ( $file_tmpl =~ m/\%[_-]?[A-Za-z]/ ) {
1757        if ( $file_tmpl =~ m/<\$?MT/ ) {
1758            $file_tmpl =~
1759s!(<\$?MT[^>]+?>)|(%[_-]?[A-Za-z])!$1 ? $1 : '<MTFileTemplate format="'. $2 . '">'!gie;
1760        }
1761        else {
1762            $file_tmpl = qq{<MTFileTemplate format="$file_tmpl">};
1763        }
1764    }
1765    if ($file_tmpl) {
1766        require MT::Template::Context;
1767        $ctx = MT::Template::Context->new;
1768        $ctx->stash( 'blog', $blog );
1769    }
1770    local $ctx->{__stash}{category}         = $cat if $cat;
1771    local $ctx->{__stash}{archive_category} = $cat if $cat;
1772    $timestamp = $entry->authored_on() if $entry;
1773    local $ctx->{__stash}{entry} = $entry if $entry;
1774    local $ctx->{__stash}{author} =
1775      $author ? $author : $entry ? $entry->author : undef;
1776
1777    my %blog_at = map { $_ => 1 } split /,/, $blog->archive_type;
1778    return '' unless $blog_at{$at};
1779
1780    $file = $archiver->archive_file(
1781        $ctx,
1782        Timestamp => $timestamp,
1783        Template  => $file_tmpl
1784    );
1785    if ( $file_tmpl && !$file ) {
1786        local $ctx->{archive_type} = $at;
1787        require MT::Builder;
1788        my $build = MT::Builder->new;
1789        my $tokens = $build->compile( $ctx, $file_tmpl )
1790          or return $blog->error( $build->errstr() );
1791        defined( $file = $build->build( $ctx, $tokens ) )
1792          or return $blog->error( $build->errstr() );
1793    }
1794    else {
1795        my $ext = $blog->file_extension;
1796        $file .= '.' . $ext if $ext;
1797    }
1798    $file;
1799}
1800
1801# Adds an element to the rebuild queue when the plugin is enabled.
1802sub queue_build_file_filter {
1803    my $mt = shift;
1804    my ( $cb, %args ) = @_;
1805
1806    my $fi = $args{file_info};
1807    return 1 if $fi->{from_queue};
1808
1809    require MT::PublishOption;
1810    my $throttle = MT::PublishOption::get_throttle($fi);
1811
1812    # Prevent building of disabled templates if they get this far
1813    return 0 if $throttle->{type} == MT::PublishOption::DISABLED();
1814
1815    # Check for 'force' flag for 'manual' publish option, which
1816    # forces the template to build; used for 'rebuild' list actions
1817    # and publish site operations
1818    if ($throttle->{type} == MT::PublishOption::MANUALLY()) {
1819        return $args{force} ? 1 : 0;
1820    }
1821
1822    # From here on, we're committed to publishing this file via TheSchwartz
1823    return 1 if $throttle->{type} != MT::PublishOption::ASYNC();
1824
1825    require MT::TheSchwartz;
1826    require TheSchwartz::Job;
1827    my $job = TheSchwartz::Job->new();
1828    $job->funcname('MT::Worker::Publish');
1829    $job->uniqkey( $fi->id );
1830
1831    my $priority = 0;
1832
1833    my $at = $fi->archive_type || '';
1834    # Default priority assignment....
1835    if (($at eq 'Individual') || ($at eq 'Page')) {
1836        require MT::TemplateMap;
1837        my $map = MT::TemplateMap->load($fi->templatemap_id);
1838        # Individual/Page archive pages that are the 'permalink' pages
1839        # should have highest build priority.
1840        if ($map && $map->is_preferred) {
1841            $priority = 10;
1842        } else {
1843            $priority = 5;
1844        }
1845    } elsif ($at eq 'index') {
1846        # Index pages are second in priority, if they are named 'index'
1847        # or 'default'
1848        if ($fi->file_path =~ m!/(index|default|atom|feed)!i) {
1849            $priority = 9;
1850        } else {
1851            $priority = 8;
1852        }
1853    } elsif ($at =~ m/Category|Author/) {
1854        $priority = 1;
1855    } elsif ($at =~ m/Yearly/) {
1856        $priority = 1;
1857    } elsif ($at =~ m/Monthly/) {
1858        $priority = 2;
1859    } elsif ($at =~ m/Weekly/) {
1860        $priority = 3;
1861    } elsif ($at =~ m/Daily/) {
1862        $priority = 4;
1863    }
1864
1865    $job->priority( $priority );
1866    $job->coalesce( ( $fi->blog_id || 0 ) . ':' . $$ . ':' . $priority . ':' . ( time - ( time % 10 ) ) );
1867
1868    MT::TheSchwartz->insert($job);
1869
1870    return 0;
1871}
1872
18731;
1874
1875__END__
1876
1877=head1 NAME
1878
1879MT::WeblogPublisher - Express weblog templates and content into a specific URL structure
1880
1881=head1 SYNOPSIS
1882
1883    use MT::WeblogPublisher;
1884    my $pub = MT::WeblogPublisher->new;
1885    $pub->rebuild(Blog => $blog, ArchiveType => "Individual");
1886
1887=head1 METHODS
1888
1889=head2 MT::WeblogPublisher->new()
1890
1891Return a new C<MT::WeblogPublisher>. Additionally, call
1892L<MT::ConfigMgr/instance> and set the I<NoTempFiles> and
1893I<PublishCommenterIcon> attributes, if not already set.
1894
1895=head2 $mt->rebuild( %args )
1896
1897Rebuilds your entire blog, indexes and archives; or some subset of your blog,
1898as specified in the arguments.
1899
1900I<%args> can contain:
1901
1902=over 4
1903
1904=item * Blog
1905
1906An I<MT::Blog> object corresponding to the blog that you would like to
1907rebuild.
1908
1909Either this or C<BlogID> is required.
1910
1911=item * BlogID
1912
1913The ID of the blog that you would like to rebuild.
1914
1915Either this or C<Blog> is required.
1916
1917=item * ArchiveType
1918
1919The archive type that you would like to rebuild. This should be one of
1920the following string values: C<Individual>, C<Daily>, C<Weekly>,
1921C<Monthly>, or C<Category>.
1922
1923This argument is optional; if not provided, all archive types will be rebuilt.
1924
1925=item * EntryCallback
1926
1927A callback that will be called for each entry that is rebuilt. If provided,
1928the value should be a subroutine reference; the subroutine will be handed
1929the I<MT::Entry> object for the entry that is about to be rebuilt. You could
1930use this to keep a running log of which entry is being rebuilt, for example:
1931
1932    $mt->rebuild(
1933              BlogID => $blog_id,
1934              EntryCallback => sub { print $_[0]->title, "\n" },
1935          );
1936
1937Or to provide a status indicator:
1938
1939    use MT::Entry;
1940    my $total = MT::Entry->count({ blog_id => $blog_id });
1941    my $i = 0;
1942    local $| = 1;
1943    $mt->rebuild(
1944              BlogID => $blog_id,
1945              EntryCallback => sub { printf "%d/%d\r", ++$i, $total },
1946          );
1947    print "\n";
1948
1949This argument is optional; by default no callbacks are executed.
1950
1951=item * NoIndexes
1952
1953By default I<rebuild> will rebuild the index templates after rebuilding all
1954of the entries; if you do not want to rebuild the index templates, set the
1955value for this argument to a true value.
1956
1957This argument is optional.
1958
1959=item * Limit
1960
1961Limit the number of entries to be rebuilt to the last C<N> entries in the
1962blog. For example, if you set this to C<20> and do not provide an offset (see
1963L<Offset>, below), the 20 most recent entries in the blog will be rebuilt.
1964
1965This is only useful if you are rebuilding C<Individual> archives.
1966
1967This argument is optional; by default all entries will be rebuilt.
1968
1969=item * Offset
1970
1971When used with C<Limit>, specifies the entry at which to start rebuilding
1972your individual entry archives. For example, if you set this to C<10>, and
1973set a C<Limit> of C<5> (see L<Limit>, above), entries 10-14 (inclusive) will
1974be rebuilt. The offset starts at C<0>, and the ordering is reverse
1975chronological.
1976
1977This is only useful if you are rebuilding C<Individual> archives, and if you
1978are using C<Limit>.
1979
1980This argument is optional; by default all entries will be rebuilt, starting
1981at the first entry.
1982
1983=back
1984
1985=head2 $mt->rebuild_entry( %args )
1986
1987Rebuilds a particular entry in your blog (and its dependencies, if specified).
1988
1989I<%args> can contain:
1990
1991=over 4
1992
1993=item * Entry
1994
1995An I<MT::Entry> object corresponding to the object you would like to rebuild.
1996
1997This argument is required.
1998
1999=item * Blog
2000
2001An I<MT::Blog> object corresponding to the blog to which the I<Entry> belongs.
2002
2003This argument is optional; if not provided, the I<MT::Blog> object will be
2004loaded in I<rebuild_entry> from the I<$entry-E<gt>blog_id> column of the
2005I<MT::Entry> object passed in. If you already have the I<MT::Blog> object
2006loaded, however, it makes sense to pass it in yourself, as it will skip one
2007small step in I<rebuild_entry> (loading the object).
2008
2009=item * BuildDependencies
2010
2011Saving an entry can have effects on other entries; so after saving, it is
2012often necessary to rebuild other entries, to reflect the changes onto all
2013of the affected archive pages, indexes, etc.
2014
2015If you supply this parameter with a true value, I<rebuild_indexes> will
2016rebuild: the archives for the next and previous entries, chronologically;
2017all of the index templates; the archives for the next and previous daily,
2018weekly, and monthly archives.
2019
2020=item * Previous, Next, OldPrevious, OldNext
2021
2022These values identify entries which may need to be updated now that
2023"this" entry has changed. When the authored_on field of an entry is
2024changed, its new neighbors (Previous and Next) need to be rebuilt as
2025well as its former neighbors (OldPrevious and OldNext).
2026
2027=item * NoStatic
2028
2029When this value is true, it acts as a hint to the rebuilding routine
2030that static output files need not be rebuilt; the "rebuild" operation
2031is just to update the bookkeeping that supports dynamic rebuilds.
2032
2033=back
2034
2035=head2 $mt->rebuild_file($blog, $archive_root, $map, $archive_type, $ctx, \%cond, $build_static, %args)
2036
2037Method responsible for building a single archive page from a template and
2038writing it to the file management layer.
2039
2040I<$blog> refers to the target weblog. I<$archive_root> is the target archive
2041path to publish the file. I<$map> is a L<MT::TemplateMap> object that
2042relates to publishing the file. I<$archive_type> is one of "Daily",
2043"Weekly", "Monthly", "Category" or "Individual". I<$ctx> is a handle to
2044the L<MT::Template::Context> object to use to build the file. I<\%cond>
2045is a hashref to conditional arguments used to drive the build process.
2046I<$build_static> is a boolean flag that controls whether static files are
2047created (otherwise, the records necessary for serving dynamic pages are
2048created and that is all).
2049
2050I<%args> is a hash that uniquely identifies the specific instance
2051of the given archive type. That is, for a category archive page it
2052identifies the category; for a date-based archive page it identifies
2053which time period is covered by the page; for an individual archive it
2054identifies the entry. I<%args> should contain just one of these
2055keys:
2056
2057=over 4
2058
2059=item * Category
2060
2061A category ID or L<MT::Category> instance of the category archive page to
2062be built.
2063
2064=item * Entry
2065
2066An entry ID or L<MT::Entry> instance of the entry archive page to be
2067built.
2068
2069=item * StartDate
2070
2071The starting timestamp of the date-based archive to be built.
2072
2073=back
2074
2075=head2 $mt->rebuild_indexes( %args )
2076
2077Rebuilds all of the index templates in your blog, or just one, if you use
2078the I<Template> argument (below). Only rebuilds templates that are set to
2079be rebuilt automatically, unless you use the I<Force> (below).
2080
2081I<%args> can contain:
2082
2083=over 4
2084
2085=item * Blog
2086
2087An I<MT::Blog> object corresponding to the blog whose indexes you would like
2088to rebuild.
2089
2090Either this or C<BlogID> is required.
2091
2092=item * BlogID
2093
2094The ID of the blog whose indexes you would like to rebuild.
2095
2096Either this or C<Blog> is required.
2097
2098=item * Template
2099
2100An I<MT::Template> object specifying the index template to rebuild; if you use
2101this argument, I<only> this index template will be rebuilt.
2102
2103Note that if the template that you specify here is set to not rebuild
2104automatically, you I<must> specify the I<Force> argument in order to force it
2105to be rebuilt.
2106
2107=item * Force
2108
2109A boolean flag specifying whether or not to rebuild index templates who have
2110been marked not to be rebuilt automatically.
2111
2112The default is C<0> (do not rebuild such templates).
2113
2114=back
2115
2116=head2 $mt->trans_error
2117
2118Call L<MT/translate>.
2119
2120=head2 $mt->remove_entry_archive_file(%param)
2121
2122Delete the archive files for an entry based on the following
2123parameters.  One of a I<Blog>, I<Entry> or I<Category> are required.
2124
2125=over 4
2126
2127=item * Blog
2128=item * Entry
2129=item * Category
2130=item * ArchiveType (Default 'Individual')
2131=item * TemplateID
2132
2133=back
2134
2135=head2 $mt->rebuild_categories(%param)
2136
2137Rebuild category archives based on the following parameters:
2138
2139=over 4
2140
2141=item * Blog
2142=item * BlogID
2143=item * Limit
2144=item * Offset
2145=item * Limit
2146=item * NoStatic
2147
2148=back
2149
2150=head2 $mt->publish_future_posts
2151
2152Build and publish all scheduled entries with a I<authored_on> timestamp
2153that is less than the current time.
2154
2155=head1 CALLBACKS
2156
2157=over 4
2158
2159=item BuildFileFilter
2160
2161This filter is called when Movable Type wants to rebuild a file, but
2162before doing so. This gives plugins the chance to determine whether a
2163file should actually be rebuild in particular situations.
2164
2165A BuildFileFilter callback routine is called as follows:
2166
2167    sub build_file_filter($eh, %args)
2168    {
2169        ...
2170        return $boolean;
2171    }
2172
2173As with other callback funcions, the first parameter is an
2174C<MT::ErrorHandler> object. This can be used by the callback to
2175propagate an error message to the surrounding context.
2176
2177The C<%args> parameters identify the page to be built. See
2178L<MT::FileInfo> for more information on how a page is determined by
2179these parameters. Elements in C<%args> are as follows:
2180
2181=over 4
2182
2183=item C<Context>
2184
2185Holds the template context that has been constructed for building (see
2186C<MT::Template::Context>).
2187
2188=item C<ArchiveType>
2189
2190The archive type of the file, usually one of C<'Index'>,
2191C<'Individual'>, C<'Category'>, C<'Daily'>, C<'Monthly'>, or
2192C<'Weekly'>.
2193
2194=item C<Templatemap>
2195
2196An C<MT::TemplateMap> object; this singles out which template is being
2197built, and the filesystem path of the file to be written.
2198
2199=item C<Blog>
2200
2201The C<MT::Blog> object representing the blog whose pages are being
2202rebuilt.
2203
2204=item C<Entry>
2205
2206In the case of an individual archive page, this points to the
2207C<MT::Entry> object whose page is being rebuilt. In the case of an
2208archive page other than an individual page, this parameter is not
2209necessarily undefined. It is best to rely on the C<$at> parameter to
2210determine whether a single entry is on deck to be built.
2211
2212=item C<PeriodStart>
2213
2214In the case of a date-based archive page, this is a timestamp at the
2215beginning of the period from which entries will be included on this
2216page, in Movable Type's standard 14-digit "timestamp" format. For
2217example, if the page is a Daily archive for April 17, 1796, this value
2218would be 17960417000000. If the page were a Monthly archive for March,
22192003, C<$start> would be 20030301000000. Again, this parameter may be
2220defined even when the page on deck is not a date-based archive page.
2221
2222=item C<Category>
2223
2224In the case of a Category archive, this parameter identifies the
2225category which will be built on the page.
2226
2227=item C<FileInfo>
2228
2229If defined, an L<MT::FileInfo> object which contains information about the
2230file. See L<MT::FileInfo> for more information about what a C<MT::FileInfo>
2231contains. Chief amongst all the members of C<MT::FileInfo>, for these
2232purposes, will be the C<virtual> member. This is a boolean value which will be
2233false if a page was actually created on disk for this "page," and false if no
2234page was created (because the corresponding template is set to be
2235built dynamically).
2236
2237It is possible for the FileInfo parameter to be undefined, namely if the blog has not been configured to publish anything dynamically, or if the
2238installation is using a data driver that does not support dynamic publishing.
2239
2240=back
2241
2242=item BuildPage
2243
2244BuildPage callbacks are invoked just after a page has been built, but
2245before the content has been written to the file system.
2246
2247    sub build_page($eh, %args)
2248    {
2249    }
2250
2251The parameters given are include those sent to the BuildFileFilter callback.
2252In addition, the following parameters are also given:
2253
2254=over 4
2255
2256=item C<Content>
2257
2258This is a scalar reference to the content that will eventually be
2259published.
2260
2261=item C<BuildResult> / (or C<RawContent>, deprecated)
2262
2263This is a scalar reference to the content originally produced by building
2264the page. This value is provided mainly for reference; modifications to it
2265will be ignored.
2266
2267=back
2268
2269=item BuildFile
2270
2271BuildFile callbacks are invoked just after a file has been built.
2272
2273    sub build_file($eh, %args)
2274    {
2275    }
2276
2277Parameters in %args are as with BuildPage.
2278
2279=back
2280
2281=head1 AUTHOR & COPYRIGHT
2282
2283Please see L<MT/AUTHOR & COPYRIGHT>.
2284
2285=cut
Note: See TracBrowser for help on using the browser.