root/branches/release-36/lib/MT/CMS/Template.pm @ 2115

Revision 2115, 82.1 kB (checked in by bchoate, 19 months ago)

Paginate rebuild operation for list action to publish archive templates. Added 'save and rebuild' button for archive templates. BugId:79515

  • Property svn:keywords set to Id Revision
Line 
1# Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd.
2# This program is distributed under the terms of the
3# GNU General Public License, version 2.
4#
5# $Id$
6
7package MT::CMS::Template;
8
9use strict;
10
11sub edit {
12    my $cb = shift;
13    my ($app, $id, $obj, $param) = @_;
14
15    my $q = $app->param;
16    my $blog_id = $q->param('blog_id');
17
18    # FIXME: enumeration of types
19    unless ( $blog_id ) {
20        my $type = $q->param('type') || ( $obj ? $obj->type : undef );
21        return $app->return_to_dashboard( redirect => 1 )
22            unless $type eq 'module'
23                || $type eq 'custom'
24                || $type eq 'widget'
25                || $type eq 'widgetset';
26    }
27
28    my $type = $q->param('_type');
29    my $blog = $app->blog;
30    my $cfg = $app->config;
31    my $perms = $app->permissions;
32    my $can_preview = 0;
33
34    if ($blog) {
35        # include_system/include_cache are only applicable
36        # to blog-level templates
37        $param->{include_system} = $blog->include_system;
38        $param->{include_cache} = $blog->include_cache;
39    }
40
41    if ($id) {
42        # FIXME: Template types should not be enumerated here
43        $param->{nav_templates} = 1;
44        my $tab;
45        if ( $obj->type eq 'index' ) {
46            $tab = 'index';
47            $param->{template_group_trans} = $app->translate('index');
48        }
49        elsif ($obj->type eq 'archive'
50            || $obj->type eq 'individual'
51            || $obj->type eq 'category'
52            || $obj->type eq 'page' )
53        {
54
55            # FIXME: enumeration of types
56            $tab = 'archive';
57            $param->{template_group_trans} = $app->translate('archive');
58        }
59        elsif ( $obj->type eq 'custom' ) {
60            $tab = 'module';
61            $param->{template_group_trans} = $app->translate('module');
62        }
63        elsif ( $obj->type eq 'widget' ) {
64            $tab = 'widget';
65            $param->{template_group_trans} = $app->translate('widget');
66        }
67        elsif ( $obj->type eq 'email' ) {
68            $tab = 'email';
69            $param->{template_group_trans} = $app->translate('email');
70        }
71        else {
72            $tab = 'system';
73            $param->{template_group_trans} = $app->translate('system');
74        }
75        $param->{template_group} = $tab;
76        $blog_id = $obj->blog_id;
77
78        # FIXME: enumeration of types
79             $param->{has_name} = $obj->type eq 'index'
80          || $obj->type eq 'custom'
81          || $obj->type eq 'widget'
82          || $obj->type eq 'archive'
83          || $obj->type eq 'category'
84          || $obj->type eq 'page'
85          || $obj->type eq 'individual';
86        if ( !$param->{has_name} ) {
87            $param->{ 'type_' . $obj->type } = 1;
88            $param->{name} = $obj->name;
89        }
90        $app->add_breadcrumb( $param->{name} );
91        $param->{has_outfile} = $obj->type eq 'index';
92        $param->{has_rebuild} =
93          (      ( $obj->type eq 'index' )
94              && ( ( $blog->custom_dynamic_templates || "" ) ne 'all' ) );
95
96        # FIXME: enumeration of types
97             $param->{is_special} = $param->{type} ne 'index'
98          && $param->{type} ne 'archive'
99          && $param->{type} ne 'category'
100          && $param->{type} ne 'page'
101          && $param->{type} ne 'individual';
102             $param->{has_build_options} = $param->{has_build_options}
103          && $param->{type} ne 'custom'
104          && $param->{type} ne 'widget'
105          && !$param->{is_special};
106        $param->{search_label} = $app->translate('Templates');
107        $param->{object_type}  = 'template';
108        my $published_url = $obj->published_url;
109        $param->{published_url} = $published_url if $published_url;
110        $param->{saved_rebuild} = 1 if $q->param('saved_rebuild');
111
112        my $filter = $app->param('filter_key');
113        if ($param->{template_group} eq 'email') {
114            $app->param( 'filter_key', 'email_templates' );
115        }elsif  ($param->{template_group} eq 'system') {
116            $app->param( 'filter_key', 'system_templates' );
117        }
118        $app->load_list_actions( 'template', $param );
119        $app->param( 'filter_key', $filter );
120
121        $obj->compile;
122        if ( $obj->{errors} && @{ $obj->{errors} } ) {
123            $param->{error} = $app->translate(
124                "One or more errors were found in this template.");
125            $param->{error} .= "<ul>\n";
126            foreach my $err ( @{ $obj->{errors} } ) {
127                $param->{error} .= "<li>"
128                  . MT::Util::encode_html( $err->{message} )
129                  . "</li>\n";
130            }
131            $param->{error} .= "</ul>\n";
132        }
133
134        # Populate list of included templates
135        if ( my $includes = $obj->getElementsByTagName('Include') ) {
136            my @includes;
137            my @widgets;
138            my %seen;
139            foreach my $tag (@$includes) {
140                my $include = {};
141                my $mod = $include->{include_module} = $tag->[1]->{module} || $tag->[1]->{widget};
142                next unless $mod;
143                my $type = $tag->[1]->{widget} ? 'widget' : 'custom';
144                next if exists $seen{$type}{$mod};
145                $seen{$type}{$mod} = 1;
146                my $other = MT::Template->load(
147                    {
148                        blog_id => [ $obj->blog_id, 0 ],
149                        name    => $mod,
150                        type    => $type,
151                    }, {
152                        sort      => 'blog_id',
153                        direction => 'descend',
154                    }
155                );
156                if ($other) {
157                    $include->{include_link} = $app->mt_uri(
158                        mode => 'view',
159                        args => {
160                            blog_id => $other->blog_id || 0,
161                            '_type' => 'template',
162                            id      => $other->id
163                        }
164                    );
165                    # Try to compile template module if using MTInclude in this template.
166                    $other->compile;
167                    if ( $other->{errors} && @{ $other->{errors} } ) {
168                        $param->{error} = $app->translate(
169                            "One or more errors were found in included template module (".$other->name.").");
170                        $param->{error} .= "<ul>\n";
171                        foreach my $err ( @{ $other->{errors} } ) {
172                            $param->{error} .= "<li>"
173                              . MT::Util::encode_html( $err->{message} )
174                              . "</li>\n";
175                        }
176                        $param->{error} .= "</ul>\n";
177                    }
178                }
179                else {
180                    $include->{create_link} = $app->mt_uri(
181                        mode => 'view',
182                        args => {
183                            blog_id => $obj->blog_id,
184                            '_type' => 'template',
185                            type    => $type,
186                            name    => $mod,
187                        }
188                    );
189                }
190                if ($type eq 'widget') {
191                    push @widgets, $include;
192                } else {
193                    push @includes, $include;
194                }
195            }
196            $param->{include_loop} = \@includes if @includes;
197            $param->{widget_loop} = \@widgets if @widgets;
198        }
199        my @sets = ( @{ $obj->getElementsByTagName('WidgetSet') || [] }, @{ $obj->getElementsByTagName('WidgetManager') || [] } );
200        if ( @sets ) {
201            my @widget_sets;
202            my %seen;
203            foreach my $set (@sets) {
204                my $name = $set->[1]->{name};
205                next unless $name;
206                next if $seen{$name};
207                $seen{$name} = 1;
208                push @widget_sets, {
209                    include_link => $app->mt_uri(
210                        mode => 'edit_widget',
211                        args => {
212                            blog_id => $obj->blog_id,
213                            widgetmanager => $name,
214                        },
215                    ),
216                    include_module => $name,
217                };
218            }
219            $param->{widget_set_loop} = \@widget_sets if @widget_sets;
220        }
221        $param->{have_includes} = 1 if $param->{widget_set_loop} || $param->{include_loop} || $param->{widget_loop};
222        # Populate archive types for creating new map
223        my $obj_type = $obj->type;
224        if (   $obj_type eq 'individual'
225            || $obj_type eq 'page'
226            || $obj_type eq 'author'
227            || $obj_type eq 'category'
228            || $obj_type eq 'archive' )
229        {
230            my @at = $app->publisher->archive_types;
231            my @archive_types;
232            for my $at (@at) {
233                my $archiver      = $app->publisher->archiver($at);
234                my $archive_label = $archiver->archive_label;
235                $archive_label = $at unless $archive_label;
236                $archive_label = $archive_label->()
237                  if ( ref $archive_label ) eq 'CODE';
238                if (   ( $obj_type eq 'archive' )
239                    || ( $obj_type eq 'author' )
240                    || ( $obj_type eq 'category' ) )
241                {
242
243                    # only include if it is NOT an entry-based archive type
244                    next if $archiver->entry_based;
245                }
246                elsif ( $obj_type eq 'page' ) {
247                    # only include if it is a entry-based archive type and page
248                    next unless $archiver->entry_based;
249                    next if $archiver->entry_class ne 'page';
250                }
251                elsif ( $obj_type eq 'individual' ) {
252                    # only include if it is a entry-based archive type and entry
253                    next unless $archiver->entry_based;
254                    next if $archiver->entry_class eq 'page';
255                }
256                push @archive_types,
257                  {
258                    archive_type_translated => $archive_label,
259                    archive_type            => $at,
260                  };
261                @archive_types =
262                  sort { MT::App::CMS::archive_type_sorter( $a, $b ) } @archive_types;
263            }
264            $param->{archive_types} = \@archive_types;
265
266            # Populate template maps for this template
267            my $maps = _populate_archive_loop( $app, $blog, $obj );
268            if (@$maps) {
269                $param->{object_loop} = $param->{template_map_loop} = $maps
270                  if @$maps;
271                my %archive_types = map { $_->{archive_label} => () } @$maps;
272                $param->{enabled_archive_types} = join(", ", sort keys %archive_types);
273            }
274        }
275        # publish options
276        $param->{build_type} = $obj->build_type;
277        $param->{ 'build_type_' . ( $obj->build_type || 0 ) } = 1;
278        #my ( $period, $interval ) = _get_schedule( $obj->build_interval );
279        #$param->{ 'schedule_period_' . $period } = 1;
280        #$param->{schedule_interval} = $interval;
281        $param->{type} = 'custom' if $param->{type} eq 'module';
282    } else {
283        my $new_tmpl = $q->param('create_new_template');
284        my $template_type;
285        if ($new_tmpl) {
286            if ( $new_tmpl =~ m/^blank:(.+)/ ) {
287                $template_type = $1;
288                $param->{type} = $1;
289            }
290            elsif ( $new_tmpl =~ m/^default:([^:]+):(.+)/ ) {
291                $template_type = $1;
292                $template_type = 'custom' if $template_type eq 'module';
293                my $template_id = $2;
294                my $set = $blog ? $blog->template_set : undef;
295                require MT::DefaultTemplates;
296                my $def_tmpl = MT::DefaultTemplates->templates($set) || [];
297                my ($tmpl) =
298                  grep { $_->{identifier} eq $template_id } @$def_tmpl;
299                $param->{text} = $app->translate_templatized( $tmpl->{text} )
300                  if $tmpl;
301                $param->{type} = $template_type;
302            }
303        }
304        else {
305            $template_type = $q->param('type');
306            $template_type = 'custom' if 'module' eq $template_type;
307            $param->{type}   = $template_type;
308        }
309        return $app->errtrans("Create template requires type")
310          unless $template_type;
311        $param->{nav_templates} = 1;
312        my $tab;
313
314        # FIXME: enumeration of types
315        if ( $template_type eq 'index' ) {
316            $tab = 'index';
317            $param->{template_group_trans} = $app->translate('index');
318        }
319        elsif ($template_type eq 'archive'
320            || $template_type eq 'individual'
321            || $template_type eq 'category'
322            || $template_type eq 'page' )
323        {
324            $tab                         = 'archive';
325            $param->{template_group_trans} = $app->translate('archive');
326            $param->{type_archive}         = 1;
327            my @types = (
328                {
329                    key   => 'archive',
330                    label => $app->translate('Archive')
331                },
332                {
333                    key   => 'individual',
334                    label => $app->translate('Entry or Page')
335                },
336            );
337            $param->{new_archive_types} = \@types;
338        }
339        elsif ( $template_type eq 'custom' ) {
340            $tab = 'module';
341            $param->{template_group_trans} = $app->translate('module');
342        }
343        elsif ( $template_type eq 'widget' ) {
344            $tab = 'widget';
345            $param->{template_group_trans} = $app->translate('widget');
346        }
347        else {
348            $tab = 'system';
349            $param->{template_group_trans} = $app->translate('system');
350        }
351        $param->{template_group} = $tab;
352        $app->translate($tab);
353        $app->add_breadcrumb( $app->translate('New Template') );
354
355        # FIXME: enumeration of types
356             $param->{has_name} = $template_type eq 'index'
357          || $template_type eq 'custom'
358          || $template_type eq 'widget'
359          || $template_type eq 'archive'
360          || $template_type eq 'category'
361          || $template_type eq 'page'
362          || $template_type eq 'individual';
363        $param->{has_outfile} = $template_type eq 'index';
364        $param->{has_rebuild} =
365          (      ( $template_type eq 'index' )
366              && ( ( $blog->custom_dynamic_templates || "" ) ne 'all' ) );
367        $param->{custom_dynamic} =
368          $blog && $blog->custom_dynamic_templates eq 'custom';
369        $param->{has_build_options} =
370             $blog && ($blog->custom_dynamic_templates eq 'custom'
371          || $param->{has_rebuild});
372
373        # FIXME: enumeration of types
374             $param->{is_special} = $param->{type} ne 'index'
375          && $param->{type} ne 'archive'
376          && $param->{type} ne 'category'
377          && $param->{type} ne 'page'
378          && $param->{type} ne 'individual';
379             $param->{has_build_options} = $param->{has_build_options}
380          && $param->{type} ne 'custom'
381          && $param->{type} ne 'widget'
382          && !$param->{is_special};
383
384        $param->{name}       = MT::Util::decode_url( $app->param('name') )
385          if $app->param('name');
386    }
387    $param->{publish_queue_available} = eval 'require List::Util; require Scalar::Util; 1;';
388
389    my $set = $blog ? $blog->template_set : undef;
390    require MT::DefaultTemplates;
391    my $tmpls = MT::DefaultTemplates->templates($set);
392    my @tmpl_ids;
393    foreach my $dtmpl (@$tmpls) {
394        if ( !$param->{has_name} ) {
395            if ($obj->type eq 'email') {
396                if ($dtmpl->{identifier} eq $obj->identifier) {
397                    $param->{template_name_label} = $dtmpl->{label};
398                    $param->{template_name}       = $dtmpl->{name};
399                }
400            }
401            else {
402                if ( $dtmpl->{type} eq $obj->type ) {
403                    $param->{template_name_label} = $dtmpl->{label};
404                    $param->{template_name}       = $dtmpl->{name};
405                }
406            }
407        }
408        if ( $dtmpl->{type} eq 'index' ) {
409            push @tmpl_ids,
410              {
411                label    => $dtmpl->{label},
412                key      => $dtmpl->{key},
413                selected => $dtmpl->{key} eq
414                  ( ( $obj ? $obj->identifier : undef ) || '' ),
415              };
416        }
417    }
418    $param->{index_identifiers} = \@tmpl_ids;
419
420    $param->{"type_$param->{type}"} = 1;
421    if ($perms) {
422        my $pref_param =
423          $app->load_template_prefs( $perms->template_prefs );
424        %$param = ( %$param, %$pref_param );
425    }
426
427    # Populate structure for template snippets
428    if ( my $snippets = $app->registry('template_snippets') || {} ) {
429        my @snippets;
430        for my $snip_id ( keys %$snippets ) {
431            my $label = $snippets->{$snip_id}{label};
432            $label = $label->() if ref($label) eq 'CODE';
433            push @snippets,
434              {
435                id      => $snip_id,
436                trigger => $snippets->{$snip_id}{trigger},
437                label   => $label,
438                content => $snippets->{$snip_id}{content},
439              };
440        }
441        @snippets = sort { $a->{label} cmp $b->{label} } @snippets;
442        $param->{template_snippets} = \@snippets;
443    }
444
445    # Populate structure for tag documentation
446    my $all_tags = MT::Component->registry("tags");
447    my $tag_docs = {};
448    foreach my $tag_set (@$all_tags) {
449        my $url = $tag_set->{help_url};
450        $url = $url->() if ref($url) eq 'CODE';
451        # hey, at least give them a google search
452        $url ||= 'http://www.google.com/search?q=mt%t';
453        my $tag_list = '';
454        foreach my $type (qw( block function )) {
455            my $tags = $tag_set->{$type} or next;
456            $tag_list .= ($tag_list eq '' ? '' : ',') . join(",", keys(%$tags));
457        }
458        $tag_list =~ s/(^|,)plugin(,|$)/,/;
459        if (exists $tag_docs->{$url}) {
460            $tag_docs->{$url} .= ',' . $tag_list;
461        }
462        else {
463            $tag_docs->{$url} = $tag_list;
464        }
465    }
466    $param->{tag_docs} = $tag_docs;
467    $param->{link_doc} = $app->help_url('appendices/tags/');
468
469    $param->{screen_id} = "edit-template-" . $param->{type};
470
471    # template language
472    $param->{template_lang} = 'html';
473    if ( $obj && $obj->outfile ) {
474        if ( $obj->outfile =~ m/\.(css|js|html|php|pl|asp)$/ ) {
475            $param->{template_lang} = {
476                css => 'css',
477                js => 'javascript',
478                html => 'html',
479                php => 'php',
480                pl => 'perl',
481                asp => 'asp',
482            }->{$1};
483        }
484    }
485
486    if (($param->{type} eq 'custom') || ($param->{type} eq 'widget')) {
487        if ($blog) {
488            $param->{include_with_ssi}      = 0;
489            $param->{cache_path}            = '';
490            $param->{cache_expire_type}     = 0;
491            $param->{cache_expire_period}   = '';
492            $param->{cache_expire_interval} = 0;
493            $param->{ssi_type} = uc $blog->include_system;
494        }
495        if ($obj) {
496            $param->{include_with_ssi} = $obj->include_with_ssi
497              if defined $obj->include_with_ssi;
498            $param->{cache_path}       = $obj->cache_path
499              if defined $obj->cache_path;
500            $param->{cache_expire_type} = $obj->cache_expire_type
501              if defined $obj->cache_expire_type;
502            my ( $period, $interval ) =
503              _get_schedule( $obj->cache_expire_interval );
504            $param->{cache_expire_period}   = $period   if defined $period;
505            $param->{cache_expire_interval} = $interval if defined $interval;
506            my @events = split ',', ($obj->cache_expire_event || '');
507            foreach my $name (@events) {
508                $param->{ 'cache_expire_event_' . $name } = 1;
509            }
510        }
511    }
512
513    # if unset, default to 30 so if they choose to enable caching,
514    # it will be preset to something sane.
515    $param->{cache_expire_interval} ||= 30;
516
517    $param->{dirty} = 1
518        if $app->param('dirty');
519
520    $param->{can_preview} = 1
521        if (!$param->{is_special}) && (!$obj || ($obj && $obj->outfile !~ m/\.(css|xml|rss|js)$/));
522
523    1;
524}
525
526sub list {
527    my $app = shift;
528
529    my $perms = $app->blog ? $app->permissions : $app->user->permissions;
530    return $app->return_to_dashboard( redirect => 1 )
531      unless $perms || $app->user->is_superuser;
532    if ( $perms && !$perms->can_edit_templates ) {
533        return $app->return_to_dashboard( permission => 1 );
534    }
535    my $blog = $app->blog;
536
537    require MT::Template;
538    my $blog_id = $app->param('blog_id') || 0;
539    my $terms = { blog_id => $blog_id };
540    my $args  = { sort    => 'name' };
541
542    my $hasher = sub {
543        my ( $obj, $row ) = @_;
544        my $template_type;
545        my $type = $row->{type} || '';
546        if ( $type =~ m/^(individual|page|category|archive)$/ ) {
547            $template_type = 'archive';
548            # populate context with templatemap loop
549            my $tblog = $obj->blog_id == $blog->id ? $blog : MT::Blog->load( $obj->blog_id );
550            if ($tblog) {
551                $row->{archive_types} = _populate_archive_loop( $app, $tblog, $obj );
552            }
553        }
554        elsif ( $type eq 'widget' ) {
555            $template_type = 'widget';
556        }
557        elsif ( $type eq 'index' ) {
558            $template_type = 'index';
559        }
560        elsif ( $type eq 'custom' ) {
561            $template_type = 'module';
562        }
563        elsif ( $type eq 'email' ) {
564            $template_type = 'email';
565        }
566        elsif ( $type eq 'backup' ) {
567            $template_type = 'backup';
568        }
569        else {
570            $template_type = 'system';
571        }
572        $row->{use_cache} = ( ($obj->cache_expire_type || 0) != 0 ) ? 1 : 0;
573        $row->{template_type} = $template_type;
574        $row->{type} = 'entry' if $type eq 'individual';
575        my $published_url = $obj->published_url;
576        $row->{published_url} = $published_url if $published_url;
577    };
578
579    my $params        = {};
580    my $filter = $app->param('filter_key');
581    my $template_type = $filter || '';
582    $template_type =~ s/_templates//;
583
584    $params->{screen_class} = "list-template";
585    $params->{listing_screen} = 1;
586
587    $app->load_list_actions( 'template', $params );
588    $params->{page_actions} = $app->page_actions('list_templates');
589    $params->{search_label} = $app->translate("Templates");
590    $params->{object_type} = 'template';
591    $params->{blog_view} = 1;
592    $params->{refreshed} = $app->param('refreshed');
593    $params->{published} = $app->param('published');
594    $params->{saved_copied} = $app->param('saved_copied');
595    $params->{saved_deleted} = $app->param('saved_deleted');
596    $params->{saved} = $app->param('saved');
597
598    # determine list of system template types:
599    my $scope;
600    my $set;
601    if ( $blog ) {
602        $set   = $blog->template_set;
603        $scope = 'system';
604    }
605    else {
606        $scope = 'global:system';
607    }
608    my @tmpl_path = ( $set && ($set ne 'mt_blog')) ? ("template_sets", $set, 'templates', $scope) : ("default_templates", $scope);
609    my $sys_tmpl = MT->registry(@tmpl_path) || {};
610
611    my @tmpl_loop;
612    my %types;
613    if ($template_type ne 'backup') {
614        if ($blog) {
615            # blog template listings
616            %types = ( 
617                'index' => {
618                    label => $app->translate("Index Templates"),
619                    type => 'index',
620                    order => 100,
621                },
622                'archive' => {
623                    label => $app->translate("Archive Templates"),
624                    type => ['archive', 'individual', 'page', 'category'],
625                    order => 200,
626                },
627                'module' => {
628                    label => $app->translate("Template Modules"),
629                    type => 'custom',
630                    order => 300,
631                },
632                'system' => {
633                    label => $app->translate("System Templates"),
634                    type => [ keys %$sys_tmpl ],
635                    order => 400,
636                },
637            );
638        } else {
639            # global template listings
640            %types = ( 
641                'module' => {
642                    label => $app->translate("Template Modules"),
643                    type => 'custom',
644                    order => 100,
645                },
646                'email' => {
647                    label => $app->translate("Email Templates"),
648                    type => 'email',
649                    order => 200,
650                },
651                'system' => {
652                    label => $app->translate("System Templates"),
653                    type => [ keys %$sys_tmpl ],
654                    order => 300,
655                },
656            );
657        }
658    } else {
659        # global template listings
660        %types = ( 
661            'backup' => {
662                label => $app->translate("Template Backups"),
663                type => 'backup',
664                order => 100,
665            },
666        );
667    }
668    my @types = sort { $types{$a}->{order} <=> $types{$b}->{order} } keys %types;
669    if ($template_type) {
670        @types = ( $template_type );
671    }
672    $app->delete_param('filter_key') if $filter;
673    foreach my $tmpl_type (@types) {
674        if ( $tmpl_type eq 'index' ) {
675            $app->param( 'filter_key', 'index_templates' );
676        }
677        elsif ( $tmpl_type eq 'archive' ) {
678            $app->param( 'filter_key', 'archive_templates' );
679        }
680        elsif ( $tmpl_type eq 'system' ) {
681            $app->param( 'filter_key', 'system_templates' );
682        }
683        elsif ( $tmpl_type eq 'email' ) {
684            $app->param( 'filter_key', 'email_templates' );
685        }
686        elsif ( $tmpl_type eq 'module' ) {
687            $app->param( 'filter_key', 'module_templates' );
688        }
689        $terms->{type} = $types{$tmpl_type}->{type};
690        my $tmpl_param = $app->listing(
691            {
692                type     => 'template',
693                terms    => $terms,
694                args     => $args,
695                no_limit => 1,
696                no_html  => 1,
697                code     => $hasher,
698            }
699        );
700
701        my $template_type_label = $types{$tmpl_type}->{label};
702        $tmpl_param->{template_type} = $tmpl_type;
703        $tmpl_param->{template_type_label} = $template_type_label;
704        push @tmpl_loop, $tmpl_param;
705    }
706    if ($filter) {
707        $params->{filter_key} = $filter;
708        $params->{filter_label} = $types{$template_type}{label}
709            if exists $types{$template_type};
710        $app->param('filter_key', $filter);
711    } else {
712        # restore filter_key param (we modified it for the
713        # sake of the individual table listings)
714        $app->delete_param('filter_key');
715    }
716
717    $params->{template_type_loop} = \@tmpl_loop;
718    $params->{screen_id} = "list-template";
719
720    return $app->load_tmpl('list_template.tmpl', $params);
721}
722
723sub preview {
724    my $app         = shift;
725    my $q           = $app->param;
726    my $blog_id     = $q->param('blog_id');
727    my $blog        = $app->blog;
728    my $id          = $q->param('id');
729    my $tmpl;
730    my $user_id = $app->user->id;
731
732    # We can only do previews on blog templates. Have to publish
733    # the preview file somewhere!
734    return $app->errtrans("Invalid request.") unless $blog;
735
736    require MT::Template;
737    if ($id) {
738        $tmpl = MT::Template->load( { id => $id, blog_id => $blog_id } )
739            or return $app->errtrans( "Invalid request." );
740    }
741    else {
742        $tmpl = MT::Template->new;
743        $tmpl->id(-1);
744        $tmpl->blog_id($blog_id);
745    }
746
747    my $names = $tmpl->column_names;
748    my %values = map { $_ => scalar $app->param($_) } @$names;
749    delete $values{'id'} unless $q->param('id');
750
751    ## Strip linefeed characters.
752    for my $col (qw( text )) {
753        $values{$col} =~ tr/\r//d if $values{$col};
754    }
755    $tmpl->set_values( \%values );
756
757    my $preview_basename = $app->preview_object_basename;
758
759    my $type = $tmpl->type;
760    my $preview_tmpl = $tmpl;
761    my $archive_file;
762    my $archive_url;
763    my %param;
764    my $blog_path = $blog->site_path;
765    my $blog_url = $blog->site_url;
766
767    if (($type eq 'custom') || ($type eq 'widget')) {
768        # determine 'host' template
769        $preview_tmpl = MT::Template->load({ blog_id => $blog_id, identifier => 'main_index' });
770        if (!$preview_tmpl) {
771            return $app->errtrans("Can't locate host template to preview module/widget.");
772        }
773        my $req = $app->request;
774        # stash this module so that it is selected through a
775        # MTInclude tag instead of the one in the database:
776        my $tmpl_name = $tmpl->name;
777        $tmpl_name =~ s/^Widget: // if $type eq 'widget';
778        my $stash_id = 'template_' . $type . '::' . $blog_id . '::' . $tmpl_name;
779        $req->stash($stash_id, [ $tmpl, $tmpl->tokens ]);
780    } elsif (($type eq 'individual') || ($type eq 'page')) {
781        my $ctx = $preview_tmpl->context;
782        my $entry_type = $type eq 'individual' ? 'entry' : 'page';
783        my $entry_class = $app->model($entry_type);
784        my $obj = $entry_class->load({
785            blog_id => $blog_id,
786            status => MT::Entry::RELEASE()
787        }, {
788            limit => 1,
789            direction => 'descend',
790            'sort' => 'authored_on'
791        });
792        unless ( $obj ) {
793            # create a dummy object
794            $obj = $entry_class->new;
795            $obj->blog_id($blog_id);
796            $obj->id(-1);
797            $obj->author_id( $app->user->id );
798            $obj->authored_on( $blog->current_timestamp );
799            $obj->status( MT::Entry::RELEASE() );
800            $obj->basename( $preview_basename );
801            $obj->title($app->translate("Lorem ipsum"));
802            my $preview_text = $app->translate('LOREM_IPSUM_TEXT');
803            if ($preview_text eq 'LOREM_IPSUM_TEXT') {
804                $preview_text = q{Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut diam quam, accumsan eu, aliquam vel, ultrices a, augue. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Fusce hendrerit, lacus eget bibendum sollicitudin, mi tellus interdum neque, sit amet pretium tortor tellus id erat. Duis placerat justo ac erat. Duis posuere, risus eu elementum viverra, nisl lacus sagittis lorem, ac fermentum neque pede vitae arcu. Phasellus arcu elit, placerat eu, luctus posuere, tristique non, augue. In hac habitasse platea dictumst. Nunc non dolor et ipsum mattis malesuada. Praesent porta orci eu ligula. Ut dui augue, dapibus vitae, sodales in, lobortis non, felis. Aliquam feugiat mollis ipsum.};
805            }
806            my $preview_more = $app->translate('LORE_IPSUM_TEXT_MORE');
807            if ($preview_text eq 'LOREM_IPSUM_TEXT_MORE') {
808                $preview_more = q{Integer nunc nulla, vulputate sit amet, varius ac, faucibus ac, lectus. Nulla semper bibendum justo. In hac habitasse platea dictumst. Aliquam auctor pretium ante. Etiam porta consectetuer erat. Phasellus consequat, nisi eu suscipit elementum, metus leo malesuada pede, vel scelerisque lorem ligula in augue. Sed aliquet. Donec malesuada metus sit amet sapien. Integer non libero. Morbi egestas, mauris posuere consequat sodales, augue lectus suscipit velit, eu commodo lacus dolor congue justo. Suspendisse justo. Curabitur sagittis, lorem tincidunt elementum rhoncus, odio dolor mattis odio, quis ultrices ligula ipsum ac lacus. Nam et sapien ac lacus ultrices sollicitudin. Vestibulum ut dolor nec dui malesuada imperdiet. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
809
810                Quisque pharetra libero quis nibh. Cras lacus orci, commodo et, fringilla non, lobortis non, mauris. Curabitur dui sapien, tristique imperdiet, ultrices vitae, gravida varius, ante. Maecenas ac arcu nec nibh euismod feugiat. Pellentesque sed orci eget enim egestas faucibus. Aenean laoreet leo ornare velit. Nunc fermentum dolor eget massa. Fusce fringilla, tellus in pellentesque sodales, urna mi hendrerit leo, vel adipiscing ligula odio sit amet risus. Cras rhoncus, mi et posuere gravida, purus sem porttitor nisl, auctor laoreet nisl turpis quis ligula. Aliquam in nisi tristique augue egestas lacinia. Aenean ante magna, facilisis a, faucibus at, aliquam laoreet, dui. Ut tellus leo, tristique a, pellentesque ac, bibendum non, ipsum. Curabitur eu neque pretium arcu accumsan tincidunt. Ut ipsum. Quisque congue accumsan elit. Nulla ligula felis, aliquam ultricies, vestibulum vestibulum, semper vel, sapien. Aenean sodales ligula venenatis tellus. Vestibulum leo. Morbi viverra convallis eros.
811
812                Phasellus rhoncus pulvinar enim. Ut gravida ante nec lectus. Nam luctus gravida odio. Morbi vitae lorem vitae justo fermentum porttitor. Suspendisse vestibulum magna at purus. Cras nec sem. Duis id felis. Mauris hendrerit dapibus est. Donec semper. Praesent vehicula interdum velit. Ut sed tellus et diam venenatis pulvinar.};
813            }
814            $obj->text($preview_text);
815            $obj->text_more($preview_more);
816            $obj->keywords(MT->translate("sample, entry, preview"));
817            $obj->tags(qw( lorem ipsum sample preview ));
818        }
819        $ctx->stash('entry', $obj);
820        $ctx->{current_archive_type} = $type eq 'individual' ? 'Individual' : 'Page';
821        if (($type eq 'individual') && $blog->archive_path) {
822            $blog_path = $blog->archive_path;
823            $blog_url = $blog->archive_url;
824        }
825        $archive_file = File::Spec->catfile( $blog_path, $obj->archive_file );
826        $archive_url = $obj->archive_url;
827    } elsif ($type eq 'archive') {
828        # some variety of archive template
829    } elsif ($type eq 'index') {
830    } else {
831        # for now, only index templates can be previewed
832        return $app->errtrans("Invalid request.");
833    }
834
835    my $orig_file;
836    my $path;
837
838    # Default case; works for index templates (other template types should
839    # have defined $archive_file by now).
840    $archive_file = File::Spec->catfile( $blog_path, $preview_tmpl->outfile )
841        unless defined $archive_file;
842
843    ( $orig_file, $path ) = File::Basename::fileparse( $archive_file );
844
845    $archive_url = MT::Util::caturl( $blog_url, $orig_file )
846        unless defined $archive_url;
847
848    my $file_ext;
849    require File::Basename;
850    $file_ext = $archive_file;
851    if ($file_ext =~ m/\.[a-z]+$/) {
852        $file_ext =~ s!.+\.!.!;
853    } else {
854        $file_ext = '';
855    }
856    $archive_file = File::Spec->catfile( $path, $preview_basename . $file_ext );
857
858    my @data;
859    $app->run_callbacks( 'cms_pre_preview.template', $app, $preview_tmpl, \@data );
860
861    my $has_hires = eval 'require Time::HiRes; 1' ? 1 : 0;
862    my $start_time = $has_hires ? Time::HiRes::time() : time;
863
864    my $ctx = $preview_tmpl->context;
865    my $html = $preview_tmpl->output;
866
867    $param{build_time} = $has_hires ? sprintf("%.3f", Time::HiRes::time() - $start_time ) : "~" . ( time - $start_time );
868
869    unless ( defined($html) ) {
870        return $app->error( $app->translate( "Publish error: [_1]",
871            MT::Util::encode_html( $preview_tmpl->errstr ) ) );
872    }
873
874    # If MT is configured to do 'local' previews, convert all
875    # the normal blog URLs into the domain used by MT itself (ie,
876    # blog is published to www.example.com, which is a different
877    # server from where MT runs, mt.example.com; previews therefore
878    # should occur locally, so replace all http://www.example.com/
879    # with http://mt.example.com/).
880    my ($old_url, $new_url);
881    if ($app->config('LocalPreviews')) {
882        $old_url = $blog_url;
883        $old_url =~ s!^(https?://[^/]+?/)(.*)?!$1!;
884        $new_url = $app->base . '/';
885        $html =~ s!\Q$old_url\E!$new_url!g;
886    }
887
888    my $fmgr = $blog->file_mgr;
889
890    ## Determine if we need to build directory structure,
891    ## and build it if we do. DirUmask determines
892    ## directory permissions.
893    require File::Basename;
894    $path =~ s!/$!!
895      unless $path eq '/';    ## OS X doesn't like / at the end in mkdir().
896    unless ( $fmgr->exists($path) ) {
897        $fmgr->mkpath($path);
898    }
899
900    if ( $fmgr->exists($path) && $fmgr->can_write($path) ) {
901        $param{preview_file} = $preview_basename;
902        my $preview_url = $archive_url;
903        $preview_url =~ s! / \Q$orig_file\E ( /? ) $!/$preview_basename$file_ext$1!x;
904
905        # We also have to translate the URL used for the
906        # published file to be on the MT app domain.
907        if (defined $new_url) {
908            $preview_url =~ s!^\Q$old_url\E!$new_url!;
909        }
910
911        $param{preview_url}  = $preview_url;
912
913        $fmgr->put_data( $html, $archive_file );
914
915        # we have to make a record of this preview just in case it
916        # isn't cleaned up by re-editing, saving or cancelling on
917        # by the user.
918        require MT::Session;
919        my $sess_obj = MT::Session->get_by_key(
920            {
921                id   => $preview_basename,
922                kind => 'TF',                # TF = Temporary File
923                name => $archive_file,
924            }
925        );
926        $sess_obj->start(time);
927        $sess_obj->save;
928    }
929    else {
930        return $app->error( $app->translate(
931            "Unable to create preview file in this location: [_1]", $path ) );
932    }
933
934    $param{id} = $id if $id;
935    $param{new_object} = $param{id} ? 0 : 1;
936    $param{name} = $tmpl->name;
937    my $cols = $tmpl->column_names;
938    for my $col (@$cols) {
939        push @data,
940          {
941            data_name  => $col,
942            data_value => scalar $q->param($col)
943          };
944    }
945    $param{template_loop} = \@data;
946    $param{object_type}  = $type;
947    return $app->load_tmpl( 'preview_template_strip.tmpl', \%param );
948}
949
950sub reset_blog_templates {
951    my $app   = shift;
952    my $q     = $app->param;
953    my $perms = $app->permissions
954      or return $app->error( $app->translate("No permissions") );
955    return $app->error( $app->translate("Permission denied.") )
956      unless $perms->can_edit_templates;
957    $app->validate_magic() or return;
958    my $blog = MT::Blog->load( $perms->blog_id )
959        or return $app->error($app->translate('Can\'t load blog #[_1].', $perms->blog_id));
960    require MT::Template;
961    my @tmpl = MT::Template->load( { blog_id => $blog->id } );
962
963    for my $tmpl (@tmpl) {
964        $tmpl->remove or return $app->error( $tmpl->errstr );
965    }
966    my $set = $blog ? $blog->template_set : undef;
967    require MT::DefaultTemplates;
968    my $tmpl_list = MT::DefaultTemplates->templates($set) || [];
969    my @arch_tmpl;
970    for my $val (@$tmpl_list) {
971        $val->{name} = $app->translate( $val->{name} );
972        $val->{text} = $app->translate_templatized( $val->{text} );
973        my $tmpl = MT::Template->new;
974        if ( ( 'widgetset' eq $val->{type} )
975          && ( exists $val->{modulesets} ) ) {
976            my $modulesets = delete $val->{modulesets};
977            $tmpl->modulesets( join ',', @$modulesets );
978        }
979        $tmpl->set_values($val);
980        $tmpl->build_dynamic(0);
981        $tmpl->blog_id( $blog->id );
982        $tmpl->save
983          or return $app->error(
984            $app->translate(
985                "Populating blog with default templates failed: [_1]",
986                $tmpl->errstr
987            )
988          );
989
990        # FIXME: enumeration of types
991        if (   $val->{type} eq 'archive'
992            || $val->{type} eq 'category'
993            || $val->{type} eq 'page'
994            || $val->{type} eq 'individual' )
995        {
996            push @arch_tmpl, $tmpl;
997        }
998    }
999
1000    ## Set up mappings from new templates to archive types.
1001    for my $tmpl (@arch_tmpl) {
1002        my (@at);
1003
1004        # FIXME: enumeration of types
1005        if ( $tmpl->type eq 'archive' ) {
1006            @at = qw( Daily Weekly Monthly Category );
1007        }
1008        elsif ( $tmpl->type eq 'page' ) {
1009            @at = qw( Page );
1010        }
1011        elsif ( $tmpl->type eq 'individual' ) {
1012            @at = qw( Individual );
1013        }
1014        require MT::TemplateMap;
1015        for my $at (@at) {
1016            my $map = MT::TemplateMap->new;
1017            $map->archive_type($at);
1018            $map->is_preferred(1);
1019            $map->template_id( $tmpl->id );
1020            $map->blog_id( $tmpl->blog_id );
1021            $map->save
1022              or return $app->error(
1023                $app->translate(
1024                    "Setting up mappings failed: [_1]",
1025                    $map->errstr
1026                )
1027              );
1028        }
1029    }
1030    $app->redirect(
1031        $app->uri(
1032            'mode' => 'list',
1033            args =>
1034              { '_type' => 'template', blog_id => $blog->id, 'reset' => 1 }
1035        )
1036    );
1037}
1038
1039sub _generate_map_table {
1040    my $app = shift;
1041    my ( $blog_id, $template_id ) = @_;
1042
1043    require MT::Template;
1044    require MT::Blog;
1045    my $blog     = MT::Blog->load($blog_id);
1046    my $template = MT::Template->load($template_id);
1047    my $tmpl     = $app->load_tmpl('include/archive_maps.tmpl');
1048    my $maps     = _populate_archive_loop( $app, $blog, $template );
1049    $tmpl->param( object_type => 'templatemap' );
1050    $tmpl->param( publish_queue_available => eval 'require List::Util; require Scalar::Util; 1;' );
1051    $tmpl->param( object_loop => $maps ) if @$maps;
1052    my $html = $tmpl->output();
1053
1054    if ( $html =~ m/<__trans / ) {
1055        $html = $app->translate_templatized($html);
1056    }
1057    $html;
1058}
1059
1060sub _populate_archive_loop {
1061    my $app = shift;
1062    my ( $blog, $obj ) = @_;
1063
1064    my $index = $app->config('IndexBasename');
1065    my $ext = $blog->file_extension || '';
1066    $ext = '.' . $ext if $ext ne '';
1067
1068    require MT::TemplateMap;
1069    my @tmpl_maps = MT::TemplateMap->load( { template_id => $obj->id } );
1070    my @maps;
1071    my %types;
1072    foreach my $map_obj (@tmpl_maps) {
1073        my $map = {};
1074        $map->{map_id}           = $map_obj->id;
1075        $map->{map_is_preferred} = $map_obj->is_preferred;
1076        # publish options
1077        $map->{map_build_type} = $map_obj->build_type;
1078        $map->{ 'map_build_type_' . ( $map_obj->build_type || 0 ) } = 1;
1079        my ( $period, $interval ) = _get_schedule( $map_obj->build_interval );
1080        $map->{ 'map_schedule_period_' . $period } = 1
1081            if defined $period;
1082        $map->{map_schedule_interval} = $interval
1083            if defined $interval;
1084
1085        my $at = $map->{archive_type} = $map_obj->archive_type;
1086        $types{$at}++;
1087        $map->{ 'archive_type_preferred_' . $blog->archive_type_preferred } = 1
1088          if $blog->archive_type_preferred;
1089        $map->{file_template} = $map_obj->file_template
1090          if $map_obj->file_template;
1091
1092        my $archiver = $app->publisher->archiver($at);
1093        next unless $archiver;
1094        $map->{archive_label} = $archiver->archive_label;
1095        my $tmpls     = $archiver->default_archive_templates;
1096        my $tmpl_loop = [];
1097        foreach (@$tmpls) {
1098            my $name = $_->{label};
1099            $name =~ s/\.html$/$ext/;
1100            $name =~ s/index$ext$/$index$ext/;
1101            push @$tmpl_loop,
1102              {
1103                name    => $name,
1104                value   => $_->{template},
1105                default => ( $_->{default} || 0 ),
1106              };
1107        }
1108
1109        my $custom = 1;
1110
1111        foreach (@$tmpl_loop) {
1112            if (   ( !$map->{file_template} && $_->{default} )
1113                || ( $map->{file_template} eq $_->{value} ) )
1114            {
1115                $_->{selected}        = 1;
1116                $custom               = 0;
1117                $map->{file_template} = $_->{value}
1118                  if !$map->{file_template};
1119            }
1120        }
1121        if ($custom) {
1122            unshift @$tmpl_loop,
1123              {
1124                name     => $map->{file_template},
1125                value    => $map->{file_template},
1126                selected => 1,
1127              };
1128        }
1129
1130        $map->{archive_tmpl_loop} = $tmpl_loop;
1131        if (
1132            1 < MT::TemplateMap->count(
1133                { archive_type => $at, blog_id => $obj->blog_id }
1134            )
1135          )
1136        {
1137            $map->{has_multiple_archives} = 1;
1138        }
1139
1140        push @maps, $map;
1141    }
1142    @maps = sort { MT::App::CMS::archive_type_sorter( $a, $b ) } @maps;
1143    return \@maps;
1144}
1145
1146sub delete_map {
1147    my $app = shift;
1148    $app->validate_magic() or return;
1149    my $perms = $app->{perms}
1150      or return $app->error( $app->translate("No permissions") );
1151    my $q  = $app->param;
1152    my $id = $q->param('id');
1153
1154    require MT::TemplateMap;
1155    MT::TemplateMap->remove( { id => $id } );
1156    my $html =
1157      _generate_map_table( $app, $q->param('blog_id'),
1158        $q->param('template_id') );
1159    $app->{no_print_body} = 1;
1160    $app->send_http_header("text/plain");
1161    $app->print($html);
1162}
1163
1164sub add_map {
1165    my $app = shift;
1166    $app->validate_magic() or return;
1167    my $perms = $app->{perms}
1168      or return $app->error( $app->translate("No permissions") );
1169
1170    my $q = $app->param;
1171
1172    require MT::TemplateMap;
1173    my $blog_id = $q->param('blog_id');
1174    my $at      = $q->param('new_archive_type');
1175    my $exist   = MT::TemplateMap->exist(
1176        {
1177            blog_id      => $blog_id,
1178            archive_type => $at
1179        }
1180    );
1181    my $map = MT::TemplateMap->new;
1182    $map->is_preferred( $exist ? 0 : 1 );
1183    $map->template_id( scalar $q->param('template_id') );
1184    $map->blog_id($blog_id);
1185    $map->archive_type($at);
1186    $map->save
1187      or return $app->error(
1188        $app->translate( "Saving map failed: [_1]", $map->errstr ) );
1189    my $html =
1190      _generate_map_table( $app, $blog_id, scalar $q->param('template_id') );
1191    $app->{no_print_body} = 1;
1192    $app->send_http_header("text/plain");
1193    $app->print($html);
1194}
1195
1196sub can_view {
1197    my ( $eh, $app, $id ) = @_;
1198    my $perms = $app->permissions;
1199    return !$id || ($perms && $perms->can_edit_templates) || (!$app->blog && $app->user->can_edit_templates);
1200}
1201
1202sub can_save {
1203    my ( $eh, $app, $id ) = @_;
1204    my $perms = $app->permissions;
1205    return ($perms && $perms->can_edit_templates) || (!$perms && $app->user->can_edit_templates);
1206}
1207
1208sub can_delete {
1209    my ( $eh, $app, $obj ) = @_;
1210    return 1 if $app->user->is_superuser();
1211    my $perms = $app->permissions;
1212    return ($perms && $perms->can_edit_templates) || (!$perms && $app->user->can_edit_templates);
1213}
1214
1215sub pre_save {
1216    my $eh = shift;
1217    my ( $app, $obj ) = @_;
1218
1219    ## Strip linefeed characters.
1220    ( my $text = $obj->text ) =~ tr/\r//d;
1221
1222    if ($text =~ m/<(MT|_)_trans/i) {
1223        $text = $app->translate_templatized($text);
1224    }
1225
1226    $obj->text($text);
1227
1228    # update text heights if necessary
1229    if ( my $perms = $app->permissions ) {
1230        my $prefs = $perms->template_prefs || '';
1231        my $text_height = $app->param('text_height');
1232        if ( defined $text_height ) {
1233            my ($pref_text_height) = $prefs =~ m/\btext:(\d+)\b/;
1234            $pref_text_height ||= 0;
1235            if ( $text_height != $pref_text_height ) {
1236                if ( $prefs =~ m/\btext\b/ ) {
1237                    $prefs =~ s/\btext(:\d+)\b/text:$text_height/;
1238                }
1239                else {
1240                    $prefs = 'text:' . $text_height . ',' . $prefs;
1241                }
1242            }
1243        }
1244
1245        if ( $prefs ne ( $perms->template_prefs || '' ) ) {
1246            $perms->template_prefs($prefs);
1247            $perms->save;
1248        }
1249    }
1250
1251    # module caching
1252    $obj->include_with_ssi( $app->param('include_with_ssi') ? 1 : 0 );
1253    $obj->cache_path( $app->param('cache_path'));
1254    my $cache_expire_type = $app->param('cache_expire_type');
1255    $obj->cache_expire_type($cache_expire_type);
1256    my $period   = $app->param('cache_expire_period');
1257    my $interval = $app->param('cache_expire_interval');
1258    my $sec      = _get_interval( $period, $interval );
1259    $obj->cache_expire_interval($sec);
1260    my $q = $app->param;
1261    my @events;
1262
1263    foreach my $name ( $q->param('cache_expire_event') ) {
1264        push @events, $name;
1265    }
1266    $obj->cache_expire_event( join ',', @events );
1267    if ( $cache_expire_type == 1 ) {
1268        return $eh->error(
1269            $app->translate("You should not be able to enter 0 as the time.") )
1270          if $interval == 0;
1271    }
1272    elsif ( $cache_expire_type == 2 ) {
1273        return $eh->error(
1274            $app->translate("You must select at least one event checkbox.") )
1275          if !@events;
1276    }
1277
1278    require MT::PublishOption;
1279    my $build_type = $app->param('build_type');
1280
1281    if ( $build_type == MT::PublishOption::SCHEDULED() ) {
1282        my $period   = $app->param('schedule_period');
1283        my $interval = $app->param('schedule_interval');
1284        my $sec      = _get_interval( $period, $interval );
1285        $obj->build_interval($sec);
1286    }
1287    my $rebuild_me = 1;
1288    if (   $build_type == MT::PublishOption::DISABLED()
1289        || $build_type == MT::PublishOption::MANUALLY() )
1290    {
1291        $rebuild_me = 0;
1292    }
1293    $obj->rebuild_me($rebuild_me);
1294    1;
1295}
1296
1297sub post_save {
1298    my $eh = shift;
1299    my ( $app, $obj, $original ) = @_;
1300
1301    my $sess_obj = $app->autosave_session_obj;
1302    $sess_obj->remove if $sess_obj;
1303
1304    my $dynamic = 0;
1305    my $q = $app->param;
1306    my $type = $q->param('type');
1307    # FIXME: enumeration of types
1308    if ( $type eq 'custom'
1309      || $type eq 'index'
1310      || $type eq 'widget'
1311      || $type eq 'widgetset' )
1312    {
1313        $dynamic = $obj->build_dynamic;
1314    }
1315    else
1316    {
1317        # archive template specific post_save tasks
1318        require MT::TemplateMap;
1319        my @p = $q->param;
1320        for my $p (@p) {
1321            my $map;
1322            if ( $p =~ /^archive_tmpl_preferred_(\w+)_(\d+)$/ ) {
1323                my $at     = $1;
1324                my $map_id = $2;
1325                $map    = MT::TemplateMap->load($map_id)
1326                    or next;
1327                $map->prefer( $q->param($p) );    # prefer method saves in itself
1328            }
1329            elsif ( $p =~ /^archive_file_tmpl_(\d+)$/ ) {
1330                my $map_id = $1;
1331                $map    = MT::TemplateMap->load($map_id)
1332                    or next;
1333                $map->file_template( $q->param($p) );
1334                $map->save;
1335            }
1336            elsif ( $p =~ /^map_build_type_(\d+)$/ ) {
1337                my $map_id     = $1;
1338                $map        = MT::TemplateMap->load($map_id)
1339                    or next;
1340                my $build_type = $q->param($p);
1341                require MT::PublishOption;
1342                $map->build_type($build_type);
1343                if ( $build_type == MT::PublishOption::SCHEDULED() ) {
1344                    my $period   = $q->param( 'map_schedule_period_' . $map_id );
1345                    my $interval = $q->param( 'map_schedule_interval_' . $map_id );
1346                    my $sec      = _get_interval( $period, $interval );
1347                    $map->build_interval($sec);
1348                }
1349                $map->save;
1350            }
1351            if ( !$dynamic
1352              && $map && $map->build_type == MT::PublishOption::DYNAMIC() )
1353            {
1354                $dynamic = 1;
1355            }
1356        }
1357    }
1358
1359    if ( !$original->id ) {
1360        $app->log(
1361            {
1362                message => $app->translate(
1363                    "Template '[_1]' (ID:[_2]) created by '[_3]'",
1364                    $obj->name, $obj->id, $app->user->name
1365                ),
1366                level    => MT::Log::INFO(),
1367                class    => 'template',
1368                category => 'new',
1369            }
1370        );
1371    }
1372
1373    if ( $dynamic ) {
1374        if ( $obj->type eq 'index' ) {
1375            $app->rebuild_indexes(
1376                BlogID   => $obj->blog_id,
1377                Template => $obj,
1378                NoStatic => 1,
1379            ) or return $app->publish_error();    # XXXX
1380        }
1381        if ( my $blog = $app->blog ) {
1382            require MT::CMS::Blog;
1383            my ( $path, $url );
1384            if ( $obj->type eq 'index' ) {
1385                $path = $blog->site_path;
1386                $url = $blog->site_url;
1387            }
1388            else {
1389                # must be archive since other types can't be dynamic
1390                if ( $path = $blog->archive_path ) {
1391                    $url = $blog->archive_url;
1392                }
1393                else {
1394                    $path = $blog->site_path;
1395                    $url = $blog->site_url;
1396                }
1397            }
1398            # specific arguments so not to overwrite mtview and htaccess
1399            MT::CMS::Blog::prepare_dynamic_publishing(
1400                $eh, 
1401                $blog,
1402                undef,
1403                undef,
1404                $path,
1405                $url
1406            );
1407        }
1408    }
1409    1;
1410}
1411
1412sub post_delete {
1413    my ( $eh, $app, $obj ) = @_;
1414
1415    $app->log(
1416        {
1417            message => $app->translate(
1418                "Template '[_1]' (ID:[_2]) deleted by '[_3]'",
1419                $obj->name, $obj->id, $app->user->name
1420            ),
1421            level    => MT::Log::INFO(),
1422            class    => 'system',
1423            category => 'delete'
1424        }
1425    );
1426}
1427
1428sub build_template_table {
1429    my $app = shift;
1430    my (%args) = @_;
1431
1432    my $perms     = $app->permissions;
1433    my $list_pref = $app->list_pref('template');
1434    my $limit     = $args{limit};
1435    my $param     = $args{param} || {};
1436    my $iter;
1437    if ( $args{load_args} ) {
1438        my $class = $app->model('template');
1439        $iter = $class->load_iter( @{ $args{load_args} } );
1440    }
1441    elsif ( $args{iter} ) {
1442        $iter = $args{iter};
1443    }
1444    elsif ( $args{items} ) {
1445        $iter = sub { pop @{ $args{items} } };
1446        $limit = scalar @{ $args{items} };
1447    }
1448    return [] unless $iter;
1449
1450    my @data;
1451    my $i;
1452    my %blogs;
1453    while ( my $tmpl = $iter->() ) {
1454        my $blog = $blogs{ $tmpl->blog_id } ||=
1455          MT::Blog->load( $tmpl->blog_id ) if $tmpl->blog_id;
1456
1457        my $row = $tmpl->column_values;
1458        $row->{name} = '' if !defined $row->{name};
1459        $row->{name} =~ s/^\s+|\s+$//g;
1460        $row->{name} = "(" . $app->translate("No Name") . ")"
1461          if $row->{name} eq '';
1462        my $published_url = $tmpl->published_url;
1463        $row->{published_url} = $published_url if $published_url;
1464        $row->{use_cache} = ( ($tmpl->cache_expire_type || 0) != 0 )  ? 1 : 0;
1465
1466        # FIXME: enumeration of types
1467        $row->{can_delete} = 1
1468          if $tmpl->type =~ m/(custom|index|archive|page|individual|category|widget)/;
1469        if ($blog) {
1470            $row->{weblog_name} = $blog->name;
1471        }
1472        elsif ($tmpl->blog_id) {
1473            $row->{weblog_name} = '* ' . $app->translate('Orphaned') . ' *';
1474        }
1475        else {
1476            $row->{weblog_name} = '* ' . $app->translate('Global Templates') . ' *';
1477        }
1478        $row->{object} = $tmpl;
1479        push @data, $row;
1480        last if defined($limit) && (@data > $limit);
1481    }
1482    return [] unless @data;
1483
1484    $param->{template_table}[0]              = {%$list_pref};
1485    $param->{template_table}[0]{object_loop} = \@data;
1486    $param->{template_table}[0]{object_type} = 'template';
1487    $app->load_list_actions( 'template', $param );
1488    $param->{object_loop} = \@data;
1489    \@data;
1490}
1491
1492sub dialog_publishing_profile {
1493    my $app = shift;
1494    $app->validate_magic or return;
1495
1496    my $blog = $app->blog;
1497    $app->assert( $blog ) or return;
1498
1499    # permission check
1500    my $perms = $app->permissions;
1501    return $app->errtrans("Permission denied.")
1502        unless $app->user->is_superuser ||
1503            $perms->can_administer_blog ||
1504            $perms->can_edit_templates;
1505
1506    my $param = {};
1507    $param->{dynamicity} = $blog->custom_dynamic_templates || 'none';
1508    $param->{screen_id} = "publishing-profile-dialog";
1509    $param->{return_args} = $app->param('return_args');
1510
1511    $app->build_page('dialog/publishing_profile.tmpl',
1512        $param);
1513}
1514
1515sub dialog_refresh_templates {
1516    my $app = shift;
1517    $app->validate_magic or return;
1518
1519    # permission check
1520    my $perms = $app->permissions;
1521    return $app->errtrans("Permission denied.")
1522        unless $app->user->is_superuser ||
1523            $perms->can_administer_blog ||
1524            $perms->can_edit_templates;
1525
1526    my $param = {};
1527    my $blog = $app->blog;
1528    $param->{return_args} = $app->param('return_args');
1529
1530    if ($blog) {
1531        $param->{blog_id} = $blog->id;
1532
1533        my $sets = $app->registry("template_sets");
1534        $sets->{$_}{key} = $_ for keys %$sets;
1535        $sets = $app->filter_conditional_list([ values %$sets ]);
1536
1537        no warnings; # some sets may not define an order
1538        @$sets = sort { $a->{order} <=> $b->{order} } @$sets;
1539        $param->{'template_set_loop'} = $sets;
1540
1541        my $existing_set = $blog->template_set || 'mt_blog';
1542        foreach (@$sets) {
1543            if ($_->{key} eq $existing_set) {
1544                $_->{selected} = 1;
1545            }
1546        }
1547        $param->{'template_set_index'} = $#$sets;
1548        $param->{'template_set_count'} = scalar @$sets;
1549
1550        $param->{template_sets} = $sets;
1551        $param->{screen_id} = "refresh-templates-dialog";
1552    }
1553
1554    # load template sets
1555    $app->build_page('dialog/refresh_templates.tmpl',
1556        $param);
1557}
1558
1559sub refresh_all_templates {
1560    my ($app) = @_;
1561
1562    my $backup = 0;
1563    if ($app->param('backup')) {
1564        # refresh templates dialog uses a 'backup' field
1565        $backup = 1;
1566    }
1567
1568    my $template_set = $app->param('template_set');
1569    my $refresh_type = $app->param('refresh_type') || 'refresh';
1570
1571    my $t = time;
1572
1573    my @id;
1574    if ($app->param('blog_id')) {
1575        @id = ( scalar $app->param('blog_id') );
1576    }
1577    else {
1578        @id = $app->param('id');
1579        if (! @id) {
1580            # refresh global templates
1581            @id = ( 0 );
1582        }
1583    }
1584
1585    require MT::Template;
1586    require MT::DefaultTemplates;
1587    require MT::Blog;
1588    require MT::Permission;
1589    require MT::Util;
1590
1591    foreach my $blog_id (@id) {
1592        my $blog;
1593        if ($blog_id) {
1594            $blog = MT::Blog->load($blog_id);
1595            next unless $blog;
1596        }
1597        if ( !$app->user->is_superuser() ) {
1598            my $perms = MT::Permission->load(
1599                { blog_id => $blog_id, author_id => $app->user->id } );
1600            if (
1601                !$perms
1602                || (   !$perms->can_edit_templates()
1603                    && !$perms->can_administer_blog() )
1604              )
1605            {
1606                next;
1607            }
1608        }
1609
1610        my $tmpl_list;
1611        if ($blog_id) {
1612
1613            if ($refresh_type eq 'clean') {
1614                # the user wants to back up all templates and
1615                # install the new ones
1616
1617                my @ts = MT::Util::offset_time_list( $t, $blog_id );
1618                my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d",
1619                    $ts[5] + 1900, $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1620
1621                my $tmpl_iter = MT::Template->load_iter({
1622                    blog_id => $blog_id,
1623                    type => { not => 'backup' },
1624                });
1625
1626                while (my $tmpl = $tmpl_iter->()) {
1627                    if ($backup) {
1628                        # zap all template maps
1629                        require MT::TemplateMap;
1630                        MT::TemplateMap->remove({
1631                            template_id => $tmpl->id,
1632                        });
1633                        $tmpl->type('backup');
1634                        $tmpl->name(
1635                            $tmpl->name . ' (Backup from ' . $ts . ')' );
1636                        $tmpl->identifier(undef);
1637                        $tmpl->rebuild_me(0);
1638                        $tmpl->linked_file(undef);
1639                        $tmpl->outfile('');
1640                        $tmpl->save;
1641                    } else {
1642                        $tmpl->remove;
1643                    }
1644                }
1645
1646                # This also creates our template mappings
1647                $blog->create_default_templates( $template_set ||
1648                    $blog->template_set || 'mt_blog' );
1649
1650                if ($template_set) {
1651                    $blog->template_set( $template_set );
1652                    $blog->save;
1653                    $app->run_callbacks( 'blog_template_set_change', { blog => $blog } );
1654                }
1655
1656                next;
1657            }
1658
1659            $tmpl_list = MT::DefaultTemplates->templates($template_set || $blog->template_set) || MT::DefaultTemplates->templates();
1660        }
1661        else {
1662            $tmpl_list = MT::DefaultTemplates->templates();
1663        }
1664
1665        foreach my $val (@$tmpl_list) {
1666            if ($blog_id) {
1667                # when refreshing blog templates,
1668                # skip over global templates which
1669                # specify a blog_id of 0...
1670                next if $val->{global};
1671            }
1672            else {
1673                next unless exists $val->{global};
1674            }
1675
1676            if ( !$val->{orig_name} ) {
1677                $val->{orig_name} = $val->{name};
1678                $val->{name}      = $app->translate( $val->{name} );
1679                $val->{text}      = $app->translate_templatized( $val->{text} );
1680            }
1681
1682            my $orig_name = $val->{orig_name};
1683
1684            my @ts = MT::Util::offset_time_list( $t, ( $blog_id ? $blog_id : undef ) );
1685            my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d", $ts[5] + 1900,
1686              $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1687
1688            my $terms = {};
1689            $terms->{blog_id} = $blog_id;
1690            $terms->{type} = $val->{type};
1691            if ( $val->{type} =~
1692                m/^(archive|individual|page|category|index|custom|widget)$/ )
1693            {
1694                $terms->{name} = $val->{name};
1695            }
1696            else {
1697                $terms->{identifier} = $val->{identifier};
1698            }
1699
1700            # this should only return 1 template; we're searching
1701            # within a given blog for a specific type of template (for
1702            # "system" templates; or for a type + name, which should be
1703            # unique for that blog.
1704            my $tmpl = MT::Template->load($terms);
1705            if ($tmpl && $backup) {
1706
1707                # check for default template text...
1708                # if it is a default template, then outright replace it
1709                my $text = $tmpl->text;
1710                $text =~ s/\s+//g;
1711
1712                my $def_text = $val->{text};
1713                $def_text =~ s/\s+//g;
1714
1715                # if it has been customized, back it up to a new tmpl record
1716                if ($def_text ne $text) {
1717                    my $backup = $tmpl->clone;
1718                    delete $backup->{column_values}
1719                      ->{id};    # make sure we don't overwrite original
1720                    delete $backup->{changed_cols}->{id};
1721                    $backup->name(
1722                        $backup->name . $app->translate( ' (Backup from [_1])', $ts ) );
1723                    $backup->type('backup');
1724                    # if ( $backup->type !~
1725                    #         m/^(archive|individual|page|category|index|custom|widget)$/ )
1726                    # {
1727                    #     $backup->type('custom')
1728                    #       ;      # system templates can't be created
1729                    # }
1730                    $backup->outfile('');
1731                    $backup->linked_file( $tmpl->linked_file );
1732                    $backup->identifier(undef);
1733                    $backup->rebuild_me(0);
1734                    $backup->build_dynamic(0);
1735                    $backup->save;
1736                }
1737            }
1738            if ($tmpl) {
1739                # we found that the previous template had not been
1740                # altered, so replace it with new default template...
1741                if ( ( 'widgetset' eq $val->{type} )
1742                  && ( exists $val->{widgets} ) ) {
1743                    my $modulesets = delete $val->{widgets};
1744                    $tmpl->modulesets( MT::Template->widgets_to_modulesets($modulesets, $blog_id) );
1745                }
1746                $tmpl->text( $val->{text} );
1747                $tmpl->identifier( $val->{identifier} );
1748                $tmpl->type( $val->{type} )
1749                  ; # fixes mismatch of types for cases like "archive" => "individual"
1750                $tmpl->linked_file('');
1751                $tmpl->save;
1752            }
1753            else {
1754                # create this one...
1755                my $tmpl = new MT::Template;
1756                if ( ( 'widgetset' eq $val->{type} )
1757                  && ( exists $val->{widgets} ) ) {
1758                    my $modulesets = delete $val->{widgets};
1759                    $tmpl->modulesets( MT::Template->widgets_to_modulesets($modulesets, $blog_id) );
1760                }
1761                $tmpl->build_dynamic(0);
1762                $tmpl->set_values(
1763                    {
1764                        text       => $val->{text},
1765                        name       => $val->{name},
1766                        type       => $val->{type},
1767                        identifier => $val->{identifier},
1768                        outfile    => $val->{outfile},
1769                        rebuild_me => $val->{rebuild_me},
1770                    }
1771                );
1772                $tmpl->blog_id($blog_id);
1773                $tmpl->save
1774                  or return $app->error(
1775                        $app->translate("Error creating new template: ")
1776                      . $tmpl->errstr );
1777            }
1778        }
1779    }
1780
1781    $app->add_return_arg( 'refreshed' => 1 );
1782    $app->call_return;
1783}
1784
1785sub refresh_individual_templates {
1786    my ($app) = @_;
1787
1788    require MT::Util;
1789
1790    my $user = $app->user;
1791    my $perms = $app->permissions;
1792    return $app->error(
1793        $app->translate(
1794            "Permission denied.")
1795      )
1796      #TODO: system level-designer permission
1797      unless $user->is_superuser() || $user->can_edit_templates()
1798      || ( $perms
1799        && ( $perms->can_edit_templates()
1800          || $perms->can_administer_blog ) );
1801
1802    my $set;
1803    if ( my $blog_id = $app->param('blog_id') ) {
1804        my $blog = $app->model('blog')->load($blog_id)
1805            or return $app->error($app->translate('Can\'t load blog #[_1].', $blog_id));
1806        $set = $blog->template_set()
1807            if $blog;
1808    }
1809
1810    require MT::DefaultTemplates;
1811    my $tmpl_list = MT::DefaultTemplates->templates($set) or return;
1812
1813    my $tmpl_types = {};
1814    my $tmpl_ids   = {};
1815    my $tmpls      = {};
1816    foreach my $tmpl (@$tmpl_list) {
1817        $tmpl->{text} = $app->translate_templatized( $tmpl->{text} );
1818        $tmpl_ids->{ $tmpl->{identifier} } = $tmpl
1819            if $tmpl->{identifier};
1820        if ( $tmpl->{type} !~ m/^(archive|individual|page|category|index|custom|widget)$/ )
1821        {
1822            $tmpl_types->{ $tmpl->{type} } = $tmpl;
1823        }
1824        else {
1825            $tmpls->{ $tmpl->{type} }{ $tmpl->{name} } = $tmpl;
1826        }
1827    }
1828
1829    my $t = time;
1830
1831    my @msg;
1832    my @id = $app->param('id');
1833    require MT::Template;
1834    foreach my $tmpl_id (@id) {
1835        my $tmpl = MT::Template->load($tmpl_id);
1836        next unless $tmpl;
1837        my $blog_id = $tmpl->blog_id;
1838
1839        # FIXME: permission check -- for this blog_id
1840
1841        my @ts = MT::Util::offset_time_list( $t, $blog_id );
1842        my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d", $ts[5] + 1900,
1843          $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1844
1845        my $val = ( $tmpl->identifier ? $tmpl_ids->{ $tmpl->identifier() } : undef )
1846          || $tmpl_types->{ $tmpl->type() }
1847          || $tmpls->{ $tmpl->type() }{ $tmpl->name };
1848        if ( !$val ) {
1849            push @msg,
1850              $app->translate(
1851"Skipping template '[_1]' since it appears to be a custom template.",
1852                $tmpl->name
1853              );
1854            next;
1855        }
1856
1857        my $text = $tmpl->text;
1858        $text =~ s/\s+//g;
1859
1860        my $def_text = $val->{text};
1861        $def_text =~ s/\s+//g;
1862
1863        if ($text ne $def_text) {
1864            # if it has been customized, back it up to a new tmpl record
1865            my $backup = $tmpl->clone;
1866            delete $backup->{column_values}
1867              ->{id};    # make sure we don't overwrite original
1868            delete $backup->{changed_cols}->{id};
1869            $backup->name( $backup->name . ' (Backup from ' . $ts . ')' );
1870            $backup->type('backup');
1871            $backup->outfile('');
1872            $backup->linked_file( $tmpl->linked_file );
1873            $backup->rebuild_me(0);
1874            $backup->build_dynamic(0);
1875            $backup->identifier(undef);
1876            $backup->save;
1877            push @msg,
1878              $app->translate(
1879    'Refreshing template <strong>[_3]</strong> with <a href="?__mode=view&amp;blog_id=[_1]&amp;_type=template&amp;id=[_2]">backup</a>',
1880                  $blog_id, $backup->id, $tmpl->name );
1881
1882            # we found that the previous template had not been
1883            # altered, so replace it with new default template...
1884            $tmpl->text( $val->{text} );
1885            $tmpl->identifier( $val->{identifier} );
1886            $tmpl->linked_file('');
1887            $tmpl->save;
1888        } else {
1889            push @msg, $app->translate("Skipping template '[_1]' since it has not been changed.", $tmpl->name);
1890        }
1891    }
1892    my @msg_loop;
1893    push @msg_loop, { message => $_ } foreach @msg;
1894
1895    $app->build_page( 'refresh_results.tmpl',
1896        { message_loop => \@msg_loop, return_url => $app->return_uri } );
1897}
1898
1899sub clone_templates {
1900    my ($app) = @_;
1901
1902    my $user = $app->user;
1903    my $perms = $app->permissions;
1904    return $app->error(
1905        $app->translate(
1906            "Permission denied.")
1907      )
1908      #TODO: system level-designer permission
1909      unless $user->is_superuser() || $user->can_edit_templates()
1910      || ( $perms
1911        && ( $perms->can_edit_templates()
1912          || $perms->can_administer_blog ) );
1913
1914    my @id = $app->param('id');
1915    require MT::Template;
1916    foreach my $tmpl_id (@id) {
1917        my $tmpl = MT::Template->load($tmpl_id);
1918        next unless $tmpl;
1919
1920        my $new_tmpl = $tmpl->clone({
1921            Except => {
1922                id => 1,
1923                name => 1,
1924                identifier => 1,
1925            },
1926        });
1927
1928        my $new_basename = $app->translate("Copy of [_1]", $tmpl->name);
1929        my $new_name = $new_basename;
1930        my $i = 0;
1931        while (MT::Template->exist({ name => $new_name, blog_id => $tmpl->blog_id })) {
1932            $new_name = $new_basename . ' (' . ++$i . ')';
1933        }
1934
1935        $new_tmpl->name($new_name);
1936        $new_tmpl->save;
1937    }
1938
1939    $app->add_return_arg( 'saved_copied' => 1 );
1940    $app->call_return;
1941}
1942
1943sub publish_index_templates {
1944    my $app = shift;
1945    $app->validate_magic or return;
1946
1947    # permission check
1948    my $perms = $app->permissions;
1949    return $app->errtrans("Permission denied.")
1950        unless $app->user->is_superuser ||
1951            $perms->can_administer_blog ||
1952            $perms->can_rebuild;
1953
1954    my $blog = $app->blog;
1955    my $templates = MT->model('template')->lookup_multi([ $app->param('id') ]);
1956    TEMPLATE: for my $tmpl (@$templates) {
1957        next TEMPLATE if !defined $tmpl;
1958        next TEMPLATE if $tmpl->blog_id != $blog->id;
1959        next TEMPLATE unless $tmpl->build_type;
1960
1961        $app->rebuild_indexes(
1962            Blog     => $blog,
1963            Template => $tmpl,
1964            Force    => 1,
1965        );
1966    }
1967
1968    $app->call_return( published => 1 );
1969}
1970
1971sub publish_archive_templates {
1972    my $app = shift;
1973    $app->validate_magic or return;
1974
1975    # permission check
1976    my $perms = $app->permissions;
1977    return $app->errtrans("Permission denied.")
1978      unless $app->user->is_superuser
1979      || $perms->can_administer_blog
1980      || $perms->can_rebuild;
1981
1982    my @ids = $app->param('id');
1983    if (scalar @ids == 1) {
1984        # we also support a list of comma-delimited ids like this
1985        @ids = split /,/, $ids[0];
1986    }
1987    return $app->error($app->translate("Invalid request."))
1988        unless @ids;
1989
1990    my $tmpl_id;
1991    my %ats;
1992    require MT::TemplateMap;
1993    while (!$tmpl_id && @ids) {
1994        $tmpl_id = shift @ids;
1995        my @tmpl_maps = MT::TemplateMap->load( { template_id => $tmpl_id } );
1996        foreach my $map (@tmpl_maps) {
1997            next unless $map->build_type;
1998            $ats{ $map->archive_type } = 1;
1999        }
2000        undef $tmpl_id unless keys %ats;
2001    }
2002
2003    # we have a template and archive types to publish!
2004
2005    require MT::CMS::Blog;
2006    my $return_args;
2007    if (@ids) {
2008        # we have more to do after this, so save the list
2009        # of remaining archive templates...
2010        $return_args = $app->uri_params(
2011            mode => 'publish_archive_templates',
2012            args => {
2013                magic_token => $app->current_magic,
2014                blog_id => scalar $app->param('blog_id'),
2015                id => join(",", @ids),
2016            }
2017        );
2018    } else {
2019        # nothing left after this publish operation; just return
2020        # to the listing screen.
2021        $return_args = $app->uri_params(
2022            mode => 'list',
2023            args => {
2024                _type => 'template',
2025                blog_id => scalar $app->param('blog_id'),
2026                published => 1
2027            }
2028        );
2029    }
2030    $return_args =~ s/^\?//;
2031
2032    $app->return_args( $return_args );
2033    $app->param( 'template_id', $tmpl_id );
2034    $app->param( 'single_template', 1 ); # forces fullscreen mode
2035    $app->param( 'type', join(",", keys %ats) );
2036    return MT::CMS::Blog::start_rebuild_pages($app);
2037}
2038
2039sub save_widget {
2040    my $app = shift;
2041    my $q   = $app->param;
2042
2043    $app->validate_magic() or return;
2044    my $author = $app->user;
2045
2046    my $id = $q->param('id');
2047
2048    if ( !$author->is_superuser ) {
2049        $app->run_callbacks( 'cms_save_permission_filter.template', $app, $id )
2050          || return $app->error(
2051            $app->translate( "Permission denied: [_1]", $app->errstr() ) );
2052    }
2053
2054    my $filter_result = $app->run_callbacks( 'cms_save_filter.widgetset', $app );
2055
2056    if ( !$filter_result ) {
2057        return edit_widget( $app, { error => $app->translate( "Save failed: [_1]", $app->errstr ) } );
2058    }
2059
2060    my $class = $app->model('template');
2061    my $obj;
2062    if ( $id ) {
2063        $obj = $class->load($id)
2064            or return $app->error($app->translate("Invalid ID [_1]", $id));
2065    }
2066    else {
2067        $obj = $class->new;
2068    }
2069
2070    my $original = $obj->clone();
2071    $obj->name($q->param('name'));
2072    $obj->type('widgetset');
2073    $obj->blog_id( $q->param('blog_id') || 0 );
2074    $obj->modulesets($q->param('modules'));
2075
2076    unless (
2077        $app->run_callbacks( 'cms_pre_save.template', $app, $obj, $original ) )
2078    {
2079        return edit_widget( $app, { error => $app->translate( "Save failed: [_1]", $app->errstr ) } );
2080    }
2081
2082    $obj->save
2083      or return $app->error(
2084        $app->translate( "Saving object failed: [_1]", $obj->errstr ) );
2085
2086    $app->run_callbacks( 'cms_post_save.template', $app, $obj, $original )
2087      or return $app->error( $app->errstr() );
2088
2089    $app->redirect(
2090        $app->uri(
2091            'mode' => 'edit_widget',
2092            args =>
2093              { blog_id => $obj->blog_id, 'saved' => 1, rebuild => 1, id => $obj->id }
2094        )
2095    );
2096}
2097
2098sub edit_widget {
2099    my $app = shift;
2100    my (%opt) = @_;
2101
2102    my $q       = $app->param();
2103    my $id      = scalar($q->param('id')) || $opt{id};
2104    my $blog_id = scalar $q->param('blog_id') || 0;
2105
2106    my $tmpl_class = $app->model('template');
2107    require MT::Promise;
2108    my $obj_promise = MT::Promise::delay(
2109        sub {
2110            return $tmpl_class->load($id) || undef;
2111        }
2112    );
2113
2114    if ( !$app->user->is_superuser ) {
2115        $app->run_callbacks( 'cms_view_permission_filter.template',
2116            $app, $id, $obj_promise )
2117          || return $app->error(
2118            $app->translate( "Permission denied: [_1]", $app->errstr() ) );
2119    }
2120
2121    my $param = {
2122        blog_id      => $blog_id,
2123        search_type  => "template",
2124        search_label => MT::Template->class_label_plural,
2125        $id ? ( id => $id ) : (), 
2126        exists($opt{rebuild}) ? ( rebuild => $opt{rebuild} ) : (),
2127        exists($opt{error}) ? ( error => $opt{error} ) : (),
2128        exists($opt{saved}) ? ( saved => $opt{saved} ) : ()
2129    };
2130    if ($blog_id) {
2131        my $blog = $app->blog;
2132        # include_system/include_cache are only applicable
2133        # to blog-level templates
2134        $param->{include_system} = $blog->include_system;
2135        $param->{include_cache} = $blog->include_cache;
2136        $param->{include_with_ssi}      = 0;
2137        $param->{cache_path}            = '';
2138        $param->{cache_enabled}         = 0;
2139        $param->{cache_expire_type}     = 0;
2140        $param->{cache_expire_period}   = '';
2141        $param->{cache_expire_interval} = 0;
2142        $param->{ssi_type} = uc $blog->include_system;
2143    }
2144   
2145    my $iter = $tmpl_class->load_iter(
2146        { type => 'widget', blog_id => $blog_id ? [ $blog_id, 0 ] : 0 },
2147        { sort => 'name', direction => 'ascend' }
2148    );
2149
2150    my %all_widgets;
2151    while (my $m = $iter->()) {
2152        next unless $m;
2153        $all_widgets{ $m->id } = $m->name;
2154    }
2155
2156    my @inst_modules;
2157    my $wtmpl;
2158    if ( $id ) {
2159        $wtmpl = $obj_promise->force()
2160          or return $app->error(
2161            $app->translate(
2162                "Load failed: [_1]",
2163                $tmpl_class->errstr || $app->translate("(no reason given)")
2164            )
2165          );
2166        $param->{name} = $wtmpl->name;
2167        $param->{include_with_ssi} = $wtmpl->include_with_ssi
2168          if defined $wtmpl->include_with_ssi;
2169        $param->{cache_path}       = $wtmpl->cache_path
2170          if defined $wtmpl->cache_path;
2171        $param->{cache_expire_type} = $wtmpl->cache_expire_type
2172          if defined $wtmpl->cache_expire_type;
2173        my ( $period, $interval ) =
2174          _get_schedule( $wtmpl->cache_expire_interval );
2175        $param->{cache_expire_period}   = $period   if defined $period;
2176        $param->{cache_expire_interval} = $interval if defined $interval;
2177        my @events = split ',', $wtmpl->cache_expire_event;
2178        foreach my $name (@events) {
2179            $param->{ 'cache_expire_event_' . $name } = 1;
2180        }
2181        my $modulesets = $wtmpl->modulesets;
2182        if ( $modulesets ) {
2183            my @modules = split ',', $modulesets;
2184            foreach my $mid ( @modules ) {
2185                push @inst_modules, { id => $mid, name => $all_widgets{$mid} };
2186                delete $all_widgets{$mid};
2187            }
2188        }
2189    }
2190    $param->{installed} = \@inst_modules if @inst_modules;
2191    my @avail_modules = map { { id => $_, name => $all_widgets{$_} } }
2192        keys %all_widgets;
2193    $param->{available} = \@avail_modules;
2194
2195    my $res = $app->run_callbacks('cms_edit.widgetset', $app, $id, $wtmpl, $param);
2196    if (!$res) {
2197        return $app->error($app->callback_errstr());
2198    }
2199
2200    $app->load_tmpl('edit_widget.tmpl', $param);
2201}
2202
2203sub list_widget {
2204    my $app = shift;
2205    my (%opt) = @_;
2206    my $q = $app->param;
2207
2208    my $perms = $app->blog ? $app->permissions : $app->user->permissions;
2209    return $app->return_to_dashboard( redirect => 1 )
2210      unless $perms || $app->user->is_superuser;
2211    if ( $perms && !$perms->can_edit_templates ) {
2212        return $app->return_to_dashboard( permission => 1 );
2213    }
2214    my $blog_id = $q->param('blog_id') || 0;
2215
2216    my $widget_loop = &build_template_table( $app,
2217        load_args => [ 
2218            { type => 'widget', blog_id => $blog_id ? [ $blog_id, 0 ] : 0 },
2219            { sort => 'name', direction => 'ascend' }
2220        ],
2221    );
2222   
2223    my $param = {
2224        widget_table   => $widget_loop,
2225        object_type    => "widgetset",
2226        search_type    => "template",
2227        search_label   => MT::Template->class_label_plural,
2228        listing_screen => 1,
2229        screen_id      => "list-widget-set",
2230        $blog_id ? ( blog_view => 1, blog_id => $blog_id ) : (),
2231        exists($opt{rebuild}) ? ( rebuild => $opt{rebuild} ) : (),
2232        exists($opt{error}) ? ( error => $opt{error} ) : (),
2233        exists($opt{deleted}) ? ( saved => $opt{deleted} ) : ()
2234    };
2235
2236    my $iter = $app->model('template')->load_iter(
2237        { type => 'widgetset', blog_id => $blog_id ? [ $blog_id, 0 ] : 0 },
2238        { sort => 'name', direction => 'ascend' }
2239    );
2240    my @widgetmanagers;
2241    while ( my $widgetset = $iter->() ) {
2242        next unless $widgetset;
2243        my $ws = { 
2244            id => $widgetset->id,
2245            widgetmanager => $widgetset->name,
2246        };
2247        if ( my $modulesets = $widgetset->modulesets ) {
2248            $ws->{widgets} = $modulesets;
2249            my @names;
2250            foreach my $module ( split ',', $modulesets ) { 
2251                my ( $widget ) = grep { $_->{id} eq $module } @$widget_loop;
2252                push @names, $widget->{name} if $widget;
2253            }
2254            $ws->{names} = join(', ', @names) if @names;
2255        }
2256        push @widgetmanagers, $ws;
2257    }
2258    $param->{object_loop} = \@widgetmanagers if @widgetmanagers;
2259
2260    $app->load_tmpl('list_widget.tmpl', $param);
2261}
2262
2263sub delete_widget {
2264    my $app  = shift;
2265    my $q    = $app->param;
2266    my $type = $q->param('_type');
2267
2268    return $app->errtrans("Invalid request.")
2269      unless $type;
2270
2271    return $app->error( $app->translate("Invalid request.") )
2272      if $app->request_method() ne 'POST';
2273
2274    $app->validate_magic() or return;
2275
2276    my $tmpl_class = $app->model('template');
2277
2278    for my $id ( $q->param('id') ) {
2279        next unless $id;    # avoid 'empty' ids
2280
2281        my $obj = $tmpl_class->load($id);
2282        next unless $obj;
2283        $app->run_callbacks( 'cms_delete_permission_filter.template',
2284            $app, $obj )
2285          || return $app->error(
2286            $app->translate( "Permission denied: [_1]", $app->errstr() ) );
2287
2288        $obj->remove
2289          or return $app->errtrans(
2290            'Removing [_1] failed: [_2]',
2291            $app->translate('template'),
2292            $obj->errstr
2293          );
2294        $app->run_callbacks( 'cms_post_delete.template', $app, $obj );
2295    }
2296    $app->call_return;
2297}
2298
2299sub restore_widgetmanagers {
2300    my ($cb, $objects, $deferred, $errors, $callback) = @_;
2301    my @keys = grep { $_ =~ /^MT::Template#/ } keys( %$objects );
2302    foreach my $key ( @keys ) {
2303        my $tmpl = $objects->{$key};
2304        next unless 'widgetset' eq $tmpl->type;
2305        my $modulesets = $tmpl->modulesets;
2306        next unless $modulesets;
2307        $callback->( MT->translate( 'Restoring widget set [_1]... ', $tmpl->name ) );
2308
2309        my @tmpl_ids = split ',', $modulesets;
2310        my @new_ids;
2311        foreach my $id ( @tmpl_ids ) {
2312            my $new_tmpl = $objects->{"MT::Template#$id"};
2313            next unless $new_tmpl;
2314            push @new_ids, $new_tmpl->id;
2315        }
2316        if ( @new_ids ) {
2317            $tmpl->modulesets( join(',', @new_ids) );
2318            $tmpl->save;
2319            $callback->( MT->translate("Done.") . "\n" );
2320        }
2321        else {
2322            $callback->( MT->translate("Failed.") . "\n" );
2323        }
2324    }
2325    1;
2326}
2327
2328{
2329    my @period_options = (
2330        {
2331            name => 'minutes',
2332            expr => 60,
2333        },
2334        {
2335            name => 'hours',
2336            expr => 60 * 60,
2337        },
2338        {
2339            name => 'days',
2340            expr => 24 * 60 * 60,
2341        },
2342    );
2343
2344    sub _get_schedule {
2345        my ($sec) = @_;
2346        return unless defined $sec;
2347        my ( $period, $interval );
2348        for (@period_options) {
2349            last if $sec % $_->{expr};
2350            $period   = $_->{name};
2351            $interval = $sec / $_->{expr};
2352        }
2353        ( $period, $interval );
2354    }
2355
2356    sub _get_interval {
2357        my ( $period, $interval ) = @_;
2358        return unless defined $period;
2359        my $sec = 0;
2360        for (@period_options) {
2361            if ( $_->{name} eq $period ) {
2362                $sec = $interval * $_->{expr};
2363                last;
2364            }
2365        }
2366        $sec;
2367    }
2368}
2369
23701;
Note: See TracBrowser for help on using the browser.