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

Revision 1775, 65.9 kB (checked in by bchoate, 20 months ago)

Fix for editing system-wide templates (can't assume blog is available). BugId:75819

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