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

Revision 1730, 64.3 kB (checked in by bsmith, 20 months ago)

bugzid:75101 - Add create link for listings in zero state

  • 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    $params->{screen_id} = "list-template";
663
664    return $app->load_tmpl('list_template.tmpl', $params);
665}
666
667sub preview {
668    my $app         = shift;
669    my $q           = $app->param;
670    my $blog_id     = $q->param('blog_id');
671    my $blog        = $app->blog;
672    my $id          = $q->param('id');
673    my $tmpl;
674    my $user_id = $app->user->id;
675
676    # We can only do previews on blog templates. Have to publish
677    # the preview file somewhere!
678    return $app->errtrans("Invalid request.") unless $blog;
679
680    require MT::Template;
681    if ($id) {
682        $tmpl = MT::Template->load( { id => $id, blog_id => $blog_id } )
683            or return $app->errtrans( "Invalid request." );
684    }
685    else {
686        $tmpl = MT::Template->new;
687        $tmpl->id(-1);
688        $tmpl->blog_id($blog_id);
689    }
690
691    my $names = $tmpl->column_names;
692    my %values = map { $_ => scalar $app->param($_) } @$names;
693    delete $values{'id'} unless $q->param('id');
694
695    ## Strip linefeed characters.
696    for my $col (qw( text )) {
697        $values{$col} =~ tr/\r//d if $values{$col};
698    }
699    $tmpl->set_values( \%values );
700
701    my $preview_basename = $app->preview_object_basename;
702
703    my $type = $tmpl->type;
704    my $preview_tmpl = $tmpl;
705    my $archive_file;
706    my $archive_url;
707    my %param;
708    my $blog_path = $blog->site_path;
709    my $blog_url = $blog->site_url;
710
711    if (($type eq 'custom') || ($type eq 'widget')) {
712        # determine 'host' template
713        $preview_tmpl = MT::Template->load({ blog_id => $blog_id, identifier => 'main_index' });
714        if (!$preview_tmpl) {
715            return $app->errtrans("Can't locate host template to preview module/widget.");
716        }
717        my $req = $app->request;
718        # stash this module so that it is selected through a
719        # MTInclude tag instead of the one in the database:
720        my $tmpl_name = $tmpl->name;
721        $tmpl_name =~ s/^Widget: // if $type eq 'widget';
722        my $stash_id = 'template_' . $type . '::' . $blog_id . '::' . $tmpl_name;
723        $req->stash($stash_id, [ $tmpl, $tmpl->tokens ]);
724    } elsif (($type eq 'individual') || ($type eq 'page')) {
725        my $ctx = $preview_tmpl->context;
726        my $entry_type = $type eq 'individual' ? 'entry' : 'page';
727        my $entry_class = $app->model($entry_type);
728        my $obj = $entry_class->load({
729            blog_id => $blog_id,
730            status => MT::Entry::RELEASE()
731        }, {
732            limit => 1,
733            direction => 'descend',
734            'sort' => 'authored_on'
735        });
736        unless ( $obj ) {
737            # create a dummy object
738            $obj = $entry_class->new;
739            $obj->blog_id($blog_id);
740            $obj->id(-1);
741            $obj->author_id( $app->user->id );
742            $obj->authored_on( $blog->current_timestamp );
743            $obj->status( MT::Entry::RELEASE() );
744            $obj->basename( $preview_basename );
745            $obj->title($app->translate("Lorem ipsum"));
746            $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.});
747            $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;
748
749            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.
750
751            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.});
752            $obj->keywords("sample, entry, preview");
753            $obj->tags(qw( lorem ipsum sample preview ));
754        }
755        $ctx->stash('entry', $obj);
756        $ctx->{current_archive_type} = $type eq 'individual' ? 'Individual' : 'Page';
757        if (($type eq 'individual') && $blog->archive_path) {
758            $blog_path = $blog->archive_path;
759            $blog_url = $blog->archive_url;
760        }
761        $archive_file = File::Spec->catfile( $blog_path, $obj->archive_file );
762        $archive_url = $obj->archive_url;
763    } elsif ($type eq 'archive') {
764        # some variety of archive template
765    } elsif ($type eq 'index') {
766    } else {
767        # for now, only index templates can be previewed
768        return $app->errtrans("Invalid request.");
769    }
770
771    my $orig_file;
772    my $path;
773
774    # Default case; works for index templates (other template types should
775    # have defined $archive_file by now).
776    $archive_file = File::Spec->catfile( $blog_path, $preview_tmpl->outfile )
777        unless defined $archive_file;
778
779    ( $orig_file, $path ) = File::Basename::fileparse( $archive_file );
780
781    $archive_url = MT::Util::caturl( $blog_url, $orig_file )
782        unless defined $archive_url;
783
784    my $file_ext;
785    require File::Basename;
786    $file_ext = $archive_file;
787    if ($file_ext =~ m/\.[a-z]+$/) {
788        $file_ext =~ s!.+\.!.!;
789    } else {
790        $file_ext = '';
791    }
792    $archive_file = File::Spec->catfile( $path, $preview_basename . $file_ext );
793
794    my @data;
795    $app->run_callbacks( 'cms_pre_preview.template', $app, $preview_tmpl, \@data );
796
797    my $has_hires = eval 'require Time::HiRes; 1' ? 1 : 0;
798    my $start_time = $has_hires ? Time::HiRes::time() : time;
799
800    my $ctx = $preview_tmpl->context;
801    my $html = $preview_tmpl->output;
802
803    $param{build_time} = $has_hires ? sprintf("%.3f", Time::HiRes::time() - $start_time ) : "~" . ( time - $start_time );
804
805    unless ( defined($html) ) {
806        return $app->error( $app->translate( "Publish error: [_1]",
807            MT::Util::encode_html( $preview_tmpl->errstr ) ) );
808    }
809
810    # If MT is configured to do 'local' previews, convert all
811    # the normal blog URLs into the domain used by MT itself (ie,
812    # blog is published to www.example.com, which is a different
813    # server from where MT runs, mt.example.com; previews therefore
814    # should occur locally, so replace all http://www.example.com/
815    # with http://mt.example.com/).
816    my ($old_url, $new_url);
817    if ($app->config('LocalPreviews')) {
818        $old_url = $blog_url;
819        $old_url =~ s!^(https?://[^/]+?/)(.*)?!$1!;
820        $new_url = $app->base . '/';
821        $html =~ s!\Q$old_url\E!$new_url!g;
822    }
823
824    my $fmgr = $blog->file_mgr;
825
826    ## Determine if we need to build directory structure,
827    ## and build it if we do. DirUmask determines
828    ## directory permissions.
829    require File::Basename;
830    $path =~ s!/$!!
831      unless $path eq '/';    ## OS X doesn't like / at the end in mkdir().
832    unless ( $fmgr->exists($path) ) {
833        $fmgr->mkpath($path);
834    }
835
836    if ( $fmgr->exists($path) && $fmgr->can_write($path) ) {
837        $param{preview_file} = $preview_basename;
838        my $preview_url = $archive_url;
839        $preview_url =~ s! / \Q$orig_file\E ( /? ) $!/$preview_basename$file_ext$1!x;
840
841        # We also have to translate the URL used for the
842        # published file to be on the MT app domain.
843        if (defined $new_url) {
844            $preview_url =~ s!^\Q$old_url\E!$new_url!;
845        }
846
847        $param{preview_url}  = $preview_url;
848
849        $fmgr->put_data( $html, $archive_file );
850
851        # we have to make a record of this preview just in case it
852        # isn't cleaned up by re-editing, saving or cancelling on
853        # by the user.
854        require MT::Session;
855        my $sess_obj = MT::Session->get_by_key(
856            {
857                id   => $preview_basename,
858                kind => 'TF',                # TF = Temporary File
859                name => $archive_file,
860            }
861        );
862        $sess_obj->start(time);
863        $sess_obj->save;
864    }
865    else {
866        return $app->error( $app->translate(
867            "Unable to create preview file in this location: [_1]", $path ) );
868    }
869
870    $param{id} = $id if $id;
871    $param{new_object} = $param{id} ? 0 : 1;
872    $param{name} = $tmpl->name;
873    my $cols = $tmpl->column_names;
874    for my $col (@$cols) {
875        push @data,
876          {
877            data_name  => $col,
878            data_value => scalar $q->param($col)
879          };
880    }
881    $param{template_loop} = \@data;
882    $param{object_type}  = $type;
883    return $app->load_tmpl( 'preview_template_strip.tmpl', \%param );
884}
885
886sub reset_blog_templates {
887    my $app   = shift;
888    my $q     = $app->param;
889    my $perms = $app->permissions
890      or return $app->error( $app->translate("No permissions") );
891    return $app->error( $app->translate("Permission denied.") )
892      unless $perms->can_edit_templates;
893    $app->validate_magic() or return;
894    my $blog = MT::Blog->load( $perms->blog_id );
895    require MT::Template;
896    my @tmpl = MT::Template->load( { blog_id => $blog->id } );
897
898    for my $tmpl (@tmpl) {
899        $tmpl->remove or return $app->error( $tmpl->errstr );
900    }
901    my $set = $blog ? $blog->template_set : undef;
902    require MT::DefaultTemplates;
903    my $tmpl_list = MT::DefaultTemplates->templates($set) || [];
904    my @arch_tmpl;
905    for my $val (@$tmpl_list) {
906        $val->{name} = $app->translate( $val->{name} );
907        $val->{text} = $app->translate_templatized( $val->{text} );
908        my $tmpl = MT::Template->new;
909        $tmpl->set_values($val);
910        $tmpl->build_dynamic(0);
911        $tmpl->blog_id( $blog->id );
912        $tmpl->save
913          or return $app->error(
914            $app->translate(
915                "Populating blog with default templates failed: [_1]",
916                $tmpl->errstr
917            )
918          );
919
920        # FIXME: enumeration of types
921        if (   $val->{type} eq 'archive'
922            || $val->{type} eq 'category'
923            || $val->{type} eq 'page'
924            || $val->{type} eq 'individual' )
925        {
926            push @arch_tmpl, $tmpl;
927        }
928    }
929
930    ## Set up mappings from new templates to archive types.
931    for my $tmpl (@arch_tmpl) {
932        my (@at);
933
934        # FIXME: enumeration of types
935        if ( $tmpl->type eq 'archive' ) {
936            @at = qw( Daily Weekly Monthly Category );
937        }
938        elsif ( $tmpl->type eq 'page' ) {
939            @at = qw( Page );
940        }
941        elsif ( $tmpl->type eq 'individual' ) {
942            @at = qw( Individual );
943        }
944        require MT::TemplateMap;
945        for my $at (@at) {
946            my $map = MT::TemplateMap->new;
947            $map->archive_type($at);
948            $map->is_preferred(1);
949            $map->template_id( $tmpl->id );
950            $map->blog_id( $tmpl->blog_id );
951            $map->save
952              or return $app->error(
953                $app->translate(
954                    "Setting up mappings failed: [_1]",
955                    $map->errstr
956                )
957              );
958        }
959    }
960    $app->redirect(
961        $app->uri(
962            'mode' => 'list',
963            args =>
964              { '_type' => 'template', blog_id => $blog->id, 'reset' => 1 }
965        )
966    );
967}
968
969sub _generate_map_table {
970    my $app = shift;
971    my ( $blog_id, $template_id ) = @_;
972
973    require MT::Template;
974    require MT::Blog;
975    my $blog     = MT::Blog->load($blog_id);
976    my $template = MT::Template->load($template_id);
977    my $tmpl     = $app->load_tmpl('include/archive_maps.tmpl');
978    my $maps     = _populate_archive_loop( $app, $blog, $template );
979    $tmpl->param( { object_type => 'templatemap' } );
980    $tmpl->param( { object_loop => $maps } ) if @$maps;
981    my $html = $tmpl->output();
982
983    if ( $html =~ m/<__trans / ) {
984        $html = $app->translate_templatized($html);
985    }
986    $html;
987}
988
989sub _populate_archive_loop {
990    my $app = shift;
991    my ( $blog, $obj ) = @_;
992
993    my $index = $app->config('IndexBasename');
994    my $ext = $blog->file_extension || '';
995    $ext = '.' . $ext if $ext ne '';
996
997    require MT::TemplateMap;
998    my @tmpl_maps = MT::TemplateMap->load( { template_id => $obj->id } );
999    my @maps;
1000    my %types;
1001    foreach my $map_obj (@tmpl_maps) {
1002        my $map = {};
1003        $map->{map_id}           = $map_obj->id;
1004        $map->{map_is_preferred} = $map_obj->is_preferred;
1005        # publish options
1006        $map->{map_build_type} = $map_obj->build_type;
1007        $map->{ 'map_build_type_' . ( $map_obj->build_type || 0 ) } = 1;
1008        my ( $period, $interval ) = _get_schedule( $map_obj->build_interval );
1009        $map->{ 'map_schedule_period_' . $period } = 1
1010            if defined $period;
1011        $map->{map_schedule_interval} = $interval
1012            if defined $interval;
1013
1014        my $at = $map->{archive_type} = $map_obj->archive_type;
1015        $types{$at}++;
1016        $map->{ 'archive_type_preferred_' . $blog->archive_type_preferred } = 1
1017          if $blog->archive_type_preferred;
1018        $map->{file_template} = $map_obj->file_template
1019          if $map_obj->file_template;
1020
1021        my $archiver = $app->publisher->archiver($at);
1022        next unless $archiver;
1023        $map->{archive_label} = $archiver->archive_label;
1024        my $tmpls     = $archiver->default_archive_templates;
1025        my $tmpl_loop = [];
1026        foreach (@$tmpls) {
1027            my $name = $_->{label};
1028            $name =~ s/\.html$/$ext/;
1029            $name =~ s/index$ext$/$index$ext/;
1030            push @$tmpl_loop,
1031              {
1032                name    => $name,
1033                value   => $_->{template},
1034                default => ( $_->{default} || 0 )
1035              };
1036        }
1037
1038        my $custom = 1;
1039
1040        foreach (@$tmpl_loop) {
1041            if (   ( !$map->{file_template} && $_->{default} )
1042                || ( $map->{file_template} eq $_->{value} ) )
1043            {
1044                $_->{selected}        = 1;
1045                $custom               = 0;
1046                $map->{file_template} = $_->{value}
1047                  if !$map->{file_template};
1048            }
1049        }
1050        if ($custom) {
1051            unshift @$tmpl_loop,
1052              {
1053                name     => $map->{file_template},
1054                value    => $map->{file_template},
1055                selected => 1,
1056              };
1057        }
1058
1059        $map->{archive_tmpl_loop} = $tmpl_loop;
1060        if (
1061            1 < MT::TemplateMap->count(
1062                { archive_type => $at, blog_id => $obj->blog_id }
1063            )
1064          )
1065        {
1066            $map->{has_multiple_archives} = 1;
1067        }
1068
1069        push @maps, $map;
1070    }
1071    @maps = sort { MT::App::CMS::archive_type_sorter( $a, $b ) } @maps;
1072    return \@maps;
1073}
1074
1075sub delete_map {
1076    my $app = shift;
1077    $app->validate_magic() or return;
1078    my $perms = $app->{perms}
1079      or return $app->error( $app->translate("No permissions") );
1080    my $q  = $app->param;
1081    my $id = $q->param('id');
1082
1083    require MT::TemplateMap;
1084    MT::TemplateMap->remove( { id => $id } );
1085    my $html =
1086      _generate_map_table( $app, $q->param('blog_id'),
1087        $q->param('template_id') );
1088    $app->{no_print_body} = 1;
1089    $app->send_http_header("text/plain");
1090    $app->print($html);
1091}
1092
1093sub add_map {
1094    my $app = shift;
1095    $app->validate_magic() or return;
1096    my $perms = $app->{perms}
1097      or return $app->error( $app->translate("No permissions") );
1098
1099    my $q = $app->param;
1100
1101    require MT::TemplateMap;
1102    my $blog_id = $q->param('blog_id');
1103    my $at      = $q->param('new_archive_type');
1104    my $count   = MT::TemplateMap->count(
1105        {
1106            blog_id      => $blog_id,
1107            archive_type => $at
1108        }
1109    );
1110    my $map = MT::TemplateMap->new;
1111    $map->is_preferred( $count ? 0 : 1 );
1112    $map->template_id( scalar $q->param('template_id') );
1113    $map->blog_id($blog_id);
1114    $map->archive_type($at);
1115    $map->save
1116      or return $app->error(
1117        $app->translate( "Saving map failed: [_1]", $map->errstr ) );
1118    my $html =
1119      _generate_map_table( $app, $blog_id, scalar $q->param('template_id') );
1120    $app->rebuild(
1121        BlogID      => $blog_id,
1122        ArchiveType => $at,
1123        TemplateMap => $map,
1124        TemplateID  => scalar $q->param('template_id'),
1125        NoStatic    => 1
1126    ) or return $app->publish_error();
1127    $app->{no_print_body} = 1;
1128    $app->send_http_header("text/plain");
1129    $app->print($html);
1130}
1131
1132sub can_view {
1133    my ( $eh, $app, $id ) = @_;
1134    my $perms = $app->permissions;
1135    return !$id || ($perms && $perms->can_edit_templates) || (!$app->blog && $app->user->can_edit_templates);
1136}
1137
1138sub can_save {
1139    my ( $eh, $app, $id ) = @_;
1140    my $perms = $app->permissions;
1141    return ($perms && $perms->can_edit_templates) || (!$perms && $app->user->can_edit_templates);
1142}
1143
1144sub can_delete {
1145    my ( $eh, $app, $obj ) = @_;
1146    return 1 if $app->user->is_superuser();
1147    my $perms = $app->permissions;
1148    return ($perms && $perms->can_edit_templates) || (!$perms && $app->user->can_edit_templates);
1149}
1150
1151sub pre_save {
1152    my $eh = shift;
1153    my ( $app, $obj ) = @_;
1154
1155    $obj->rebuild_me(0) 
1156      if $app->param('current_rebuild_me')
1157      && !$app->param('rebuild_me');
1158    $obj->build_dynamic(0)
1159      if $app->param('current_build_dynamic')
1160      && !$app->param('build_dynamic');
1161
1162    ## Strip linefeed characters.
1163    ( my $text = $obj->text ) =~ tr/\r//d;
1164
1165    if ($text =~ m/<(MT|_)_trans/i) {
1166        $text = $app->translate_templatized($text);
1167    }
1168
1169    $obj->text($text);
1170
1171    # update text heights if necessary
1172    if ( my $perms = $app->permissions ) {
1173        my $prefs = $perms->template_prefs || '';
1174        my $text_height = $app->param('text_height');
1175        if ( defined $text_height ) {
1176            my ($pref_text_height) = $prefs =~ m/\btext:(\d+)\b/;
1177            $pref_text_height ||= 0;
1178            if ( $text_height != $pref_text_height ) {
1179                if ( $prefs =~ m/\btext\b/ ) {
1180                    $prefs =~ s/\btext(:\d+)\b/text:$text_height/;
1181                }
1182                else {
1183                    $prefs = 'text:' . $text_height . ',' . $prefs;
1184                }
1185            }
1186        }
1187
1188        if ( $prefs ne ( $perms->template_prefs || '' ) ) {
1189            $perms->template_prefs($prefs);
1190            $perms->save;
1191        }
1192    }
1193
1194    # module caching
1195    $obj->include_with_ssi( $app->param('include_with_ssi') ? 1 : 0 );
1196    $obj->use_cache( $app->param('cache_enabled')           ? 1 : 0 );
1197    my $cache_expire_type = $app->param('cache_expire_type');
1198    $obj->cache_expire_type($cache_expire_type);
1199    my $period   = $app->param('cache_expire_period');
1200    my $interval = $app->param('cache_expire_interval');
1201    my $sec      = _get_interval( $period, $interval );
1202    $obj->cache_expire_interval($sec);
1203    my $q = $app->param;
1204    my @events;
1205
1206    foreach my $name ( $q->param('cache_expire_event') ) {
1207        push @events, $name;
1208    }
1209    $obj->cache_expire_event( join ',', @events );
1210    if ( $cache_expire_type == 1 ) {
1211        return $eh->error(
1212            $app->translate("You should not be able to enter 0 as the time.") )
1213          if $interval == 0;
1214    }
1215    elsif ( $cache_expire_type == 2 ) {
1216        return $eh->error(
1217            $app->translate("You must select at least one event checkbox.") )
1218          if !@events;
1219    }
1220
1221    require MT::PublishOption;
1222    my $build_type = $app->param('build_type');
1223    if ( $build_type == MT::PublishOption::SCHEDULED() ) {
1224        my $period   = $app->param('schedule_period');
1225        my $interval = $app->param('schedule_interval');
1226        my $sec      = _get_interval( $period, $interval );
1227        $obj->build_interval($sec);
1228    }
1229    my $rebuild_me = 1;
1230    if (   $build_type == MT::PublishOption::DISABLED()
1231        || $build_type == MT::PublishOption::MANUALLY() )
1232    {
1233        $rebuild_me = 0;
1234    }
1235    $obj->rebuild_me($rebuild_me);
1236    $obj->build_dynamic( $build_type == MT::PublishOption::DYNAMIC() ? 1 : 0 );
1237
1238    1;
1239}
1240
1241sub post_save {
1242    my $eh = shift;
1243    my ( $app, $obj, $original ) = @_;
1244
1245    my $sess_obj = $app->autosave_session_obj;
1246    $sess_obj->remove if $sess_obj;
1247
1248    require MT::TemplateMap;
1249    my $q = $app->param;
1250    my @p = $q->param;
1251    for my $p (@p) {
1252        if ( $p =~ /^archive_tmpl_preferred_(\w+)_(\d+)$/ ) {
1253            my $at     = $1;
1254            my $map_id = $2;
1255            my $map    = MT::TemplateMap->load($map_id);
1256            $map->prefer( $q->param($p) );    # prefer method saves in itself
1257        }
1258        elsif ( $p =~ /^archive_file_tmpl_(\d+)$/ ) {
1259            my $map_id = $1;
1260            my $map    = MT::TemplateMap->load($map_id);
1261            $map->file_template( $q->param($p) );
1262            $map->save;
1263        }
1264        elsif ( $p =~ /^map_build_type_(\d+)$/ ) {
1265            my $map_id     = $1;
1266            my $map        = MT::TemplateMap->load($map_id);
1267            my $build_type = $q->param($p);
1268            require MT::PublishOption;
1269            $map->build_type($build_type);
1270            if ( $build_type == MT::PublishOption::SCHEDULED() ) {
1271                my $period   = $q->param( 'map_schedule_period_' . $map_id );
1272                my $interval = $q->param( 'map_schedule_interval_' . $map_id );
1273                my $sec      = _get_interval( $period, $interval );
1274                $map->build_interval($sec);
1275            }
1276            $map->save;
1277        }
1278    }
1279
1280    if ( !$original->id ) {
1281        $app->log(
1282            {
1283                message => $app->translate(
1284                    "Template '[_1]' (ID:[_2]) created by '[_3]'",
1285                    $obj->name, $obj->id, $app->user->name
1286                ),
1287                level    => MT::Log::INFO(),
1288                class    => 'template',
1289                category => 'new',
1290            }
1291        );
1292    }
1293
1294    if ( $obj->build_dynamic ) {
1295        if ( $obj->type eq 'index' ) {
1296            $app->rebuild_indexes(
1297                BlogID   => $obj->blog_id,
1298                Template => $obj,
1299                NoStatic => 1,
1300            ) or return $app->publish_error();    # XXXX
1301        }
1302        else {
1303            $app->rebuild(
1304                BlogID     => $obj->blog_id,
1305                TemplateID => $obj->id,
1306                NoStatic   => 1,
1307            ) or return $app->publish_error();
1308        }
1309    }
1310    1;
1311}
1312
1313sub post_delete {
1314    my ( $eh, $app, $obj ) = @_;
1315
1316    $app->log(
1317        {
1318            message => $app->translate(
1319                "Template '[_1]' (ID:[_2]) deleted by '[_3]'",
1320                $obj->name, $obj->id, $app->user->name
1321            ),
1322            level    => MT::Log::INFO(),
1323            class    => 'system',
1324            category => 'delete'
1325        }
1326    );
1327}
1328
1329sub build_template_table {
1330    my $app = shift;
1331    my (%args) = @_;
1332
1333    my $perms     = $app->permissions;
1334    my $list_pref = $app->list_pref('template');
1335    my $limit     = $args{limit};
1336    my $param     = $args{param} || {};
1337    my $iter;
1338    if ( $args{load_args} ) {
1339        my $class = $app->model('template');
1340        $iter = $class->load_iter( @{ $args{load_args} } );
1341    }
1342    elsif ( $args{iter} ) {
1343        $iter = $args{iter};
1344    }
1345    elsif ( $args{items} ) {
1346        $iter = sub { pop @{ $args{items} } };
1347        $limit = scalar @{ $args{items} };
1348    }
1349    return [] unless $iter;
1350
1351    my @data;
1352    my $i;
1353    my %blogs;
1354    while ( my $tmpl = $iter->() ) {
1355        my $blog = $blogs{ $tmpl->blog_id } ||=
1356          MT::Blog->load( $tmpl->blog_id );
1357        my $row = $tmpl->column_values;
1358        $row->{name} = '' if !defined $row->{name};
1359        $row->{name} =~ s/^\s+|\s+$//g;
1360        $row->{name} = "(" . $app->translate("No Name") . ")"
1361          if $row->{name} eq '';
1362        my $published_url = $tmpl->published_url;
1363        $row->{published_url} = $published_url if $published_url;
1364
1365        # FIXME: enumeration of types
1366        $row->{can_delete} = 1
1367          if $tmpl->type =~ m/(custom|index|archive|page|individual|category|widget)/;
1368        if ($blog) {
1369            $row->{weblog_name} = $blog->name;
1370        }
1371        elsif ($tmpl->blog_id) {
1372            $row->{weblog_name} = '* ' . $app->translate('Orphaned') . ' *';
1373        }
1374        else {
1375            $row->{weblog_name} = '* ' . $app->translate('Global Templates') . ' *';
1376        }
1377        $row->{object} = $tmpl;
1378        push @data, $row;
1379        last if defined($limit) && (@data > $limit);
1380    }
1381    return [] unless @data;
1382
1383    $param->{template_table}[0]              = {%$list_pref};
1384    $param->{template_table}[0]{object_loop} = \@data;
1385    $param->{template_table}[0]{object_type} = 'template';
1386    $app->load_list_actions( 'template', $param );
1387    $param->{object_loop} = \@data;
1388    \@data;
1389}
1390
1391sub dialog_refresh_templates {
1392    my $app = shift;
1393    $app->validate_magic or return;
1394
1395    # permission check
1396    my $perms = $app->permissions;
1397    return $app->errtrans("Permission denied.")
1398        unless $app->user->is_superuser ||
1399            $perms->can_administer_blog ||
1400            $perms->can_edit_templates;
1401
1402    my $param = {};
1403    my $blog = $app->blog;
1404    $param->{return_args} = $app->param('return_args');
1405
1406    if ($blog) {
1407        $param->{blog_id} = $blog->id;
1408
1409        my $sets = $app->registry("template_sets");
1410        $sets->{$_}{key} = $_ for keys %$sets;
1411        $sets = $app->filter_conditional_list([ values %$sets ]);
1412
1413        no warnings; # some sets may not define an order
1414        @$sets = sort { $a->{order} <=> $b->{order} } @$sets;
1415        $param->{'template_set_loop'} = $sets;
1416
1417        my $existing_set = $blog->template_set || 'mt_blog';
1418        foreach (@$sets) {
1419            if ($_->{key} eq $existing_set) {
1420                $_->{selected} = 1;
1421            }
1422        }
1423        $param->{'template_set_index'} = $#$sets;
1424        $param->{'template_set_count'} = scalar @$sets;
1425
1426        $param->{template_sets} = $sets;
1427        $param->{screen_id} = "refresh-templates-dialog";
1428    }
1429
1430    # load template sets
1431    $app->build_page('dialog/refresh_templates.tmpl',
1432        $param);
1433}
1434
1435sub refresh_all_templates {
1436    my ($app) = @_;
1437
1438    my $backup = 0;
1439    if ($app->param('backup')) {
1440        # refresh templates dialog uses a 'backup' field
1441        $backup = 1;
1442    }
1443
1444    my $template_set = $app->param('template_set');
1445    my $refresh_type = $app->param('refresh_type') || 'refresh';
1446
1447    my $t = time;
1448
1449    my @id;
1450    if ($app->param('blog_id')) {
1451        @id = ( scalar $app->param('blog_id') );
1452    }
1453    else {
1454        @id = $app->param('id');
1455        if (! @id) {
1456            # refresh global templates
1457            @id = ( 0 );
1458        }
1459    }
1460
1461    require MT::Template;
1462    require MT::DefaultTemplates;
1463    require MT::Blog;
1464    require MT::Permission;
1465    require MT::Util;
1466
1467    foreach my $blog_id (@id) {
1468        my $blog;
1469        if ($blog_id) {
1470            $blog = MT::Blog->load($blog_id);
1471            next unless $blog;
1472        }
1473        if ( !$app->user->is_superuser() ) {
1474            my $perms = MT::Permission->load(
1475                { blog_id => $blog_id, author_id => $app->user->id } );
1476            if (
1477                !$perms
1478                || (   !$perms->can_edit_templates()
1479                    && !$perms->can_administer_blog() )
1480              )
1481            {
1482                next;
1483            }
1484        }
1485
1486        my $tmpl_list;
1487        if ($blog_id) {
1488
1489            if ($refresh_type eq 'clean') {
1490                # the user wants to back up all templates and
1491                # install the new ones
1492
1493                my @ts = MT::Util::offset_time_list( $t, $blog_id );
1494                my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d",
1495                    $ts[5] + 1900, $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1496
1497                my $tmpl_iter = MT::Template->load_iter({
1498                    blog_id => $blog_id,
1499                    type => { not => 'backup' },
1500                });
1501
1502                while (my $tmpl = $tmpl_iter->()) {
1503                    if ($backup) {
1504                        # zap all template maps
1505                        require MT::TemplateMap;
1506                        MT::TemplateMap->remove({
1507                            template_id => $tmpl->id,
1508                        });
1509                        $tmpl->type('backup');
1510                        $tmpl->name(
1511                            $tmpl->name . ' (Backup from ' . $ts . ')' );
1512                        $tmpl->identifier(undef);
1513                        $tmpl->rebuild_me(0);
1514                        $tmpl->linked_file(undef);
1515                        $tmpl->outfile('');
1516                        $tmpl->save;
1517                    } else {
1518                        $tmpl->remove;
1519                    }
1520                }
1521
1522                # This also creates our template mappings
1523                $blog->create_default_templates( $template_set ||
1524                    $blog->template_set || 'mt_blog' );
1525
1526                if ($template_set) {
1527                    $blog->template_set( $template_set );
1528                    $blog->save;
1529                    $app->run_callbacks( 'blog_template_set_change', { blog => $blog } );
1530                }
1531
1532                next;
1533            }
1534
1535            $tmpl_list = MT::DefaultTemplates->templates($template_set || $blog->template_set) || MT::DefaultTemplates->templates();
1536        }
1537        else {
1538            $tmpl_list = MT::DefaultTemplates->templates();
1539        }
1540
1541        foreach my $val (@$tmpl_list) {
1542            if ($blog_id) {
1543                # when refreshing blog templates,
1544                # skip over global templates which
1545                # specify a blog_id of 0...
1546                next if $val->{global};
1547            }
1548            else {
1549                next unless exists $val->{global};
1550            }
1551
1552            if ( !$val->{orig_name} ) {
1553                $val->{orig_name} = $val->{name};
1554                $val->{name}      = $app->translate( $val->{name} );
1555                $val->{text}      = $app->translate_templatized( $val->{text} );
1556            }
1557
1558            my $orig_name = $val->{orig_name};
1559
1560            my @ts = MT::Util::offset_time_list( $t, ( $blog_id ? $blog_id : undef ) );
1561            my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d", $ts[5] + 1900,
1562              $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1563
1564            my $terms = {};
1565            $terms->{blog_id} = $blog_id;
1566            $terms->{type} = $val->{type};
1567            if ( $val->{type} =~
1568                m/^(archive|individual|page|category|index|custom|widget)$/ )
1569            {
1570                $terms->{name} = $val->{name};
1571            }
1572            else {
1573                $terms->{identifier} = $val->{identifier};
1574            }
1575
1576            # this should only return 1 template; we're searching
1577            # within a given blog for a specific type of template (for
1578            # "system" templates; or for a type + name, which should be
1579            # unique for that blog.
1580            my $tmpl = MT::Template->load($terms);
1581            if ($tmpl && $backup) {
1582
1583                # check for default template text...
1584                # if it is a default template, then outright replace it
1585                my $text = $tmpl->text;
1586                $text =~ s/\s+//g;
1587
1588                my $def_text = $val->{text};
1589                $def_text =~ s/\s+//g;
1590
1591                # if it has been customized, back it up to a new tmpl record
1592                if ($def_text ne $text) {
1593                    my $backup = $tmpl->clone;
1594                    delete $backup->{column_values}
1595                      ->{id};    # make sure we don't overwrite original
1596                    delete $backup->{changed_cols}->{id};
1597                    $backup->name(
1598                        $backup->name . $app->translate( ' (Backup from [_1])', $ts ) );
1599                    $backup->type('backup');
1600                    # if ( $backup->type !~
1601                    #         m/^(archive|individual|page|category|index|custom|widget)$/ )
1602                    # {
1603                    #     $backup->type('custom')
1604                    #       ;      # system templates can't be created
1605                    # }
1606                    $backup->outfile('');
1607                    $backup->linked_file( $tmpl->linked_file );
1608                    $backup->identifier(undef);
1609                    $backup->rebuild_me(0);
1610                    $backup->build_dynamic(0);
1611                    $backup->save;
1612                }
1613            }
1614            if ($tmpl) {
1615                # we found that the previous template had not been
1616                # altered, so replace it with new default template...
1617                $tmpl->text( $val->{text} );
1618                $tmpl->identifier( $val->{identifier} );
1619                $tmpl->type( $val->{type} )
1620                  ; # fixes mismatch of types for cases like "archive" => "individual"
1621                $tmpl->linked_file('');
1622                $tmpl->save;
1623            }
1624            else {
1625                # create this one...
1626                my $tmpl = new MT::Template;
1627                $tmpl->build_dynamic(0);
1628                $tmpl->set_values(
1629                    {
1630                        text       => $val->{text},
1631                        name       => $val->{name},
1632                        type       => $val->{type},
1633                        identifier => $val->{identifier},
1634                        outfile    => $val->{outfile},
1635                        rebuild_me => $val->{rebuild_me}
1636                    }
1637                );
1638                $tmpl->blog_id($blog_id);
1639                $tmpl->save
1640                  or return $app->error(
1641                        $app->translate("Error creating new template: ")
1642                      . $tmpl->errstr );
1643            }
1644        }
1645    }
1646
1647    $app->add_return_arg( 'refreshed' => 1 );
1648    $app->call_return;
1649}
1650
1651sub refresh_individual_templates {
1652    my ($app) = @_;
1653
1654    require MT::Util;
1655
1656    my $user = $app->user;
1657    my $perms = $app->permissions;
1658    return $app->error(
1659        $app->translate(
1660            "Permission denied.")
1661      )
1662      #TODO: system level-designer permission
1663      unless $user->is_superuser() || $user->can_edit_templates()
1664      || ( $perms
1665        && ( $perms->can_edit_templates()
1666          || $perms->can_administer_blog ) );
1667
1668    my $set;
1669    if ( my $blog_id = $app->param('blog_id') ) {
1670        my $blog = $app->model('blog')->load($blog_id);
1671        $set = $blog->template_set()
1672            if $blog;
1673    }
1674
1675    require MT::DefaultTemplates;
1676    my $tmpl_list = MT::DefaultTemplates->templates($set) or return;
1677
1678    my $trnames    = {};
1679    my $tmpl_types = {};
1680    my $tmpl_ids   = {};
1681    my $tmpls      = {};
1682    foreach my $tmpl (@$tmpl_list) {
1683        $tmpl->{text} = $app->translate_templatized( $tmpl->{text} );
1684        $tmpl_ids->{ $tmpl->{identifier} } = $tmpl
1685            if $tmpl->{identifier};
1686        $trnames->{ $app->translate( $tmpl->{name} ) } = $tmpl->{name};
1687        if ( $tmpl->{type} !~ m/^(archive|individual|page|category|index|custom|widget)$/ )
1688        {
1689            $tmpl_types->{ $tmpl->{type} } = $tmpl;
1690        }
1691        else {
1692            $tmpls->{ $tmpl->{type} }{ $tmpl->{name} } = $tmpl;
1693        }
1694    }
1695
1696    my $t = time;
1697
1698    my @msg;
1699    my @id = $app->param('id');
1700    require MT::Template;
1701    foreach my $tmpl_id (@id) {
1702        my $tmpl = MT::Template->load($tmpl_id);
1703        next unless $tmpl;
1704        my $blog_id = $tmpl->blog_id;
1705
1706        # FIXME: permission check -- for this blog_id
1707
1708        my @ts = MT::Util::offset_time_list( $t, $blog_id );
1709        my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d", $ts[5] + 1900,
1710          $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1711
1712        my $orig_name = $trnames->{ $tmpl->name } || $tmpl->name;
1713        my $val = ( $tmpl->identifier ? $tmpl_ids->{ $tmpl->identifier() } : undef )
1714          || $tmpl_types->{ $tmpl->type() }
1715          || $tmpls->{ $tmpl->type() }{$orig_name};
1716        if ( !$val ) {
1717            push @msg,
1718              $app->translate(
1719"Skipping template '[_1]' since it appears to be a custom template.",
1720                $tmpl->name
1721              );
1722            next;
1723        }
1724
1725        my $text = $tmpl->text;
1726        $text =~ s/\s+//g;
1727
1728        my $def_text = $val->{text};
1729        $def_text =~ s/\s+//g;
1730
1731        if ($text ne $def_text) {
1732            # if it has been customized, back it up to a new tmpl record
1733            my $backup = $tmpl->clone;
1734            delete $backup->{column_values}
1735              ->{id};    # make sure we don't overwrite original
1736            delete $backup->{changed_cols}->{id};
1737            $backup->name( $backup->name . ' (Backup from ' . $ts . ')' );
1738            $backup->type('backup');
1739            $backup->outfile('');
1740            $backup->linked_file( $tmpl->linked_file );
1741            $backup->rebuild_me(0);
1742            $backup->build_dynamic(0);
1743            $backup->identifier(undef);
1744            $backup->save;
1745            push @msg,
1746              $app->translate(
1747    'Refreshing template <strong>[_3]</strong> with <a href="?__mode=view&amp;blog_id=[_1]&amp;_type=template&amp;id=[_2]">backup</a>',
1748                  $blog_id, $backup->id, $tmpl->name );
1749
1750            # we found that the previous template had not been
1751            # altered, so replace it with new default template...
1752            $tmpl->text( $val->{text} );
1753            $tmpl->identifier( $val->{identifier} );
1754            $tmpl->linked_file('');
1755            $tmpl->save;
1756        } else {
1757            push @msg, $app->translate("Skipping template '[_1]' since it has not been changed.", $tmpl->name);
1758        }
1759    }
1760    my @msg_loop;
1761    push @msg_loop, { message => $_ } foreach @msg;
1762
1763    $app->build_page( 'refresh_results.tmpl',
1764        { message_loop => \@msg_loop, return_url => $app->return_uri } );
1765}
1766
1767sub publish_index_templates {
1768    my $app = shift;
1769    $app->validate_magic or return;
1770
1771    # permission check
1772    my $perms = $app->permissions;
1773    return $app->errtrans("Permission denied.")
1774        unless $app->user->is_superuser ||
1775            $perms->can_administer_blog ||
1776            $perms->can_rebuild;
1777
1778    my $blog = $app->blog;
1779    my $templates = MT->model('template')->lookup_multi([ $app->param('id') ]);
1780    TEMPLATE: for my $tmpl (@$templates) {
1781        next TEMPLATE if !defined $tmpl;
1782        next TEMPLATE if $tmpl->blog_id != $blog->id;
1783        $app->rebuild_indexes(
1784            Blog     => $blog,
1785            Template => $tmpl,
1786        );
1787    }
1788
1789    $app->call_return( published => 1 );
1790}
1791
1792{
1793    my @period_options = (
1794        {
1795            name => 'minutes',
1796            expr => 60,
1797        },
1798        {
1799            name => 'hours',
1800            expr => 60 * 60,
1801        },
1802        {
1803            name => 'days',
1804            expr => 24 * 60 * 60,
1805        },
1806    );
1807
1808    sub _get_schedule {
1809        my ($sec) = @_;
1810        return unless defined $sec;
1811        my ( $period, $interval );
1812        for (@period_options) {
1813            last if $sec % $_->{expr};
1814            $period   = $_->{name};
1815            $interval = $sec / $_->{expr};
1816        }
1817        ( $period, $interval );
1818    }
1819
1820    sub _get_interval {
1821        my ( $period, $interval ) = @_;
1822        return unless defined $period;
1823        my $sec = 0;
1824        for (@period_options) {
1825            if ( $_->{name} eq $period ) {
1826                $sec = $interval * $_->{expr};
1827                last;
1828            }
1829        }
1830        $sec;
1831    }
1832}
1833
18341;
Note: See TracBrowser for help on using the browser.