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

Revision 1875, 68.1 kB (checked in by bchoate, 20 months ago)

Fixes to preserve return_args for template listing screen; display 'clear filter' link when viewing a specific template type.

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