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

Revision 1737, 64.4 kB (checked in by bchoate, 20 months ago)

Populated include_system, include_cache blog settings. BugId:75108

  • 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    $param->{dirty} = 1
477        if $app->param('dirty');
478
479    $param->{can_preview} = 1
480        if (!$param->{is_special}) && (!$obj || ($obj && $obj->outfile !~ m/\.(css|xml|rss|js)$/));
481
482    1;
483}
484
485sub list {
486    my $app = shift;
487
488    my $perms = $app->blog ? $app->permissions : $app->user->permissions;
489    return $app->return_to_dashboard( redirect => 1 )
490      unless $perms || $app->user->is_superuser;
491    if ( $perms && !$perms->can_edit_templates ) {
492        return $app->return_to_dashboard( permission => 1 );
493    }
494    my $blog = $app->blog;
495
496    require MT::Template;
497    my $blog_id = $app->param('blog_id') || 0;
498    # my $filter  = $app->param('filter_key');
499    # if ( !$filter ) {
500    #     if ($blog) {
501    #         $filter = 'templates';
502    #         $app->param( 'filter_key', 'templates' );
503    #     }
504    #     else {
505    #         $filter = 'module_templates';
506    #         $app->param( 'filter_key', 'module_templates' );
507    #     }
508    # }
509    # else {
510    #     # global index templates redirect to module templates
511    #     if ( !$blog && $filter eq 'templates' ) {
512    #         $filter = 'module_templates';
513    #         $app->param( 'filter_key', 'module_templates' );
514    #     }
515    # }
516    my $terms = { blog_id => $blog_id };
517    my $args  = { sort    => 'name' };
518
519    my $hasher = sub {
520        my ( $obj, $row ) = @_;
521        my $template_type;
522        my $type = $row->{type} || '';
523        if ( $type =~ m/^(individual|page|category|archive)$/ ) {
524            $template_type = 'archive';
525            # populate context with templatemap loop
526            my $tblog = $obj->blog_id == $blog->id ? $blog : MT::Blog->load( $obj->blog_id );
527            if ($tblog) {
528                $row->{archive_types} = _populate_archive_loop( $app, $tblog, $obj );
529            }
530        }
531        elsif ( $type eq 'widget' ) {
532            $template_type = 'widget';
533        }
534        elsif ( $type eq 'index' ) {
535            $template_type = 'index';
536        }
537        elsif ( $type eq 'custom' ) {
538            $template_type = 'module';
539        }
540        elsif ( $type eq 'email' ) {
541            $template_type = 'email';
542        }
543        elsif ( $type eq 'backup' ) {
544            $template_type = 'backup';
545        }
546        else {
547            $template_type = 'system';
548        }
549        $row->{template_type} = $template_type;
550        $row->{type} = 'entry' if $type eq 'individual';
551        my $published_url = $obj->published_url;
552        $row->{published_url} = $published_url if $published_url;
553    };
554
555    my $params        = {};
556    my $filter = $app->param('filter_key');
557    $app->delete_param('filter_key') if $filter;
558    my $template_type = $filter || '';
559    $template_type =~ s/_templates//;
560
561    $params->{screen_class} = "list-template";
562    $params->{listing_screen} = 1;
563
564    $app->load_list_actions( 'template', $params );
565    $params->{page_actions} = $app->page_actions('list_templates');
566    $params->{search_label} = $app->translate("Templates");
567    $params->{blog_view} = 1;
568    $params->{refreshed} = $app->param('refreshed');
569    $params->{published} = $app->param('published');
570
571    # determine list of system template types:
572    my $scope;
573    my $set;
574    if ( $blog ) {
575        $set   = $blog->template_set;
576        $scope = 'system';
577    }
578    else {
579        $scope = 'global:system';
580    }
581    my @tmpl_path = ( $set && ($set ne 'mt_blog')) ? ("template_sets", $set, 'templates', $scope) : ("default_templates", $scope);
582    my $sys_tmpl = MT->registry(@tmpl_path) || {};
583
584    my @tmpl_loop;
585    my %types;
586    if ($template_type ne 'backup') {
587        if ($blog) {
588            # blog template listings
589            %types = ( 
590                'index' => {
591                    label => $app->translate("Index Templates"),
592                    type => 'index',
593                    order => 100,
594                },
595                'archive' => {
596                    label => $app->translate("Archive Templates"),
597                    type => ['archive', 'individual', 'page', 'category'],
598                    order => 200,
599                },
600                'module' => {
601                    label => $app->translate("Template Modules"),
602                    type => 'custom',
603                    order => 300,
604                },
605                'system' => {
606                    label => $app->translate("System Templates"),
607                    type => [ keys %$sys_tmpl ],
608                    order => 400,
609                },
610            );
611        } else {
612            # global template listings
613            %types = ( 
614                'module' => {
615                    label => $app->translate("Template Modules"),
616                    type => 'custom',
617                    order => 100,
618                },
619                'email' => {
620                    label => $app->translate("Email Templates"),
621                    type => 'email',
622                    order => 200,
623                },
624                'system' => {
625                    label => $app->translate("System Templates"),
626                    type => [ keys %$sys_tmpl ],
627                    order => 300,
628                },
629            );
630        }
631    } else {
632        # global template listings
633        %types = ( 
634            'backup' => {
635                label => $app->translate("Template Backups"),
636                type => 'backup',
637                order => 100,
638            },
639        );
640    }
641    my @types = sort { $types{$a}->{order} <=> $types{$b}->{order} } keys %types;
642    if ($template_type) {
643        @types = ( $template_type );
644    }
645    foreach my $tmpl_type (@types) {
646        $terms->{type} = $types{$tmpl_type}->{type};
647        my $tmpl_param = $app->listing(
648            {
649                type     => 'template',
650                terms    => $terms,
651                args     => $args,
652                no_limit => 1,
653                no_html  => 1,
654                code     => $hasher,
655            }
656        );
657
658        my $template_type_label = $types{$tmpl_type}->{label};
659        $tmpl_param->{template_type} = $tmpl_type;
660        $tmpl_param->{template_type_label} = $template_type_label;
661        push @tmpl_loop, $tmpl_param;
662    }
663
664    $params->{template_type_loop} = \@tmpl_loop;
665    $params->{screen_id} = "list-template";
666
667    return $app->load_tmpl('list_template.tmpl', $params);
668}
669
670sub preview {
671    my $app         = shift;
672    my $q           = $app->param;
673    my $blog_id     = $q->param('blog_id');
674    my $blog        = $app->blog;
675    my $id          = $q->param('id');
676    my $tmpl;
677    my $user_id = $app->user->id;
678
679    # We can only do previews on blog templates. Have to publish
680    # the preview file somewhere!
681    return $app->errtrans("Invalid request.") unless $blog;
682
683    require MT::Template;
684    if ($id) {
685        $tmpl = MT::Template->load( { id => $id, blog_id => $blog_id } )
686            or return $app->errtrans( "Invalid request." );
687    }
688    else {
689        $tmpl = MT::Template->new;
690        $tmpl->id(-1);
691        $tmpl->blog_id($blog_id);
692    }
693
694    my $names = $tmpl->column_names;
695    my %values = map { $_ => scalar $app->param($_) } @$names;
696    delete $values{'id'} unless $q->param('id');
697
698    ## Strip linefeed characters.
699    for my $col (qw( text )) {
700        $values{$col} =~ tr/\r//d if $values{$col};
701    }
702    $tmpl->set_values( \%values );
703
704    my $preview_basename = $app->preview_object_basename;
705
706    my $type = $tmpl->type;
707    my $preview_tmpl = $tmpl;
708    my $archive_file;
709    my $archive_url;
710    my %param;
711    my $blog_path = $blog->site_path;
712    my $blog_url = $blog->site_url;
713
714    if (($type eq 'custom') || ($type eq 'widget')) {
715        # determine 'host' template
716        $preview_tmpl = MT::Template->load({ blog_id => $blog_id, identifier => 'main_index' });
717        if (!$preview_tmpl) {
718            return $app->errtrans("Can't locate host template to preview module/widget.");
719        }
720        my $req = $app->request;
721        # stash this module so that it is selected through a
722        # MTInclude tag instead of the one in the database:
723        my $tmpl_name = $tmpl->name;
724        $tmpl_name =~ s/^Widget: // if $type eq 'widget';
725        my $stash_id = 'template_' . $type . '::' . $blog_id . '::' . $tmpl_name;
726        $req->stash($stash_id, [ $tmpl, $tmpl->tokens ]);
727    } elsif (($type eq 'individual') || ($type eq 'page')) {
728        my $ctx = $preview_tmpl->context;
729        my $entry_type = $type eq 'individual' ? 'entry' : 'page';
730        my $entry_class = $app->model($entry_type);
731        my $obj = $entry_class->load({
732            blog_id => $blog_id,
733            status => MT::Entry::RELEASE()
734        }, {
735            limit => 1,
736            direction => 'descend',
737            'sort' => 'authored_on'
738        });
739        unless ( $obj ) {
740            # create a dummy object
741            $obj = $entry_class->new;
742            $obj->blog_id($blog_id);
743            $obj->id(-1);
744            $obj->author_id( $app->user->id );
745            $obj->authored_on( $blog->current_timestamp );
746            $obj->status( MT::Entry::RELEASE() );
747            $obj->basename( $preview_basename );
748            $obj->title($app->translate("Lorem ipsum"));
749            $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.});
750            $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;
751
752            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.
753
754            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.});
755            $obj->keywords("sample, entry, preview");
756            $obj->tags(qw( lorem ipsum sample preview ));
757        }
758        $ctx->stash('entry', $obj);
759        $ctx->{current_archive_type} = $type eq 'individual' ? 'Individual' : 'Page';
760        if (($type eq 'individual') && $blog->archive_path) {
761            $blog_path = $blog->archive_path;
762            $blog_url = $blog->archive_url;
763        }
764        $archive_file = File::Spec->catfile( $blog_path, $obj->archive_file );
765        $archive_url = $obj->archive_url;
766    } elsif ($type eq 'archive') {
767        # some variety of archive template
768    } elsif ($type eq 'index') {
769    } else {
770        # for now, only index templates can be previewed
771        return $app->errtrans("Invalid request.");
772    }
773
774    my $orig_file;
775    my $path;
776
777    # Default case; works for index templates (other template types should
778    # have defined $archive_file by now).
779    $archive_file = File::Spec->catfile( $blog_path, $preview_tmpl->outfile )
780        unless defined $archive_file;
781
782    ( $orig_file, $path ) = File::Basename::fileparse( $archive_file );
783
784    $archive_url = MT::Util::caturl( $blog_url, $orig_file )
785        unless defined $archive_url;
786
787    my $file_ext;
788    require File::Basename;
789    $file_ext = $archive_file;
790    if ($file_ext =~ m/\.[a-z]+$/) {
791        $file_ext =~ s!.+\.!.!;
792    } else {
793        $file_ext = '';
794    }
795    $archive_file = File::Spec->catfile( $path, $preview_basename . $file_ext );
796
797    my @data;
798    $app->run_callbacks( 'cms_pre_preview.template', $app, $preview_tmpl, \@data );
799
800    my $has_hires = eval 'require Time::HiRes; 1' ? 1 : 0;
801    my $start_time = $has_hires ? Time::HiRes::time() : time;
802
803    my $ctx = $preview_tmpl->context;
804    my $html = $preview_tmpl->output;
805
806    $param{build_time} = $has_hires ? sprintf("%.3f", Time::HiRes::time() - $start_time ) : "~" . ( time - $start_time );
807
808    unless ( defined($html) ) {
809        return $app->error( $app->translate( "Publish error: [_1]",
810            MT::Util::encode_html( $preview_tmpl->errstr ) ) );
811    }
812
813    # If MT is configured to do 'local' previews, convert all
814    # the normal blog URLs into the domain used by MT itself (ie,
815    # blog is published to www.example.com, which is a different
816    # server from where MT runs, mt.example.com; previews therefore
817    # should occur locally, so replace all http://www.example.com/
818    # with http://mt.example.com/).
819    my ($old_url, $new_url);
820    if ($app->config('LocalPreviews')) {
821        $old_url = $blog_url;
822        $old_url =~ s!^(https?://[^/]+?/)(.*)?!$1!;
823        $new_url = $app->base . '/';
824        $html =~ s!\Q$old_url\E!$new_url!g;
825    }
826
827    my $fmgr = $blog->file_mgr;
828
829    ## Determine if we need to build directory structure,
830    ## and build it if we do. DirUmask determines
831    ## directory permissions.
832    require File::Basename;
833    $path =~ s!/$!!
834      unless $path eq '/';    ## OS X doesn't like / at the end in mkdir().
835    unless ( $fmgr->exists($path) ) {
836        $fmgr->mkpath($path);
837    }
838
839    if ( $fmgr->exists($path) && $fmgr->can_write($path) ) {
840        $param{preview_file} = $preview_basename;
841        my $preview_url = $archive_url;
842        $preview_url =~ s! / \Q$orig_file\E ( /? ) $!/$preview_basename$file_ext$1!x;
843
844        # We also have to translate the URL used for the
845        # published file to be on the MT app domain.
846        if (defined $new_url) {
847            $preview_url =~ s!^\Q$old_url\E!$new_url!;
848        }
849
850        $param{preview_url}  = $preview_url;
851
852        $fmgr->put_data( $html, $archive_file );
853
854        # we have to make a record of this preview just in case it
855        # isn't cleaned up by re-editing, saving or cancelling on
856        # by the user.
857        require MT::Session;
858        my $sess_obj = MT::Session->get_by_key(
859            {
860                id   => $preview_basename,
861                kind => 'TF',                # TF = Temporary File
862                name => $archive_file,
863            }
864        );
865        $sess_obj->start(time);
866        $sess_obj->save;
867    }
868    else {
869        return $app->error( $app->translate(
870            "Unable to create preview file in this location: [_1]", $path ) );
871    }
872
873    $param{id} = $id if $id;
874    $param{new_object} = $param{id} ? 0 : 1;
875    $param{name} = $tmpl->name;
876    my $cols = $tmpl->column_names;
877    for my $col (@$cols) {
878        push @data,
879          {
880            data_name  => $col,
881            data_value => scalar $q->param($col)
882          };
883    }
884    $param{template_loop} = \@data;
885    $param{object_type}  = $type;
886    return $app->load_tmpl( 'preview_template_strip.tmpl', \%param );
887}
888
889sub reset_blog_templates {
890    my $app   = shift;
891    my $q     = $app->param;
892    my $perms = $app->permissions
893      or return $app->error( $app->translate("No permissions") );
894    return $app->error( $app->translate("Permission denied.") )
895      unless $perms->can_edit_templates;
896    $app->validate_magic() or return;
897    my $blog = MT::Blog->load( $perms->blog_id );
898    require MT::Template;
899    my @tmpl = MT::Template->load( { blog_id => $blog->id } );
900
901    for my $tmpl (@tmpl) {
902        $tmpl->remove or return $app->error( $tmpl->errstr );
903    }
904    my $set = $blog ? $blog->template_set : undef;
905    require MT::DefaultTemplates;
906    my $tmpl_list = MT::DefaultTemplates->templates($set) || [];
907    my @arch_tmpl;
908    for my $val (@$tmpl_list) {
909        $val->{name} = $app->translate( $val->{name} );
910        $val->{text} = $app->translate_templatized( $val->{text} );
911        my $tmpl = MT::Template->new;
912        $tmpl->set_values($val);
913        $tmpl->build_dynamic(0);
914        $tmpl->blog_id( $blog->id );
915        $tmpl->save
916          or return $app->error(
917            $app->translate(
918                "Populating blog with default templates failed: [_1]",
919                $tmpl->errstr
920            )
921          );
922
923        # FIXME: enumeration of types
924        if (   $val->{type} eq 'archive'
925            || $val->{type} eq 'category'
926            || $val->{type} eq 'page'
927            || $val->{type} eq 'individual' )
928        {
929            push @arch_tmpl, $tmpl;
930        }
931    }
932
933    ## Set up mappings from new templates to archive types.
934    for my $tmpl (@arch_tmpl) {
935        my (@at);
936
937        # FIXME: enumeration of types
938        if ( $tmpl->type eq 'archive' ) {
939            @at = qw( Daily Weekly Monthly Category );
940        }
941        elsif ( $tmpl->type eq 'page' ) {
942            @at = qw( Page );
943        }
944        elsif ( $tmpl->type eq 'individual' ) {
945            @at = qw( Individual );
946        }
947        require MT::TemplateMap;
948        for my $at (@at) {
949            my $map = MT::TemplateMap->new;
950            $map->archive_type($at);
951            $map->is_preferred(1);
952            $map->template_id( $tmpl->id );
953            $map->blog_id( $tmpl->blog_id );
954            $map->save
955              or return $app->error(
956                $app->translate(
957                    "Setting up mappings failed: [_1]",
958                    $map->errstr
959                )
960              );
961        }
962    }
963    $app->redirect(
964        $app->uri(
965            'mode' => 'list',
966            args =>
967              { '_type' => 'template', blog_id => $blog->id, 'reset' => 1 }
968        )
969    );
970}
971
972sub _generate_map_table {
973    my $app = shift;
974    my ( $blog_id, $template_id ) = @_;
975
976    require MT::Template;
977    require MT::Blog;
978    my $blog     = MT::Blog->load($blog_id);
979    my $template = MT::Template->load($template_id);
980    my $tmpl     = $app->load_tmpl('include/archive_maps.tmpl');
981    my $maps     = _populate_archive_loop( $app, $blog, $template );
982    $tmpl->param( { object_type => 'templatemap' } );
983    $tmpl->param( { object_loop => $maps } ) if @$maps;
984    my $html = $tmpl->output();
985
986    if ( $html =~ m/<__trans / ) {
987        $html = $app->translate_templatized($html);
988    }
989    $html;
990}
991
992sub _populate_archive_loop {
993    my $app = shift;
994    my ( $blog, $obj ) = @_;
995
996    my $index = $app->config('IndexBasename');
997    my $ext = $blog->file_extension || '';
998    $ext = '.' . $ext if $ext ne '';
999
1000    require MT::TemplateMap;
1001    my @tmpl_maps = MT::TemplateMap->load( { template_id => $obj->id } );
1002    my @maps;
1003    my %types;
1004    foreach my $map_obj (@tmpl_maps) {
1005        my $map = {};
1006        $map->{map_id}           = $map_obj->id;
1007        $map->{map_is_preferred} = $map_obj->is_preferred;
1008        # publish options
1009        $map->{map_build_type} = $map_obj->build_type;
1010        $map->{ 'map_build_type_' . ( $map_obj->build_type || 0 ) } = 1;
1011        my ( $period, $interval ) = _get_schedule( $map_obj->build_interval );
1012        $map->{ 'map_schedule_period_' . $period } = 1
1013            if defined $period;
1014        $map->{map_schedule_interval} = $interval
1015            if defined $interval;
1016
1017        my $at = $map->{archive_type} = $map_obj->archive_type;
1018        $types{$at}++;
1019        $map->{ 'archive_type_preferred_' . $blog->archive_type_preferred } = 1
1020          if $blog->archive_type_preferred;
1021        $map->{file_template} = $map_obj->file_template
1022          if $map_obj->file_template;
1023
1024        my $archiver = $app->publisher->archiver($at);
1025        next unless $archiver;
1026        $map->{archive_label} = $archiver->archive_label;
1027        my $tmpls     = $archiver->default_archive_templates;
1028        my $tmpl_loop = [];
1029        foreach (@$tmpls) {
1030            my $name = $_->{label};
1031            $name =~ s/\.html$/$ext/;
1032            $name =~ s/index$ext$/$index$ext/;
1033            push @$tmpl_loop,
1034              {
1035                name    => $name,
1036                value   => $_->{template},
1037                default => ( $_->{default} || 0 )
1038              };
1039        }
1040
1041        my $custom = 1;
1042
1043        foreach (@$tmpl_loop) {
1044            if (   ( !$map->{file_template} && $_->{default} )
1045                || ( $map->{file_template} eq $_->{value} ) )
1046            {
1047                $_->{selected}        = 1;
1048                $custom               = 0;
1049                $map->{file_template} = $_->{value}
1050                  if !$map->{file_template};
1051            }
1052        }
1053        if ($custom) {
1054            unshift @$tmpl_loop,
1055              {
1056                name     => $map->{file_template},
1057                value    => $map->{file_template},
1058                selected => 1,
1059              };
1060        }
1061
1062        $map->{archive_tmpl_loop} = $tmpl_loop;
1063        if (
1064            1 < MT::TemplateMap->count(
1065                { archive_type => $at, blog_id => $obj->blog_id }
1066            )
1067          )
1068        {
1069            $map->{has_multiple_archives} = 1;
1070        }
1071
1072        push @maps, $map;
1073    }
1074    @maps = sort { MT::App::CMS::archive_type_sorter( $a, $b ) } @maps;
1075    return \@maps;
1076}
1077
1078sub delete_map {
1079    my $app = shift;
1080    $app->validate_magic() or return;
1081    my $perms = $app->{perms}
1082      or return $app->error( $app->translate("No permissions") );
1083    my $q  = $app->param;
1084    my $id = $q->param('id');
1085
1086    require MT::TemplateMap;
1087    MT::TemplateMap->remove( { id => $id } );
1088    my $html =
1089      _generate_map_table( $app, $q->param('blog_id'),
1090        $q->param('template_id') );
1091    $app->{no_print_body} = 1;
1092    $app->send_http_header("text/plain");
1093    $app->print($html);
1094}
1095
1096sub add_map {
1097    my $app = shift;
1098    $app->validate_magic() or return;
1099    my $perms = $app->{perms}
1100      or return $app->error( $app->translate("No permissions") );
1101
1102    my $q = $app->param;
1103
1104    require MT::TemplateMap;
1105    my $blog_id = $q->param('blog_id');
1106    my $at      = $q->param('new_archive_type');
1107    my $count   = MT::TemplateMap->count(
1108        {
1109            blog_id      => $blog_id,
1110            archive_type => $at
1111        }
1112    );
1113    my $map = MT::TemplateMap->new;
1114    $map->is_preferred( $count ? 0 : 1 );
1115    $map->template_id( scalar $q->param('template_id') );
1116    $map->blog_id($blog_id);
1117    $map->archive_type($at);
1118    $map->save
1119      or return $app->error(
1120        $app->translate( "Saving map failed: [_1]", $map->errstr ) );
1121    my $html =
1122      _generate_map_table( $app, $blog_id, scalar $q->param('template_id') );
1123    $app->rebuild(
1124        BlogID      => $blog_id,
1125        ArchiveType => $at,
1126        TemplateMap => $map,
1127        TemplateID  => scalar $q->param('template_id'),
1128        NoStatic    => 1
1129    ) or return $app->publish_error();
1130    $app->{no_print_body} = 1;
1131    $app->send_http_header("text/plain");
1132    $app->print($html);
1133}
1134
1135sub can_view {
1136    my ( $eh, $app, $id ) = @_;
1137    my $perms = $app->permissions;
1138    return !$id || ($perms && $perms->can_edit_templates) || (!$app->blog && $app->user->can_edit_templates);
1139}
1140
1141sub can_save {
1142    my ( $eh, $app, $id ) = @_;
1143    my $perms = $app->permissions;
1144    return ($perms && $perms->can_edit_templates) || (!$perms && $app->user->can_edit_templates);
1145}
1146
1147sub can_delete {
1148    my ( $eh, $app, $obj ) = @_;
1149    return 1 if $app->user->is_superuser();
1150    my $perms = $app->permissions;
1151    return ($perms && $perms->can_edit_templates) || (!$perms && $app->user->can_edit_templates);
1152}
1153
1154sub pre_save {
1155    my $eh = shift;
1156    my ( $app, $obj ) = @_;
1157
1158    $obj->rebuild_me(0) 
1159      if $app->param('current_rebuild_me')
1160      && !$app->param('rebuild_me');
1161    $obj->build_dynamic(0)
1162      if $app->param('current_build_dynamic')
1163      && !$app->param('build_dynamic');
1164
1165    ## Strip linefeed characters.
1166    ( my $text = $obj->text ) =~ tr/\r//d;
1167
1168    if ($text =~ m/<(MT|_)_trans/i) {
1169        $text = $app->translate_templatized($text);
1170    }
1171
1172    $obj->text($text);
1173
1174    # update text heights if necessary
1175    if ( my $perms = $app->permissions ) {
1176        my $prefs = $perms->template_prefs || '';
1177        my $text_height = $app->param('text_height');
1178        if ( defined $text_height ) {
1179            my ($pref_text_height) = $prefs =~ m/\btext:(\d+)\b/;
1180            $pref_text_height ||= 0;
1181            if ( $text_height != $pref_text_height ) {
1182                if ( $prefs =~ m/\btext\b/ ) {
1183                    $prefs =~ s/\btext(:\d+)\b/text:$text_height/;
1184                }
1185                else {
1186                    $prefs = 'text:' . $text_height . ',' . $prefs;
1187                }
1188            }
1189        }
1190
1191        if ( $prefs ne ( $perms->template_prefs || '' ) ) {
1192            $perms->template_prefs($prefs);
1193            $perms->save;
1194        }
1195    }
1196
1197    # module caching
1198    $obj->include_with_ssi( $app->param('include_with_ssi') ? 1 : 0 );
1199    $obj->use_cache( $app->param('cache_enabled')           ? 1 : 0 );
1200    my $cache_expire_type = $app->param('cache_expire_type');
1201    $obj->cache_expire_type($cache_expire_type);
1202    my $period   = $app->param('cache_expire_period');
1203    my $interval = $app->param('cache_expire_interval');
1204    my $sec      = _get_interval( $period, $interval );
1205    $obj->cache_expire_interval($sec);
1206    my $q = $app->param;
1207    my @events;
1208
1209    foreach my $name ( $q->param('cache_expire_event') ) {
1210        push @events, $name;
1211    }
1212    $obj->cache_expire_event( join ',', @events );
1213    if ( $cache_expire_type == 1 ) {
1214        return $eh->error(
1215            $app->translate("You should not be able to enter 0 as the time.") )
1216          if $interval == 0;
1217    }
1218    elsif ( $cache_expire_type == 2 ) {
1219        return $eh->error(
1220            $app->translate("You must select at least one event checkbox.") )
1221          if !@events;
1222    }
1223
1224    require MT::PublishOption;
1225    my $build_type = $app->param('build_type');
1226    if ( $build_type == MT::PublishOption::SCHEDULED() ) {
1227        my $period   = $app->param('schedule_period');
1228        my $interval = $app->param('schedule_interval');
1229        my $sec      = _get_interval( $period, $interval );
1230        $obj->build_interval($sec);
1231    }
1232    my $rebuild_me = 1;
1233    if (   $build_type == MT::PublishOption::DISABLED()
1234        || $build_type == MT::PublishOption::MANUALLY() )
1235    {
1236        $rebuild_me = 0;
1237    }
1238    $obj->rebuild_me($rebuild_me);
1239    $obj->build_dynamic( $build_type == MT::PublishOption::DYNAMIC() ? 1 : 0 );
1240
1241    1;
1242}
1243
1244sub post_save {
1245    my $eh = shift;
1246    my ( $app, $obj, $original ) = @_;
1247
1248    my $sess_obj = $app->autosave_session_obj;
1249    $sess_obj->remove if $sess_obj;
1250
1251    require MT::TemplateMap;
1252    my $q = $app->param;
1253    my @p = $q->param;
1254    for my $p (@p) {
1255        if ( $p =~ /^archive_tmpl_preferred_(\w+)_(\d+)$/ ) {
1256            my $at     = $1;
1257            my $map_id = $2;
1258            my $map    = MT::TemplateMap->load($map_id);
1259            $map->prefer( $q->param($p) );    # prefer method saves in itself
1260        }
1261        elsif ( $p =~ /^archive_file_tmpl_(\d+)$/ ) {
1262            my $map_id = $1;
1263            my $map    = MT::TemplateMap->load($map_id);
1264            $map->file_template( $q->param($p) );
1265            $map->save;
1266        }
1267        elsif ( $p =~ /^map_build_type_(\d+)$/ ) {
1268            my $map_id     = $1;
1269            my $map        = MT::TemplateMap->load($map_id);
1270            my $build_type = $q->param($p);
1271            require MT::PublishOption;
1272            $map->build_type($build_type);
1273            if ( $build_type == MT::PublishOption::SCHEDULED() ) {
1274                my $period   = $q->param( 'map_schedule_period_' . $map_id );
1275                my $interval = $q->param( 'map_schedule_interval_' . $map_id );
1276                my $sec      = _get_interval( $period, $interval );
1277                $map->build_interval($sec);
1278            }
1279            $map->save;
1280        }
1281    }
1282
1283    if ( !$original->id ) {
1284        $app->log(
1285            {
1286                message => $app->translate(
1287                    "Template '[_1]' (ID:[_2]) created by '[_3]'",
1288                    $obj->name, $obj->id, $app->user->name
1289                ),
1290                level    => MT::Log::INFO(),
1291                class    => 'template',
1292                category => 'new',
1293            }
1294        );
1295    }
1296
1297    if ( $obj->build_dynamic ) {
1298        if ( $obj->type eq 'index' ) {
1299            $app->rebuild_indexes(
1300                BlogID   => $obj->blog_id,
1301                Template => $obj,
1302                NoStatic => 1,
1303            ) or return $app->publish_error();    # XXXX
1304        }
1305        else {
1306            $app->rebuild(
1307                BlogID     => $obj->blog_id,
1308                TemplateID => $obj->id,
1309                NoStatic   => 1,
1310            ) or return $app->publish_error();
1311        }
1312    }
1313    1;
1314}
1315
1316sub post_delete {
1317    my ( $eh, $app, $obj ) = @_;
1318
1319    $app->log(
1320        {
1321            message => $app->translate(
1322                "Template '[_1]' (ID:[_2]) deleted by '[_3]'",
1323                $obj->name, $obj->id, $app->user->name
1324            ),
1325            level    => MT::Log::INFO(),
1326            class    => 'system',
1327            category => 'delete'
1328        }
1329    );
1330}
1331
1332sub build_template_table {
1333    my $app = shift;
1334    my (%args) = @_;
1335
1336    my $perms     = $app->permissions;
1337    my $list_pref = $app->list_pref('template');
1338    my $limit     = $args{limit};
1339    my $param     = $args{param} || {};
1340    my $iter;
1341    if ( $args{load_args} ) {
1342        my $class = $app->model('template');
1343        $iter = $class->load_iter( @{ $args{load_args} } );
1344    }
1345    elsif ( $args{iter} ) {
1346        $iter = $args{iter};
1347    }
1348    elsif ( $args{items} ) {
1349        $iter = sub { pop @{ $args{items} } };
1350        $limit = scalar @{ $args{items} };
1351    }
1352    return [] unless $iter;
1353
1354    my @data;
1355    my $i;
1356    my %blogs;
1357    while ( my $tmpl = $iter->() ) {
1358        my $blog = $blogs{ $tmpl->blog_id } ||=
1359          MT::Blog->load( $tmpl->blog_id );
1360        my $row = $tmpl->column_values;
1361        $row->{name} = '' if !defined $row->{name};
1362        $row->{name} =~ s/^\s+|\s+$//g;
1363        $row->{name} = "(" . $app->translate("No Name") . ")"
1364          if $row->{name} eq '';
1365        my $published_url = $tmpl->published_url;
1366        $row->{published_url} = $published_url if $published_url;
1367
1368        # FIXME: enumeration of types
1369        $row->{can_delete} = 1
1370          if $tmpl->type =~ m/(custom|index|archive|page|individual|category|widget)/;
1371        if ($blog) {
1372            $row->{weblog_name} = $blog->name;
1373        }
1374        elsif ($tmpl->blog_id) {
1375            $row->{weblog_name} = '* ' . $app->translate('Orphaned') . ' *';
1376        }
1377        else {
1378            $row->{weblog_name} = '* ' . $app->translate('Global Templates') . ' *';
1379        }
1380        $row->{object} = $tmpl;
1381        push @data, $row;
1382        last if defined($limit) && (@data > $limit);
1383    }
1384    return [] unless @data;
1385
1386    $param->{template_table}[0]              = {%$list_pref};
1387    $param->{template_table}[0]{object_loop} = \@data;
1388    $param->{template_table}[0]{object_type} = 'template';
1389    $app->load_list_actions( 'template', $param );
1390    $param->{object_loop} = \@data;
1391    \@data;
1392}
1393
1394sub dialog_refresh_templates {
1395    my $app = shift;
1396    $app->validate_magic or return;
1397
1398    # permission check
1399    my $perms = $app->permissions;
1400    return $app->errtrans("Permission denied.")
1401        unless $app->user->is_superuser ||
1402            $perms->can_administer_blog ||
1403            $perms->can_edit_templates;
1404
1405    my $param = {};
1406    my $blog = $app->blog;
1407    $param->{return_args} = $app->param('return_args');
1408
1409    if ($blog) {
1410        $param->{blog_id} = $blog->id;
1411
1412        my $sets = $app->registry("template_sets");
1413        $sets->{$_}{key} = $_ for keys %$sets;
1414        $sets = $app->filter_conditional_list([ values %$sets ]);
1415
1416        no warnings; # some sets may not define an order
1417        @$sets = sort { $a->{order} <=> $b->{order} } @$sets;
1418        $param->{'template_set_loop'} = $sets;
1419
1420        my $existing_set = $blog->template_set || 'mt_blog';
1421        foreach (@$sets) {
1422            if ($_->{key} eq $existing_set) {
1423                $_->{selected} = 1;
1424            }
1425        }
1426        $param->{'template_set_index'} = $#$sets;
1427        $param->{'template_set_count'} = scalar @$sets;
1428
1429        $param->{template_sets} = $sets;
1430        $param->{screen_id} = "refresh-templates-dialog";
1431    }
1432
1433    # load template sets
1434    $app->build_page('dialog/refresh_templates.tmpl',
1435        $param);
1436}
1437
1438sub refresh_all_templates {
1439    my ($app) = @_;
1440
1441    my $backup = 0;
1442    if ($app->param('backup')) {
1443        # refresh templates dialog uses a 'backup' field
1444        $backup = 1;
1445    }
1446
1447    my $template_set = $app->param('template_set');
1448    my $refresh_type = $app->param('refresh_type') || 'refresh';
1449
1450    my $t = time;
1451
1452    my @id;
1453    if ($app->param('blog_id')) {
1454        @id = ( scalar $app->param('blog_id') );
1455    }
1456    else {
1457        @id = $app->param('id');
1458        if (! @id) {
1459            # refresh global templates
1460            @id = ( 0 );
1461        }
1462    }
1463
1464    require MT::Template;
1465    require MT::DefaultTemplates;
1466    require MT::Blog;
1467    require MT::Permission;
1468    require MT::Util;
1469
1470    foreach my $blog_id (@id) {
1471        my $blog;
1472        if ($blog_id) {
1473            $blog = MT::Blog->load($blog_id);
1474            next unless $blog;
1475        }
1476        if ( !$app->user->is_superuser() ) {
1477            my $perms = MT::Permission->load(
1478                { blog_id => $blog_id, author_id => $app->user->id } );
1479            if (
1480                !$perms
1481                || (   !$perms->can_edit_templates()
1482                    && !$perms->can_administer_blog() )
1483              )
1484            {
1485                next;
1486            }
1487        }
1488
1489        my $tmpl_list;
1490        if ($blog_id) {
1491
1492            if ($refresh_type eq 'clean') {
1493                # the user wants to back up all templates and
1494                # install the new ones
1495
1496                my @ts = MT::Util::offset_time_list( $t, $blog_id );
1497                my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d",
1498                    $ts[5] + 1900, $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1499
1500                my $tmpl_iter = MT::Template->load_iter({
1501                    blog_id => $blog_id,
1502                    type => { not => 'backup' },
1503                });
1504
1505                while (my $tmpl = $tmpl_iter->()) {
1506                    if ($backup) {
1507                        # zap all template maps
1508                        require MT::TemplateMap;
1509                        MT::TemplateMap->remove({
1510                            template_id => $tmpl->id,
1511                        });
1512                        $tmpl->type('backup');
1513                        $tmpl->name(
1514                            $tmpl->name . ' (Backup from ' . $ts . ')' );
1515                        $tmpl->identifier(undef);
1516                        $tmpl->rebuild_me(0);
1517                        $tmpl->linked_file(undef);
1518                        $tmpl->outfile('');
1519                        $tmpl->save;
1520                    } else {
1521                        $tmpl->remove;
1522                    }
1523                }
1524
1525                # This also creates our template mappings
1526                $blog->create_default_templates( $template_set ||
1527                    $blog->template_set || 'mt_blog' );
1528
1529                if ($template_set) {
1530                    $blog->template_set( $template_set );
1531                    $blog->save;
1532                    $app->run_callbacks( 'blog_template_set_change', { blog => $blog } );
1533                }
1534
1535                next;
1536            }
1537
1538            $tmpl_list = MT::DefaultTemplates->templates($template_set || $blog->template_set) || MT::DefaultTemplates->templates();
1539        }
1540        else {
1541            $tmpl_list = MT::DefaultTemplates->templates();
1542        }
1543
1544        foreach my $val (@$tmpl_list) {
1545            if ($blog_id) {
1546                # when refreshing blog templates,
1547                # skip over global templates which
1548                # specify a blog_id of 0...
1549                next if $val->{global};
1550            }
1551            else {
1552                next unless exists $val->{global};
1553            }
1554
1555            if ( !$val->{orig_name} ) {
1556                $val->{orig_name} = $val->{name};
1557                $val->{name}      = $app->translate( $val->{name} );
1558                $val->{text}      = $app->translate_templatized( $val->{text} );
1559            }
1560
1561            my $orig_name = $val->{orig_name};
1562
1563            my @ts = MT::Util::offset_time_list( $t, ( $blog_id ? $blog_id : undef ) );
1564            my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d", $ts[5] + 1900,
1565              $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1566
1567            my $terms = {};
1568            $terms->{blog_id} = $blog_id;
1569            $terms->{type} = $val->{type};
1570            if ( $val->{type} =~
1571                m/^(archive|individual|page|category|index|custom|widget)$/ )
1572            {
1573                $terms->{name} = $val->{name};
1574            }
1575            else {
1576                $terms->{identifier} = $val->{identifier};
1577            }
1578
1579            # this should only return 1 template; we're searching
1580            # within a given blog for a specific type of template (for
1581            # "system" templates; or for a type + name, which should be
1582            # unique for that blog.
1583            my $tmpl = MT::Template->load($terms);
1584            if ($tmpl && $backup) {
1585
1586                # check for default template text...
1587                # if it is a default template, then outright replace it
1588                my $text = $tmpl->text;
1589                $text =~ s/\s+//g;
1590
1591                my $def_text = $val->{text};
1592                $def_text =~ s/\s+//g;
1593
1594                # if it has been customized, back it up to a new tmpl record
1595                if ($def_text ne $text) {
1596                    my $backup = $tmpl->clone;
1597                    delete $backup->{column_values}
1598                      ->{id};    # make sure we don't overwrite original
1599                    delete $backup->{changed_cols}->{id};
1600                    $backup->name(
1601                        $backup->name . $app->translate( ' (Backup from [_1])', $ts ) );
1602                    $backup->type('backup');
1603                    # if ( $backup->type !~
1604                    #         m/^(archive|individual|page|category|index|custom|widget)$/ )
1605                    # {
1606                    #     $backup->type('custom')
1607                    #       ;      # system templates can't be created
1608                    # }
1609                    $backup->outfile('');
1610                    $backup->linked_file( $tmpl->linked_file );
1611                    $backup->identifier(undef);
1612                    $backup->rebuild_me(0);
1613                    $backup->build_dynamic(0);
1614                    $backup->save;
1615                }
1616            }
1617            if ($tmpl) {
1618                # we found that the previous template had not been
1619                # altered, so replace it with new default template...
1620                $tmpl->text( $val->{text} );
1621                $tmpl->identifier( $val->{identifier} );
1622                $tmpl->type( $val->{type} )
1623                  ; # fixes mismatch of types for cases like "archive" => "individual"
1624                $tmpl->linked_file('');
1625                $tmpl->save;
1626            }
1627            else {
1628                # create this one...
1629                my $tmpl = new MT::Template;
1630                $tmpl->build_dynamic(0);
1631                $tmpl->set_values(
1632                    {
1633                        text       => $val->{text},
1634                        name       => $val->{name},
1635                        type       => $val->{type},
1636                        identifier => $val->{identifier},
1637                        outfile    => $val->{outfile},
1638                        rebuild_me => $val->{rebuild_me}
1639                    }
1640                );
1641                $tmpl->blog_id($blog_id);
1642                $tmpl->save
1643                  or return $app->error(
1644                        $app->translate("Error creating new template: ")
1645                      . $tmpl->errstr );
1646            }
1647        }
1648    }
1649
1650    $app->add_return_arg( 'refreshed' => 1 );
1651    $app->call_return;
1652}
1653
1654sub refresh_individual_templates {
1655    my ($app) = @_;
1656
1657    require MT::Util;
1658
1659    my $user = $app->user;
1660    my $perms = $app->permissions;
1661    return $app->error(
1662        $app->translate(
1663            "Permission denied.")
1664      )
1665      #TODO: system level-designer permission
1666      unless $user->is_superuser() || $user->can_edit_templates()
1667      || ( $perms
1668        && ( $perms->can_edit_templates()
1669          || $perms->can_administer_blog ) );
1670
1671    my $set;
1672    if ( my $blog_id = $app->param('blog_id') ) {
1673        my $blog = $app->model('blog')->load($blog_id);
1674        $set = $blog->template_set()
1675            if $blog;
1676    }
1677
1678    require MT::DefaultTemplates;
1679    my $tmpl_list = MT::DefaultTemplates->templates($set) or return;
1680
1681    my $trnames    = {};
1682    my $tmpl_types = {};
1683    my $tmpl_ids   = {};
1684    my $tmpls      = {};
1685    foreach my $tmpl (@$tmpl_list) {
1686        $tmpl->{text} = $app->translate_templatized( $tmpl->{text} );
1687        $tmpl_ids->{ $tmpl->{identifier} } = $tmpl
1688            if $tmpl->{identifier};
1689        $trnames->{ $app->translate( $tmpl->{name} ) } = $tmpl->{name};
1690        if ( $tmpl->{type} !~ m/^(archive|individual|page|category|index|custom|widget)$/ )
1691        {
1692            $tmpl_types->{ $tmpl->{type} } = $tmpl;
1693        }
1694        else {
1695            $tmpls->{ $tmpl->{type} }{ $tmpl->{name} } = $tmpl;
1696        }
1697    }
1698
1699    my $t = time;
1700
1701    my @msg;
1702    my @id = $app->param('id');
1703    require MT::Template;
1704    foreach my $tmpl_id (@id) {
1705        my $tmpl = MT::Template->load($tmpl_id);
1706        next unless $tmpl;
1707        my $blog_id = $tmpl->blog_id;
1708
1709        # FIXME: permission check -- for this blog_id
1710
1711        my @ts = MT::Util::offset_time_list( $t, $blog_id );
1712        my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d", $ts[5] + 1900,
1713          $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1714
1715        my $orig_name = $trnames->{ $tmpl->name } || $tmpl->name;
1716        my $val = ( $tmpl->identifier ? $tmpl_ids->{ $tmpl->identifier() } : undef )
1717          || $tmpl_types->{ $tmpl->type() }
1718          || $tmpls->{ $tmpl->type() }{$orig_name};
1719        if ( !$val ) {
1720            push @msg,
1721              $app->translate(
1722"Skipping template '[_1]' since it appears to be a custom template.",
1723                $tmpl->name
1724              );
1725            next;
1726        }
1727
1728        my $text = $tmpl->text;
1729        $text =~ s/\s+//g;
1730
1731        my $def_text = $val->{text};
1732        $def_text =~ s/\s+//g;
1733
1734        if ($text ne $def_text) {
1735            # if it has been customized, back it up to a new tmpl record
1736            my $backup = $tmpl->clone;
1737            delete $backup->{column_values}
1738              ->{id};    # make sure we don't overwrite original
1739            delete $backup->{changed_cols}->{id};
1740            $backup->name( $backup->name . ' (Backup from ' . $ts . ')' );
1741            $backup->type('backup');
1742            $backup->outfile('');
1743            $backup->linked_file( $tmpl->linked_file );
1744            $backup->rebuild_me(0);
1745            $backup->build_dynamic(0);
1746            $backup->identifier(undef);
1747            $backup->save;
1748            push @msg,
1749              $app->translate(
1750    'Refreshing template <strong>[_3]</strong> with <a href="?__mode=view&amp;blog_id=[_1]&amp;_type=template&amp;id=[_2]">backup</a>',
1751                  $blog_id, $backup->id, $tmpl->name );
1752
1753            # we found that the previous template had not been
1754            # altered, so replace it with new default template...
1755            $tmpl->text( $val->{text} );
1756            $tmpl->identifier( $val->{identifier} );
1757            $tmpl->linked_file('');
1758            $tmpl->save;
1759        } else {
1760            push @msg, $app->translate("Skipping template '[_1]' since it has not been changed.", $tmpl->name);
1761        }
1762    }
1763    my @msg_loop;
1764    push @msg_loop, { message => $_ } foreach @msg;
1765
1766    $app->build_page( 'refresh_results.tmpl',
1767        { message_loop => \@msg_loop, return_url => $app->return_uri } );
1768}
1769
1770sub publish_index_templates {
1771    my $app = shift;
1772    $app->validate_magic or return;
1773
1774    # permission check
1775    my $perms = $app->permissions;
1776    return $app->errtrans("Permission denied.")
1777        unless $app->user->is_superuser ||
1778            $perms->can_administer_blog ||
1779            $perms->can_rebuild;
1780
1781    my $blog = $app->blog;
1782    my $templates = MT->model('template')->lookup_multi([ $app->param('id') ]);
1783    TEMPLATE: for my $tmpl (@$templates) {
1784        next TEMPLATE if !defined $tmpl;
1785        next TEMPLATE if $tmpl->blog_id != $blog->id;
1786        $app->rebuild_indexes(
1787            Blog     => $blog,
1788            Template => $tmpl,
1789        );
1790    }
1791
1792    $app->call_return( published => 1 );
1793}
1794
1795{
1796    my @period_options = (
1797        {
1798            name => 'minutes',
1799            expr => 60,
1800        },
1801        {
1802            name => 'hours',
1803            expr => 60 * 60,
1804        },
1805        {
1806            name => 'days',
1807            expr => 24 * 60 * 60,
1808        },
1809    );
1810
1811    sub _get_schedule {
1812        my ($sec) = @_;
1813        return unless defined $sec;
1814        my ( $period, $interval );
1815        for (@period_options) {
1816            last if $sec % $_->{expr};
1817            $period   = $_->{name};
1818            $interval = $sec / $_->{expr};
1819        }
1820        ( $period, $interval );
1821    }
1822
1823    sub _get_interval {
1824        my ( $period, $interval ) = @_;
1825        return unless defined $period;
1826        my $sec = 0;
1827        for (@period_options) {
1828            if ( $_->{name} eq $period ) {
1829                $sec = $interval * $_->{expr};
1830                last;
1831            }
1832        }
1833        $sec;
1834    }
1835}
1836
18371;
Note: See TracBrowser for help on using the browser.