root/branches/release-36/lib/MT/CMS/Template.pm @ 2049

Revision 2049, 69.5 kB (checked in by auno, 19 months ago)

"cache_enable" valuable is removed from the page and cleaned up back-end codes. BugzID:79445

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