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

Revision 1743, 64.6 kB (checked in by takayama, 20 months ago)

Fixed BugId:74877
* Change value of type in the param if type equal 'module'

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