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

Revision 2343, 74.5 kB (checked in by bchoate, 19 months ago)

Removed template cache-by-id from weblogpublisher (D::OD ram cache does this).

  • 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->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    require MT::TheSchwartz;
1806    require TheSchwartz::Job;
1807    my $job = TheSchwartz::Job->new();
1808    $job->funcname('MT::Worker::Publish');
1809    $job->uniqkey( $fi->id );
1810
1811    my $priority = 0;
1812
1813    my $at = $fi->archive_type || '';
1814    # Default priority assignment....
1815    if (($at eq 'Individual') || ($at eq 'Page')) {
1816        require MT::TemplateMap;
1817        my $map = MT::TemplateMap->load($fi->templatemap_id);
1818        # Individual/Page archive pages that are the 'permalink' pages
1819        # should have highest build priority.
1820        if ($map && $map->is_preferred) {
1821            $priority = 10;
1822        } else {
1823            $priority = 5;
1824        }
1825    } elsif ($at eq 'index') {
1826        # Index pages are second in priority, if they are named 'index'
1827        # or 'default'
1828        if ($fi->file_path =~ m!/(index|default|atom|feed)!i) {
1829            $priority = 9;
1830        } else {
1831            $priority = 8;
1832        }
1833    } elsif ($at =~ m/Category|Author/) {
1834        $priority = 1;
1835    } elsif ($at =~ m/Yearly/) {
1836        $priority = 1;
1837    } elsif ($at =~ m/Monthly/) {
1838        $priority = 2;
1839    } elsif ($at =~ m/Weekly/) {
1840        $priority = 3;
1841    } elsif ($at =~ m/Daily/) {
1842        $priority = 4;
1843    }
1844
1845    $job->priority( $priority );
1846    $job->coalesce( ( $fi->blog_id || 0 ) . ':' . $$ . ':' . $priority . ':' . ( time - ( time % 10 ) ) );
1847
1848    MT::TheSchwartz->insert($job);
1849
1850    return 0;
1851}
1852
18531;
1854
1855__END__
1856
1857=head1 NAME
1858
1859MT::WeblogPublisher - Express weblog templates and content into a specific URL structure
1860
1861=head1 SYNOPSIS
1862
1863    use MT::WeblogPublisher;
1864    my $pub = MT::WeblogPublisher->new;
1865    $pub->rebuild(Blog => $blog, ArchiveType => "Individual");
1866
1867=head1 METHODS
1868
1869=head2 MT::WeblogPublisher->new()
1870
1871Return a new C<MT::WeblogPublisher>. Additionally, call
1872L<MT::ConfigMgr/instance> and set the I<NoTempFiles> and
1873I<PublishCommenterIcon> attributes, if not already set.
1874
1875=head2 $mt->rebuild( %args )
1876
1877Rebuilds your entire blog, indexes and archives; or some subset of your blog,
1878as specified in the arguments.
1879
1880I<%args> can contain:
1881
1882=over 4
1883
1884=item * Blog
1885
1886An I<MT::Blog> object corresponding to the blog that you would like to
1887rebuild.
1888
1889Either this or C<BlogID> is required.
1890
1891=item * BlogID
1892
1893The ID of the blog that you would like to rebuild.
1894
1895Either this or C<Blog> is required.
1896
1897=item * ArchiveType
1898
1899The archive type that you would like to rebuild. This should be one of
1900the following string values: C<Individual>, C<Daily>, C<Weekly>,
1901C<Monthly>, or C<Category>.
1902
1903This argument is optional; if not provided, all archive types will be rebuilt.
1904
1905=item * EntryCallback
1906
1907A callback that will be called for each entry that is rebuilt. If provided,
1908the value should be a subroutine reference; the subroutine will be handed
1909the I<MT::Entry> object for the entry that is about to be rebuilt. You could
1910use this to keep a running log of which entry is being rebuilt, for example:
1911
1912    $mt->rebuild(
1913              BlogID => $blog_id,
1914              EntryCallback => sub { print $_[0]->title, "\n" },
1915          );
1916
1917Or to provide a status indicator:
1918
1919    use MT::Entry;
1920    my $total = MT::Entry->count({ blog_id => $blog_id });
1921    my $i = 0;
1922    local $| = 1;
1923    $mt->rebuild(
1924              BlogID => $blog_id,
1925              EntryCallback => sub { printf "%d/%d\r", ++$i, $total },
1926          );
1927    print "\n";
1928
1929This argument is optional; by default no callbacks are executed.
1930
1931=item * NoIndexes
1932
1933By default I<rebuild> will rebuild the index templates after rebuilding all
1934of the entries; if you do not want to rebuild the index templates, set the
1935value for this argument to a true value.
1936
1937This argument is optional.
1938
1939=item * Limit
1940
1941Limit the number of entries to be rebuilt to the last C<N> entries in the
1942blog. For example, if you set this to C<20> and do not provide an offset (see
1943L<Offset>, below), the 20 most recent entries in the blog will be rebuilt.
1944
1945This is only useful if you are rebuilding C<Individual> archives.
1946
1947This argument is optional; by default all entries will be rebuilt.
1948
1949=item * Offset
1950
1951When used with C<Limit>, specifies the entry at which to start rebuilding
1952your individual entry archives. For example, if you set this to C<10>, and
1953set a C<Limit> of C<5> (see L<Limit>, above), entries 10-14 (inclusive) will
1954be rebuilt. The offset starts at C<0>, and the ordering is reverse
1955chronological.
1956
1957This is only useful if you are rebuilding C<Individual> archives, and if you
1958are using C<Limit>.
1959
1960This argument is optional; by default all entries will be rebuilt, starting
1961at the first entry.
1962
1963=back
1964
1965=head2 $mt->rebuild_entry( %args )
1966
1967Rebuilds a particular entry in your blog (and its dependencies, if specified).
1968
1969I<%args> can contain:
1970
1971=over 4
1972
1973=item * Entry
1974
1975An I<MT::Entry> object corresponding to the object you would like to rebuild.
1976
1977This argument is required.
1978
1979=item * Blog
1980
1981An I<MT::Blog> object corresponding to the blog to which the I<Entry> belongs.
1982
1983This argument is optional; if not provided, the I<MT::Blog> object will be
1984loaded in I<rebuild_entry> from the I<$entry-E<gt>blog_id> column of the
1985I<MT::Entry> object passed in. If you already have the I<MT::Blog> object
1986loaded, however, it makes sense to pass it in yourself, as it will skip one
1987small step in I<rebuild_entry> (loading the object).
1988
1989=item * BuildDependencies
1990
1991Saving an entry can have effects on other entries; so after saving, it is
1992often necessary to rebuild other entries, to reflect the changes onto all
1993of the affected archive pages, indexes, etc.
1994
1995If you supply this parameter with a true value, I<rebuild_indexes> will
1996rebuild: the archives for the next and previous entries, chronologically;
1997all of the index templates; the archives for the next and previous daily,
1998weekly, and monthly archives.
1999
2000=item * Previous, Next, OldPrevious, OldNext
2001
2002These values identify entries which may need to be updated now that
2003"this" entry has changed. When the authored_on field of an entry is
2004changed, its new neighbors (Previous and Next) need to be rebuilt as
2005well as its former neighbors (OldPrevious and OldNext).
2006
2007=item * NoStatic
2008
2009When this value is true, it acts as a hint to the rebuilding routine
2010that static output files need not be rebuilt; the "rebuild" operation
2011is just to update the bookkeeping that supports dynamic rebuilds.
2012
2013=back
2014
2015=head2 $mt->rebuild_file($blog, $archive_root, $map, $archive_type, $ctx, \%cond, $build_static, %args)
2016
2017Method responsible for building a single archive page from a template and
2018writing it to the file management layer.
2019
2020I<$blog> refers to the target weblog. I<$archive_root> is the target archive
2021path to publish the file. I<$map> is a L<MT::TemplateMap> object that
2022relates to publishing the file. I<$archive_type> is one of "Daily",
2023"Weekly", "Monthly", "Category" or "Individual". I<$ctx> is a handle to
2024the L<MT::Template::Context> object to use to build the file. I<\%cond>
2025is a hashref to conditional arguments used to drive the build process.
2026I<$build_static> is a boolean flag that controls whether static files are
2027created (otherwise, the records necessary for serving dynamic pages are
2028created and that is all).
2029
2030I<%args> is a hash that uniquely identifies the specific instance
2031of the given archive type. That is, for a category archive page it
2032identifies the category; for a date-based archive page it identifies
2033which time period is covered by the page; for an individual archive it
2034identifies the entry. I<%args> should contain just one of these
2035keys:
2036
2037=over 4
2038
2039=item * Category
2040
2041A category ID or L<MT::Category> instance of the category archive page to
2042be built.
2043
2044=item * Entry
2045
2046An entry ID or L<MT::Entry> instance of the entry archive page to be
2047built.
2048
2049=item * StartDate
2050
2051The starting timestamp of the date-based archive to be built.
2052
2053=back
2054
2055=head2 $mt->rebuild_indexes( %args )
2056
2057Rebuilds all of the index templates in your blog, or just one, if you use
2058the I<Template> argument (below). Only rebuilds templates that are set to
2059be rebuilt automatically, unless you use the I<Force> (below).
2060
2061I<%args> can contain:
2062
2063=over 4
2064
2065=item * Blog
2066
2067An I<MT::Blog> object corresponding to the blog whose indexes you would like
2068to rebuild.
2069
2070Either this or C<BlogID> is required.
2071
2072=item * BlogID
2073
2074The ID of the blog whose indexes you would like to rebuild.
2075
2076Either this or C<Blog> is required.
2077
2078=item * Template
2079
2080An I<MT::Template> object specifying the index template to rebuild; if you use
2081this argument, I<only> this index template will be rebuilt.
2082
2083Note that if the template that you specify here is set to not rebuild
2084automatically, you I<must> specify the I<Force> argument in order to force it
2085to be rebuilt.
2086
2087=item * Force
2088
2089A boolean flag specifying whether or not to rebuild index templates who have
2090been marked not to be rebuilt automatically.
2091
2092The default is C<0> (do not rebuild such templates).
2093
2094=back
2095
2096=head2 $mt->trans_error
2097
2098Call L<MT/translate>.
2099
2100=head2 $mt->remove_entry_archive_file(%param)
2101
2102Delete the archive files for an entry based on the following
2103parameters.  One of a I<Blog>, I<Entry> or I<Category> are required.
2104
2105=over 4
2106
2107=item * Blog
2108=item * Entry
2109=item * Category
2110=item * ArchiveType (Default 'Individual')
2111=item * TemplateID
2112
2113=back
2114
2115=head2 $mt->rebuild_categories(%param)
2116
2117Rebuild category archives based on the following parameters:
2118
2119=over 4
2120
2121=item * Blog
2122=item * BlogID
2123=item * Limit
2124=item * Offset
2125=item * Limit
2126=item * NoStatic
2127
2128=back
2129
2130=head2 $mt->publish_future_posts
2131
2132Build and publish all scheduled entries with a I<authored_on> timestamp
2133that is less than the current time.
2134
2135=head1 CALLBACKS
2136
2137=over 4
2138
2139=item BuildFileFilter
2140
2141This filter is called when Movable Type wants to rebuild a file, but
2142before doing so. This gives plugins the chance to determine whether a
2143file should actually be rebuild in particular situations.
2144
2145A BuildFileFilter callback routine is called as follows:
2146
2147    sub build_file_filter($eh, %args)
2148    {
2149        ...
2150        return $boolean;
2151    }
2152
2153As with other callback funcions, the first parameter is an
2154C<MT::ErrorHandler> object. This can be used by the callback to
2155propagate an error message to the surrounding context.
2156
2157The C<%args> parameters identify the page to be built. See
2158L<MT::FileInfo> for more information on how a page is determined by
2159these parameters. Elements in C<%args> are as follows:
2160
2161=over 4
2162
2163=item C<Context>
2164
2165Holds the template context that has been constructed for building (see
2166C<MT::Template::Context>).
2167
2168=item C<ArchiveType>
2169
2170The archive type of the file, usually one of C<'Index'>,
2171C<'Individual'>, C<'Category'>, C<'Daily'>, C<'Monthly'>, or
2172C<'Weekly'>.
2173
2174=item C<Templatemap>
2175
2176An C<MT::TemplateMap> object; this singles out which template is being
2177built, and the filesystem path of the file to be written.
2178
2179=item C<Blog>
2180
2181The C<MT::Blog> object representing the blog whose pages are being
2182rebuilt.
2183
2184=item C<Entry>
2185
2186In the case of an individual archive page, this points to the
2187C<MT::Entry> object whose page is being rebuilt. In the case of an
2188archive page other than an individual page, this parameter is not
2189necessarily undefined. It is best to rely on the C<$at> parameter to
2190determine whether a single entry is on deck to be built.
2191
2192=item C<PeriodStart>
2193
2194In the case of a date-based archive page, this is a timestamp at the
2195beginning of the period from which entries will be included on this
2196page, in Movable Type's standard 14-digit "timestamp" format. For
2197example, if the page is a Daily archive for April 17, 1796, this value
2198would be 17960417000000. If the page were a Monthly archive for March,
21992003, C<$start> would be 20030301000000. Again, this parameter may be
2200defined even when the page on deck is not a date-based archive page.
2201
2202=item C<Category>
2203
2204In the case of a Category archive, this parameter identifies the
2205category which will be built on the page.
2206
2207=item C<FileInfo>
2208
2209If defined, an L<MT::FileInfo> object which contains information about the
2210file. See L<MT::FileInfo> for more information about what a C<MT::FileInfo>
2211contains. Chief amongst all the members of C<MT::FileInfo>, for these
2212purposes, will be the C<virtual> member. This is a boolean value which will be
2213false if a page was actually created on disk for this "page," and false if no
2214page was created (because the corresponding template is set to be
2215built dynamically).
2216
2217It is possible for the FileInfo parameter to be undefined, namely if the blog has not been configured to publish anything dynamically, or if the
2218installation is using a data driver that does not support dynamic publishing.
2219
2220=back
2221
2222=item BuildPage
2223
2224BuildPage callbacks are invoked just after a page has been built, but
2225before the content has been written to the file system.
2226
2227    sub build_page($eh, %args)
2228    {
2229    }
2230
2231The parameters given are include those sent to the BuildFileFilter callback.
2232In addition, the following parameters are also given:
2233
2234=over 4
2235
2236=item C<Content>
2237
2238This is a scalar reference to the content that will eventually be
2239published.
2240
2241=item C<BuildResult> / (or C<RawContent>, deprecated)
2242
2243This is a scalar reference to the content originally produced by building
2244the page. This value is provided mainly for reference; modifications to it
2245will be ignored.
2246
2247=back
2248
2249=item BuildFile
2250
2251BuildFile callbacks are invoked just after a file has been built.
2252
2253    sub build_file($eh, %args)
2254    {
2255    }
2256
2257Parameters in %args are as with BuildPage.
2258
2259=back
2260
2261=head1 AUTHOR & COPYRIGHT
2262
2263Please see L<MT/AUTHOR & COPYRIGHT>.
2264
2265=cut
Note: See TracBrowser for help on using the browser.