root/branches/release-39/lib/MT/WeblogPublisher.pm @ 2504

Revision 2504, 74.6 kB (checked in by fumiakiy, 18 months ago)

Show Save&Rebuild button when template has a static map even if the profile is set to dynamic. BugId:80014

  • 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 ( $map->build_type == MT::PublishOption::DYNAMIC() ) 
991    {
992        rename(
993            $finfo->file_path,    # is this just $file ?
994            $finfo->file_path . '.static'
995        );
996
997        ## If the FileInfo is set to static, flip it to virtual.
998        if ( !$finfo->virtual ) {
999            $finfo->virtual(1);
1000            $finfo->save();
1001        }
1002    }
1003
1004    return 1 if ( $map->build_type == MT::PublishOption::DYNAMIC() );
1005    return 1 if ( $entry && $entry->status != MT::Entry::RELEASE() );
1006    return 1 unless ( $map->build_type );
1007
1008    my $timer = MT->get_timer;
1009    if ($timer) {
1010        $timer->pause_partial;
1011    }
1012    local $timer->{elapsed} = 0 if $timer;
1013
1014    if (
1015        $build_static
1016        && MT->run_callbacks(
1017            'build_file_filter',
1018            Context      => $ctx,
1019            context      => $ctx,
1020            ArchiveType  => $at,
1021            archive_type => $at,
1022            TemplateMap  => $map,
1023            template_map => $map,
1024            Blog         => $blog,
1025            blog         => $blog,
1026            Entry        => $entry,
1027            entry        => $entry,
1028            FileInfo     => $finfo,
1029            file_info    => $finfo,
1030            File         => $file,
1031            file         => $file,
1032            Template     => $tmpl,
1033            template     => $tmpl,
1034            PeriodStart  => $start,
1035            period_start => $start,
1036            Category     => $category,
1037            category     => $category,
1038            force        => ($args{Force} ? 1 : 0),
1039        )
1040      )
1041    {
1042
1043        if ( $archiver->group_based ) {
1044            require MT::Promise;
1045            my $entries = sub { $archiver->archive_group_entries($ctx) };
1046            $ctx->stash( 'entries', MT::Promise::delay($entries) );
1047        }
1048
1049        my $html = undef;
1050        $ctx->stash( 'blog', $blog );
1051        $ctx->stash( 'entry', $entry ) if $entry;
1052
1053        require MT::Request;
1054        MT::Request->instance->cache('build_template', $tmpl);
1055
1056        $html = $tmpl->build( $ctx, $cond );
1057        unless (defined($html)) {
1058            $timer->unpause if $timer;
1059            require MT::I18N;
1060            return $mt->error(
1061            (
1062                $category ? MT->translate(
1063                    "An error occurred publishing [_1] '[_2]': [_3]",
1064                    MT::I18N::lowercase( $category->class_label ),
1065                    $category->id,
1066                    $tmpl->errstr
1067                  )
1068                : $entry ? MT->translate(
1069                    "An error occurred publishing [_1] '[_2]': [_3]",
1070                    MT::I18N::lowercase( $entry->class_label ),
1071                    $entry->title,
1072                    $tmpl->errstr
1073                  )
1074                : MT->translate(
1075"An error occurred publishing date-based archive '[_1]': [_2]",
1076                    $at . $start,
1077                    $tmpl->errstr
1078                )
1079            )
1080          );
1081        }
1082        my $orig_html = $html;
1083        MT->run_callbacks(
1084            'build_page',
1085            Context      => $ctx,
1086            context      => $ctx,
1087            ArchiveType  => $at,
1088            archive_type => $at,
1089            TemplateMap  => $map,
1090            template_map => $map,
1091            Blog         => $blog,
1092            blog         => $blog,
1093            Entry        => $entry,
1094            entry        => $entry,
1095            FileInfo     => $finfo,
1096            file_info    => $finfo,
1097            PeriodStart  => $start,
1098            period_start => $start,
1099            Category     => $category,
1100            category     => $category,
1101            RawContent   => \$orig_html,
1102            raw_content  => \$orig_html,
1103            Content      => \$html,
1104            content      => \$html,
1105            BuildResult  => \$orig_html,
1106            build_result => \$orig_html,
1107            Template     => $tmpl,
1108            template     => $tmpl,
1109            File         => $file,
1110            file         => $file
1111        );
1112        ## First check whether the content is actually
1113        ## changed. If not, we won't update the published
1114        ## file, so as not to modify the mtime.
1115        unless ($fmgr->content_is_updated( $file, \$html )) {
1116            $timer->unpause if $timer;
1117            return 1;
1118        }
1119
1120        ## Determine if we need to build directory structure,
1121        ## and build it if we do. DirUmask determines
1122        ## directory permissions.
1123        require File::Spec;
1124        my $path = dirname($file);
1125        $path =~ s!/$!!
1126          unless $path eq '/'; ## OS X doesn't like / at the end in mkdir().
1127        unless ( $fmgr->exists($path) ) {
1128            if (!$fmgr->mkpath($path)) {
1129                $timer->unpause if $timer;
1130                return $mt->trans_error( "Error making path '[_1]': [_2]",
1131                    $path, $fmgr->errstr );
1132            }
1133        }
1134
1135        ## By default we write all data to temp files, then rename
1136        ## the temp files to the real files (an atomic
1137        ## operation). Some users don't like this (requires too
1138        ## liberal directory permissions). So we have a config
1139        ## option to turn it off (NoTempFiles).
1140        my $use_temp_files = !$mt->{NoTempFiles};
1141        my $temp_file = $use_temp_files ? "$file.new" : $file;
1142        unless ( defined $fmgr->put_data( $html, $temp_file ) ) {
1143            $timer->unpause if $timer;
1144            return $mt->trans_error( "Writing to '[_1]' failed: [_2]",
1145                $temp_file, $fmgr->errstr );
1146        }
1147        if ($use_temp_files) {
1148            if (!$fmgr->rename( $temp_file, $file )) {
1149                $timer->unpause if $timer;
1150                return $mt->trans_error(
1151                    "Renaming tempfile '[_1]' failed: [_2]",
1152                    $temp_file, $fmgr->errstr );
1153            }
1154        }
1155        MT->run_callbacks(
1156            'build_file',
1157            Context      => $ctx,
1158            context      => $ctx,
1159            ArchiveType  => $at,
1160            archive_type => $at,
1161            TemplateMap  => $map,
1162            template_map => $map,
1163            FileInfo     => $finfo,
1164            file_info    => $finfo,
1165            Blog         => $blog,
1166            blog         => $blog,
1167            Entry        => $entry,
1168            entry        => $entry,
1169            PeriodStart  => $start,
1170            period_start => $start,
1171            RawContent   => \$orig_html,
1172            raw_content  => \$orig_html,
1173            Content      => \$html,
1174            content      => \$html,
1175            BuildResult  => \$orig_html,
1176            build_result => \$orig_html,
1177            Template     => $tmpl,
1178            template     => $tmpl,
1179            Category     => $category,
1180            category     => $category,
1181            File         => $file,
1182            file         => $file
1183        );
1184    }
1185    $timer->mark("total:rebuild_file[template_id:" . $tmpl->id . "]")
1186        if $timer;
1187    1;
1188}
1189
1190sub rebuild_indexes {
1191    my $mt    = shift;
1192    my %param = @_;
1193    require MT::Template;
1194    require MT::Template::Context;
1195    require MT::Entry;
1196    my $blog;
1197    unless ( $blog = $param{Blog} ) {
1198        my $blog_id = $param{BlogID};
1199        $blog = MT::Blog->load($blog_id)
1200          or return $mt->error(
1201            MT->translate(
1202                "Load of blog '[_1]' failed: [_2]", $blog_id,
1203                MT::Blog->errstr
1204            )
1205          );
1206    }
1207    my $tmpl = $param{Template};
1208    unless ($blog) {
1209        $blog = MT::Blog->load( $tmpl->blog_id );
1210    }
1211    return 1 if $blog->is_dynamic;
1212    my $iter;
1213    if ($tmpl) {
1214        my $i = 0;
1215        $iter = sub { $i++ < 1 ? $tmpl : undef };
1216    }
1217    else {
1218        $iter = MT::Template->load_iter(
1219            {
1220                type    => 'index',
1221                blog_id => $blog->id
1222            }
1223        );
1224    }
1225    my $force = $param{Force};
1226
1227    local *FH;
1228    my $site_root = $blog->site_path;
1229    return $mt->error(
1230        MT->translate("You did not set your blog publishing path") )
1231      unless $site_root;
1232    my $fmgr = $blog->file_mgr;
1233    while ( my $tmpl = $iter->() ) {
1234        ## Skip index templates that the user has designated not to be
1235        ## rebuilt automatically. We need to do the defined-ness check
1236        ## because we added the flag in 2.01, and for templates saved
1237        ## before that time, the rebuild_me flag will be undefined. But
1238        ## we assume that these templates should be rebuilt, since that
1239        ## was the previous behavior.
1240        ## Note that dynamic templates do need to be "rebuilt"--the
1241        ## FileInfo table needs to be maintained.
1242        if ( !$tmpl->build_dynamic && !$force ) {
1243            next if ( defined $tmpl->rebuild_me && !$tmpl->rebuild_me );
1244        }
1245        next if ( defined $tmpl->build_type && !$tmpl->build_type );
1246
1247        my $file = $tmpl->outfile;
1248        $file = '' unless defined $file;
1249        if ( $tmpl->build_dynamic && ( $file eq '' ) ) {
1250            next;
1251        }
1252        return $mt->error(
1253            MT->translate(
1254                "Template '[_1]' does not have an Output File.",
1255                $tmpl->name
1256            )
1257        ) unless $file ne '';
1258        my $url = join( '/', $blog->site_url, $file );
1259        unless ( File::Spec->file_name_is_absolute($file) ) {
1260            $file = File::Spec->catfile( $site_root, $file );
1261        }
1262
1263        # Everything from here out is identical with rebuild_file
1264        my ($rel_url) = ( $url =~ m|^(?:[^:]*\:\/\/)?[^/]*(.*)| );
1265        $rel_url =~ s|//+|/|g;
1266        ## Untaint. We have to assume that we can trust the user's setting of
1267        ## the site_path and the template outfile.
1268        ($file) = $file =~ /(.+)/s;
1269        my $finfo = $param{FileInfo};  # available for single template calls
1270        unless ( $finfo ) {
1271            require MT::FileInfo;
1272            my @finfos = MT::FileInfo->load(
1273                {
1274                    blog_id     => $tmpl->blog_id,
1275                    template_id => $tmpl->id
1276                }
1277            );
1278            if (   ( scalar @finfos == 1 )
1279                && ( $finfos[0]->file_path eq $file )
1280                && ( ( $finfos[0]->url || '' ) eq $rel_url ) )
1281            {
1282                $finfo = $finfos[0];
1283            }
1284            else {
1285                foreach (@finfos) { $_->remove(); }
1286                $finfo = MT::FileInfo->set_info_for_url(
1287                    $rel_url, $file, 'index',
1288                    {
1289                        Blog     => $tmpl->blog_id,
1290                        Template => $tmpl->id,
1291                    }
1292                  )
1293                  || die "Couldn't create FileInfo because " . MT::FileInfo->errstr;
1294            }
1295        }
1296        if ( $tmpl->build_dynamic ) {
1297            rename( $file, $file . ".static" );
1298
1299            ## If the FileInfo is set to static, flip it to virtual.
1300            if ( !$finfo->virtual ) {
1301                $finfo->virtual(1);
1302                $finfo->save();
1303            }
1304        }
1305
1306        next if ( $tmpl->build_dynamic );
1307        next unless ( $tmpl->build_type );
1308
1309        ## We're not building dynamically, so if the FileInfo is currently
1310        ## set as dynamic (virtual), change it to static.
1311        if ( $finfo && $finfo->virtual ) {
1312            $finfo->virtual(0);
1313            $finfo->save();
1314        }
1315
1316        my $timer = MT->get_timer;
1317        if ($timer) {
1318            $timer->pause_partial;
1319        }
1320        local $timer->{elapsed} = 0 if $timer;
1321
1322        my $ctx = MT::Template::Context->new;
1323        next
1324          unless (
1325            MT->run_callbacks(
1326                'build_file_filter',
1327                Context      => $ctx,
1328                context      => $ctx,
1329                ArchiveType  => 'index',
1330                archive_type => 'index',
1331                Blog         => $blog,
1332                blog         => $blog,
1333                FileInfo     => $finfo,
1334                file_info    => $finfo,
1335                Template     => $tmpl,
1336                template     => $tmpl,
1337                File         => $file,
1338                file         => $file,
1339                force        => $force,
1340            )
1341          );
1342        $ctx->stash( 'blog', $blog );
1343
1344        require MT::Request;
1345        MT::Request->instance->cache('build_template', $tmpl);
1346
1347        my $html = $tmpl->build($ctx);
1348        unless (defined $html) {
1349            $timer->unpause if $timer;
1350            return $mt->error( $tmpl->errstr );
1351        }
1352
1353        my $orig_html = $html;
1354        MT->run_callbacks(
1355            'build_page',
1356            Context      => $ctx,
1357            context      => $ctx,
1358            Blog         => $blog,
1359            blog         => $blog,
1360            FileInfo     => $finfo,
1361            file_info    => $finfo,
1362            ArchiveType  => 'index',
1363            archive_type => 'index',
1364            RawContent   => \$orig_html,
1365            raw_content  => \$orig_html,
1366            Content      => \$html,
1367            content      => \$html,
1368            BuildResult  => \$orig_html,
1369            build_result => \$orig_html,
1370            Template     => $tmpl,
1371            template     => $tmpl,
1372            File         => $file,
1373            file         => $file
1374        );
1375
1376        ## First check whether the content is actually changed. If not,
1377        ## we won't update the published file, so as not to modify the mtime.
1378        next unless $fmgr->content_is_updated( $file, \$html );
1379
1380        ## Determine if we need to build directory structure,
1381        ## and build it if we do. DirUmask determines
1382        ## directory permissions.
1383        require File::Spec;
1384        my $path = dirname($file);
1385        $path =~ s!/$!!
1386          unless $path eq '/';    ## OS X doesn't like / at the end in mkdir().
1387        unless ( $fmgr->exists($path) ) {
1388            if (! $fmgr->mkpath($path) ) {
1389                $timer->unpause if $timer;
1390                return $mt->trans_error( "Error making path '[_1]': [_2]",
1391                    $path, $fmgr->errstr );
1392            }
1393        }
1394
1395        ## Update the published file.
1396        my $use_temp_files = !$mt->{NoTempFiles};
1397        my $temp_file = $use_temp_files ? "$file.new" : $file;
1398        unless (defined( $fmgr->put_data( $html, $temp_file ) )) {
1399            $timer->unpause if $timer;
1400            return $mt->trans_error( "Writing to '[_1]' failed: [_2]",
1401                $temp_file, $fmgr->errstr );
1402        }
1403        if ($use_temp_files) {
1404            if (!$fmgr->rename( $temp_file, $file )) {
1405                $timer->unpause if $timer;
1406                return $mt->trans_error( "Renaming tempfile '[_1]' failed: [_2]",
1407                    $temp_file, $fmgr->errstr );
1408            }
1409        }
1410        MT->run_callbacks(
1411            'build_file',
1412            Context      => $ctx,
1413            context      => $ctx,
1414            ArchiveType  => 'index',
1415            archive_type => 'index',
1416            FileInfo     => $finfo,
1417            file_info    => $finfo,
1418            Blog         => $blog,
1419            blog         => $blog,
1420            RawContent   => \$orig_html,
1421            raw_content  => \$orig_html,
1422            Content      => \$html,
1423            content      => \$html,
1424            BuildResult  => \$orig_html,
1425            build_result => \$orig_html,
1426            Template     => $tmpl,
1427            template     => $tmpl,
1428            File         => $file,
1429            file         => $file
1430        );
1431
1432        $timer->mark("total:rebuild_indexes[template_id:" . $tmpl->id . ";file:$file]")
1433            if $timer;
1434    }
1435    1;
1436}
1437
1438sub rebuild_from_fileinfo {
1439    my $pub = shift;
1440    my ($fi) = @_;
1441
1442    require MT::Blog;
1443    require MT::Entry;
1444    require MT::Category;
1445    require MT::Template;
1446    require MT::TemplateMap;
1447    require MT::Template::Context;
1448
1449    my $at = $fi->archive_type
1450      or return $pub->error(
1451        MT->translate( "Parameter '[_1]' is required", 'ArchiveType' ) );
1452
1453    # callback for custom archive types
1454    return
1455      unless MT->run_callbacks(
1456        'build_archive_filter',
1457        archive_type => $at,
1458        file_info    => $fi
1459      );
1460
1461    if ( $at eq 'index' ) {
1462        $pub->rebuild_indexes(
1463            BlogID   => $fi->blog_id,
1464            Template => MT::Template->load( $fi->template_id ),
1465            FileInfo => $fi,
1466            Force    => 1,
1467        ) or return;
1468        return 1;
1469    }
1470
1471    return 1 if $at eq 'None';
1472
1473    my ( $start, $end );
1474    my $blog = MT::Blog->load( $fi->blog_id )
1475      if $fi->blog_id;
1476    my $entry = MT::Entry->load( $fi->entry_id )
1477      or return $pub->error(
1478        MT->translate( "Parameter '[_1]' is required", 'Entry' ) )
1479      if $fi->entry_id;
1480    if ( $fi->startdate ) {
1481        my $archiver = $pub->archiver($at);
1482
1483        if ( ( $start, $end ) = $archiver->date_range( $fi->startdate ) ) {
1484            $entry = MT::Entry->load( { authored_on => [ $start, $end ] },
1485                { range_incl => { authored_on => 1 }, limit => 1 } )
1486              or return $pub->error(
1487                MT->translate( "Parameter '[_1]' is required", 'Entry' ) );
1488        }
1489    }
1490    my $cat = MT::Category->load( $fi->category_id )
1491      if $fi->category_id;
1492    my $author = MT::Author->load( $fi->author_id )
1493      if $fi->author_id;
1494
1495    ## Load the template-archive-type map entries for this blog and
1496    ## archive type. We do this before we load the list of entries, because
1497    ## we will run through the files and check if we even need to rebuild
1498    ## anything. If there is nothing to rebuild at all for this entry,
1499    ## we save some time by not loading the list of entries.
1500    my $map = MT::TemplateMap->load( $fi->templatemap_id );
1501    my $file = $pub->archive_file_for( $entry, $blog, $at, $cat, $map,
1502        undef, $author );
1503    if ( !defined($file) ) {
1504        return $pub->error( $blog->errstr() );
1505    }
1506    $map->{__saved_output_file} = $file;
1507
1508    my $ctx = MT::Template::Context->new;
1509    $ctx->{current_archive_type} = $at;
1510    if ( $start && $end ) {
1511        $ctx->{current_timestamp} = $start;
1512        $ctx->{current_timestamp_end} = $end;
1513    }
1514
1515    my $arch_root =
1516      ( $at eq 'Page' ) ? $blog->site_path : $blog->archive_path;
1517    return $pub->error(
1518        MT->translate("You did not set your blog publishing path") )
1519      unless $arch_root;
1520
1521    my %cond;
1522    $pub->rebuild_file( $blog, $arch_root, $map, $at, $ctx, \%cond, 1,
1523        FileInfo => $fi, )
1524      or return;
1525
1526    1;
1527}
1528
1529sub trans_error {
1530    my $this = shift;
1531    return $this->error( MT->translate(@_) );
1532}
1533
1534sub publish_future_posts {
1535    my $this = shift;
1536
1537    require MT::Blog;
1538    require MT::Entry;
1539    require MT::Util;
1540    my $mt            = MT->instance;
1541    my $total_changed = 0;
1542    my @blogs = MT::Blog->load(undef, {
1543        join => MT::Entry->join_on('blog_id', {
1544            status => MT::Entry::FUTURE(),
1545        }, { unique => 1 })
1546    });
1547    foreach my $blog (@blogs) {
1548        my @ts = MT::Util::offset_time_list( time, $blog );
1549        my $now = sprintf "%04d%02d%02d%02d%02d%02d", $ts[5] + 1900, $ts[4] + 1,
1550          @ts[ 3, 2, 1, 0 ];
1551        my $iter = MT::Entry->load_iter(
1552            {
1553                blog_id => $blog->id,
1554                status  => MT::Entry::FUTURE(),
1555                class   => '*'
1556            },
1557            {
1558                'sort'    => 'authored_on',
1559                direction => 'descend'
1560            }
1561        );
1562        my @queue;
1563        while ( my $entry = $iter->() ) {
1564            push @queue, $entry->id if $entry->authored_on le $now;
1565        }
1566
1567        my $changed = 0;
1568        my @results;
1569        my %rebuild_queue;
1570        my %ping_queue;
1571        foreach my $entry_id (@queue) {
1572            my $entry = MT::Entry->load($entry_id)
1573                or next;
1574            $entry->status( MT::Entry::RELEASE() );
1575            $entry->save
1576              or die $entry->errstr;
1577
1578            $rebuild_queue{ $entry->id } = $entry;
1579            $ping_queue{ $entry->id }    = 1;
1580            my $n = $entry->next(1);
1581            $rebuild_queue{ $n->id } = $n if $n;
1582            my $p = $entry->previous(1);
1583            $rebuild_queue{ $p->id } = $p if $p;
1584            $changed++;
1585            $total_changed++;
1586        }
1587        if ($changed) {
1588            my %rebuilt_okay;
1589            my $rebuilt;
1590            eval {
1591                foreach my $id ( keys %rebuild_queue )
1592                {
1593                    my $entry = $rebuild_queue{$id};
1594                    $mt->rebuild_entry( Entry => $entry, Blog => $blog )
1595                      or die $mt->errstr;
1596                    $rebuilt_okay{$id} = 1;
1597                    if ( $ping_queue{$id} ) {
1598                        $mt->ping_and_save( Entry => $entry, Blog => $blog );
1599                    }
1600                    $rebuilt++;
1601                }
1602                $mt->rebuild_indexes( Blog => $blog )
1603                  or die $mt->errstr;
1604            };
1605            if ( my $err = $@ ) {
1606
1607                # a fatal error occured while processing the rebuild
1608                # step. LOG the error and revert the entry/entries:
1609                require MT::Log;
1610                $mt->log(
1611                    {
1612                        message => $mt->translate(
1613"An error occurred while publishing scheduled entries: [_1]",
1614                            $err
1615                        ),
1616                        class   => "system",
1617                        blog_id => $blog->id,
1618                        level   => MT::Log::ERROR()
1619                    }
1620                );
1621                foreach my $id (@queue) {
1622                    next if exists $rebuilt_okay{$id};
1623                    my $e = $rebuild_queue{$id};
1624                    next unless $e;
1625                    $e->status( MT::Entry::FUTURE() );
1626                    $e->save or die $e->errstr;
1627                }
1628            }
1629        }
1630    }
1631    $total_changed > 0 ? 1 : 0;
1632}
1633
1634sub remove_entry_archive_file {
1635    my $mt    = shift;
1636    my %param = @_;
1637
1638    my $entry = $param{Entry};
1639    my $at    = $param{ArchiveType} || 'Individual';
1640    my $cat   = $param{Category};
1641    my $auth  = $param{Author};
1642
1643    require MT::TemplateMap;
1644    my $blog = $param{Blog};
1645    unless ($blog) {
1646        if ($entry) {
1647            $blog = $entry->blog;
1648        }
1649        elsif ($cat) {
1650            require MT::Blog;
1651            $blog = MT::Blog->load( $cat->blog_id )
1652                or return;
1653        }
1654    }
1655    my @map = MT::TemplateMap->load(
1656        {
1657            archive_type => $at,
1658            blog_id      => $blog->id,
1659            $param{TemplateID} ? ( template_id => $param{TemplateID} ) : (),
1660        }
1661    );
1662    return 1 unless @map;
1663
1664    my $fmgr = $blog->file_mgr;
1665    my $arch_root = ( $at eq 'Page' ) ? $blog->site_path : $blog->archive_path;
1666
1667    require File::Spec;
1668    for my $map (@map) {
1669        my $file =
1670          $mt->archive_file_for( $entry, $blog, $at, $cat, $map, undef, $auth );
1671        $file = File::Spec->catfile( $arch_root, $file );
1672        if ( !defined($file) ) {
1673            die MT->translate( $blog->errstr() );
1674            return $mt->error( MT->translate( $blog->errstr() ) );
1675        }
1676
1677        # Run callbacks
1678        MT->run_callbacks( 'pre_delete_archive_file', $file, $at, $entry);
1679
1680        $fmgr->delete($file);
1681
1682        # Run callbacks
1683        MT->run_callbacks( 'post_delete_archive_file', $file, $at, $entry);
1684    }
1685    1;
1686}
1687
1688##
1689## archive_file_for takes an entry to determine the timestamps,
1690## but if the entry is not available it uses the time_start
1691## and time_end values
1692##
1693sub archive_file_for {
1694    my $mt = shift;
1695    init_archive_types() unless %ArchiveTypes;
1696
1697    my ( $entry, $blog, $at, $cat, $map, $timestamp, $author ) = @_;
1698    return if $at eq 'None';
1699    my $archiver = $mt->archiver($at);
1700    return '' unless $archiver;
1701
1702    my $file;
1703    if ( $blog->is_dynamic ) {
1704        require MT::TemplateMap;
1705        $map = MT::TemplateMap->new;
1706        $map->file_template( $archiver->dynamic_template );
1707    }
1708    unless ($map) {
1709        my $cache = MT::Request->instance->cache('maps');
1710        unless ($cache) {
1711            MT::Request->instance->cache( 'maps', $cache = {} );
1712        }
1713        unless ( $map = $cache->{ $blog->id . $at } ) {
1714            require MT::TemplateMap;
1715            $map = MT::TemplateMap->load(
1716                {
1717                    blog_id      => $blog->id,
1718                    archive_type => $at,
1719                    is_preferred => 1
1720                }
1721            );
1722            $cache->{ $blog->id . $at } = $map if $map;
1723        }
1724    }
1725    my $file_tmpl = $map->file_template if $map;
1726    unless ($file_tmpl) {
1727        if ( my $tmpls = $archiver->default_archive_templates ) {
1728            my ($default) = grep { $_->{default} } @$tmpls;
1729            $file_tmpl = $default->{template} if $default;
1730        }
1731    }
1732    $file_tmpl ||= '';
1733    my ($ctx);
1734    if ( $file_tmpl =~ m/\%[_-]?[A-Za-z]/ ) {
1735        if ( $file_tmpl =~ m/<\$?MT/i ) {
1736            $file_tmpl =~
1737s!(<\$?MT[^>]+?>)|(%[_-]?[A-Za-z])!$1 ? $1 : '<MTFileTemplate format="'. $2 . '">'!gie;
1738        }
1739        else {
1740            $file_tmpl = qq{<MTFileTemplate format="$file_tmpl">};
1741        }
1742    }
1743    if ($file_tmpl) {
1744        require MT::Template::Context;
1745        $ctx = MT::Template::Context->new;
1746        $ctx->stash( 'blog', $blog );
1747    }
1748    local $ctx->{__stash}{category}         = $cat if $cat;
1749    local $ctx->{__stash}{archive_category} = $cat if $cat;
1750    $timestamp = $entry->authored_on() if $entry;
1751    local $ctx->{__stash}{entry} = $entry if $entry;
1752    local $ctx->{__stash}{author} =
1753      $author ? $author : $entry ? $entry->author : undef;
1754
1755    my %blog_at = map { $_ => 1 } split /,/, $blog->archive_type;
1756    return '' unless $blog_at{$at};
1757
1758    $file = $archiver->archive_file(
1759        $ctx,
1760        Timestamp => $timestamp,
1761        Template  => $file_tmpl
1762    );
1763    if ( $file_tmpl && !$file ) {
1764        local $ctx->{archive_type} = $at;
1765        require MT::Builder;
1766        my $build = MT::Builder->new;
1767        my $tokens = $build->compile( $ctx, $file_tmpl )
1768          or return $blog->error( $build->errstr() );
1769        defined( $file = $build->build( $ctx, $tokens ) )
1770          or return $blog->error( $build->errstr() );
1771    }
1772    else {
1773        my $ext = $blog->file_extension;
1774        $file .= '.' . $ext if $ext;
1775    }
1776    $file;
1777}
1778
1779# Adds an element to the rebuild queue when the plugin is enabled.
1780sub queue_build_file_filter {
1781    my $mt = shift;
1782    my ( $cb, %args ) = @_;
1783
1784    my $fi = $args{file_info};
1785    return 1 if $fi->{from_queue};
1786
1787    require MT::PublishOption;
1788    my $throttle = MT::PublishOption::get_throttle($fi);
1789
1790    # Prevent building of disabled templates if they get this far
1791    return 0 if $throttle->{type} == MT::PublishOption::DISABLED();
1792
1793    # Check for 'force' flag for 'manual' publish option, which
1794    # forces the template to build; used for 'rebuild' list actions
1795    # and publish site operations
1796    if ($throttle->{type} == MT::PublishOption::MANUALLY()) {
1797        return $args{force} ? 1 : 0;
1798    }
1799
1800    # From here on, we're committed to publishing this file via TheSchwartz
1801    return 1 if $throttle->{type} != MT::PublishOption::ASYNC();
1802
1803    return 1 if $args{force}; # if async, but force is used, publish
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.