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

Revision 1409, 47.4 kB (checked in by bchoate, 21 months ago)

Fixed handler for refresh template dialog.

  • 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    $params->{screen_class} = "list-template";
517
518    $app->load_list_actions( 'template', $params );
519    $params->{page_actions} = $app->page_actions('list_templates');
520    $params->{search_label} = $app->translate("Templates");
521    $params->{blog_view} = 1;
522    $params->{refreshed} = $app->param('refreshed');
523    $params->{published} = $app->param('published');
524
525    return $app->listing(
526        {
527            type     => 'template',
528            terms    => $terms,
529            args     => $args,
530            params   => $params,
531            no_limit => 1,
532            code     => $hasher,
533        }
534    );
535}
536
537sub reset_blog_templates {
538    my $app   = shift;
539    my $q     = $app->param;
540    my $perms = $app->permissions
541      or return $app->error( $app->translate("No permissions") );
542    return $app->error( $app->translate("Permission denied.") )
543      unless $perms->can_edit_templates;
544    $app->validate_magic() or return;
545    my $blog = MT::Blog->load( $perms->blog_id );
546    require MT::Template;
547    my @tmpl = MT::Template->load( { blog_id => $blog->id } );
548
549    for my $tmpl (@tmpl) {
550        $tmpl->remove or return $app->error( $tmpl->errstr );
551    }
552    my $set = $blog ? $blog->template_set : undef;
553    require MT::DefaultTemplates;
554    my $tmpl_list = MT::DefaultTemplates->templates($set) || [];
555    my @arch_tmpl;
556    for my $val (@$tmpl_list) {
557        $val->{name} = $app->translate( $val->{name} );
558        $val->{text} = $app->translate_templatized( $val->{text} );
559        my $tmpl = MT::Template->new;
560        $tmpl->set_values($val);
561        $tmpl->build_dynamic(0);
562        $tmpl->blog_id( $blog->id );
563        $tmpl->save
564          or return $app->error(
565            $app->translate(
566                "Populating blog with default templates failed: [_1]",
567                $tmpl->errstr
568            )
569          );
570
571        # FIXME: enumeration of types
572        if (   $val->{type} eq 'archive'
573            || $val->{type} eq 'category'
574            || $val->{type} eq 'page'
575            || $val->{type} eq 'individual' )
576        {
577            push @arch_tmpl, $tmpl;
578        }
579    }
580
581    ## Set up mappings from new templates to archive types.
582    for my $tmpl (@arch_tmpl) {
583        my (@at);
584
585        # FIXME: enumeration of types
586        if ( $tmpl->type eq 'archive' ) {
587            @at = qw( Daily Weekly Monthly Category );
588        }
589        elsif ( $tmpl->type eq 'page' ) {
590            @at = qw( Page );
591        }
592        elsif ( $tmpl->type eq 'individual' ) {
593            @at = qw( Individual );
594        }
595        require MT::TemplateMap;
596        for my $at (@at) {
597            my $map = MT::TemplateMap->new;
598            $map->archive_type($at);
599            $map->is_preferred(1);
600            $map->template_id( $tmpl->id );
601            $map->blog_id( $tmpl->blog_id );
602            $map->save
603              or return $app->error(
604                $app->translate(
605                    "Setting up mappings failed: [_1]",
606                    $map->errstr
607                )
608              );
609        }
610    }
611    $app->redirect(
612        $app->uri(
613            'mode' => 'list',
614            args =>
615              { '_type' => 'template', blog_id => $blog->id, 'reset' => 1 }
616        )
617    );
618}
619
620sub _generate_map_table {
621    my $app = shift;
622    my ( $blog_id, $template_id ) = @_;
623
624    require MT::Template;
625    require MT::Blog;
626    my $blog     = MT::Blog->load($blog_id);
627    my $template = MT::Template->load($template_id);
628    my $tmpl     = $app->load_tmpl('include/archive_maps.tmpl');
629    my $maps     = _populate_archive_loop( $app, $blog, $template );
630    $tmpl->param( { object_type => 'templatemap' } );
631    $tmpl->param( { object_loop => $maps } ) if @$maps;
632    my $html = $tmpl->output();
633
634    if ( $html =~ m/<__trans / ) {
635        $html = $app->translate_templatized($html);
636    }
637    $html;
638}
639
640sub _populate_archive_loop {
641    my $app = shift;
642    my ( $blog, $obj ) = @_;
643
644    my $index = $app->config('IndexBasename');
645    my $ext = $blog->file_extension || '';
646    $ext = '.' . $ext if $ext ne '';
647
648    require MT::TemplateMap;
649    my @tmpl_maps = MT::TemplateMap->load( { template_id => $obj->id } );
650    my @maps;
651    my %types;
652    foreach my $map_obj (@tmpl_maps) {
653        my $map = {};
654        $map->{map_id}           = $map_obj->id;
655        $map->{map_is_preferred} = $map_obj->is_preferred;
656        my $at = $map->{archive_type} = $map_obj->archive_type;
657        $types{$at}++;
658        $map->{ 'archive_type_preferred_' . $blog->archive_type_preferred } = 1
659          if $blog->archive_type_preferred;
660        $map->{file_template} = $map_obj->file_template
661          if $map_obj->file_template;
662
663        my $archiver = $app->publisher->archiver($at);
664        next unless $archiver;
665        $map->{archive_label} = $archiver->archive_label;
666        my $tmpls     = $archiver->default_archive_templates;
667        my $tmpl_loop = [];
668        foreach (@$tmpls) {
669            my $name = $_->{label};
670            $name =~ s/\.html$/$ext/;
671            $name =~ s/index$ext$/$index$ext/;
672            push @$tmpl_loop,
673              {
674                name    => $name,
675                value   => $_->{template},
676                default => ( $_->{default} || 0 )
677              };
678        }
679
680        my $custom = 1;
681
682        foreach (@$tmpl_loop) {
683            if (   ( !$map->{file_template} && $_->{default} )
684                || ( $map->{file_template} eq $_->{value} ) )
685            {
686                $_->{selected}        = 1;
687                $custom               = 0;
688                $map->{file_template} = $_->{value}
689                  if !$map->{file_template};
690            }
691        }
692        if ($custom) {
693            unshift @$tmpl_loop,
694              {
695                name     => $map->{file_template},
696                value    => $map->{file_template},
697                selected => 1,
698              };
699        }
700
701        $map->{archive_tmpl_loop} = $tmpl_loop;
702        if (
703            1 < MT::TemplateMap->count(
704                { archive_type => $at, blog_id => $obj->blog_id }
705            )
706          )
707        {
708            $map->{has_multiple_archives} = 1;
709        }
710
711        push @maps, $map;
712    }
713    @maps = sort { MT::App::CMS::archive_type_sorter( $a, $b ) } @maps;
714    return \@maps;
715}
716
717sub delete_map {
718    my $app = shift;
719    $app->validate_magic() or return;
720    my $perms = $app->{perms}
721      or return $app->error( $app->translate("No permissions") );
722    my $q  = $app->param;
723    my $id = $q->param('id');
724
725    require MT::TemplateMap;
726    MT::TemplateMap->remove( { id => $id } );
727    my $html =
728      _generate_map_table( $app, $q->param('blog_id'),
729        $q->param('template_id') );
730    $app->{no_print_body} = 1;
731    $app->send_http_header("text/plain");
732    $app->print($html);
733}
734
735sub add_map {
736    my $app = shift;
737    $app->validate_magic() or return;
738    my $perms = $app->{perms}
739      or return $app->error( $app->translate("No permissions") );
740
741    my $q = $app->param;
742
743    require MT::TemplateMap;
744    my $blog_id = $q->param('blog_id');
745    my $at      = $q->param('new_archive_type');
746    my $count   = MT::TemplateMap->count(
747        {
748            blog_id      => $blog_id,
749            archive_type => $at
750        }
751    );
752    my $map = MT::TemplateMap->new;
753    $map->is_preferred( $count ? 0 : 1 );
754    $map->template_id( scalar $q->param('template_id') );
755    $map->blog_id($blog_id);
756    $map->archive_type($at);
757    $map->save
758      or return $app->error(
759        $app->translate( "Saving map failed: [_1]", $map->errstr ) );
760    my $html =
761      _generate_map_table( $app, $blog_id, scalar $q->param('template_id') );
762    $app->rebuild(
763        BlogID      => $blog_id,
764        ArchiveType => $at,
765        TemplateMap => $map,
766        TemplateID  => scalar $q->param('template_id'),
767        NoStatic    => 1
768    ) or return $app->publish_error();
769    $app->{no_print_body} = 1;
770    $app->send_http_header("text/plain");
771    $app->print($html);
772}
773
774sub delete_map {
775    my $app = shift;
776    $app->validate_magic() or return;
777    my $perms = $app->{perms}
778      or return $app->error( $app->translate("No permissions") );
779    my $q  = $app->param;
780    my $id = $q->param('id');
781
782    require MT::TemplateMap;
783    MT::TemplateMap->remove( { id => $id } );
784    my $html =
785      _generate_map_table( $app, $q->param('blog_id'),
786        $q->param('template_id') );
787    $app->{no_print_body} = 1;
788    $app->send_http_header("text/plain");
789    $app->print($html);
790}
791
792sub add_map {
793    my $app = shift;
794    $app->validate_magic() or return;
795    my $perms = $app->{perms}
796      or return $app->error( $app->translate("No permissions") );
797
798    my $q = $app->param;
799
800    require MT::TemplateMap;
801    my $blog_id = $q->param('blog_id');
802    my $at      = $q->param('new_archive_type');
803    my $count   = MT::TemplateMap->count(
804        {
805            blog_id      => $blog_id,
806            archive_type => $at
807        }
808    );
809    my $map = MT::TemplateMap->new;
810    $map->is_preferred( $count ? 0 : 1 );
811    $map->template_id( scalar $q->param('template_id') );
812    $map->blog_id($blog_id);
813    $map->archive_type($at);
814    $map->save
815      or return $app->error(
816        $app->translate( "Saving map failed: [_1]", $map->errstr ) );
817    my $html =
818      _generate_map_table( $app, $blog_id, scalar $q->param('template_id') );
819    $app->rebuild(
820        BlogID      => $blog_id,
821        ArchiveType => $at,
822        TemplateMap => $map,
823        TemplateID  => scalar $q->param('template_id'),
824        NoStatic    => 1
825    ) or return $app->publish_error();
826    $app->{no_print_body} = 1;
827    $app->send_http_header("text/plain");
828    $app->print($html);
829}
830
831sub can_view {
832    my ( $eh, $app, $id ) = @_;
833    my $perms = $app->permissions;
834    return !$id || ($perms && $perms->can_edit_templates) || (!$app->blog && $app->user->can_edit_templates);
835}
836
837sub can_save {
838    my ( $eh, $app, $id ) = @_;
839    my $perms = $app->permissions;
840    return ($perms && $perms->can_edit_templates) || (!$perms && $app->user->can_edit_templates);
841}
842
843sub can_delete {
844    my ( $eh, $app, $obj ) = @_;
845    return 1 if $app->user->is_superuser();
846    my $perms = $app->permissions;
847    return ($perms && $perms->can_edit_templates) || (!$perms && $app->user->can_edit_templates);
848}
849
850sub pre_save {
851    my $eh = shift;
852    my ( $app, $obj ) = @_;
853
854    $obj->rebuild_me(0) 
855      if $app->param('current_rebuild_me')
856      && !$app->param('rebuild_me');
857    $obj->build_dynamic(0)
858      if $app->param('current_build_dynamic')
859      && !$app->param('build_dynamic');
860
861    ## Strip linefeed characters.
862    ( my $text = $obj->text ) =~ tr/\r//d;
863
864    if ($text =~ m/<(MT|_)_trans/i) {
865        $text = $app->translate_templatized($text);
866    }
867
868    $obj->text($text);
869
870    # update text heights if necessary
871    if ( my $perms = $app->permissions ) {
872        my $prefs = $perms->template_prefs || '';
873        my $text_height = $app->param('text_height');
874        if ( defined $text_height ) {
875            my ($pref_text_height) = $prefs =~ m/\btext:(\d+)\b/;
876            $pref_text_height ||= 0;
877            if ( $text_height != $pref_text_height ) {
878                if ( $prefs =~ m/\btext\b/ ) {
879                    $prefs =~ s/\btext(:\d+)\b/text:$text_height/;
880                }
881                else {
882                    $prefs = 'text:' . $text_height . ',' . $prefs;
883                }
884            }
885        }
886
887        if ( $prefs ne ( $perms->template_prefs || '' ) ) {
888            $perms->template_prefs($prefs);
889            $perms->save;
890        }
891    }
892    1;
893}
894
895sub post_save {
896    my $eh = shift;
897    my ( $app, $obj, $original ) = @_;
898
899    my $sess_obj = $app->autosave_session_obj;
900    $sess_obj->remove if $sess_obj;
901
902    require MT::TemplateMap;
903    my $q = $app->param;
904    my @p = $q->param;
905    for my $p (@p) {
906        if ( $p =~ /^archive_tmpl_preferred_(\w+)_(\d+)$/ ) {
907            my $at     = $1;
908            my $map_id = $2;
909            my $map    = MT::TemplateMap->load($map_id);
910            $map->prefer( $q->param($p) );    # prefer method saves in itself
911        }
912        elsif ( $p =~ /^archive_file_tmpl_(\d+)$/ ) {
913            my $map_id = $1;
914            my $map    = MT::TemplateMap->load($map_id);
915            $map->file_template( $q->param($p) );
916            $map->save;
917        }
918    }
919
920    if ( !$original->id ) {
921        $app->log(
922            {
923                message => $app->translate(
924                    "Template '[_1]' (ID:[_2]) created by '[_3]'",
925                    $obj->name, $obj->id, $app->user->name
926                ),
927                level    => MT::Log::INFO(),
928                class    => 'template',
929                category => 'new',
930            }
931        );
932    }
933
934    if ( $obj->build_dynamic ) {
935        if ( $obj->type eq 'index' ) {
936            $app->rebuild_indexes(
937                BlogID   => $obj->blog_id,
938                Template => $obj,
939                NoStatic => 1,
940            ) or return $app->publish_error();    # XXXX
941        }
942        else {
943            $app->rebuild(
944                BlogID     => $obj->blog_id,
945                TemplateID => $obj->id,
946                NoStatic   => 1,
947            ) or return $app->publish_error();
948        }
949    }
950    1;
951}
952
953sub post_delete {
954    my ( $eh, $app, $obj ) = @_;
955
956    $app->log(
957        {
958            message => $app->translate(
959                "Template '[_1]' (ID:[_2]) deleted by '[_3]'",
960                $obj->name, $obj->id, $app->user->name
961            ),
962            level    => MT::Log::INFO(),
963            class    => 'system',
964            category => 'delete'
965        }
966    );
967}
968
969sub build_template_table {
970    my $app = shift;
971    my (%args) = @_;
972
973    my $perms     = $app->permissions;
974    my $list_pref = $app->list_pref('template');
975    my $limit     = $args{limit};
976    my $param     = $args{param} || {};
977    my $iter;
978    if ( $args{load_args} ) {
979        my $class = $app->model('template');
980        $iter = $class->load_iter( @{ $args{load_args} } );
981    }
982    elsif ( $args{iter} ) {
983        $iter = $args{iter};
984    }
985    elsif ( $args{items} ) {
986        $iter = sub { pop @{ $args{items} } };
987        $limit = scalar @{ $args{items} };
988    }
989    return [] unless $iter;
990
991    my @data;
992    my $i;
993    my %blogs;
994    while ( my $tmpl = $iter->() ) {
995        my $blog = $blogs{ $tmpl->blog_id } ||=
996          MT::Blog->load( $tmpl->blog_id );
997        my $row = $tmpl->column_values;
998        $row->{name} = '' if !defined $row->{name};
999        $row->{name} =~ s/^\s+|\s+$//g;
1000        $row->{name} = "(" . $app->translate("No Name") . ")"
1001          if $row->{name} eq '';
1002        my $published_url = $tmpl->published_url;
1003        $row->{published_url} = $published_url if $published_url;
1004
1005        # FIXME: enumeration of types
1006        $row->{can_delete} = 1
1007          if $tmpl->type =~ m/(custom|index|archive|page|individual|category)/;
1008        if ($blog) {
1009            $row->{weblog_name} = $blog->name;
1010        }
1011        elsif ($tmpl->blog_id) {
1012            $row->{weblog_name} = '* ' . $app->translate('Orphaned') . ' *';
1013        }
1014        else {
1015            $row->{weblog_name} = '* ' . $app->translate('Global Templates') . ' *';
1016        }
1017        $row->{object} = $tmpl;
1018        push @data, $row;
1019        last if @data > $limit;
1020    }
1021    return [] unless @data;
1022
1023    $param->{template_table}[0]              = {%$list_pref};
1024    $param->{template_table}[0]{object_loop} = \@data;
1025    $param->{template_table}[0]{object_type} = 'template';
1026    $app->load_list_actions( 'template', $param );
1027    $param->{object_loop} = \@data;
1028    \@data;
1029}
1030
1031sub dialog_refresh_templates {
1032    my $app = shift;
1033    $app->validate_magic or return;
1034
1035    # permission check
1036    my $perms = $app->permissions;
1037    return $app->errtrans("Permission denied.")
1038        unless $app->user->is_superuser ||
1039            $perms->can_administer_blog ||
1040            $perms->can_edit_templates;
1041
1042    my $param = {};
1043    my $blog = $app->blog;
1044    $param->{return_args} = $app->param('return_args');
1045
1046    if ($blog) {
1047        $param->{blog_id} = $blog->id;
1048
1049        my $sets = $app->registry("template_sets");
1050        $sets->{$_}{key} = $_ for keys %$sets;
1051        $sets = $app->filter_conditional_list([ values %$sets ]);
1052
1053        no warnings; # some sets may not define an order
1054        @$sets = sort { $a->{order} <=> $b->{order} } @$sets;
1055        $param->{'template_set_loop'} = $sets;
1056
1057        my $existing_set = $blog->template_set || 'mt_blog';
1058        foreach (@$sets) {
1059            if ($_->{key} eq $existing_set) {
1060                $_->{selected} = 1;
1061            }
1062        }
1063        $param->{'template_set_index'} = $#$sets;
1064        $param->{'template_set_count'} = scalar @$sets;
1065
1066        $param->{template_sets} = $sets;
1067        $param->{screen_id} = "refresh-templates-dialog";
1068    }
1069
1070    # load template sets
1071    $app->build_page('dialog/refresh_templates.tmpl',
1072        $param);
1073}
1074
1075sub refresh_all_templates {
1076    my ($app) = @_;
1077
1078    my $backup = 0;
1079    if ($app->param('backup')) {
1080        # refresh templates dialog uses a 'backup' field
1081        $backup = 1;
1082    }
1083
1084    my $template_set = $app->param('template_set');
1085    my $refresh_type = $app->param('refresh_type') || 'refresh';
1086
1087    my $t = time;
1088
1089    my @id;
1090    if ($app->param('blog_id')) {
1091        @id = ( scalar $app->param('blog_id') );
1092    }
1093    else {
1094        @id = $app->param('id');
1095        if (! @id) {
1096            # refresh global templates
1097            @id = ( 0 );
1098        }
1099    }
1100
1101    require MT::Template;
1102    require MT::DefaultTemplates;
1103    require MT::Blog;
1104    require MT::Permission;
1105    require MT::Util;
1106
1107    foreach my $blog_id (@id) {
1108        my $blog;
1109        if ($blog_id) {
1110            $blog = MT::Blog->load($blog_id);
1111            next unless $blog;
1112        }
1113        if ( !$app->user->is_superuser() ) {
1114            my $perms = MT::Permission->load(
1115                { blog_id => $blog_id, author_id => $app->user->id } );
1116            if (
1117                !$perms
1118                || (   !$perms->can_edit_templates()
1119                    && !$perms->can_administer_blog() )
1120              )
1121            {
1122                next;
1123            }
1124        }
1125
1126        my $tmpl_list;
1127        if ($blog_id) {
1128
1129            if ($refresh_type eq 'clean') {
1130                # the user wants to back up all templates and
1131                # install the new ones
1132
1133                my @ts = MT::Util::offset_time_list( $t, $blog_id );
1134                my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d",
1135                    $ts[5] + 1900, $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1136
1137                my $tmpl_iter = MT::Template->load_iter({
1138                    blog_id => $blog_id,
1139                    type => { not => 'backup' },
1140                });
1141
1142                while (my $tmpl = $tmpl_iter->()) {
1143                    if ($backup) {
1144                        # zap all template maps
1145                        require MT::TemplateMap;
1146                        MT::TemplateMap->remove({
1147                            template_id => $tmpl->id,
1148                        });
1149                        $tmpl->type('backup');
1150                        $tmpl->name(
1151                            $tmpl->name . ' (Backup from ' . $ts . ')' );
1152                        $tmpl->identifier(undef);
1153                        $tmpl->rebuild_me(0);
1154                        $tmpl->linked_file(undef);
1155                        $tmpl->outfile('');
1156                        $tmpl->save;
1157                    } else {
1158                        $tmpl->remove;
1159                    }
1160                }
1161
1162                # This also creates our template mappings
1163                $blog->create_default_templates( $template_set ||
1164                    $blog->template_set || 'mt_blog' );
1165
1166                if ($template_set) {
1167                    $blog->template_set( $template_set );
1168                    $blog->save;
1169                    $app->run_callbacks( 'blog_template_set_change', { blog => $blog } );
1170                }
1171
1172                next;
1173            }
1174
1175            $tmpl_list = MT::DefaultTemplates->templates($template_set || $blog->template_set) || MT::DefaultTemplates->templates();
1176        }
1177        else {
1178            $tmpl_list = MT::DefaultTemplates->templates();
1179        }
1180
1181        foreach my $val (@$tmpl_list) {
1182            if ($blog_id) {
1183                # when refreshing blog templates,
1184                # skip over global templates which
1185                # specify a blog_id of 0...
1186                next if $val->{global};
1187            }
1188            else {
1189                next unless exists $val->{global};
1190            }
1191
1192            if ( !$val->{orig_name} ) {
1193                $val->{orig_name} = $val->{name};
1194                $val->{name}      = $app->translate( $val->{name} );
1195                $val->{text}      = $app->translate_templatized( $val->{text} );
1196            }
1197
1198            my $orig_name = $val->{orig_name};
1199
1200            my @ts = MT::Util::offset_time_list( $t, ( $blog_id ? $blog_id : undef ) );
1201            my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d", $ts[5] + 1900,
1202              $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1203
1204            my $terms = {};
1205            $terms->{blog_id} = $blog_id;
1206            $terms->{type} = $val->{type};
1207            if ( $val->{type} =~
1208                m/^(archive|individual|page|category|index|custom|widget)$/ )
1209            {
1210                $terms->{name} = $val->{name};
1211            }
1212            else {
1213                $terms->{identifier} = $val->{identifier};
1214            }
1215
1216            # this should only return 1 template; we're searching
1217            # within a given blog for a specific type of template (for
1218            # "system" templates; or for a type + name, which should be
1219            # unique for that blog.
1220            my $tmpl = MT::Template->load($terms);
1221            if ($tmpl && $backup) {
1222
1223                # check for default template text...
1224                # if it is a default template, then outright replace it
1225                my $text = $tmpl->text;
1226                $text =~ s/\s+//g;
1227
1228                my $def_text = $val->{text};
1229                $def_text =~ s/\s+//g;
1230
1231                # if it has been customized, back it up to a new tmpl record
1232                if ($def_text ne $text) {
1233                    my $backup = $tmpl->clone;
1234                    delete $backup->{column_values}
1235                      ->{id};    # make sure we don't overwrite original
1236                    delete $backup->{changed_cols}->{id};
1237                    $backup->name(
1238                        $backup->name . $app->translate( ' (Backup from [_1])', $ts ) );
1239                    $backup->type('backup');
1240                    # if ( $backup->type !~
1241                    #         m/^(archive|individual|page|category|index|custom|widget)$/ )
1242                    # {
1243                    #     $backup->type('custom')
1244                    #       ;      # system templates can't be created
1245                    # }
1246                    $backup->outfile('');
1247                    $backup->linked_file( $tmpl->linked_file );
1248                    $backup->identifier(undef);
1249                    $backup->rebuild_me(0);
1250                    $backup->build_dynamic(0);
1251                    $backup->save;
1252                }
1253            }
1254            if ($tmpl) {
1255                # we found that the previous template had not been
1256                # altered, so replace it with new default template...
1257                $tmpl->text( $val->{text} );
1258                $tmpl->identifier( $val->{identifier} );
1259                $tmpl->type( $val->{type} )
1260                  ; # fixes mismatch of types for cases like "archive" => "individual"
1261                $tmpl->linked_file('');
1262                $tmpl->save;
1263            }
1264            else {
1265                # create this one...
1266                my $tmpl = new MT::Template;
1267                $tmpl->build_dynamic(0);
1268                $tmpl->set_values(
1269                    {
1270                        text       => $val->{text},
1271                        name       => $val->{name},
1272                        type       => $val->{type},
1273                        identifier => $val->{identifier},
1274                        outfile    => $val->{outfile},
1275                        rebuild_me => $val->{rebuild_me}
1276                    }
1277                );
1278                $tmpl->blog_id($blog_id);
1279                $tmpl->save
1280                  or return $app->error(
1281                        $app->translate("Error creating new template: ")
1282                      . $tmpl->errstr );
1283            }
1284        }
1285    }
1286
1287    $app->add_return_arg( 'refreshed' => 1 );
1288    $app->call_return;
1289}
1290
1291sub refresh_individual_templates {
1292    my ($app) = @_;
1293
1294    require MT::Util;
1295
1296    my $user = $app->user;
1297    my $perms = $app->permissions;
1298    return $app->error(
1299        $app->translate(
1300            "Permission denied.")
1301      )
1302      #TODO: system level-designer permission
1303      unless $user->is_superuser() || $user->can_edit_templates()
1304      || ( $perms
1305        && ( $perms->can_edit_templates()
1306          || $perms->can_administer_blog ) );
1307
1308    my $set;
1309    if ( my $blog_id = $app->param('blog_id') ) {
1310        my $blog = $app->model('blog')->load($blog_id);
1311        $set = $blog->template_set()
1312            if $blog;
1313    }
1314
1315    require MT::DefaultTemplates;
1316    my $tmpl_list = MT::DefaultTemplates->templates($set) or return;
1317
1318    my $trnames    = {};
1319    my $tmpl_types = {};
1320    my $tmpl_ids   = {};
1321    my $tmpls      = {};
1322    foreach my $tmpl (@$tmpl_list) {
1323        $tmpl->{text} = $app->translate_templatized( $tmpl->{text} );
1324        $tmpl_ids->{ $tmpl->{identifier} } = $tmpl
1325            if $tmpl->{identifier};
1326        $trnames->{ $app->translate( $tmpl->{name} ) } = $tmpl->{name};
1327        if ( $tmpl->{type} !~ m/^(archive|individual|page|category|index|custom|widget)$/ )
1328        {
1329            $tmpl_types->{ $tmpl->{type} } = $tmpl;
1330        }
1331        else {
1332            $tmpls->{ $tmpl->{type} }{ $tmpl->{name} } = $tmpl;
1333        }
1334    }
1335
1336    my $t = time;
1337
1338    my @msg;
1339    my @id = $app->param('id');
1340    require MT::Template;
1341    foreach my $tmpl_id (@id) {
1342        my $tmpl = MT::Template->load($tmpl_id);
1343        next unless $tmpl;
1344        my $blog_id = $tmpl->blog_id;
1345
1346        # FIXME: permission check -- for this blog_id
1347
1348        my @ts = MT::Util::offset_time_list( $t, $blog_id );
1349        my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d", $ts[5] + 1900,
1350          $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1351
1352        my $orig_name = $trnames->{ $tmpl->name } || $tmpl->name;
1353        my $val = ( $tmpl->identifier ? $tmpl_ids->{ $tmpl->identifier() } : undef )
1354          || $tmpl_types->{ $tmpl->type() }
1355          || $tmpls->{ $tmpl->type() }{$orig_name};
1356        if ( !$val ) {
1357            push @msg,
1358              $app->translate(
1359"Skipping template '[_1]' since it appears to be a custom template.",
1360                $tmpl->name
1361              );
1362            next;
1363        }
1364
1365        my $text = $tmpl->text;
1366        $text =~ s/\s+//g;
1367
1368        my $def_text = $val->{text};
1369        $def_text =~ s/\s+//g;
1370
1371        if ($text ne $def_text) {
1372            # if it has been customized, back it up to a new tmpl record
1373            my $backup = $tmpl->clone;
1374            delete $backup->{column_values}
1375              ->{id};    # make sure we don't overwrite original
1376            delete $backup->{changed_cols}->{id};
1377            $backup->name( $backup->name . ' (Backup from ' . $ts . ')' );
1378            $backup->type('backup');
1379            $backup->outfile('');
1380            $backup->linked_file( $tmpl->linked_file );
1381            $backup->rebuild_me(0);
1382            $backup->build_dynamic(0);
1383            $backup->identifier(undef);
1384            $backup->save;
1385            push @msg,
1386              $app->translate(
1387    'Refreshing template <strong>[_3]</strong> with <a href="?__mode=view&amp;blog_id=[_1]&amp;_type=template&amp;id=[_2]">backup</a>',
1388                  $blog_id, $backup->id, $tmpl->name );
1389
1390            # we found that the previous template had not been
1391            # altered, so replace it with new default template...
1392            $tmpl->text( $val->{text} );
1393            $tmpl->identifier( $val->{identifier} );
1394            $tmpl->linked_file('');
1395            $tmpl->save;
1396        } else {
1397            push @msg, $app->translate("Skipping template '[_1]' since it has not been changed.", $tmpl->name);
1398        }
1399    }
1400    my @msg_loop;
1401    push @msg_loop, { message => $_ } foreach @msg;
1402
1403    $app->build_page( 'refresh_results.tmpl',
1404        { message_loop => \@msg_loop, return_url => $app->return_uri } );
1405}
1406
1407sub publish_index_templates {
1408    my $app = shift;
1409    $app->validate_magic or return;
1410
1411    # permission check
1412    my $perms = $app->permissions;
1413    return $app->errtrans("Permission denied.")
1414        unless $app->user->is_superuser ||
1415            $perms->can_administer_blog ||
1416            $perms->can_rebuild;
1417
1418    my $blog = $app->blog;
1419    my $templates = MT->model('template')->lookup_multi([ $app->param('id') ]);
1420    TEMPLATE: for my $tmpl (@$templates) {
1421        next TEMPLATE if !defined $tmpl;
1422        next TEMPLATE if $tmpl->blog_id != $blog->id;
1423        $app->rebuild_indexes(
1424            Blog     => $blog,
1425            Template => $tmpl,
1426        );
1427    }
1428
1429    $app->call_return( published => 1 );
1430}
1431
14321;
Note: See TracBrowser for help on using the browser.