root/branches/release-30/lib/MT/CMS/Template.pm @ 1395

Revision 1395, 46.1 kB (checked in by bchoate, 21 months ago)

Updates to combine archive/index templates listing. BugId:68695

  • 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
16    if ($id) {
17        # FIXME: Template types should not be enumerated here
18        $param->{nav_templates} = 1;
19        my $tab;
20        if ( $obj->type eq 'index' ) {
21            $tab = 'index';
22            $param->{template_group_trans} = $app->translate('index');
23        }
24        elsif ($obj->type eq 'archive'
25            || $obj->type eq 'individual'
26            || $obj->type eq 'category'
27            || $obj->type eq 'page' )
28        {
29
30            # FIXME: enumeration of types
31            $tab = 'archive';
32            $param->{template_group_trans} = $app->translate('archive');
33        }
34        elsif ( $obj->type eq 'custom' ) {
35            $tab = 'module';
36            $param->{template_group_trans} = $app->translate('module');
37        }
38        elsif ( $obj->type eq 'widget' ) {
39            $tab = 'widget';
40            $param->{template_group_trans} = $app->translate('widget');
41        }
42        elsif ( $obj->type eq 'email' ) {
43            $tab = 'email';
44            $param->{template_group_trans} = $app->translate('email');
45        }
46        else {
47            $tab = 'system';
48            $param->{template_group_trans} = $app->translate('system');
49        }
50        $param->{template_group} = $tab;
51        $blog_id = $obj->blog_id;
52
53        # FIXME: enumeration of types
54             $param->{has_name} = $obj->type eq 'index'
55          || $obj->type eq 'custom'
56          || $obj->type eq 'widget'
57          || $obj->type eq 'archive'
58          || $obj->type eq 'category'
59          || $obj->type eq 'page'
60          || $obj->type eq 'individual';
61        if ( !$param->{has_name} ) {
62            $param->{ 'type_' . $obj->type } = 1;
63            $param->{name} = $obj->name;
64        }
65        $app->add_breadcrumb( $param->{name} );
66        $param->{has_outfile} = $obj->type eq 'index';
67        $param->{has_rebuild} =
68          (      ( $obj->type eq 'index' )
69              && ( ( $blog->custom_dynamic_templates || "" ) ne 'all' ) );
70        $param->{custom_dynamic} =
71          $blog && ( $blog->custom_dynamic_templates || "" ) eq 'custom';
72        $param->{has_build_options} =
73          ( $param->{custom_dynamic} || $param->{has_rebuild} );
74
75        # FIXME: enumeration of types
76             $param->{is_special} = $param->{type} ne 'index'
77          && $param->{type} ne 'archive'
78          && $param->{type} ne 'category'
79          && $param->{type} ne 'page'
80          && $param->{type} ne 'individual';
81             $param->{has_build_options} = $param->{has_build_options}
82          && $param->{type} ne 'custom'
83          && $param->{type} ne 'widget'
84          && !$param->{is_special};
85        $param->{rebuild_me} =
86          defined $obj->rebuild_me ? $obj->rebuild_me : 1;
87        $param->{search_label} = $app->translate('Templates');
88        $param->{object_type}  = 'template';
89        my $published_url = $obj->published_url;
90        $param->{published_url} = $published_url if $published_url;
91        $param->{saved_rebuild} = 1 if $q->param('saved_rebuild');
92
93        $app->load_list_actions( 'template', $param );
94
95        $obj->compile;
96        if ( $obj->{errors} && @{ $obj->{errors} } ) {
97            $param->{error} = $app->translate(
98                "One or more errors were found in this template.");
99            $param->{error} .= "<ul>\n";
100            foreach my $err ( @{ $obj->{errors} } ) {
101                $param->{error} .= "<li>"
102                  . MT::Util::encode_html( $err->{message} )
103                  . "</li>\n";
104            }
105            $param->{error} .= "</ul>\n";
106        }
107
108        # Populate list of included templates
109        if ( my $includes = $obj->getElementsByTagName('Include') ) {
110            my @includes;
111            my @widgets;
112            my %seen;
113            foreach my $tag (@$includes) {
114                my $include = {};
115                my $mod = $include->{include_module} = $tag->[1]->{module} || $tag->[1]->{widget};
116                next unless $mod;
117                my $type = $tag->[1]->{widget} ? 'widget' : 'custom';
118                next if exists $seen{$type}{$mod};
119                $seen{$type}{$mod} = 1;
120                my $other = MT::Template->load(
121                    {
122                        blog_id => [ $obj->blog_id, 0 ],
123                        name    => $mod,
124                        type    => $type,
125                    }, {
126                        sort      => 'blog_id',
127                        direction => 'descend',
128                    }
129                );
130                if ($other) {
131                    $include->{include_link} = $app->mt_uri(
132                        mode => 'view',
133                        args => {
134                            blog_id => $other->blog_id || 0,
135                            '_type' => 'template',
136                            id      => $other->id
137                        }
138                    );
139                }
140                else {
141                    $include->{create_link} = $app->mt_uri(
142                        mode => 'view',
143                        args => {
144                            blog_id => $obj->blog_id,
145                            '_type' => 'template',
146                            type    => $type,
147                            name    => $mod,
148                        }
149                    );
150                }
151                if ($type eq 'widget') {
152                    push @widgets, $include;
153                } else {
154                    push @includes, $include;
155                }
156            }
157            $param->{include_loop} = \@includes if @includes;
158            $param->{widget_loop} = \@widgets if @widgets;
159        }
160        my @sets = ( @{ $obj->getElementsByTagName('WidgetSet') || [] }, @{ $obj->getElementsByTagName('WidgetManager') || [] } );
161        if ( @sets ) {
162            my @widget_sets;
163            my %seen;
164            foreach my $set (@sets) {
165                my $name = $set->[1]->{name};
166                next unless $name;
167                next if $seen{$name};
168                $seen{$name} = 1;
169                push @widget_sets, {
170                    include_link => $app->mt_uri(
171                        mode => 'edit_widget',
172                        args => {
173                            blog_id => $obj->blog_id,
174                            widgetmanager => $name,
175                        },
176                    ),
177                    include_module => $name,
178                };
179            }
180            $param->{widget_set_loop} = \@widget_sets if @widget_sets;
181        }
182        $param->{have_includes} = 1 if $param->{widget_set_loop} || $param->{include_loop} || $param->{widget_loop};
183        # Populate archive types for creating new map
184        my $obj_type = $obj->type;
185        if (   $obj_type eq 'individual'
186            || $obj_type eq 'page'
187            || $obj_type eq 'author'
188            || $obj_type eq 'category'
189            || $obj_type eq 'archive' )
190        {
191            my @at = $app->publisher->archive_types;
192            my @archive_types;
193            for my $at (@at) {
194                my $archiver      = $app->publisher->archiver($at);
195                my $archive_label = $archiver->archive_label;
196                $archive_label = $at unless $archive_label;
197                $archive_label = $archive_label->()
198                  if ( ref $archive_label ) eq 'CODE';
199                if (   ( $obj_type eq 'archive' )
200                    || ( $obj_type eq 'author' )
201                    || ( $obj_type eq 'category' ) )
202                {
203
204                    # only include if it is NOT an entry-based archive type
205                    next if $archiver->entry_based;
206                }
207                elsif ( $obj_type eq 'page' ) {
208                    # only include if it is a entry-based archive type and page
209                    next unless $archiver->entry_based;
210                    next if $archiver->entry_class ne 'page';
211                }
212                elsif ( $obj_type eq 'individual' ) {
213                    # only include if it is a entry-based archive type and entry
214                    next unless $archiver->entry_based;
215                    next if $archiver->entry_class eq 'page';
216                }
217                push @archive_types,
218                  {
219                    archive_type_translated => $archive_label,
220                    archive_type            => $at,
221                  };
222                @archive_types =
223                  sort { MT::App::CMS::archive_type_sorter( $a, $b ) } @archive_types;
224            }
225            $param->{archive_types} = \@archive_types;
226
227            # Populate template maps for this template
228            my $maps = _populate_archive_loop( $app, $blog, $obj );
229            $param->{object_loop} = $param->{template_map_loop} = $maps
230              if @$maps;
231        }
232    } else {
233        my $new_tmpl = $q->param('create_new_template');
234        my $template_type;
235        if ($new_tmpl) {
236            if ( $new_tmpl =~ m/^blank:(.+)/ ) {
237                $template_type = $1;
238                $param->{type} = $1;
239            }
240            elsif ( $new_tmpl =~ m/^default:([^:]+):(.+)/ ) {
241                $template_type = $1;
242                $template_type = 'custom' if $template_type eq 'module';
243                my $template_id = $2;
244                my $set = $blog ? $blog->template_set : undef;
245                require MT::DefaultTemplates;
246                my $def_tmpl = MT::DefaultTemplates->templates($set) || [];
247                my ($tmpl) =
248                  grep { $_->{identifier} eq $template_id } @$def_tmpl;
249                $param->{text} = $app->translate_templatized( $tmpl->{text} )
250                  if $tmpl;
251                $param->{type} = $template_type;
252            }
253        }
254        else {
255            $template_type = $q->param('type');
256            $template_type = 'custom' if 'module' eq $template_type;
257            $param->{type}   = $template_type;
258        }
259        return $app->errtrans("Create template requires type")
260          unless $template_type;
261        $param->{nav_templates} = 1;
262        my $tab;
263
264        # FIXME: enumeration of types
265        if ( $template_type eq 'index' ) {
266            $tab = 'index';
267            $param->{template_group_trans} = $app->translate('index');
268        }
269        elsif ($template_type eq 'archive'
270            || $template_type eq 'individual'
271            || $template_type eq 'category'
272            || $template_type eq 'page' )
273        {
274            $tab                         = 'archive';
275            $param->{template_group_trans} = $app->translate('archive');
276            $param->{type_archive}         = 1;
277            my @types = (
278                {
279                    key   => 'archive',
280                    label => $app->translate('Archive')
281                },
282                {
283                    key   => 'individual',
284                    label => $app->translate('Entry or Page')
285                },
286            );
287            $param->{new_archive_types} = \@types;
288        }
289        elsif ( $template_type eq 'custom' ) {
290            $tab = 'module';
291            $param->{template_group_trans} = $app->translate('module');
292        }
293        elsif ( $template_type eq 'widget' ) {
294            $tab = 'widget';
295            $param->{template_group_trans} = $app->translate('widget');
296        }
297        else {
298            $tab = 'system';
299            $param->{template_group_trans} = $app->translate('system');
300        }
301        $param->{template_group} = $tab;
302        $app->translate($tab);
303        $app->add_breadcrumb( $app->translate('New Template') );
304
305        # FIXME: enumeration of types
306             $param->{has_name} = $template_type eq 'index'
307          || $template_type eq 'custom'
308          || $template_type eq 'widget'
309          || $template_type eq 'archive'
310          || $template_type eq 'category'
311          || $template_type eq 'page'
312          || $template_type eq 'individual';
313        $param->{has_outfile} = $template_type eq 'index';
314        $param->{has_rebuild} =
315          (      ( $template_type eq 'index' )
316              && ( ( $blog->custom_dynamic_templates || "" ) ne 'all' ) );
317        $param->{custom_dynamic} =
318          $blog && $blog->custom_dynamic_templates eq 'custom';
319        $param->{has_build_options} =
320             $blog && ($blog->custom_dynamic_templates eq 'custom'
321          || $param->{has_rebuild});
322
323        # FIXME: enumeration of types
324             $param->{is_special} = $param->{type} ne 'index'
325          && $param->{type} ne 'archive'
326          && $param->{type} ne 'category'
327          && $param->{type} ne 'page'
328          && $param->{type} ne 'individual';
329             $param->{has_build_options} = $param->{has_build_options}
330          && $param->{type} ne 'custom'
331          && $param->{type} ne 'widget'
332          && !$param->{is_special};
333
334        $param->{rebuild_me} = 1;
335        $param->{name}       = MT::Util::decode_url( $app->param('name') )
336          if $app->param('name');
337    }
338    my $set = $blog ? $blog->template_set : undef;
339    require MT::DefaultTemplates;
340    my $tmpls = MT::DefaultTemplates->templates($set);
341    my @tmpl_ids;
342    foreach my $dtmpl (@$tmpls) {
343        if ( !$param->{has_name} ) {
344            if ($obj->type eq 'email') {
345                if ($dtmpl->{identifier} eq $obj->identifier) {
346                    $param->{template_name_label} = $dtmpl->{label};
347                    $param->{template_name}       = $dtmpl->{name};
348                }
349            }
350            else {
351                if ( $dtmpl->{type} eq $obj->type ) {
352                    $param->{template_name_label} = $dtmpl->{label};
353                    $param->{template_name}       = $dtmpl->{name};
354                }
355            }
356        }
357        if ( $dtmpl->{type} eq 'index' ) {
358            push @tmpl_ids,
359              {
360                label    => $dtmpl->{label},
361                key      => $dtmpl->{key},
362                selected => $dtmpl->{key} eq
363                  ( ( $obj ? $obj->identifier : undef ) || '' ),
364              };
365        }
366    }
367    $param->{index_identifiers} = \@tmpl_ids;
368
369    $param->{"type_$param->{type}"} = 1;
370    if ($perms) {
371        my $pref_param =
372          $app->load_template_prefs( $perms->template_prefs );
373        %$param = ( %$param, %$pref_param );
374    }
375
376    # Populate structure for template snippets
377    if ( my $snippets = $app->registry('template_snippets') || {} ) {
378        my @snippets;
379        for my $snip_id ( keys %$snippets ) {
380            my $label = $snippets->{$snip_id}{label};
381            $label = $label->() if ref($label) eq 'CODE';
382            push @snippets,
383              {
384                id      => $snip_id,
385                trigger => $snippets->{$snip_id}{trigger},
386                label   => $label,
387                content => $snippets->{$snip_id}{content},
388              };
389        }
390        @snippets = sort { $a->{label} cmp $b->{label} } @snippets;
391        $param->{template_snippets} = \@snippets;
392    }
393
394    # Populate structure for tag documentation
395    my $all_tags = MT::Component->registry("tags");
396    my $tag_docs = {};
397    foreach my $tag_set (@$all_tags) {
398        my $url = $tag_set->{help_url};
399        $url = $url->() if ref($url) eq 'CODE';
400        # hey, at least give them a google search
401        $url ||= 'http://www.google.com/search?q=mt%t';
402        my $tag_list = '';
403        foreach my $type (qw( block function )) {
404            my $tags = $tag_set->{$type} or next;
405            $tag_list .= ($tag_list eq '' ? '' : ',') . join(",", keys(%$tags));
406        }
407        $tag_list =~ s/(^|,)plugin(,|$)/,/;
408        if (exists $tag_docs->{$url}) {
409            $tag_docs->{$url} .= ',' . $tag_list;
410        }
411        else {
412            $tag_docs->{$url} = $tag_list;
413        }
414    }
415    $param->{tag_docs} = $tag_docs;
416    $param->{link_doc} = $app->help_url('appendices/tags/');
417
418    # template language
419    $param->{template_lang} = 'html';
420    if ( $obj && $obj->outfile ) {
421        if ( $obj->outfile =~ m/\.(css|js|html|php|pl|asp)$/ ) {
422            $param->{template_lang} = {
423                css => 'css',
424                js => 'javascript',
425                html => 'html',
426                php => 'php',
427                pl => 'perl',
428                asp => 'asp',
429            }->{$1};
430        }
431    }
432
433    1;
434}
435
436sub list {
437    my $app = shift;
438
439    my $perms = $app->blog ? $app->permissions : $app->user->permissions;
440    return $app->return_to_dashboard( redirect => 1 )
441      unless $perms || $app->user->is_superuser;
442    if ( $perms && !$perms->can_edit_templates ) {
443        return $app->return_to_dashboard( permission => 1 );
444    }
445    my $blog = $app->blog;
446
447    require MT::Template;
448    my $blog_id = $app->param('blog_id') || 0;
449    my $filter  = $app->param('filter_key');
450    if ( !$filter ) {
451        if ($blog) {
452            $filter = 'templates';
453            $app->param( 'filter_key', 'templates' );
454        }
455        else {
456            $filter = 'module_templates';
457            $app->param( 'filter_key', 'module_templates' );
458        }
459    }
460    else {
461        # global index templates redirect to module templates
462        if ( !$blog && $filter eq 'templates' ) {
463            $filter = 'module_templates';
464            $app->param( 'filter_key', 'module_templates' );
465        }
466    }
467    my $terms = { blog_id => $blog_id };
468    my $args  = { sort    => 'name' };
469
470    my $hasher = sub {
471        my ( $obj, $row ) = @_;
472        my $template_type;
473        my $type = $row->{type} || '';
474        if ( $type =~ m/^(individual|page|category|archive)$/ ) {
475            $template_type = 'archive';
476            # populate context with templatemap loop
477            my $tblog = $obj->blog_id == $blog->id ? $blog : MT::Blog->load( $obj->blog_id );
478            if ($tblog) {
479                $row->{archive_types} = _populate_archive_loop( $app, $tblog, $obj );
480            }
481        }
482        elsif ( $type eq 'widget' ) {
483            $template_type = 'widget';
484        }
485        elsif ( $type eq 'index' ) {
486            $template_type = 'index';
487        }
488        elsif ( $type eq 'custom' ) {
489            $template_type = 'module';
490        }
491        elsif ( $type eq 'email' ) {
492            $template_type = 'email';
493        }
494        elsif ( $type eq 'backup' ) {
495            $template_type = 'backup';
496        }
497        else {
498            $template_type = 'system';
499        }
500        $row->{template_type} = $template_type;
501        $row->{type} = 'entry' if $type eq 'individual';
502        my $published_url = $obj->published_url;
503        $row->{published_url} = $published_url if $published_url;
504    };
505
506    my $params        = {};
507    my $template_type = $filter;
508    $template_type =~ s/_templates//;
509    my $template_type_label = $app->translate($template_type);
510    if ( $template_type_label eq $template_type ) {
511        $template_type_label = "\u$template_type";
512    }
513    $params->{template_type}       = $template_type;
514    $params->{template_type_label} = $template_type_label;
515
516    $app->load_list_actions( 'template', $params );
517    $params->{page_actions} = $app->page_actions('list_templates');
518    $params->{search_label} = $app->translate("Templates");
519    $params->{blog_view} = 1;
520    $params->{refreshed} = $app->param('refreshed');
521    $params->{published} = $app->param('published');
522
523    return $app->listing(
524        {
525            type     => 'template',
526            terms    => $terms,
527            args     => $args,
528            params   => $params,
529            no_limit => 1,
530            code     => $hasher,
531        }
532    );
533}
534
535sub reset_blog_templates {
536    my $app   = shift;
537    my $q     = $app->param;
538    my $perms = $app->permissions
539      or return $app->error( $app->translate("No permissions") );
540    return $app->error( $app->translate("Permission denied.") )
541      unless $perms->can_edit_templates;
542    $app->validate_magic() or return;
543    my $blog = MT::Blog->load( $perms->blog_id );
544    require MT::Template;
545    my @tmpl = MT::Template->load( { blog_id => $blog->id } );
546
547    for my $tmpl (@tmpl) {
548        $tmpl->remove or return $app->error( $tmpl->errstr );
549    }
550    my $set = $blog ? $blog->template_set : undef;
551    require MT::DefaultTemplates;
552    my $tmpl_list = MT::DefaultTemplates->templates($set) || [];
553    my @arch_tmpl;
554    for my $val (@$tmpl_list) {
555        $val->{name} = $app->translate( $val->{name} );
556        $val->{text} = $app->translate_templatized( $val->{text} );
557        my $tmpl = MT::Template->new;
558        $tmpl->set_values($val);
559        $tmpl->build_dynamic(0);
560        $tmpl->blog_id( $blog->id );
561        $tmpl->save
562          or return $app->error(
563            $app->translate(
564                "Populating blog with default templates failed: [_1]",
565                $tmpl->errstr
566            )
567          );
568
569        # FIXME: enumeration of types
570        if (   $val->{type} eq 'archive'
571            || $val->{type} eq 'category'
572            || $val->{type} eq 'page'
573            || $val->{type} eq 'individual' )
574        {
575            push @arch_tmpl, $tmpl;
576        }
577    }
578
579    ## Set up mappings from new templates to archive types.
580    for my $tmpl (@arch_tmpl) {
581        my (@at);
582
583        # FIXME: enumeration of types
584        if ( $tmpl->type eq 'archive' ) {
585            @at = qw( Daily Weekly Monthly Category );
586        }
587        elsif ( $tmpl->type eq 'page' ) {
588            @at = qw( Page );
589        }
590        elsif ( $tmpl->type eq 'individual' ) {
591            @at = qw( Individual );
592        }
593        require MT::TemplateMap;
594        for my $at (@at) {
595            my $map = MT::TemplateMap->new;
596            $map->archive_type($at);
597            $map->is_preferred(1);
598            $map->template_id( $tmpl->id );
599            $map->blog_id( $tmpl->blog_id );
600            $map->save
601              or return $app->error(
602                $app->translate(
603                    "Setting up mappings failed: [_1]",
604                    $map->errstr
605                )
606              );
607        }
608    }
609    $app->redirect(
610        $app->uri(
611            'mode' => 'list',
612            args =>
613              { '_type' => 'template', blog_id => $blog->id, 'reset' => 1 }
614        )
615    );
616}
617
618sub _generate_map_table {
619    my $app = shift;
620    my ( $blog_id, $template_id ) = @_;
621
622    require MT::Template;
623    require MT::Blog;
624    my $blog     = MT::Blog->load($blog_id);
625    my $template = MT::Template->load($template_id);
626    my $tmpl     = $app->load_tmpl('include/archive_maps.tmpl');
627    my $maps     = _populate_archive_loop( $app, $blog, $template );
628    $tmpl->param( { object_type => 'templatemap' } );
629    $tmpl->param( { object_loop => $maps } ) if @$maps;
630    my $html = $tmpl->output();
631
632    if ( $html =~ m/<__trans / ) {
633        $html = $app->translate_templatized($html);
634    }
635    $html;
636}
637
638sub _populate_archive_loop {
639    my $app = shift;
640    my ( $blog, $obj ) = @_;
641
642    my $index = $app->config('IndexBasename');
643    my $ext = $blog->file_extension || '';
644    $ext = '.' . $ext if $ext ne '';
645
646    require MT::TemplateMap;
647    my @tmpl_maps = MT::TemplateMap->load( { template_id => $obj->id } );
648    my @maps;
649    my %types;
650    foreach my $map_obj (@tmpl_maps) {
651        my $map = {};
652        $map->{map_id}           = $map_obj->id;
653        $map->{map_is_preferred} = $map_obj->is_preferred;
654        my $at = $map->{archive_type} = $map_obj->archive_type;
655        $types{$at}++;
656        $map->{ 'archive_type_preferred_' . $blog->archive_type_preferred } = 1
657          if $blog->archive_type_preferred;
658        $map->{file_template} = $map_obj->file_template
659          if $map_obj->file_template;
660
661        my $archiver = $app->publisher->archiver($at);
662        next unless $archiver;
663        $map->{archive_label} = $archiver->archive_label;
664        my $tmpls     = $archiver->default_archive_templates;
665        my $tmpl_loop = [];
666        foreach (@$tmpls) {
667            my $name = $_->{label};
668            $name =~ s/\.html$/$ext/;
669            $name =~ s/index$ext$/$index$ext/;
670            push @$tmpl_loop,
671              {
672                name    => $name,
673                value   => $_->{template},
674                default => ( $_->{default} || 0 )
675              };
676        }
677
678        my $custom = 1;
679
680        foreach (@$tmpl_loop) {
681            if (   ( !$map->{file_template} && $_->{default} )
682                || ( $map->{file_template} eq $_->{value} ) )
683            {
684                $_->{selected}        = 1;
685                $custom               = 0;
686                $map->{file_template} = $_->{value}
687                  if !$map->{file_template};
688            }
689        }
690        if ($custom) {
691            unshift @$tmpl_loop,
692              {
693                name     => $map->{file_template},
694                value    => $map->{file_template},
695                selected => 1,
696              };
697        }
698
699        $map->{archive_tmpl_loop} = $tmpl_loop;
700        if (
701            1 < MT::TemplateMap->count(
702                { archive_type => $at, blog_id => $obj->blog_id }
703            )
704          )
705        {
706            $map->{has_multiple_archives} = 1;
707        }
708
709        push @maps, $map;
710    }
711    @maps = sort { MT::App::CMS::archive_type_sorter( $a, $b ) } @maps;
712    return \@maps;
713}
714
715sub delete_map {
716    my $app = shift;
717    $app->validate_magic() or return;
718    my $perms = $app->{perms}
719      or return $app->error( $app->translate("No permissions") );
720    my $q  = $app->param;
721    my $id = $q->param('id');
722
723    require MT::TemplateMap;
724    MT::TemplateMap->remove( { id => $id } );
725    my $html =
726      _generate_map_table( $app, $q->param('blog_id'),
727        $q->param('template_id') );
728    $app->{no_print_body} = 1;
729    $app->send_http_header("text/plain");
730    $app->print($html);
731}
732
733sub add_map {
734    my $app = shift;
735    $app->validate_magic() or return;
736    my $perms = $app->{perms}
737      or return $app->error( $app->translate("No permissions") );
738
739    my $q = $app->param;
740
741    require MT::TemplateMap;
742    my $blog_id = $q->param('blog_id');
743    my $at      = $q->param('new_archive_type');
744    my $count   = MT::TemplateMap->count(
745        {
746            blog_id      => $blog_id,
747            archive_type => $at
748        }
749    );
750    my $map = MT::TemplateMap->new;
751    $map->is_preferred( $count ? 0 : 1 );
752    $map->template_id( scalar $q->param('template_id') );
753    $map->blog_id($blog_id);
754    $map->archive_type($at);
755    $map->save
756      or return $app->error(
757        $app->translate( "Saving map failed: [_1]", $map->errstr ) );
758    my $html =
759      _generate_map_table( $app, $blog_id, scalar $q->param('template_id') );
760    $app->rebuild(
761        BlogID      => $blog_id,
762        ArchiveType => $at,
763        TemplateMap => $map,
764        TemplateID  => scalar $q->param('template_id'),
765        NoStatic    => 1
766    ) or return $app->publish_error();
767    $app->{no_print_body} = 1;
768    $app->send_http_header("text/plain");
769    $app->print($html);
770}
771
772sub delete_map {
773    my $app = shift;
774    $app->validate_magic() or return;
775    my $perms = $app->{perms}
776      or return $app->error( $app->translate("No permissions") );
777    my $q  = $app->param;
778    my $id = $q->param('id');
779
780    require MT::TemplateMap;
781    MT::TemplateMap->remove( { id => $id } );
782    my $html =
783      _generate_map_table( $app, $q->param('blog_id'),
784        $q->param('template_id') );
785    $app->{no_print_body} = 1;
786    $app->send_http_header("text/plain");
787    $app->print($html);
788}
789
790sub add_map {
791    my $app = shift;
792    $app->validate_magic() or return;
793    my $perms = $app->{perms}
794      or return $app->error( $app->translate("No permissions") );
795
796    my $q = $app->param;
797
798    require MT::TemplateMap;
799    my $blog_id = $q->param('blog_id');
800    my $at      = $q->param('new_archive_type');
801    my $count   = MT::TemplateMap->count(
802        {
803            blog_id      => $blog_id,
804            archive_type => $at
805        }
806    );
807    my $map = MT::TemplateMap->new;
808    $map->is_preferred( $count ? 0 : 1 );
809    $map->template_id( scalar $q->param('template_id') );
810    $map->blog_id($blog_id);
811    $map->archive_type($at);
812    $map->save
813      or return $app->error(
814        $app->translate( "Saving map failed: [_1]", $map->errstr ) );
815    my $html =
816      _generate_map_table( $app, $blog_id, scalar $q->param('template_id') );
817    $app->rebuild(
818        BlogID      => $blog_id,
819        ArchiveType => $at,
820        TemplateMap => $map,
821        TemplateID  => scalar $q->param('template_id'),
822        NoStatic    => 1
823    ) or return $app->publish_error();
824    $app->{no_print_body} = 1;
825    $app->send_http_header("text/plain");
826    $app->print($html);
827}
828
829sub can_view {
830    my ( $eh, $app, $id ) = @_;
831    my $perms = $app->permissions;
832    return !$id || ($perms && $perms->can_edit_templates) || (!$app->blog && $app->user->can_edit_templates);
833}
834
835sub can_save {
836    my ( $eh, $app, $id ) = @_;
837    my $perms = $app->permissions;
838    return ($perms && $perms->can_edit_templates) || (!$perms && $app->user->can_edit_templates);
839}
840
841sub can_delete {
842    my ( $eh, $app, $obj ) = @_;
843    return 1 if $app->user->is_superuser();
844    my $perms = $app->permissions;
845    return ($perms && $perms->can_edit_templates) || (!$perms && $app->user->can_edit_templates);
846}
847
848sub pre_save {
849    my $eh = shift;
850    my ( $app, $obj ) = @_;
851
852    $obj->rebuild_me(0) 
853      if $app->param('current_rebuild_me')
854      && !$app->param('rebuild_me');
855    $obj->build_dynamic(0)
856      if $app->param('current_build_dynamic')
857      && !$app->param('build_dynamic');
858
859    ## Strip linefeed characters.
860    ( my $text = $obj->text ) =~ tr/\r//d;
861
862    if ($text =~ m/<(MT|_)_trans/i) {
863        $text = $app->translate_templatized($text);
864    }
865
866    $obj->text($text);
867
868    # update text heights if necessary
869    if ( my $perms = $app->permissions ) {
870        my $prefs = $perms->template_prefs || '';
871        my $text_height = $app->param('text_height');
872        if ( defined $text_height ) {
873            my ($pref_text_height) = $prefs =~ m/\btext:(\d+)\b/;
874            $pref_text_height ||= 0;
875            if ( $text_height != $pref_text_height ) {
876                if ( $prefs =~ m/\btext\b/ ) {
877                    $prefs =~ s/\btext(:\d+)\b/text:$text_height/;
878                }
879                else {
880                    $prefs = 'text:' . $text_height . ',' . $prefs;
881                }
882            }
883        }
884
885        if ( $prefs ne ( $perms->template_prefs || '' ) ) {
886            $perms->template_prefs($prefs);
887            $perms->save;
888        }
889    }
890    1;
891}
892
893sub post_save {
894    my $eh = shift;
895    my ( $app, $obj, $original ) = @_;
896
897    my $sess_obj = $app->autosave_session_obj;
898    $sess_obj->remove if $sess_obj;
899
900    require MT::TemplateMap;
901    my $q = $app->param;
902    my @p = $q->param;
903    for my $p (@p) {
904        if ( $p =~ /^archive_tmpl_preferred_(\w+)_(\d+)$/ ) {
905            my $at     = $1;
906            my $map_id = $2;
907            my $map    = MT::TemplateMap->load($map_id);
908            $map->prefer( $q->param($p) );    # prefer method saves in itself
909        }
910        elsif ( $p =~ /^archive_file_tmpl_(\d+)$/ ) {
911            my $map_id = $1;
912            my $map    = MT::TemplateMap->load($map_id);
913            $map->file_template( $q->param($p) );
914            $map->save;
915        }
916    }
917
918    if ( !$original->id ) {
919        $app->log(
920            {
921                message => $app->translate(
922                    "Template '[_1]' (ID:[_2]) created by '[_3]'",
923                    $obj->name, $obj->id, $app->user->name
924                ),
925                level    => MT::Log::INFO(),
926                class    => 'template',
927                category => 'new',
928            }
929        );
930    }
931
932    if ( $obj->build_dynamic ) {
933        if ( $obj->type eq 'index' ) {
934            $app->rebuild_indexes(
935                BlogID   => $obj->blog_id,
936                Template => $obj,
937                NoStatic => 1,
938            ) or return $app->publish_error();    # XXXX
939        }
940        else {
941            $app->rebuild(
942                BlogID     => $obj->blog_id,
943                TemplateID => $obj->id,
944                NoStatic   => 1,
945            ) or return $app->publish_error();
946        }
947    }
948    1;
949}
950
951sub post_delete {
952    my ( $eh, $app, $obj ) = @_;
953
954    $app->log(
955        {
956            message => $app->translate(
957                "Template '[_1]' (ID:[_2]) deleted by '[_3]'",
958                $obj->name, $obj->id, $app->user->name
959            ),
960            level    => MT::Log::INFO(),
961            class    => 'system',
962            category => 'delete'
963        }
964    );
965}
966
967sub build_template_table {
968    my $app = shift;
969    my (%args) = @_;
970
971    my $perms     = $app->permissions;
972    my $list_pref = $app->list_pref('template');
973    my $limit     = $args{limit};
974    my $param     = $args{param} || {};
975    my $iter;
976    if ( $args{load_args} ) {
977        my $class = $app->model('template');
978        $iter = $class->load_iter( @{ $args{load_args} } );
979    }
980    elsif ( $args{iter} ) {
981        $iter = $args{iter};
982    }
983    elsif ( $args{items} ) {
984        $iter = sub { pop @{ $args{items} } };
985        $limit = scalar @{ $args{items} };
986    }
987    return [] unless $iter;
988
989    my @data;
990    my $i;
991    my %blogs;
992    while ( my $tmpl = $iter->() ) {
993        my $blog = $blogs{ $tmpl->blog_id } ||=
994          MT::Blog->load( $tmpl->blog_id );
995        my $row = $tmpl->column_values;
996        $row->{name} = '' if !defined $row->{name};
997        $row->{name} =~ s/^\s+|\s+$//g;
998        $row->{name} = "(" . $app->translate("No Name") . ")"
999          if $row->{name} eq '';
1000        my $published_url = $tmpl->published_url;
1001        $row->{published_url} = $published_url if $published_url;
1002
1003        # FIXME: enumeration of types
1004        $row->{can_delete} = 1
1005          if $tmpl->type =~ m/(custom|index|archive|page|individual|category)/;
1006        if ($blog) {
1007            $row->{weblog_name} = $blog->name;
1008        }
1009        elsif ($tmpl->blog_id) {
1010            $row->{weblog_name} = '* ' . $app->translate('Orphaned') . ' *';
1011        }
1012        else {
1013            $row->{weblog_name} = '* ' . $app->translate('Global Templates') . ' *';
1014        }
1015        $row->{object} = $tmpl;
1016        push @data, $row;
1017        last if @data > $limit;
1018    }
1019    return [] unless @data;
1020
1021    $param->{template_table}[0]              = {%$list_pref};
1022    $param->{template_table}[0]{object_loop} = \@data;
1023    $param->{template_table}[0]{object_type} = 'template';
1024    $app->load_list_actions( 'template', $param );
1025    $param->{object_loop} = \@data;
1026    \@data;
1027}
1028
1029sub refresh_all_templates {
1030    my ($app) = @_;
1031
1032    my $backup = 0;
1033    if ($app->param('backup')) {
1034        # refresh templates dialog uses a 'backup' field
1035        $backup = 1;
1036    }
1037
1038    my $template_set = $app->param('template_set');
1039    my $refresh_type = $app->param('refresh_type') || 'refresh';
1040
1041    my $t = time;
1042
1043    my @id;
1044    if ($app->param('blog_id')) {
1045        @id = ( scalar $app->param('blog_id') );
1046    }
1047    else {
1048        @id = $app->param('id');
1049        if (! @id) {
1050            # refresh global templates
1051            @id = ( 0 );
1052        }
1053    }
1054
1055    require MT::Template;
1056    require MT::DefaultTemplates;
1057    require MT::Blog;
1058    require MT::Permission;
1059    require MT::Util;
1060
1061    foreach my $blog_id (@id) {
1062        my $blog;
1063        if ($blog_id) {
1064            $blog = MT::Blog->load($blog_id);
1065            next unless $blog;
1066        }
1067        if ( !$app->user->is_superuser() ) {
1068            my $perms = MT::Permission->load(
1069                { blog_id => $blog_id, author_id => $app->user->id } );
1070            if (
1071                !$perms
1072                || (   !$perms->can_edit_templates()
1073                    && !$perms->can_administer_blog() )
1074              )
1075            {
1076                next;
1077            }
1078        }
1079
1080        my $tmpl_list;
1081        if ($blog_id) {
1082
1083            if ($refresh_type eq 'clean') {
1084                # the user wants to back up all templates and
1085                # install the new ones
1086
1087                my @ts = MT::Util::offset_time_list( $t, $blog_id );
1088                my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d",
1089                    $ts[5] + 1900, $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1090
1091                my $tmpl_iter = MT::Template->load_iter({
1092                    blog_id => $blog_id,
1093                    type => { not => 'backup' },
1094                });
1095
1096                while (my $tmpl = $tmpl_iter->()) {
1097                    if ($backup) {
1098                        # zap all template maps
1099                        require MT::TemplateMap;
1100                        MT::TemplateMap->remove({
1101                            template_id => $tmpl->id,
1102                        });
1103                        $tmpl->type('backup');
1104                        $tmpl->name(
1105                            $tmpl->name . ' (Backup from ' . $ts . ')' );
1106                        $tmpl->identifier(undef);
1107                        $tmpl->rebuild_me(0);
1108                        $tmpl->linked_file(undef);
1109                        $tmpl->outfile('');
1110                        $tmpl->save;
1111                    } else {
1112                        $tmpl->remove;
1113                    }
1114                }
1115
1116                # This also creates our template mappings
1117                $blog->create_default_templates( $template_set ||
1118                    $blog->template_set || 'mt_blog' );
1119
1120                if ($template_set) {
1121                    $blog->template_set( $template_set );
1122                    $blog->save;
1123                    $app->run_callbacks( 'blog_template_set_change', { blog => $blog } );
1124                }
1125
1126                next;
1127            }
1128
1129            $tmpl_list = MT::DefaultTemplates->templates($template_set || $blog->template_set) || MT::DefaultTemplates->templates();
1130        }
1131        else {
1132            $tmpl_list = MT::DefaultTemplates->templates();
1133        }
1134
1135        foreach my $val (@$tmpl_list) {
1136            if ($blog_id) {
1137                # when refreshing blog templates,
1138                # skip over global templates which
1139                # specify a blog_id of 0...
1140                next if $val->{global};
1141            }
1142            else {
1143                next unless exists $val->{global};
1144            }
1145
1146            if ( !$val->{orig_name} ) {
1147                $val->{orig_name} = $val->{name};
1148                $val->{name}      = $app->translate( $val->{name} );
1149                $val->{text}      = $app->translate_templatized( $val->{text} );
1150            }
1151
1152            my $orig_name = $val->{orig_name};
1153
1154            my @ts = MT::Util::offset_time_list( $t, ( $blog_id ? $blog_id : undef ) );
1155            my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d", $ts[5] + 1900,
1156              $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1157
1158            my $terms = {};
1159            $terms->{blog_id} = $blog_id;
1160            $terms->{type} = $val->{type};
1161            if ( $val->{type} =~
1162                m/^(archive|individual|page|category|index|custom|widget)$/ )
1163            {
1164                $terms->{name} = $val->{name};
1165            }
1166            else {
1167                $terms->{identifier} = $val->{identifier};
1168            }
1169
1170            # this should only return 1 template; we're searching
1171            # within a given blog for a specific type of template (for
1172            # "system" templates; or for a type + name, which should be
1173            # unique for that blog.
1174            my $tmpl = MT::Template->load($terms);
1175            if ($tmpl && $backup) {
1176
1177                # check for default template text...
1178                # if it is a default template, then outright replace it
1179                my $text = $tmpl->text;
1180                $text =~ s/\s+//g;
1181
1182                my $def_text = $val->{text};
1183                $def_text =~ s/\s+//g;
1184
1185                # if it has been customized, back it up to a new tmpl record
1186                if ($def_text ne $text) {
1187                    my $backup = $tmpl->clone;
1188                    delete $backup->{column_values}
1189                      ->{id};    # make sure we don't overwrite original
1190                    delete $backup->{changed_cols}->{id};
1191                    $backup->name(
1192                        $backup->name . $app->translate( ' (Backup from [_1])', $ts ) );
1193                    $backup->type('backup');
1194                    # if ( $backup->type !~
1195                    #         m/^(archive|individual|page|category|index|custom|widget)$/ )
1196                    # {
1197                    #     $backup->type('custom')
1198                    #       ;      # system templates can't be created
1199                    # }
1200                    $backup->outfile('');
1201                    $backup->linked_file( $tmpl->linked_file );
1202                    $backup->identifier(undef);
1203                    $backup->rebuild_me(0);
1204                    $backup->build_dynamic(0);
1205                    $backup->save;
1206                }
1207            }
1208            if ($tmpl) {
1209                # we found that the previous template had not been
1210                # altered, so replace it with new default template...
1211                $tmpl->text( $val->{text} );
1212                $tmpl->identifier( $val->{identifier} );
1213                $tmpl->type( $val->{type} )
1214                  ; # fixes mismatch of types for cases like "archive" => "individual"
1215                $tmpl->linked_file('');
1216                $tmpl->save;
1217            }
1218            else {
1219                # create this one...
1220                my $tmpl = new MT::Template;
1221                $tmpl->build_dynamic(0);
1222                $tmpl->set_values(
1223                    {
1224                        text       => $val->{text},
1225                        name       => $val->{name},
1226                        type       => $val->{type},
1227                        identifier => $val->{identifier},
1228                        outfile    => $val->{outfile},
1229                        rebuild_me => $val->{rebuild_me}
1230                    }
1231                );
1232                $tmpl->blog_id($blog_id);
1233                $tmpl->save
1234                  or return $app->error(
1235                        $app->translate("Error creating new template: ")
1236                      . $tmpl->errstr );
1237            }
1238        }
1239    }
1240
1241    $app->add_return_arg( 'refreshed' => 1 );
1242    $app->call_return;
1243}
1244
1245sub refresh_individual_templates {
1246    my ($app) = @_;
1247
1248    require MT::Util;
1249
1250    my $user = $app->user;
1251    my $perms = $app->permissions;
1252    return $app->error(
1253        $app->translate(
1254            "Permission denied.")
1255      )
1256      #TODO: system level-designer permission
1257      unless $user->is_superuser() || $user->can_edit_templates()
1258      || ( $perms
1259        && ( $perms->can_edit_templates()
1260          || $perms->can_administer_blog ) );
1261
1262    my $set;
1263    if ( my $blog_id = $app->param('blog_id') ) {
1264        my $blog = $app->model('blog')->load($blog_id);
1265        $set = $blog->template_set()
1266            if $blog;
1267    }
1268
1269    require MT::DefaultTemplates;
1270    my $tmpl_list = MT::DefaultTemplates->templates($set) or return;
1271
1272    my $trnames    = {};
1273    my $tmpl_types = {};
1274    my $tmpl_ids   = {};
1275    my $tmpls      = {};
1276    foreach my $tmpl (@$tmpl_list) {
1277        $tmpl->{text} = $app->translate_templatized( $tmpl->{text} );
1278        $tmpl_ids->{ $tmpl->{identifier} } = $tmpl
1279            if $tmpl->{identifier};
1280        $trnames->{ $app->translate( $tmpl->{name} ) } = $tmpl->{name};
1281        if ( $tmpl->{type} !~ m/^(archive|individual|page|category|index|custom|widget)$/ )
1282        {
1283            $tmpl_types->{ $tmpl->{type} } = $tmpl;
1284        }
1285        else {
1286            $tmpls->{ $tmpl->{type} }{ $tmpl->{name} } = $tmpl;
1287        }
1288    }
1289
1290    my $t = time;
1291
1292    my @msg;
1293    my @id = $app->param('id');
1294    require MT::Template;
1295    foreach my $tmpl_id (@id) {
1296        my $tmpl = MT::Template->load($tmpl_id);
1297        next unless $tmpl;
1298        my $blog_id = $tmpl->blog_id;
1299
1300        # FIXME: permission check -- for this blog_id
1301
1302        my @ts = MT::Util::offset_time_list( $t, $blog_id );
1303        my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d", $ts[5] + 1900,
1304          $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1305
1306        my $orig_name = $trnames->{ $tmpl->name } || $tmpl->name;
1307        my $val = ( $tmpl->identifier ? $tmpl_ids->{ $tmpl->identifier() } : undef )
1308          || $tmpl_types->{ $tmpl->type() }
1309          || $tmpls->{ $tmpl->type() }{$orig_name};
1310        if ( !$val ) {
1311            push @msg,
1312              $app->translate(
1313"Skipping template '[_1]' since it appears to be a custom template.",
1314                $tmpl->name
1315              );
1316            next;
1317        }
1318
1319        my $text = $tmpl->text;
1320        $text =~ s/\s+//g;
1321
1322        my $def_text = $val->{text};
1323        $def_text =~ s/\s+//g;
1324
1325        if ($text ne $def_text) {
1326            # if it has been customized, back it up to a new tmpl record
1327            my $backup = $tmpl->clone;
1328            delete $backup->{column_values}
1329              ->{id};    # make sure we don't overwrite original
1330            delete $backup->{changed_cols}->{id};
1331            $backup->name( $backup->name . ' (Backup from ' . $ts . ')' );
1332            $backup->type('backup');
1333            $backup->outfile('');
1334            $backup->linked_file( $tmpl->linked_file );
1335            $backup->rebuild_me(0);
1336            $backup->build_dynamic(0);
1337            $backup->identifier(undef);
1338            $backup->save;
1339            push @msg,
1340              $app->translate(
1341    'Refreshing template <strong>[_3]</strong> with <a href="?__mode=view&amp;blog_id=[_1]&amp;_type=template&amp;id=[_2]">backup</a>',
1342                  $blog_id, $backup->id, $tmpl->name );
1343
1344            # we found that the previous template had not been
1345            # altered, so replace it with new default template...
1346            $tmpl->text( $val->{text} );
1347            $tmpl->identifier( $val->{identifier} );
1348            $tmpl->linked_file('');
1349            $tmpl->save;
1350        } else {
1351            push @msg, $app->translate("Skipping template '[_1]' since it has not been changed.", $tmpl->name);
1352        }
1353    }
1354    my @msg_loop;
1355    push @msg_loop, { message => $_ } foreach @msg;
1356
1357    $app->build_page( 'refresh_results.tmpl',
1358        { message_loop => \@msg_loop, return_url => $app->return_uri } );
1359}
1360
1361sub publish_index_templates {
1362    my $app = shift;
1363    $app->validate_magic or return;
1364
1365    # permission check
1366    my $perms = $app->permissions;
1367    return $app->errtrans("Permission denied.")
1368        unless $app->user->is_superuser ||
1369            $perms->can_administer_blog ||
1370            $perms->can_rebuild;
1371
1372    my $blog = $app->blog;
1373    my $templates = MT->model('template')->lookup_multi([ $app->param('id') ]);
1374    TEMPLATE: for my $tmpl (@$templates) {
1375        next TEMPLATE if !defined $tmpl;
1376        next TEMPLATE if $tmpl->blog_id != $blog->id;
1377        $app->rebuild_indexes(
1378            Blog     => $blog,
1379            Template => $tmpl,
1380        );
1381    }
1382
1383    $app->call_return( published => 1 );
1384}
1385
13861;
Note: See TracBrowser for help on using the browser.