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

Revision 1770, 65.8 kB (checked in by takayama, 20 months ago)

Resolved BugId:68759
* Implemented new copy template feature.

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