root/branches/release-33/lib/MT/CMS/Template.pm @ 1740

Revision 1740, 64.5 kB (checked in by bchoate, 20 months ago)

Set default cache interval to 30 when interval is unset. BugId:74671

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