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

Revision 2355, 74.6 kB (checked in by takayama, 19 months ago)

Applied ogawa-san's patch. thanks ogawa-san. BugId:79726

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