root/branches/release-35/lib/MT/CMS/Template.pm @ 1959

Revision 1959, 69.1 kB (checked in by bchoate, 20 months ago)

Fixes for build_dynamic assignment. BugId:79379

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