root/branches/release-36/lib/MT/WeblogPublisher.pm @ 2051

Revision 2051, 75.2 kB (checked in by fumiakiy, 19 months ago)

Fixed to create required files for dynamic publishing when an individual template is set to publish dynamically. BugId:79337

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