root/branches/release-34/lib/MT/CMS/Template.pm @ 1847

Revision 1847, 67.4 kB (checked in by bchoate, 20 months ago)

Show enabled archive types on template edit screen. BugId:79270

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