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

Revision 2051, 70.8 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::CMS::Template;
8
9use strict;
10
11sub edit {
12    my $cb = shift;
13    my ($app, $id, $obj, $param) = @_;
14
15    my $q = $app->param;
16    my $blog_id = $q->param('blog_id');
17    my $type = $q->param('_type');
18    my $blog = $app->blog;
19    my $cfg = $app->config;
20    my $perms = $app->permissions;
21    my $can_preview = 0;
22
23    if ($blog) {
24        # include_system/include_cache are only applicable
25        # to blog-level templates
26        $param->{include_system} = $blog->include_system;
27        $param->{include_cache} = $blog->include_cache;
28    }
29
30    if ($id) {
31        # FIXME: Template types should not be enumerated here
32        $param->{nav_templates} = 1;
33        my $tab;
34        if ( $obj->type eq 'index' ) {
35            $tab = 'index';
36            $param->{template_group_trans} = $app->translate('index');
37        }
38        elsif ($obj->type eq 'archive'
39            || $obj->type eq 'individual'
40            || $obj->type eq 'category'
41            || $obj->type eq 'page' )
42        {
43
44            # FIXME: enumeration of types
45            $tab = 'archive';
46            $param->{template_group_trans} = $app->translate('archive');
47        }
48        elsif ( $obj->type eq 'custom' ) {
49            $tab = 'module';
50            $param->{template_group_trans} = $app->translate('module');
51        }
52        elsif ( $obj->type eq 'widget' ) {
53            $tab = 'widget';
54            $param->{template_group_trans} = $app->translate('widget');
55        }
56        elsif ( $obj->type eq 'email' ) {
57            $tab = 'email';
58            $param->{template_group_trans} = $app->translate('email');
59        }
60        else {
61            $tab = 'system';
62            $param->{template_group_trans} = $app->translate('system');
63        }
64        $param->{template_group} = $tab;
65        $blog_id = $obj->blog_id;
66
67        # FIXME: enumeration of types
68             $param->{has_name} = $obj->type eq 'index'
69          || $obj->type eq 'custom'
70          || $obj->type eq 'widget'
71          || $obj->type eq 'archive'
72          || $obj->type eq 'category'
73          || $obj->type eq 'page'
74          || $obj->type eq 'individual';
75        if ( !$param->{has_name} ) {
76            $param->{ 'type_' . $obj->type } = 1;
77            $param->{name} = $obj->name;
78        }
79        $app->add_breadcrumb( $param->{name} );
80        $param->{has_outfile} = $obj->type eq 'index';
81        $param->{has_rebuild} =
82          (      ( $obj->type eq 'index' )
83              && ( ( $blog->custom_dynamic_templates || "" ) ne 'all' ) );
84
85        # FIXME: enumeration of types
86             $param->{is_special} = $param->{type} ne 'index'
87          && $param->{type} ne 'archive'
88          && $param->{type} ne 'category'
89          && $param->{type} ne 'page'
90          && $param->{type} ne 'individual';
91             $param->{has_build_options} = $param->{has_build_options}
92          && $param->{type} ne 'custom'
93          && $param->{type} ne 'widget'
94          && !$param->{is_special};
95        $param->{search_label} = $app->translate('Templates');
96        $param->{object_type}  = 'template';
97        my $published_url = $obj->published_url;
98        $param->{published_url} = $published_url if $published_url;
99        $param->{saved_rebuild} = 1 if $q->param('saved_rebuild');
100
101        my $filter = $app->param('filter_key');
102        if ($param->{template_group} eq 'email') {
103            $app->param( 'filter_key', 'email_templates' );
104        }elsif  ($param->{template_group} eq 'system') {
105            $app->param( 'filter_key', 'system_templates' );
106        }
107        $app->load_list_actions( 'template', $param );
108        $app->param( 'filter_key', $filter );
109
110        $obj->compile;
111        if ( $obj->{errors} && @{ $obj->{errors} } ) {
112            $param->{error} = $app->translate(
113                "One or more errors were found in this template.");
114            $param->{error} .= "<ul>\n";
115            foreach my $err ( @{ $obj->{errors} } ) {
116                $param->{error} .= "<li>"
117                  . MT::Util::encode_html( $err->{message} )
118                  . "</li>\n";
119            }
120            $param->{error} .= "</ul>\n";
121        }
122
123        # Populate list of included templates
124        if ( my $includes = $obj->getElementsByTagName('Include') ) {
125            my @includes;
126            my @widgets;
127            my %seen;
128            foreach my $tag (@$includes) {
129                my $include = {};
130                my $mod = $include->{include_module} = $tag->[1]->{module} || $tag->[1]->{widget};
131                next unless $mod;
132                my $type = $tag->[1]->{widget} ? 'widget' : 'custom';
133                next if exists $seen{$type}{$mod};
134                $seen{$type}{$mod} = 1;
135                my $other = MT::Template->load(
136                    {
137                        blog_id => [ $obj->blog_id, 0 ],
138                        name    => $mod,
139                        type    => $type,
140                    }, {
141                        sort      => 'blog_id',
142                        direction => 'descend',
143                    }
144                );
145                if ($other) {
146                    $include->{include_link} = $app->mt_uri(
147                        mode => 'view',
148                        args => {
149                            blog_id => $other->blog_id || 0,
150                            '_type' => 'template',
151                            id      => $other->id
152                        }
153                    );
154                    # Try to compile template module if using MTInclude in this template.
155                    $other->compile;
156                    if ( $other->{errors} && @{ $other->{errors} } ) {
157                        $param->{error} = $app->translate(
158                            "One or more errors were found in included template module (".$other->name.").");
159                        $param->{error} .= "<ul>\n";
160                        foreach my $err ( @{ $other->{errors} } ) {
161                            $param->{error} .= "<li>"
162                              . MT::Util::encode_html( $err->{message} )
163                              . "</li>\n";
164                        }
165                        $param->{error} .= "</ul>\n";
166                    }
167                }
168                else {
169                    $include->{create_link} = $app->mt_uri(
170                        mode => 'view',
171                        args => {
172                            blog_id => $obj->blog_id,
173                            '_type' => 'template',
174                            type    => $type,
175                            name    => $mod,
176                        }
177                    );
178                }
179                if ($type eq 'widget') {
180                    push @widgets, $include;
181                } else {
182                    push @includes, $include;
183                }
184            }
185            $param->{include_loop} = \@includes if @includes;
186            $param->{widget_loop} = \@widgets if @widgets;
187        }
188        my @sets = ( @{ $obj->getElementsByTagName('WidgetSet') || [] }, @{ $obj->getElementsByTagName('WidgetManager') || [] } );
189        if ( @sets ) {
190            my @widget_sets;
191            my %seen;
192            foreach my $set (@sets) {
193                my $name = $set->[1]->{name};
194                next unless $name;
195                next if $seen{$name};
196                $seen{$name} = 1;
197                push @widget_sets, {
198                    include_link => $app->mt_uri(
199                        mode => 'edit_widget',
200                        args => {
201                            blog_id => $obj->blog_id,
202                            widgetmanager => $name,
203                        },
204                    ),
205                    include_module => $name,
206                };
207            }
208            $param->{widget_set_loop} = \@widget_sets if @widget_sets;
209        }
210        $param->{have_includes} = 1 if $param->{widget_set_loop} || $param->{include_loop} || $param->{widget_loop};
211        # Populate archive types for creating new map
212        my $obj_type = $obj->type;
213        if (   $obj_type eq 'individual'
214            || $obj_type eq 'page'
215            || $obj_type eq 'author'
216            || $obj_type eq 'category'
217            || $obj_type eq 'archive' )
218        {
219            my @at = $app->publisher->archive_types;
220            my @archive_types;
221            for my $at (@at) {
222                my $archiver      = $app->publisher->archiver($at);
223                my $archive_label = $archiver->archive_label;
224                $archive_label = $at unless $archive_label;
225                $archive_label = $archive_label->()
226                  if ( ref $archive_label ) eq 'CODE';
227                if (   ( $obj_type eq 'archive' )
228                    || ( $obj_type eq 'author' )
229                    || ( $obj_type eq 'category' ) )
230                {
231
232                    # only include if it is NOT an entry-based archive type
233                    next if $archiver->entry_based;
234                }
235                elsif ( $obj_type eq 'page' ) {
236                    # only include if it is a entry-based archive type and page
237                    next unless $archiver->entry_based;
238                    next if $archiver->entry_class ne 'page';
239                }
240                elsif ( $obj_type eq 'individual' ) {
241                    # only include if it is a entry-based archive type and entry
242                    next unless $archiver->entry_based;
243                    next if $archiver->entry_class eq 'page';
244                }
245                push @archive_types,
246                  {
247                    archive_type_translated => $archive_label,
248                    archive_type            => $at,
249                  };
250                @archive_types =
251                  sort { MT::App::CMS::archive_type_sorter( $a, $b ) } @archive_types;
252            }
253            $param->{archive_types} = \@archive_types;
254
255            # Populate template maps for this template
256            my $maps = _populate_archive_loop( $app, $blog, $obj );
257            if (@$maps) {
258                $param->{object_loop} = $param->{template_map_loop} = $maps
259                  if @$maps;
260                my %archive_types = map { $_->{archive_label} => () } @$maps;
261                $param->{enabled_archive_types} = join(", ", sort keys %archive_types);
262            }
263        }
264        # publish options
265        $param->{build_type} = $obj->build_type;
266        $param->{ 'build_type_' . ( $obj->build_type || 0 ) } = 1;
267        #my ( $period, $interval ) = _get_schedule( $obj->build_interval );
268        #$param->{ 'schedule_period_' . $period } = 1;
269        #$param->{schedule_interval} = $interval;
270        $param->{type} = 'custom' if $param->{type} eq 'module';
271    } else {
272        my $new_tmpl = $q->param('create_new_template');
273        my $template_type;
274        if ($new_tmpl) {
275            if ( $new_tmpl =~ m/^blank:(.+)/ ) {
276                $template_type = $1;
277                $param->{type} = $1;
278            }
279            elsif ( $new_tmpl =~ m/^default:([^:]+):(.+)/ ) {
280                $template_type = $1;
281                $template_type = 'custom' if $template_type eq 'module';
282                my $template_id = $2;
283                my $set = $blog ? $blog->template_set : undef;
284                require MT::DefaultTemplates;
285                my $def_tmpl = MT::DefaultTemplates->templates($set) || [];
286                my ($tmpl) =
287                  grep { $_->{identifier} eq $template_id } @$def_tmpl;
288                $param->{text} = $app->translate_templatized( $tmpl->{text} )
289                  if $tmpl;
290                $param->{type} = $template_type;
291            }
292        }
293        else {
294            $template_type = $q->param('type');
295            $template_type = 'custom' if 'module' eq $template_type;
296            $param->{type}   = $template_type;
297        }
298        return $app->errtrans("Create template requires type")
299          unless $template_type;
300        $param->{nav_templates} = 1;
301        my $tab;
302
303        # FIXME: enumeration of types
304        if ( $template_type eq 'index' ) {
305            $tab = 'index';
306            $param->{template_group_trans} = $app->translate('index');
307        }
308        elsif ($template_type eq 'archive'
309            || $template_type eq 'individual'
310            || $template_type eq 'category'
311            || $template_type eq 'page' )
312        {
313            $tab                         = 'archive';
314            $param->{template_group_trans} = $app->translate('archive');
315            $param->{type_archive}         = 1;
316            my @types = (
317                {
318                    key   => 'archive',
319                    label => $app->translate('Archive')
320                },
321                {
322                    key   => 'individual',
323                    label => $app->translate('Entry or Page')
324                },
325            );
326            $param->{new_archive_types} = \@types;
327        }
328        elsif ( $template_type eq 'custom' ) {
329            $tab = 'module';
330            $param->{template_group_trans} = $app->translate('module');
331        }
332        elsif ( $template_type eq 'widget' ) {
333            $tab = 'widget';
334            $param->{template_group_trans} = $app->translate('widget');
335        }
336        else {
337            $tab = 'system';
338            $param->{template_group_trans} = $app->translate('system');
339        }
340        $param->{template_group} = $tab;
341        $app->translate($tab);
342        $app->add_breadcrumb( $app->translate('New Template') );
343
344        # FIXME: enumeration of types
345             $param->{has_name} = $template_type eq 'index'
346          || $template_type eq 'custom'
347          || $template_type eq 'widget'
348          || $template_type eq 'archive'
349          || $template_type eq 'category'
350          || $template_type eq 'page'
351          || $template_type eq 'individual';
352        $param->{has_outfile} = $template_type eq 'index';
353        $param->{has_rebuild} =
354          (      ( $template_type eq 'index' )
355              && ( ( $blog->custom_dynamic_templates || "" ) ne 'all' ) );
356        $param->{custom_dynamic} =
357          $blog && $blog->custom_dynamic_templates eq 'custom';
358        $param->{has_build_options} =
359             $blog && ($blog->custom_dynamic_templates eq 'custom'
360          || $param->{has_rebuild});
361
362        # FIXME: enumeration of types
363             $param->{is_special} = $param->{type} ne 'index'
364          && $param->{type} ne 'archive'
365          && $param->{type} ne 'category'
366          && $param->{type} ne 'page'
367          && $param->{type} ne 'individual';
368             $param->{has_build_options} = $param->{has_build_options}
369          && $param->{type} ne 'custom'
370          && $param->{type} ne 'widget'
371          && !$param->{is_special};
372
373        $param->{name}       = MT::Util::decode_url( $app->param('name') )
374          if $app->param('name');
375    }
376    $param->{publish_queue_available} = eval 'require List::Util; require Scalar::Util; 1;';
377
378    my $set = $blog ? $blog->template_set : undef;
379    require MT::DefaultTemplates;
380    my $tmpls = MT::DefaultTemplates->templates($set);
381    my @tmpl_ids;
382    foreach my $dtmpl (@$tmpls) {
383        if ( !$param->{has_name} ) {
384            if ($obj->type eq 'email') {
385                if ($dtmpl->{identifier} eq $obj->identifier) {
386                    $param->{template_name_label} = $dtmpl->{label};
387                    $param->{template_name}       = $dtmpl->{name};
388                }
389            }
390            else {
391                if ( $dtmpl->{type} eq $obj->type ) {
392                    $param->{template_name_label} = $dtmpl->{label};
393                    $param->{template_name}       = $dtmpl->{name};
394                }
395            }
396        }
397        if ( $dtmpl->{type} eq 'index' ) {
398            push @tmpl_ids,
399              {
400                label    => $dtmpl->{label},
401                key      => $dtmpl->{key},
402                selected => $dtmpl->{key} eq
403                  ( ( $obj ? $obj->identifier : undef ) || '' ),
404              };
405        }
406    }
407    $param->{index_identifiers} = \@tmpl_ids;
408
409    $param->{"type_$param->{type}"} = 1;
410    if ($perms) {
411        my $pref_param =
412          $app->load_template_prefs( $perms->template_prefs );
413        %$param = ( %$param, %$pref_param );
414    }
415
416    # Populate structure for template snippets
417    if ( my $snippets = $app->registry('template_snippets') || {} ) {
418        my @snippets;
419        for my $snip_id ( keys %$snippets ) {
420            my $label = $snippets->{$snip_id}{label};
421            $label = $label->() if ref($label) eq 'CODE';
422            push @snippets,
423              {
424                id      => $snip_id,
425                trigger => $snippets->{$snip_id}{trigger},
426                label   => $label,
427                content => $snippets->{$snip_id}{content},
428              };
429        }
430        @snippets = sort { $a->{label} cmp $b->{label} } @snippets;
431        $param->{template_snippets} = \@snippets;
432    }
433
434    # Populate structure for tag documentation
435    my $all_tags = MT::Component->registry("tags");
436    my $tag_docs = {};
437    foreach my $tag_set (@$all_tags) {
438        my $url = $tag_set->{help_url};
439        $url = $url->() if ref($url) eq 'CODE';
440        # hey, at least give them a google search
441        $url ||= 'http://www.google.com/search?q=mt%t';
442        my $tag_list = '';
443        foreach my $type (qw( block function )) {
444            my $tags = $tag_set->{$type} or next;
445            $tag_list .= ($tag_list eq '' ? '' : ',') . join(",", keys(%$tags));
446        }
447        $tag_list =~ s/(^|,)plugin(,|$)/,/;
448        if (exists $tag_docs->{$url}) {
449            $tag_docs->{$url} .= ',' . $tag_list;
450        }
451        else {
452            $tag_docs->{$url} = $tag_list;
453        }
454    }
455    $param->{tag_docs} = $tag_docs;
456    $param->{link_doc} = $app->help_url('appendices/tags/');
457
458    $param->{screen_id} = "edit-template-" . $param->{type};
459
460    # template language
461    $param->{template_lang} = 'html';
462    if ( $obj && $obj->outfile ) {
463        if ( $obj->outfile =~ m/\.(css|js|html|php|pl|asp)$/ ) {
464            $param->{template_lang} = {
465                css => 'css',
466                js => 'javascript',
467                html => 'html',
468                php => 'php',
469                pl => 'perl',
470                asp => 'asp',
471            }->{$1};
472        }
473    }
474
475    if (($param->{type} eq 'custom') || ($param->{type} eq 'widget')) {
476        if ($blog) {
477            $param->{include_with_ssi}      = 0;
478            $param->{cache_path}            = '';
479            $param->{cache_expire_type}     = 0;
480            $param->{cache_expire_period}   = '';
481            $param->{cache_expire_interval} = 0;
482            $param->{ssi_type} = uc $blog->include_system;
483        }
484        if ($obj) {
485            $param->{include_with_ssi} = $obj->include_with_ssi
486              if defined $obj->include_with_ssi;
487            $param->{cache_path}       = $obj->cache_path
488              if defined $obj->cache_path;
489            $param->{cache_expire_type} = $obj->cache_expire_type
490              if defined $obj->cache_expire_type;
491            my ( $period, $interval ) =
492              _get_schedule( $obj->cache_expire_interval );
493            $param->{cache_expire_period}   = $period   if defined $period;
494            $param->{cache_expire_interval} = $interval if defined $interval;
495            my @events = split ',', $obj->cache_expire_event;
496            foreach my $name (@events) {
497                $param->{ 'cache_expire_event_' . $name } = 1;
498            }
499        }
500    }
501
502    # if unset, default to 30 so if they choose to enable caching,
503    # it will be preset to something sane.
504    $param->{cache_expire_interval} ||= 30;
505
506    $param->{dirty} = 1
507        if $app->param('dirty');
508
509    $param->{can_preview} = 1
510        if (!$param->{is_special}) && (!$obj || ($obj && $obj->outfile !~ m/\.(css|xml|rss|js)$/));
511
512    1;
513}
514
515sub list {
516    my $app = shift;
517
518    my $perms = $app->blog ? $app->permissions : $app->user->permissions;
519    return $app->return_to_dashboard( redirect => 1 )
520      unless $perms || $app->user->is_superuser;
521    if ( $perms && !$perms->can_edit_templates ) {
522        return $app->return_to_dashboard( permission => 1 );
523    }
524    my $blog = $app->blog;
525
526    require MT::Template;
527    my $blog_id = $app->param('blog_id') || 0;
528    my $terms = { blog_id => $blog_id };
529    my $args  = { sort    => 'name' };
530
531    my $hasher = sub {
532        my ( $obj, $row ) = @_;
533        my $template_type;
534        my $type = $row->{type} || '';
535        if ( $type =~ m/^(individual|page|category|archive)$/ ) {
536            $template_type = 'archive';
537            # populate context with templatemap loop
538            my $tblog = $obj->blog_id == $blog->id ? $blog : MT::Blog->load( $obj->blog_id );
539            if ($tblog) {
540                $row->{archive_types} = _populate_archive_loop( $app, $tblog, $obj );
541            }
542        }
543        elsif ( $type eq 'widget' ) {
544            $template_type = 'widget';
545        }
546        elsif ( $type eq 'index' ) {
547            $template_type = 'index';
548        }
549        elsif ( $type eq 'custom' ) {
550            $template_type = 'module';
551        }
552        elsif ( $type eq 'email' ) {
553            $template_type = 'email';
554        }
555        elsif ( $type eq 'backup' ) {
556            $template_type = 'backup';
557        }
558        else {
559            $template_type = 'system';
560        }
561        $row->{use_cache} = ( $obj->cache_expire_type != 0 ) ? 1 : 0;
562        $row->{template_type} = $template_type;
563        $row->{type} = 'entry' if $type eq 'individual';
564        my $published_url = $obj->published_url;
565        $row->{published_url} = $published_url if $published_url;
566    };
567
568    my $params        = {};
569    my $filter = $app->param('filter_key');
570    my $template_type = $filter || '';
571    $template_type =~ s/_templates//;
572
573    $params->{screen_class} = "list-template";
574    $params->{listing_screen} = 1;
575
576    $app->load_list_actions( 'template', $params );
577    $params->{page_actions} = $app->page_actions('list_templates');
578    $params->{search_label} = $app->translate("Templates");
579    $params->{object_type} = 'template';
580    $params->{blog_view} = 1;
581    $params->{refreshed} = $app->param('refreshed');
582    $params->{published} = $app->param('published');
583    $params->{saved_copied} = $app->param('saved_copied');
584    $params->{saved_deleted} = $app->param('saved_deleted');
585    $params->{saved} = $app->param('saved');
586
587    # determine list of system template types:
588    my $scope;
589    my $set;
590    if ( $blog ) {
591        $set   = $blog->template_set;
592        $scope = 'system';
593    }
594    else {
595        $scope = 'global:system';
596    }
597    my @tmpl_path = ( $set && ($set ne 'mt_blog')) ? ("template_sets", $set, 'templates', $scope) : ("default_templates", $scope);
598    my $sys_tmpl = MT->registry(@tmpl_path) || {};
599
600    my @tmpl_loop;
601    my %types;
602    if ($template_type ne 'backup') {
603        if ($blog) {
604            # blog template listings
605            %types = ( 
606                'index' => {
607                    label => $app->translate("Index Templates"),
608                    type => 'index',
609                    order => 100,
610                },
611                'archive' => {
612                    label => $app->translate("Archive Templates"),
613                    type => ['archive', 'individual', 'page', 'category'],
614                    order => 200,
615                },
616                'module' => {
617                    label => $app->translate("Template Modules"),
618                    type => 'custom',
619                    order => 300,
620                },
621                'system' => {
622                    label => $app->translate("System Templates"),
623                    type => [ keys %$sys_tmpl ],
624                    order => 400,
625                },
626            );
627        } else {
628            # global template listings
629            %types = ( 
630                'module' => {
631                    label => $app->translate("Template Modules"),
632                    type => 'custom',
633                    order => 100,
634                },
635                'email' => {
636                    label => $app->translate("Email Templates"),
637                    type => 'email',
638                    order => 200,
639                },
640                'system' => {
641                    label => $app->translate("System Templates"),
642                    type => [ keys %$sys_tmpl ],
643                    order => 300,
644                },
645            );
646        }
647    } else {
648        # global template listings
649        %types = ( 
650            'backup' => {
651                label => $app->translate("Template Backups"),
652                type => 'backup',
653                order => 100,
654            },
655        );
656    }
657    my @types = sort { $types{$a}->{order} <=> $types{$b}->{order} } keys %types;
658    if ($template_type) {
659        @types = ( $template_type );
660    }
661    $app->delete_param('filter_key') if $filter;
662    foreach my $tmpl_type (@types) {
663        if ( $tmpl_type eq 'index' ) {
664            $app->param( 'filter_key', 'index_templates' );
665        }
666        elsif ( $tmpl_type eq 'archive' ) {
667            $app->param( 'filter_key', 'archive_templates' );
668        }
669        elsif ( $tmpl_type eq 'system' ) {
670            $app->param( 'filter_key', 'system_templates' );
671        }
672        elsif ( $tmpl_type eq 'email' ) {
673            $app->param( 'filter_key', 'email_templates' );
674        }
675        elsif ( $tmpl_type eq 'module' ) {
676            $app->param( 'filter_key', 'module_templates' );
677        }
678        $terms->{type} = $types{$tmpl_type}->{type};
679        my $tmpl_param = $app->listing(
680            {
681                type     => 'template',
682                terms    => $terms,
683                args     => $args,
684                no_limit => 1,
685                no_html  => 1,
686                code     => $hasher,
687            }
688        );
689
690        my $template_type_label = $types{$tmpl_type}->{label};
691        $tmpl_param->{template_type} = $tmpl_type;
692        $tmpl_param->{template_type_label} = $template_type_label;
693        push @tmpl_loop, $tmpl_param;
694    }
695    if ($filter) {
696        $params->{filter_key} = $filter;
697        $params->{filter_label} = $types{$template_type}{label}
698            if exists $types{$template_type};
699        $app->param('filter_key', $filter);
700    } else {
701        # restore filter_key param (we modified it for the
702        # sake of the individual table listings)
703        $app->delete_param('filter_key');
704    }
705
706    $params->{template_type_loop} = \@tmpl_loop;
707    $params->{screen_id} = "list-template";
708
709    return $app->load_tmpl('list_template.tmpl', $params);
710}
711
712sub preview {
713    my $app         = shift;
714    my $q           = $app->param;
715    my $blog_id     = $q->param('blog_id');
716    my $blog        = $app->blog;
717    my $id          = $q->param('id');
718    my $tmpl;
719    my $user_id = $app->user->id;
720
721    # We can only do previews on blog templates. Have to publish
722    # the preview file somewhere!
723    return $app->errtrans("Invalid request.") unless $blog;
724
725    require MT::Template;
726    if ($id) {
727        $tmpl = MT::Template->load( { id => $id, blog_id => $blog_id } )
728            or return $app->errtrans( "Invalid request." );
729    }
730    else {
731        $tmpl = MT::Template->new;
732        $tmpl->id(-1);
733        $tmpl->blog_id($blog_id);
734    }
735
736    my $names = $tmpl->column_names;
737    my %values = map { $_ => scalar $app->param($_) } @$names;
738    delete $values{'id'} unless $q->param('id');
739
740    ## Strip linefeed characters.
741    for my $col (qw( text )) {
742        $values{$col} =~ tr/\r//d if $values{$col};
743    }
744    $tmpl->set_values( \%values );
745
746    my $preview_basename = $app->preview_object_basename;
747
748    my $type = $tmpl->type;
749    my $preview_tmpl = $tmpl;
750    my $archive_file;
751    my $archive_url;
752    my %param;
753    my $blog_path = $blog->site_path;
754    my $blog_url = $blog->site_url;
755
756    if (($type eq 'custom') || ($type eq 'widget')) {
757        # determine 'host' template
758        $preview_tmpl = MT::Template->load({ blog_id => $blog_id, identifier => 'main_index' });
759        if (!$preview_tmpl) {
760            return $app->errtrans("Can't locate host template to preview module/widget.");
761        }
762        my $req = $app->request;
763        # stash this module so that it is selected through a
764        # MTInclude tag instead of the one in the database:
765        my $tmpl_name = $tmpl->name;
766        $tmpl_name =~ s/^Widget: // if $type eq 'widget';
767        my $stash_id = 'template_' . $type . '::' . $blog_id . '::' . $tmpl_name;
768        $req->stash($stash_id, [ $tmpl, $tmpl->tokens ]);
769    } elsif (($type eq 'individual') || ($type eq 'page')) {
770        my $ctx = $preview_tmpl->context;
771        my $entry_type = $type eq 'individual' ? 'entry' : 'page';
772        my $entry_class = $app->model($entry_type);
773        my $obj = $entry_class->load({
774            blog_id => $blog_id,
775            status => MT::Entry::RELEASE()
776        }, {
777            limit => 1,
778            direction => 'descend',
779            'sort' => 'authored_on'
780        });
781        unless ( $obj ) {
782            # create a dummy object
783            $obj = $entry_class->new;
784            $obj->blog_id($blog_id);
785            $obj->id(-1);
786            $obj->author_id( $app->user->id );
787            $obj->authored_on( $blog->current_timestamp );
788            $obj->status( MT::Entry::RELEASE() );
789            $obj->basename( $preview_basename );
790            $obj->title($app->translate("Lorem ipsum"));
791            my $preview_text = $app->translate('LOREM_IPSUM_TEXT');
792            if ($preview_text eq 'LOREM_IPSUM_TEXT') {
793                $preview_text = q{Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut diam quam, accumsan eu, aliquam vel, ultrices a, augue. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Fusce hendrerit, lacus eget bibendum sollicitudin, mi tellus interdum neque, sit amet pretium tortor tellus id erat. Duis placerat justo ac erat. Duis posuere, risus eu elementum viverra, nisl lacus sagittis lorem, ac fermentum neque pede vitae arcu. Phasellus arcu elit, placerat eu, luctus posuere, tristique non, augue. In hac habitasse platea dictumst. Nunc non dolor et ipsum mattis malesuada. Praesent porta orci eu ligula. Ut dui augue, dapibus vitae, sodales in, lobortis non, felis. Aliquam feugiat mollis ipsum.};
794            }
795            my $preview_more = $app->translate('LORE_IPSUM_TEXT_MORE');
796            if ($preview_text eq 'LOREM_IPSUM_TEXT_MORE') {
797                $preview_more = q{Integer nunc nulla, vulputate sit amet, varius ac, faucibus ac, lectus. Nulla semper bibendum justo. In hac habitasse platea dictumst. Aliquam auctor pretium ante. Etiam porta consectetuer erat. Phasellus consequat, nisi eu suscipit elementum, metus leo malesuada pede, vel scelerisque lorem ligula in augue. Sed aliquet. Donec malesuada metus sit amet sapien. Integer non libero. Morbi egestas, mauris posuere consequat sodales, augue lectus suscipit velit, eu commodo lacus dolor congue justo. Suspendisse justo. Curabitur sagittis, lorem tincidunt elementum rhoncus, odio dolor mattis odio, quis ultrices ligula ipsum ac lacus. Nam et sapien ac lacus ultrices sollicitudin. Vestibulum ut dolor nec dui malesuada imperdiet. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
798
799                Quisque pharetra libero quis nibh. Cras lacus orci, commodo et, fringilla non, lobortis non, mauris. Curabitur dui sapien, tristique imperdiet, ultrices vitae, gravida varius, ante. Maecenas ac arcu nec nibh euismod feugiat. Pellentesque sed orci eget enim egestas faucibus. Aenean laoreet leo ornare velit. Nunc fermentum dolor eget massa. Fusce fringilla, tellus in pellentesque sodales, urna mi hendrerit leo, vel adipiscing ligula odio sit amet risus. Cras rhoncus, mi et posuere gravida, purus sem porttitor nisl, auctor laoreet nisl turpis quis ligula. Aliquam in nisi tristique augue egestas lacinia. Aenean ante magna, facilisis a, faucibus at, aliquam laoreet, dui. Ut tellus leo, tristique a, pellentesque ac, bibendum non, ipsum. Curabitur eu neque pretium arcu accumsan tincidunt. Ut ipsum. Quisque congue accumsan elit. Nulla ligula felis, aliquam ultricies, vestibulum vestibulum, semper vel, sapien. Aenean sodales ligula venenatis tellus. Vestibulum leo. Morbi viverra convallis eros.
800
801                Phasellus rhoncus pulvinar enim. Ut gravida ante nec lectus. Nam luctus gravida odio. Morbi vitae lorem vitae justo fermentum porttitor. Suspendisse vestibulum magna at purus. Cras nec sem. Duis id felis. Mauris hendrerit dapibus est. Donec semper. Praesent vehicula interdum velit. Ut sed tellus et diam venenatis pulvinar.};
802            }
803            $obj->text($preview_text);
804            $obj->text_more($preview_more);
805            $obj->keywords(MT->translate("sample, entry, preview"));
806            $obj->tags(qw( lorem ipsum sample preview ));
807        }
808        $ctx->stash('entry', $obj);
809        $ctx->{current_archive_type} = $type eq 'individual' ? 'Individual' : 'Page';
810        if (($type eq 'individual') && $blog->archive_path) {
811            $blog_path = $blog->archive_path;
812            $blog_url = $blog->archive_url;
813        }
814        $archive_file = File::Spec->catfile( $blog_path, $obj->archive_file );
815        $archive_url = $obj->archive_url;
816    } elsif ($type eq 'archive') {
817        # some variety of archive template
818    } elsif ($type eq 'index') {
819    } else {
820        # for now, only index templates can be previewed
821        return $app->errtrans("Invalid request.");
822    }
823
824    my $orig_file;
825    my $path;
826
827    # Default case; works for index templates (other template types should
828    # have defined $archive_file by now).
829    $archive_file = File::Spec->catfile( $blog_path, $preview_tmpl->outfile )
830        unless defined $archive_file;
831
832    ( $orig_file, $path ) = File::Basename::fileparse( $archive_file );
833
834    $archive_url = MT::Util::caturl( $blog_url, $orig_file )
835        unless defined $archive_url;
836
837    my $file_ext;
838    require File::Basename;
839    $file_ext = $archive_file;
840    if ($file_ext =~ m/\.[a-z]+$/) {
841        $file_ext =~ s!.+\.!.!;
842    } else {
843        $file_ext = '';
844    }
845    $archive_file = File::Spec->catfile( $path, $preview_basename . $file_ext );
846
847    my @data;
848    $app->run_callbacks( 'cms_pre_preview.template', $app, $preview_tmpl, \@data );
849
850    my $has_hires = eval 'require Time::HiRes; 1' ? 1 : 0;
851    my $start_time = $has_hires ? Time::HiRes::time() : time;
852
853    my $ctx = $preview_tmpl->context;
854    my $html = $preview_tmpl->output;
855
856    $param{build_time} = $has_hires ? sprintf("%.3f", Time::HiRes::time() - $start_time ) : "~" . ( time - $start_time );
857
858    unless ( defined($html) ) {
859        return $app->error( $app->translate( "Publish error: [_1]",
860            MT::Util::encode_html( $preview_tmpl->errstr ) ) );
861    }
862
863    # If MT is configured to do 'local' previews, convert all
864    # the normal blog URLs into the domain used by MT itself (ie,
865    # blog is published to www.example.com, which is a different
866    # server from where MT runs, mt.example.com; previews therefore
867    # should occur locally, so replace all http://www.example.com/
868    # with http://mt.example.com/).
869    my ($old_url, $new_url);
870    if ($app->config('LocalPreviews')) {
871        $old_url = $blog_url;
872        $old_url =~ s!^(https?://[^/]+?/)(.*)?!$1!;
873        $new_url = $app->base . '/';
874        $html =~ s!\Q$old_url\E!$new_url!g;
875    }
876
877    my $fmgr = $blog->file_mgr;
878
879    ## Determine if we need to build directory structure,
880    ## and build it if we do. DirUmask determines
881    ## directory permissions.
882    require File::Basename;
883    $path =~ s!/$!!
884      unless $path eq '/';    ## OS X doesn't like / at the end in mkdir().
885    unless ( $fmgr->exists($path) ) {
886        $fmgr->mkpath($path);
887    }
888
889    if ( $fmgr->exists($path) && $fmgr->can_write($path) ) {
890        $param{preview_file} = $preview_basename;
891        my $preview_url = $archive_url;
892        $preview_url =~ s! / \Q$orig_file\E ( /? ) $!/$preview_basename$file_ext$1!x;
893
894        # We also have to translate the URL used for the
895        # published file to be on the MT app domain.
896        if (defined $new_url) {
897            $preview_url =~ s!^\Q$old_url\E!$new_url!;
898        }
899
900        $param{preview_url}  = $preview_url;
901
902        $fmgr->put_data( $html, $archive_file );
903
904        # we have to make a record of this preview just in case it
905        # isn't cleaned up by re-editing, saving or cancelling on
906        # by the user.
907        require MT::Session;
908        my $sess_obj = MT::Session->get_by_key(
909            {
910                id   => $preview_basename,
911                kind => 'TF',                # TF = Temporary File
912                name => $archive_file,
913            }
914        );
915        $sess_obj->start(time);
916        $sess_obj->save;
917    }
918    else {
919        return $app->error( $app->translate(
920            "Unable to create preview file in this location: [_1]", $path ) );
921    }
922
923    $param{id} = $id if $id;
924    $param{new_object} = $param{id} ? 0 : 1;
925    $param{name} = $tmpl->name;
926    my $cols = $tmpl->column_names;
927    for my $col (@$cols) {
928        push @data,
929          {
930            data_name  => $col,
931            data_value => scalar $q->param($col)
932          };
933    }
934    $param{template_loop} = \@data;
935    $param{object_type}  = $type;
936    return $app->load_tmpl( 'preview_template_strip.tmpl', \%param );
937}
938
939sub reset_blog_templates {
940    my $app   = shift;
941    my $q     = $app->param;
942    my $perms = $app->permissions
943      or return $app->error( $app->translate("No permissions") );
944    return $app->error( $app->translate("Permission denied.") )
945      unless $perms->can_edit_templates;
946    $app->validate_magic() or return;
947    my $blog = MT::Blog->load( $perms->blog_id )
948        or return $app->error($app->translate('Can\'t load blog #[_1].', $perms->blog_id));
949    require MT::Template;
950    my @tmpl = MT::Template->load( { blog_id => $blog->id } );
951
952    for my $tmpl (@tmpl) {
953        $tmpl->remove or return $app->error( $tmpl->errstr );
954    }
955    my $set = $blog ? $blog->template_set : undef;
956    require MT::DefaultTemplates;
957    my $tmpl_list = MT::DefaultTemplates->templates($set) || [];
958    my @arch_tmpl;
959    for my $val (@$tmpl_list) {
960        $val->{name} = $app->translate( $val->{name} );
961        $val->{text} = $app->translate_templatized( $val->{text} );
962        my $tmpl = MT::Template->new;
963        $tmpl->set_values($val);
964        $tmpl->build_dynamic(0);
965        $tmpl->blog_id( $blog->id );
966        $tmpl->save
967          or return $app->error(
968            $app->translate(
969                "Populating blog with default templates failed: [_1]",
970                $tmpl->errstr
971            )
972          );
973
974        # FIXME: enumeration of types
975        if (   $val->{type} eq 'archive'
976            || $val->{type} eq 'category'
977            || $val->{type} eq 'page'
978            || $val->{type} eq 'individual' )
979        {
980            push @arch_tmpl, $tmpl;
981        }
982    }
983
984    ## Set up mappings from new templates to archive types.
985    for my $tmpl (@arch_tmpl) {
986        my (@at);
987
988        # FIXME: enumeration of types
989        if ( $tmpl->type eq 'archive' ) {
990            @at = qw( Daily Weekly Monthly Category );
991        }
992        elsif ( $tmpl->type eq 'page' ) {
993            @at = qw( Page );
994        }
995        elsif ( $tmpl->type eq 'individual' ) {
996            @at = qw( Individual );
997        }
998        require MT::TemplateMap;
999        for my $at (@at) {
1000            my $map = MT::TemplateMap->new;
1001            $map->archive_type($at);
1002            $map->is_preferred(1);
1003            $map->template_id( $tmpl->id );
1004            $map->blog_id( $tmpl->blog_id );
1005            $map->save
1006              or return $app->error(
1007                $app->translate(
1008                    "Setting up mappings failed: [_1]",
1009                    $map->errstr
1010                )
1011              );
1012        }
1013    }
1014    $app->redirect(
1015        $app->uri(
1016            'mode' => 'list',
1017            args =>
1018              { '_type' => 'template', blog_id => $blog->id, 'reset' => 1 }
1019        )
1020    );
1021}
1022
1023sub _generate_map_table {
1024    my $app = shift;
1025    my ( $blog_id, $template_id ) = @_;
1026
1027    require MT::Template;
1028    require MT::Blog;
1029    my $blog     = MT::Blog->load($blog_id);
1030    my $template = MT::Template->load($template_id);
1031    my $tmpl     = $app->load_tmpl('include/archive_maps.tmpl');
1032    my $maps     = _populate_archive_loop( $app, $blog, $template );
1033    $tmpl->param( object_type => 'templatemap' );
1034    $tmpl->param( publish_queue_available => eval 'require List::Util; require Scalar::Util; 1;' );
1035    $tmpl->param( object_loop => $maps ) if @$maps;
1036    my $html = $tmpl->output();
1037
1038    if ( $html =~ m/<__trans / ) {
1039        $html = $app->translate_templatized($html);
1040    }
1041    $html;
1042}
1043
1044sub _populate_archive_loop {
1045    my $app = shift;
1046    my ( $blog, $obj ) = @_;
1047
1048    my $index = $app->config('IndexBasename');
1049    my $ext = $blog->file_extension || '';
1050    $ext = '.' . $ext if $ext ne '';
1051
1052    require MT::TemplateMap;
1053    my @tmpl_maps = MT::TemplateMap->load( { template_id => $obj->id } );
1054    my @maps;
1055    my %types;
1056    foreach my $map_obj (@tmpl_maps) {
1057        my $map = {};
1058        $map->{map_id}           = $map_obj->id;
1059        $map->{map_is_preferred} = $map_obj->is_preferred;
1060        # publish options
1061        $map->{map_build_type} = $map_obj->build_type;
1062        $map->{ 'map_build_type_' . ( $map_obj->build_type || 0 ) } = 1;
1063        my ( $period, $interval ) = _get_schedule( $map_obj->build_interval );
1064        $map->{ 'map_schedule_period_' . $period } = 1
1065            if defined $period;
1066        $map->{map_schedule_interval} = $interval
1067            if defined $interval;
1068
1069        my $at = $map->{archive_type} = $map_obj->archive_type;
1070        $types{$at}++;
1071        $map->{ 'archive_type_preferred_' . $blog->archive_type_preferred } = 1
1072          if $blog->archive_type_preferred;
1073        $map->{file_template} = $map_obj->file_template
1074          if $map_obj->file_template;
1075
1076        my $archiver = $app->publisher->archiver($at);
1077        next unless $archiver;
1078        $map->{archive_label} = $archiver->archive_label;
1079        my $tmpls     = $archiver->default_archive_templates;
1080        my $tmpl_loop = [];
1081        foreach (@$tmpls) {
1082            my $name = $_->{label};
1083            $name =~ s/\.html$/$ext/;
1084            $name =~ s/index$ext$/$index$ext/;
1085            push @$tmpl_loop,
1086              {
1087                name    => $name,
1088                value   => $_->{template},
1089                default => ( $_->{default} || 0 ),
1090              };
1091        }
1092
1093        my $custom = 1;
1094
1095        foreach (@$tmpl_loop) {
1096            if (   ( !$map->{file_template} && $_->{default} )
1097                || ( $map->{file_template} eq $_->{value} ) )
1098            {
1099                $_->{selected}        = 1;
1100                $custom               = 0;
1101                $map->{file_template} = $_->{value}
1102                  if !$map->{file_template};
1103            }
1104        }
1105        if ($custom) {
1106            unshift @$tmpl_loop,
1107              {
1108                name     => $map->{file_template},
1109                value    => $map->{file_template},
1110                selected => 1,
1111              };
1112        }
1113
1114        $map->{archive_tmpl_loop} = $tmpl_loop;
1115        if (
1116            1 < MT::TemplateMap->count(
1117                { archive_type => $at, blog_id => $obj->blog_id }
1118            )
1119          )
1120        {
1121            $map->{has_multiple_archives} = 1;
1122        }
1123
1124        push @maps, $map;
1125    }
1126    @maps = sort { MT::App::CMS::archive_type_sorter( $a, $b ) } @maps;
1127    return \@maps;
1128}
1129
1130sub delete_map {
1131    my $app = shift;
1132    $app->validate_magic() or return;
1133    my $perms = $app->{perms}
1134      or return $app->error( $app->translate("No permissions") );
1135    my $q  = $app->param;
1136    my $id = $q->param('id');
1137
1138    require MT::TemplateMap;
1139    MT::TemplateMap->remove( { id => $id } );
1140    my $html =
1141      _generate_map_table( $app, $q->param('blog_id'),
1142        $q->param('template_id') );
1143    $app->{no_print_body} = 1;
1144    $app->send_http_header("text/plain");
1145    $app->print($html);
1146}
1147
1148sub add_map {
1149    my $app = shift;
1150    $app->validate_magic() or return;
1151    my $perms = $app->{perms}
1152      or return $app->error( $app->translate("No permissions") );
1153
1154    my $q = $app->param;
1155
1156    require MT::TemplateMap;
1157    my $blog_id = $q->param('blog_id');
1158    my $at      = $q->param('new_archive_type');
1159    my $exist   = MT::TemplateMap->exist(
1160        {
1161            blog_id      => $blog_id,
1162            archive_type => $at
1163        }
1164    );
1165    my $map = MT::TemplateMap->new;
1166    $map->is_preferred( $exist ? 0 : 1 );
1167    $map->template_id( scalar $q->param('template_id') );
1168    $map->blog_id($blog_id);
1169    $map->archive_type($at);
1170    $map->save
1171      or return $app->error(
1172        $app->translate( "Saving map failed: [_1]", $map->errstr ) );
1173    my $html =
1174      _generate_map_table( $app, $blog_id, scalar $q->param('template_id') );
1175    $app->{no_print_body} = 1;
1176    $app->send_http_header("text/plain");
1177    $app->print($html);
1178}
1179
1180sub can_view {
1181    my ( $eh, $app, $id ) = @_;
1182    my $perms = $app->permissions;
1183    return !$id || ($perms && $perms->can_edit_templates) || (!$app->blog && $app->user->can_edit_templates);
1184}
1185
1186sub can_save {
1187    my ( $eh, $app, $id ) = @_;
1188    my $perms = $app->permissions;
1189    return ($perms && $perms->can_edit_templates) || (!$perms && $app->user->can_edit_templates);
1190}
1191
1192sub can_delete {
1193    my ( $eh, $app, $obj ) = @_;
1194    return 1 if $app->user->is_superuser();
1195    my $perms = $app->permissions;
1196    return ($perms && $perms->can_edit_templates) || (!$perms && $app->user->can_edit_templates);
1197}
1198
1199sub pre_save {
1200    my $eh = shift;
1201    my ( $app, $obj ) = @_;
1202
1203    ## Strip linefeed characters.
1204    ( my $text = $obj->text ) =~ tr/\r//d;
1205
1206    if ($text =~ m/<(MT|_)_trans/i) {
1207        $text = $app->translate_templatized($text);
1208    }
1209
1210    $obj->text($text);
1211
1212    # update text heights if necessary
1213    if ( my $perms = $app->permissions ) {
1214        my $prefs = $perms->template_prefs || '';
1215        my $text_height = $app->param('text_height');
1216        if ( defined $text_height ) {
1217            my ($pref_text_height) = $prefs =~ m/\btext:(\d+)\b/;
1218            $pref_text_height ||= 0;
1219            if ( $text_height != $pref_text_height ) {
1220                if ( $prefs =~ m/\btext\b/ ) {
1221                    $prefs =~ s/\btext(:\d+)\b/text:$text_height/;
1222                }
1223                else {
1224                    $prefs = 'text:' . $text_height . ',' . $prefs;
1225                }
1226            }
1227        }
1228
1229        if ( $prefs ne ( $perms->template_prefs || '' ) ) {
1230            $perms->template_prefs($prefs);
1231            $perms->save;
1232        }
1233    }
1234
1235    # module caching
1236    $obj->include_with_ssi( $app->param('include_with_ssi') ? 1 : 0 );
1237    $obj->cache_path( $app->param('cache_path'));
1238    my $cache_expire_type = $app->param('cache_expire_type');
1239    $obj->cache_expire_type($cache_expire_type);
1240    my $period   = $app->param('cache_expire_period');
1241    my $interval = $app->param('cache_expire_interval');
1242    my $sec      = _get_interval( $period, $interval );
1243    $obj->cache_expire_interval($sec);
1244    my $q = $app->param;
1245    my @events;
1246
1247    foreach my $name ( $q->param('cache_expire_event') ) {
1248        push @events, $name;
1249    }
1250    $obj->cache_expire_event( join ',', @events );
1251    if ( $cache_expire_type == 1 ) {
1252        return $eh->error(
1253            $app->translate("You should not be able to enter 0 as the time.") )
1254          if $interval == 0;
1255    }
1256    elsif ( $cache_expire_type == 2 ) {
1257        return $eh->error(
1258            $app->translate("You must select at least one event checkbox.") )
1259          if !@events;
1260    }
1261
1262    require MT::PublishOption;
1263    my $build_type = $app->param('build_type');
1264
1265    if ( $build_type == MT::PublishOption::SCHEDULED() ) {
1266        my $period   = $app->param('schedule_period');
1267        my $interval = $app->param('schedule_interval');
1268        my $sec      = _get_interval( $period, $interval );
1269        $obj->build_interval($sec);
1270    }
1271    my $rebuild_me = 1;
1272    if (   $build_type == MT::PublishOption::DISABLED()
1273        || $build_type == MT::PublishOption::MANUALLY() )
1274    {
1275        $rebuild_me = 0;
1276    }
1277    $obj->rebuild_me($rebuild_me);
1278    1;
1279}
1280
1281sub post_save {
1282    my $eh = shift;
1283    my ( $app, $obj, $original ) = @_;
1284
1285    my $sess_obj = $app->autosave_session_obj;
1286    $sess_obj->remove if $sess_obj;
1287
1288    my $dynamic = 0;
1289    my $q = $app->param;
1290    my $type = $q->param('type');
1291    # FIXME: enumeration of types
1292    if ( $type eq 'custom'
1293      || $type eq 'index'
1294      || $type eq 'widget'
1295      || $type eq 'widgetset' )
1296    {
1297        $dynamic = $obj->build_dynamic;
1298    }
1299    else
1300    {
1301        # archive template specific post_save tasks
1302        require MT::TemplateMap;
1303        my @p = $q->param;
1304        for my $p (@p) {
1305            my $map;
1306            if ( $p =~ /^archive_tmpl_preferred_(\w+)_(\d+)$/ ) {
1307                my $at     = $1;
1308                my $map_id = $2;
1309                $map    = MT::TemplateMap->load($map_id)
1310                    or next;
1311                $map->prefer( $q->param($p) );    # prefer method saves in itself
1312            }
1313            elsif ( $p =~ /^archive_file_tmpl_(\d+)$/ ) {
1314                my $map_id = $1;
1315                $map    = MT::TemplateMap->load($map_id)
1316                    or next;
1317                $map->file_template( $q->param($p) );
1318                $map->save;
1319            }
1320            elsif ( $p =~ /^map_build_type_(\d+)$/ ) {
1321                my $map_id     = $1;
1322                $map        = MT::TemplateMap->load($map_id)
1323                    or next;
1324                my $build_type = $q->param($p);
1325                require MT::PublishOption;
1326                $map->build_type($build_type);
1327                if ( $build_type == MT::PublishOption::SCHEDULED() ) {
1328                    my $period   = $q->param( 'map_schedule_period_' . $map_id );
1329                    my $interval = $q->param( 'map_schedule_interval_' . $map_id );
1330                    my $sec      = _get_interval( $period, $interval );
1331                    $map->build_interval($sec);
1332                }
1333                $map->save;
1334            }
1335            if ( !$dynamic
1336              && $map && $map->build_type == MT::PublishOption::DYNAMIC() )
1337            {
1338                $dynamic = 1;
1339            }
1340        }
1341    }
1342
1343    if ( !$original->id ) {
1344        $app->log(
1345            {
1346                message => $app->translate(
1347                    "Template '[_1]' (ID:[_2]) created by '[_3]'",
1348                    $obj->name, $obj->id, $app->user->name
1349                ),
1350                level    => MT::Log::INFO(),
1351                class    => 'template',
1352                category => 'new',
1353            }
1354        );
1355    }
1356
1357    if ( $dynamic ) {
1358        if ( $obj->type eq 'index' ) {
1359            $app->rebuild_indexes(
1360                BlogID   => $obj->blog_id,
1361                Template => $obj,
1362                NoStatic => 1,
1363            ) or return $app->publish_error();    # XXXX
1364        }
1365        if ( my $blog = $app->blog ) {
1366            require MT::CMS::Blog;
1367            my ( $path, $url );
1368            if ( $obj->type eq 'index' ) {
1369                $path = $blog->site_path;
1370                $url = $blog->site_url;
1371            }
1372            else {
1373                # must be archive since other types can't be dynamic
1374                if ( $path = $blog->archive_path ) {
1375                    $url = $blog->archive_url;
1376                }
1377                else {
1378                    $path = $blog->site_path;
1379                    $url = $blog->site_url;
1380                }
1381            }
1382            # specific arguments so not to overwrite mtview and htaccess
1383            MT::CMS::Blog::prepare_dynamic_publishing(
1384                $eh, 
1385                $blog,
1386                undef,
1387                undef,
1388                $path,
1389                $url
1390            );
1391        }
1392    }
1393    1;
1394}
1395
1396sub post_delete {
1397    my ( $eh, $app, $obj ) = @_;
1398
1399    $app->log(
1400        {
1401            message => $app->translate(
1402                "Template '[_1]' (ID:[_2]) deleted by '[_3]'",
1403                $obj->name, $obj->id, $app->user->name
1404            ),
1405            level    => MT::Log::INFO(),
1406            class    => 'system',
1407            category => 'delete'
1408        }
1409    );
1410}
1411
1412sub build_template_table {
1413    my $app = shift;
1414    my (%args) = @_;
1415
1416    my $perms     = $app->permissions;
1417    my $list_pref = $app->list_pref('template');
1418    my $limit     = $args{limit};
1419    my $param     = $args{param} || {};
1420    my $iter;
1421    if ( $args{load_args} ) {
1422        my $class = $app->model('template');
1423        $iter = $class->load_iter( @{ $args{load_args} } );
1424    }
1425    elsif ( $args{iter} ) {
1426        $iter = $args{iter};
1427    }
1428    elsif ( $args{items} ) {
1429        $iter = sub { pop @{ $args{items} } };
1430        $limit = scalar @{ $args{items} };
1431    }
1432    return [] unless $iter;
1433
1434    my @data;
1435    my $i;
1436    my %blogs;
1437    while ( my $tmpl = $iter->() ) {
1438        my $blog = $blogs{ $tmpl->blog_id } ||=
1439          MT::Blog->load( $tmpl->blog_id );
1440        return $app->error($app->translate('Can\'t load blog #[_1].', $tmpl->blog_id)) unless $blog;
1441
1442        my $row = $tmpl->column_values;
1443        $row->{name} = '' if !defined $row->{name};
1444        $row->{name} =~ s/^\s+|\s+$//g;
1445        $row->{name} = "(" . $app->translate("No Name") . ")"
1446          if $row->{name} eq '';
1447        my $published_url = $tmpl->published_url;
1448        $row->{published_url} = $published_url if $published_url;
1449        $row->{use_cache} = ( $tmpl->cache_expire_type != 0 )  ? 1 : 0;
1450
1451        # FIXME: enumeration of types
1452        $row->{can_delete} = 1
1453          if $tmpl->type =~ m/(custom|index|archive|page|individual|category|widget)/;
1454        if ($blog) {
1455            $row->{weblog_name} = $blog->name;
1456        }
1457        elsif ($tmpl->blog_id) {
1458            $row->{weblog_name} = '* ' . $app->translate('Orphaned') . ' *';
1459        }
1460        else {
1461            $row->{weblog_name} = '* ' . $app->translate('Global Templates') . ' *';
1462        }
1463        $row->{object} = $tmpl;
1464        push @data, $row;
1465        last if defined($limit) && (@data > $limit);
1466    }
1467    return [] unless @data;
1468
1469    $param->{template_table}[0]              = {%$list_pref};
1470    $param->{template_table}[0]{object_loop} = \@data;
1471    $param->{template_table}[0]{object_type} = 'template';
1472    $app->load_list_actions( 'template', $param );
1473    $param->{object_loop} = \@data;
1474    \@data;
1475}
1476
1477sub dialog_publishing_profile {
1478    my $app = shift;
1479    $app->validate_magic or return;
1480
1481    my $blog = $app->blog;
1482    $app->assert( $blog ) or return;
1483
1484    # permission check
1485    my $perms = $app->permissions;
1486    return $app->errtrans("Permission denied.")
1487        unless $app->user->is_superuser ||
1488            $perms->can_administer_blog ||
1489            $perms->can_edit_templates;
1490
1491    my $param = {};
1492    $param->{dynamicity} = $blog->custom_dynamic_templates || 'none';
1493    $param->{screen_id} = "publishing-profile-dialog";
1494    $param->{return_args} = $app->param('return_args');
1495
1496    $app->build_page('dialog/publishing_profile.tmpl',
1497        $param);
1498}
1499
1500sub dialog_refresh_templates {
1501    my $app = shift;
1502    $app->validate_magic or return;
1503
1504    # permission check
1505    my $perms = $app->permissions;
1506    return $app->errtrans("Permission denied.")
1507        unless $app->user->is_superuser ||
1508            $perms->can_administer_blog ||
1509            $perms->can_edit_templates;
1510
1511    my $param = {};
1512    my $blog = $app->blog;
1513    $param->{return_args} = $app->param('return_args');
1514
1515    if ($blog) {
1516        $param->{blog_id} = $blog->id;
1517
1518        my $sets = $app->registry("template_sets");
1519        $sets->{$_}{key} = $_ for keys %$sets;
1520        $sets = $app->filter_conditional_list([ values %$sets ]);
1521
1522        no warnings; # some sets may not define an order
1523        @$sets = sort { $a->{order} <=> $b->{order} } @$sets;
1524        $param->{'template_set_loop'} = $sets;
1525
1526        my $existing_set = $blog->template_set || 'mt_blog';
1527        foreach (@$sets) {
1528            if ($_->{key} eq $existing_set) {
1529                $_->{selected} = 1;
1530            }
1531        }
1532        $param->{'template_set_index'} = $#$sets;
1533        $param->{'template_set_count'} = scalar @$sets;
1534
1535        $param->{template_sets} = $sets;
1536        $param->{screen_id} = "refresh-templates-dialog";
1537    }
1538
1539    # load template sets
1540    $app->build_page('dialog/refresh_templates.tmpl',
1541        $param);
1542}
1543
1544sub refresh_all_templates {
1545    my ($app) = @_;
1546
1547    my $backup = 0;
1548    if ($app->param('backup')) {
1549        # refresh templates dialog uses a 'backup' field
1550        $backup = 1;
1551    }
1552
1553    my $template_set = $app->param('template_set');
1554    my $refresh_type = $app->param('refresh_type') || 'refresh';
1555
1556    my $t = time;
1557
1558    my @id;
1559    if ($app->param('blog_id')) {
1560        @id = ( scalar $app->param('blog_id') );
1561    }
1562    else {
1563        @id = $app->param('id');
1564        if (! @id) {
1565            # refresh global templates
1566            @id = ( 0 );
1567        }
1568    }
1569
1570    require MT::Template;
1571    require MT::DefaultTemplates;
1572    require MT::Blog;
1573    require MT::Permission;
1574    require MT::Util;
1575
1576    foreach my $blog_id (@id) {
1577        my $blog;
1578        if ($blog_id) {
1579            $blog = MT::Blog->load($blog_id);
1580            next unless $blog;
1581        }
1582        if ( !$app->user->is_superuser() ) {
1583            my $perms = MT::Permission->load(
1584                { blog_id => $blog_id, author_id => $app->user->id } );
1585            if (
1586                !$perms
1587                || (   !$perms->can_edit_templates()
1588                    && !$perms->can_administer_blog() )
1589              )
1590            {
1591                next;
1592            }
1593        }
1594
1595        my $tmpl_list;
1596        if ($blog_id) {
1597
1598            if ($refresh_type eq 'clean') {
1599                # the user wants to back up all templates and
1600                # install the new ones
1601
1602                my @ts = MT::Util::offset_time_list( $t, $blog_id );
1603                my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d",
1604                    $ts[5] + 1900, $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1605
1606                my $tmpl_iter = MT::Template->load_iter({
1607                    blog_id => $blog_id,
1608                    type => { not => 'backup' },
1609                });
1610
1611                while (my $tmpl = $tmpl_iter->()) {
1612                    if ($backup) {
1613                        # zap all template maps
1614                        require MT::TemplateMap;
1615                        MT::TemplateMap->remove({
1616                            template_id => $tmpl->id,
1617                        });
1618                        $tmpl->type('backup');
1619                        $tmpl->name(
1620                            $tmpl->name . ' (Backup from ' . $ts . ')' );
1621                        $tmpl->identifier(undef);
1622                        $tmpl->rebuild_me(0);
1623                        $tmpl->linked_file(undef);
1624                        $tmpl->outfile('');
1625                        $tmpl->save;
1626                    } else {
1627                        $tmpl->remove;
1628                    }
1629                }
1630
1631                # This also creates our template mappings
1632                $blog->create_default_templates( $template_set ||
1633                    $blog->template_set || 'mt_blog' );
1634
1635                if ($template_set) {
1636                    $blog->template_set( $template_set );
1637                    $blog->save;
1638                    $app->run_callbacks( 'blog_template_set_change', { blog => $blog } );
1639                }
1640
1641                next;
1642            }
1643
1644            $tmpl_list = MT::DefaultTemplates->templates($template_set || $blog->template_set) || MT::DefaultTemplates->templates();
1645        }
1646        else {
1647            $tmpl_list = MT::DefaultTemplates->templates();
1648        }
1649
1650        foreach my $val (@$tmpl_list) {
1651            if ($blog_id) {
1652                # when refreshing blog templates,
1653                # skip over global templates which
1654                # specify a blog_id of 0...
1655                next if $val->{global};
1656            }
1657            else {
1658                next unless exists $val->{global};
1659            }
1660
1661            if ( !$val->{orig_name} ) {
1662                $val->{orig_name} = $val->{name};
1663                $val->{name}      = $app->translate( $val->{name} );
1664                $val->{text}      = $app->translate_templatized( $val->{text} );
1665            }
1666
1667            my $orig_name = $val->{orig_name};
1668
1669            my @ts = MT::Util::offset_time_list( $t, ( $blog_id ? $blog_id : undef ) );
1670            my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d", $ts[5] + 1900,
1671              $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1672
1673            my $terms = {};
1674            $terms->{blog_id} = $blog_id;
1675            $terms->{type} = $val->{type};
1676            if ( $val->{type} =~
1677                m/^(archive|individual|page|category|index|custom|widget)$/ )
1678            {
1679                $terms->{name} = $val->{name};
1680            }
1681            else {
1682                $terms->{identifier} = $val->{identifier};
1683            }
1684
1685            # this should only return 1 template; we're searching
1686            # within a given blog for a specific type of template (for
1687            # "system" templates; or for a type + name, which should be
1688            # unique for that blog.
1689            my $tmpl = MT::Template->load($terms);
1690            if ($tmpl && $backup) {
1691
1692                # check for default template text...
1693                # if it is a default template, then outright replace it
1694                my $text = $tmpl->text;
1695                $text =~ s/\s+//g;
1696
1697                my $def_text = $val->{text};
1698                $def_text =~ s/\s+//g;
1699
1700                # if it has been customized, back it up to a new tmpl record
1701                if ($def_text ne $text) {
1702                    my $backup = $tmpl->clone;
1703                    delete $backup->{column_values}
1704                      ->{id};    # make sure we don't overwrite original
1705                    delete $backup->{changed_cols}->{id};
1706                    $backup->name(
1707                        $backup->name . $app->translate( ' (Backup from [_1])', $ts ) );
1708                    $backup->type('backup');
1709                    # if ( $backup->type !~
1710                    #         m/^(archive|individual|page|category|index|custom|widget)$/ )
1711                    # {
1712                    #     $backup->type('custom')
1713                    #       ;      # system templates can't be created
1714                    # }
1715                    $backup->outfile('');
1716                    $backup->linked_file( $tmpl->linked_file );
1717                    $backup->identifier(undef);
1718                    $backup->rebuild_me(0);
1719                    $backup->build_dynamic(0);
1720                    $backup->save;
1721                }
1722            }
1723            if ($tmpl) {
1724                # we found that the previous template had not been
1725                # altered, so replace it with new default template...
1726                $tmpl->text( $val->{text} );
1727                $tmpl->identifier( $val->{identifier} );
1728                $tmpl->type( $val->{type} )
1729                  ; # fixes mismatch of types for cases like "archive" => "individual"
1730                $tmpl->linked_file('');
1731                $tmpl->save;
1732            }
1733            else {
1734                # create this one...
1735                my $tmpl = new MT::Template;
1736                $tmpl->build_dynamic(0);
1737                $tmpl->set_values(
1738                    {
1739                        text       => $val->{text},
1740                        name       => $val->{name},
1741                        type       => $val->{type},
1742                        identifier => $val->{identifier},
1743                        outfile    => $val->{outfile},
1744                        rebuild_me => $val->{rebuild_me},
1745                    }
1746                );
1747                $tmpl->blog_id($blog_id);
1748                $tmpl->save
1749                  or return $app->error(
1750                        $app->translate("Error creating new template: ")
1751                      . $tmpl->errstr );
1752            }
1753        }
1754    }
1755
1756    $app->add_return_arg( 'refreshed' => 1 );
1757    $app->call_return;
1758}
1759
1760sub refresh_individual_templates {
1761    my ($app) = @_;
1762
1763    require MT::Util;
1764
1765    my $user = $app->user;
1766    my $perms = $app->permissions;
1767    return $app->error(
1768        $app->translate(
1769            "Permission denied.")
1770      )
1771      #TODO: system level-designer permission
1772      unless $user->is_superuser() || $user->can_edit_templates()
1773      || ( $perms
1774        && ( $perms->can_edit_templates()
1775          || $perms->can_administer_blog ) );
1776
1777    my $set;
1778    if ( my $blog_id = $app->param('blog_id') ) {
1779        my $blog = $app->model('blog')->load($blog_id)
1780            or return $app->error($app->translate('Can\'t load blog #[_1].', $blog_id));
1781        $set = $blog->template_set()
1782            if $blog;
1783    }
1784
1785    require MT::DefaultTemplates;
1786    my $tmpl_list = MT::DefaultTemplates->templates($set) or return;
1787
1788    my $tmpl_types = {};
1789    my $tmpl_ids   = {};
1790    my $tmpls      = {};
1791    foreach my $tmpl (@$tmpl_list) {
1792        $tmpl->{text} = $app->translate_templatized( $tmpl->{text} );
1793        $tmpl_ids->{ $tmpl->{identifier} } = $tmpl
1794            if $tmpl->{identifier};
1795        if ( $tmpl->{type} !~ m/^(archive|individual|page|category|index|custom|widget)$/ )
1796        {
1797            $tmpl_types->{ $tmpl->{type} } = $tmpl;
1798        }
1799        else {
1800            $tmpls->{ $tmpl->{type} }{ $tmpl->{name} } = $tmpl;
1801        }
1802    }
1803
1804    my $t = time;
1805
1806    my @msg;
1807    my @id = $app->param('id');
1808    require MT::Template;
1809    foreach my $tmpl_id (@id) {
1810        my $tmpl = MT::Template->load($tmpl_id);
1811        next unless $tmpl;
1812        my $blog_id = $tmpl->blog_id;
1813
1814        # FIXME: permission check -- for this blog_id
1815
1816        my @ts = MT::Util::offset_time_list( $t, $blog_id );
1817        my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d", $ts[5] + 1900,
1818          $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1819
1820        my $val = ( $tmpl->identifier ? $tmpl_ids->{ $tmpl->identifier() } : undef )
1821          || $tmpl_types->{ $tmpl->type() }
1822          || $tmpls->{ $tmpl->type() }{ $tmpl->name };
1823        if ( !$val ) {
1824            push @msg,
1825              $app->translate(
1826"Skipping template '[_1]' since it appears to be a custom template.",
1827                $tmpl->name
1828              );
1829            next;
1830        }
1831
1832        my $text = $tmpl->text;
1833        $text =~ s/\s+//g;
1834
1835        my $def_text = $val->{text};
1836        $def_text =~ s/\s+//g;
1837
1838        if ($text ne $def_text) {
1839            # if it has been customized, back it up to a new tmpl record
1840            my $backup = $tmpl->clone;
1841            delete $backup->{column_values}
1842              ->{id};    # make sure we don't overwrite original
1843            delete $backup->{changed_cols}->{id};
1844            $backup->name( $backup->name . ' (Backup from ' . $ts . ')' );
1845            $backup->type('backup');
1846            $backup->outfile('');
1847            $backup->linked_file( $tmpl->linked_file );
1848            $backup->rebuild_me(0);
1849            $backup->build_dynamic(0);
1850            $backup->identifier(undef);
1851            $backup->save;
1852            push @msg,
1853              $app->translate(
1854    'Refreshing template <strong>[_3]</strong> with <a href="?__mode=view&amp;blog_id=[_1]&amp;_type=template&amp;id=[_2]">backup</a>',
1855                  $blog_id, $backup->id, $tmpl->name );
1856
1857            # we found that the previous template had not been
1858            # altered, so replace it with new default template...
1859            $tmpl->text( $val->{text} );
1860            $tmpl->identifier( $val->{identifier} );
1861            $tmpl->linked_file('');
1862            $tmpl->save;
1863        } else {
1864            push @msg, $app->translate("Skipping template '[_1]' since it has not been changed.", $tmpl->name);
1865        }
1866    }
1867    my @msg_loop;
1868    push @msg_loop, { message => $_ } foreach @msg;
1869
1870    $app->build_page( 'refresh_results.tmpl',
1871        { message_loop => \@msg_loop, return_url => $app->return_uri } );
1872}
1873
1874sub clone_templates {
1875    my ($app) = @_;
1876
1877    my $user = $app->user;
1878    my $perms = $app->permissions;
1879    return $app->error(
1880        $app->translate(
1881            "Permission denied.")
1882      )
1883      #TODO: system level-designer permission
1884      unless $user->is_superuser() || $user->can_edit_templates()
1885      || ( $perms
1886        && ( $perms->can_edit_templates()
1887          || $perms->can_administer_blog ) );
1888
1889    my @id = $app->param('id');
1890    require MT::Template;
1891    foreach my $tmpl_id (@id) {
1892        my $tmpl = MT::Template->load($tmpl_id);
1893        next unless $tmpl;
1894
1895        my $new_tmpl = $tmpl->clone({
1896            Except => {
1897                id => 1,
1898                name => 1,
1899                identifier => 1,
1900            },
1901        });
1902
1903        my $new_basename = $app->translate("Copy of [_1]", $tmpl->name);
1904        my $new_name = $new_basename;
1905        my $i = 0;
1906        while (MT::Template->exist({ name => $new_name, blog_id => $tmpl->blog_id })) {
1907            $new_name = $new_basename . ' (' . ++$i . ')';
1908        }
1909
1910        $new_tmpl->name($new_name);
1911        $new_tmpl->save;
1912    }
1913
1914    $app->add_return_arg( 'saved_copied' => 1 );
1915    $app->call_return;
1916}
1917
1918sub publish_index_templates {
1919    my $app = shift;
1920    $app->validate_magic or return;
1921
1922    # permission check
1923    my $perms = $app->permissions;
1924    return $app->errtrans("Permission denied.")
1925        unless $app->user->is_superuser ||
1926            $perms->can_administer_blog ||
1927            $perms->can_rebuild;
1928
1929    my $blog = $app->blog;
1930    my $templates = MT->model('template')->lookup_multi([ $app->param('id') ]);
1931    TEMPLATE: for my $tmpl (@$templates) {
1932        next TEMPLATE if !defined $tmpl;
1933        next TEMPLATE if $tmpl->blog_id != $blog->id;
1934        next TEMPLATE unless $tmpl->build_type;
1935
1936        $app->rebuild_indexes(
1937            Blog     => $blog,
1938            Template => $tmpl,
1939            Force    => 1,
1940        );
1941    }
1942
1943    $app->call_return( published => 1 );
1944}
1945
1946sub publish_archive_templates {
1947    my $app = shift;
1948    $app->validate_magic or return;
1949
1950    # permission check
1951    my $perms = $app->permissions;
1952    return $app->errtrans("Permission denied.")
1953      unless $app->user->is_superuser
1954      || $perms->can_administer_blog
1955      || $perms->can_rebuild;
1956
1957    my $blog = $app->blog;
1958    my $templates =
1959      MT->model('template')->lookup_multi( [ $app->param('id') ] );
1960    require MT::TemplateMap;
1961    # FIXME: Need multi-request support!
1962    TEMPLATE: for my $tmpl (@$templates) {
1963        next TEMPLATE if !defined $tmpl;
1964        next TEMPLATE if $tmpl->blog_id != $blog->id;
1965        my @tmpl_maps = MT::TemplateMap->load( { template_id => $tmpl->id } );
1966        foreach my $map (@tmpl_maps) {
1967            next unless $map->build_type;
1968            $app->rebuild(
1969                Blog        => $blog,
1970                ArchiveType => $map->archive_type,
1971                TemplateMap => $map,
1972                NoIndexes   => 1,
1973                Force       => 1,
1974            );
1975        }
1976    }
1977
1978    $app->call_return( published => 1 );
1979}
1980
1981{
1982    my @period_options = (
1983        {
1984            name => 'minutes',
1985            expr => 60,
1986        },
1987        {
1988            name => 'hours',
1989            expr => 60 * 60,
1990        },
1991        {
1992            name => 'days',
1993            expr => 24 * 60 * 60,
1994        },
1995    );
1996
1997    sub _get_schedule {
1998        my ($sec) = @_;
1999        return unless defined $sec;
2000        my ( $period, $interval );
2001        for (@period_options) {
2002            last if $sec % $_->{expr};
2003            $period   = $_->{name};
2004            $interval = $sec / $_->{expr};
2005        }
2006        ( $period, $interval );
2007    }
2008
2009    sub _get_interval {
2010        my ( $period, $interval ) = @_;
2011        return unless defined $period;
2012        my $sec = 0;
2013        for (@period_options) {
2014            if ( $_->{name} eq $period ) {
2015                $sec = $interval * $_->{expr};
2016                last;
2017            }
2018        }
2019        $sec;
2020    }
2021}
2022
20231;
Note: See TracBrowser for help on using the browser.