root/branches/release-32/lib/MT/CMS/Template.pm @ 1667

Revision 1667, 64.2 kB (checked in by bchoate, 20 months ago)

Don't preset caching options.

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