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

Revision 1891, 68.2 kB (checked in by bchoate, 20 months ago)

Fixed string comparison operator.

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