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.
5
# $Id: Template.pm 2549 2008-06-11 00:42:59Z fumiakiy $
7
package MT::CMS::Template;
13
my ($app, $id, $obj, $param) = @_;
16
my $blog_id = $q->param('blog_id');
18
# FIXME: enumeration of types
20
my $type = $q->param('type') || ( $obj ? $obj->type : undef );
21
return $app->return_to_dashboard( redirect => 1 )
23
|| $type eq 'individual'
24
|| $type eq 'category'
29
# to trigger autosave logic in main edit routine
30
$param->{autosave_support} = 1;
32
my $type = $q->param('_type');
33
my $blog = $app->blog;
34
my $cfg = $app->config;
35
my $perms = $app->permissions;
39
# include_system/include_cache are only applicable
40
# to blog-level templates
41
$param->{include_system} = $blog->include_system;
42
$param->{include_cache} = $blog->include_cache;
46
# FIXME: Template types should not be enumerated here
47
$param->{nav_templates} = 1;
49
if ( $obj->type eq 'index' ) {
51
$param->{template_group_trans} = $app->translate('index');
53
elsif ($obj->type eq 'archive'
54
|| $obj->type eq 'individual'
55
|| $obj->type eq 'category'
56
|| $obj->type eq 'page' )
59
# FIXME: enumeration of types
61
$param->{template_group_trans} = $app->translate('archive');
63
elsif ( $obj->type eq 'custom' ) {
65
$param->{template_group_trans} = $app->translate('module');
67
elsif ( $obj->type eq 'widget' ) {
69
$param->{template_group_trans} = $app->translate('widget');
71
elsif ( $obj->type eq 'email' ) {
73
$param->{template_group_trans} = $app->translate('email');
77
$param->{template_group_trans} = $app->translate('system');
79
$param->{template_group} = $tab;
80
$blog_id = $obj->blog_id;
82
# FIXME: enumeration of types
83
$param->{has_name} = $obj->type eq 'index'
84
|| $obj->type eq 'custom'
85
|| $obj->type eq 'widget'
86
|| $obj->type eq 'archive'
87
|| $obj->type eq 'category'
88
|| $obj->type eq 'page'
89
|| $obj->type eq 'individual';
90
if ( !$param->{has_name} ) {
91
$param->{ 'type_' . $obj->type } = 1;
92
$param->{name} = $obj->name;
94
$app->add_breadcrumb( $param->{name} );
95
$param->{has_outfile} = $obj->type eq 'index';
96
$param->{has_rebuild} =
97
( ( $obj->type eq 'index' )
98
&& ( ( $blog->custom_dynamic_templates || "" ) ne 'all' ) );
100
# FIXME: enumeration of types
101
$param->{is_special} = $param->{type} ne 'index'
102
&& $param->{type} ne 'archive'
103
&& $param->{type} ne 'category'
104
&& $param->{type} ne 'page'
105
&& $param->{type} ne 'individual';
106
$param->{has_build_options} = $param->{has_build_options}
107
&& $param->{type} ne 'custom'
108
&& $param->{type} ne 'widget'
109
&& !$param->{is_special};
110
$param->{search_label} = $app->translate('Templates');
111
$param->{object_type} = 'template';
112
my $published_url = $obj->published_url;
113
$param->{published_url} = $published_url if $published_url;
114
$param->{saved_rebuild} = 1 if $q->param('saved_rebuild');
115
require MT::PublishOption;
116
$param->{static_maps} = $obj->build_type == MT::PublishOption::DYNAMIC() ? 0 : 1;
118
my $filter = $app->param('filter_key');
119
if ($param->{template_group} eq 'email') {
120
$app->param( 'filter_key', 'email_templates' );
121
}elsif ($param->{template_group} eq 'system') {
122
$app->param( 'filter_key', 'system_templates' );
124
$app->load_list_actions( 'template', $param );
125
$app->param( 'filter_key', $filter );
128
if ( $obj->{errors} && @{ $obj->{errors} } ) {
129
$param->{error} = $app->translate(
130
"One or more errors were found in this template.");
131
$param->{error} .= "<ul>\n";
132
foreach my $err ( @{ $obj->{errors} } ) {
133
$param->{error} .= "<li>"
134
. MT::Util::encode_html( $err->{message} )
137
$param->{error} .= "</ul>\n";
140
# Populate list of included templates
141
if ( my $includes = $obj->getElementsByTagName('Include') ) {
145
foreach my $tag (@$includes) {
147
my $mod = $include->{include_module} = $tag->[1]->{module} || $tag->[1]->{widget};
149
my $type = $tag->[1]->{widget} ? 'widget' : 'custom';
150
next if exists $seen{$type}{$mod};
151
$seen{$type}{$mod} = 1;
152
my $other = MT::Template->load(
154
blog_id => [ $obj->blog_id, 0 ],
159
direction => 'descend',
163
$include->{include_link} = $app->mt_uri(
166
blog_id => $other->blog_id || 0,
167
'_type' => 'template',
171
# Try to compile template module if using MTInclude in this template.
173
if ( $other->{errors} && @{ $other->{errors} } ) {
174
$param->{error} = $app->translate(
175
"One or more errors were found in included template module (".$other->name.").");
176
$param->{error} .= "<ul>\n";
177
foreach my $err ( @{ $other->{errors} } ) {
178
$param->{error} .= "<li>"
179
. MT::Util::encode_html( $err->{message} )
182
$param->{error} .= "</ul>\n";
186
$include->{create_link} = $app->mt_uri(
189
blog_id => $obj->blog_id,
190
'_type' => 'template',
196
if ($type eq 'widget') {
197
push @widgets, $include;
199
push @includes, $include;
202
$param->{include_loop} = \@includes if @includes;
203
$param->{widget_loop} = \@widgets if @widgets;
205
my @sets = ( @{ $obj->getElementsByTagName('WidgetSet') || [] }, @{ $obj->getElementsByTagName('WidgetManager') || [] } );
209
foreach my $set (@sets) {
210
my $name = $set->[1]->{name};
212
next if $seen{$name};
214
my $wset = MT::Template->load(
216
blog_id => [ $obj->blog_id, 0 ],
221
direction => 'descend',
226
include_link => $app->mt_uri(
227
mode => 'edit_widget',
229
blog_id => $wset->blog_id,
233
include_module => $name,
238
create_link => $app->mt_uri(
239
mode => 'edit_widget',
245
include_module => $name,
249
$param->{widget_set_loop} = \@widget_sets if @widget_sets;
251
$param->{have_includes} = 1 if $param->{widget_set_loop} || $param->{include_loop} || $param->{widget_loop};
252
# Populate archive types for creating new map
253
my $obj_type = $obj->type;
254
if ( $obj_type eq 'individual'
255
|| $obj_type eq 'page'
256
|| $obj_type eq 'author'
257
|| $obj_type eq 'category'
258
|| $obj_type eq 'archive' )
260
my @at = $app->publisher->archive_types;
263
my $archiver = $app->publisher->archiver($at);
264
my $archive_label = $archiver->archive_label;
265
$archive_label = $at unless $archive_label;
266
$archive_label = $archive_label->()
267
if ( ref $archive_label ) eq 'CODE';
268
if ( ( $obj_type eq 'archive' )
269
|| ( $obj_type eq 'author' )
270
|| ( $obj_type eq 'category' ) )
273
# only include if it is NOT an entry-based archive type
274
next if $archiver->entry_based;
276
elsif ( $obj_type eq 'page' ) {
277
# only include if it is a entry-based archive type and page
278
next unless $archiver->entry_based;
279
next if $archiver->entry_class ne 'page';
281
elsif ( $obj_type eq 'individual' ) {
282
# only include if it is a entry-based archive type and entry
283
next unless $archiver->entry_based;
284
next if $archiver->entry_class eq 'page';
288
archive_type_translated => $archive_label,
292
sort { MT::App::CMS::archive_type_sorter( $a, $b ) } @archive_types;
294
$param->{archive_types} = \@archive_types;
296
# Populate template maps for this template
297
my $maps = _populate_archive_loop( $app, $blog, $obj );
299
$param->{object_loop} = $param->{template_map_loop} = $maps
302
my $build_type = MT::PublishOption::DYNAMIC();
303
my $build_type_0 = 0;
304
foreach my $map ( @$maps ) {
305
$at{ $map->{archive_label} } = 1;
306
$build_type_0 = $map->{map_build_type};
307
$build_type = $map->{map_build_type}
308
if MT::PublishOption::DYNAMIC() ne $map->{map_build_type};
310
$param->{enabled_archive_types} = join(", ", sort keys %at);
311
$param->{static_maps} = $build_type == MT::PublishOption::DYNAMIC() ? 0 : 1;
312
$param->{build_type_0} = 1 unless $build_type_0;
316
$param->{build_type} = $obj->build_type;
317
$param->{ 'build_type_' . ( $obj->build_type || 0 ) } = 1;
318
#my ( $period, $interval ) = _get_schedule( $obj->build_interval );
319
#$param->{ 'schedule_period_' . $period } = 1;
320
#$param->{schedule_interval} = $interval;
321
$param->{type} = 'custom' if $param->{type} eq 'module';
323
my $new_tmpl = $q->param('create_new_template');
326
if ( $new_tmpl =~ m/^blank:(.+)/ ) {
330
elsif ( $new_tmpl =~ m/^default:([^:]+):(.+)/ ) {
332
$template_type = 'custom' if $template_type eq 'module';
333
my $template_id = $2;
334
my $set = $blog ? $blog->template_set : undef;
335
require MT::DefaultTemplates;
336
my $def_tmpl = MT::DefaultTemplates->templates($set) || [];
338
grep { $_->{identifier} eq $template_id } @$def_tmpl;
339
$param->{text} = $app->translate_templatized( $tmpl->{text} )
341
$param->{type} = $template_type;
345
$template_type = $q->param('type');
346
$template_type = 'custom' if 'module' eq $template_type;
347
$param->{type} = $template_type;
349
return $app->errtrans("Create template requires type")
350
unless $template_type;
351
$param->{nav_templates} = 1;
354
# FIXME: enumeration of types
355
if ( $template_type eq 'index' ) {
357
$param->{template_group_trans} = $app->translate('index');
359
elsif ($template_type eq 'archive'
360
|| $template_type eq 'individual'
361
|| $template_type eq 'category'
362
|| $template_type eq 'page' )
365
$param->{template_group_trans} = $app->translate('archive');
366
$param->{type_archive} = 1;
370
label => $app->translate('Archive')
374
label => $app->translate('Entry or Page')
377
$param->{new_archive_types} = \@types;
379
elsif ( $template_type eq 'custom' ) {
381
$param->{template_group_trans} = $app->translate('module');
383
elsif ( $template_type eq 'widget' ) {
385
$param->{template_group_trans} = $app->translate('widget');
389
$param->{template_group_trans} = $app->translate('system');
391
$param->{template_group} = $tab;
392
$app->translate($tab);
393
$app->add_breadcrumb( $app->translate('New Template') );
395
# FIXME: enumeration of types
396
$param->{has_name} = $template_type eq 'index'
397
|| $template_type eq 'custom'
398
|| $template_type eq 'widget'
399
|| $template_type eq 'archive'
400
|| $template_type eq 'category'
401
|| $template_type eq 'page'
402
|| $template_type eq 'individual';
403
$param->{has_outfile} = $template_type eq 'index';
404
$param->{has_rebuild} =
405
( ( $template_type eq 'index' )
406
&& ( ( $blog->custom_dynamic_templates || "" ) ne 'all' ) );
407
$param->{custom_dynamic} =
408
$blog && $blog->custom_dynamic_templates eq 'custom';
409
$param->{has_build_options} =
410
$blog && ($blog->custom_dynamic_templates eq 'custom'
411
|| $param->{has_rebuild});
413
# FIXME: enumeration of types
414
$param->{is_special} = $param->{type} ne 'index'
415
&& $param->{type} ne 'archive'
416
&& $param->{type} ne 'category'
417
&& $param->{type} ne 'page'
418
&& $param->{type} ne 'individual';
419
$param->{has_build_options} = $param->{has_build_options}
420
&& $param->{type} ne 'custom'
421
&& $param->{type} ne 'widget'
422
&& !$param->{is_special};
424
$param->{name} = MT::Util::decode_url( $app->param('name') )
425
if $app->param('name');
427
$param->{publish_queue_available} = eval 'require List::Util; require Scalar::Util; 1;';
429
my $set = $blog ? $blog->template_set : undef;
430
require MT::DefaultTemplates;
431
my $tmpls = MT::DefaultTemplates->templates($set);
433
foreach my $dtmpl (@$tmpls) {
434
if ( !$param->{has_name} ) {
435
if ($obj->type eq 'email') {
436
if ($dtmpl->{identifier} eq $obj->identifier) {
437
$param->{template_name_label} = $dtmpl->{label};
438
$param->{template_name} = $dtmpl->{name};
442
if ( $dtmpl->{type} eq $obj->type ) {
443
$param->{template_name_label} = $dtmpl->{label};
444
$param->{template_name} = $dtmpl->{name};
448
if ( $dtmpl->{type} eq 'index' ) {
451
label => $dtmpl->{label},
452
key => $dtmpl->{key},
453
selected => $dtmpl->{key} eq
454
( ( $obj ? $obj->identifier : undef ) || '' ),
458
$param->{index_identifiers} = \@tmpl_ids;
460
$param->{"type_$param->{type}"} = 1;
463
$app->load_template_prefs( $perms->template_prefs );
464
%$param = ( %$param, %$pref_param );
467
# Populate structure for template snippets
468
if ( my $snippets = $app->registry('template_snippets') || {} ) {
470
for my $snip_id ( keys %$snippets ) {
471
my $label = $snippets->{$snip_id}{label};
472
$label = $label->() if ref($label) eq 'CODE';
476
trigger => $snippets->{$snip_id}{trigger},
478
content => $snippets->{$snip_id}{content},
481
@snippets = sort { $a->{label} cmp $b->{label} } @snippets;
482
$param->{template_snippets} = \@snippets;
485
# Populate structure for tag documentation
486
my $all_tags = MT::Component->registry("tags");
488
foreach my $tag_set (@$all_tags) {
489
my $url = $tag_set->{help_url};
490
$url = $url->() if ref($url) eq 'CODE';
491
# hey, at least give them a google search
492
$url ||= 'http://www.google.com/search?q=mt%t';
494
foreach my $type (qw( block function )) {
495
my $tags = $tag_set->{$type} or next;
496
$tag_list .= ($tag_list eq '' ? '' : ',') . join(",", keys(%$tags));
498
$tag_list =~ s/(^|,)plugin(,|$)/,/;
499
if (exists $tag_docs->{$url}) {
500
$tag_docs->{$url} .= ',' . $tag_list;
503
$tag_docs->{$url} = $tag_list;
506
$param->{tag_docs} = $tag_docs;
507
$param->{link_doc} = $app->help_url('appendices/tags/');
509
$param->{screen_id} = "edit-template-" . $param->{type};
512
$param->{template_lang} = 'html';
513
if ( $obj && $obj->outfile ) {
514
if ( $obj->outfile =~ m/\.(css|js|html|php|pl|asp)$/ ) {
515
$param->{template_lang} = {
526
if (($param->{type} eq 'custom') || ($param->{type} eq 'widget')) {
528
$param->{include_with_ssi} = 0;
529
$param->{cache_path} = '';
530
$param->{cache_expire_type} = 0;
531
$param->{cache_expire_period} = '';
532
$param->{cache_expire_interval} = 0;
533
$param->{ssi_type} = uc $blog->include_system;
536
$param->{include_with_ssi} = $obj->include_with_ssi
537
if defined $obj->include_with_ssi;
538
$param->{cache_path} = $obj->cache_path
539
if defined $obj->cache_path;
540
$param->{cache_expire_type} = $obj->cache_expire_type
541
if defined $obj->cache_expire_type;
542
my ( $period, $interval ) =
543
_get_schedule( $obj->cache_expire_interval );
544
$param->{cache_expire_period} = $period if defined $period;
545
$param->{cache_expire_interval} = $interval if defined $interval;
546
my @events = split ',', ($obj->cache_expire_event || '');
547
foreach my $name (@events) {
548
$param->{ 'cache_expire_event_' . $name } = 1;
553
# if unset, default to 30 so if they choose to enable caching,
554
# it will be preset to something sane.
555
$param->{cache_expire_interval} ||= 30;
558
if $app->param('dirty');
560
$param->{can_preview} = 1
561
if (!$param->{is_special}) && (!$obj || ($obj && ($obj->outfile || '') !~ m/\.(css|xml|rss|js)$/));
569
my $perms = $app->blog ? $app->permissions : $app->user->permissions;
570
return $app->return_to_dashboard( redirect => 1 )
571
unless $perms || $app->user->is_superuser;
572
if ( $perms && !$perms->can_edit_templates ) {
573
return $app->return_to_dashboard( permission => 1 );
575
my $blog = $app->blog;
577
require MT::Template;
578
my $blog_id = $app->param('blog_id') || 0;
579
my $terms = { blog_id => $blog_id };
580
my $args = { sort => 'name' };
583
my ( $obj, $row ) = @_;
585
my $type = $row->{type} || '';
586
if ( $type =~ m/^(individual|page|category|archive)$/ ) {
587
$template_type = 'archive';
588
# populate context with templatemap loop
589
my $tblog = $obj->blog_id == $blog->id ? $blog : MT::Blog->load( $obj->blog_id );
591
$row->{archive_types} = _populate_archive_loop( $app, $tblog, $obj );
594
elsif ( $type eq 'widget' ) {
595
$template_type = 'widget';
597
elsif ( $type eq 'index' ) {
598
$template_type = 'index';
600
elsif ( $type eq 'custom' ) {
601
$template_type = 'module';
603
elsif ( $type eq 'email' ) {
604
$template_type = 'email';
606
elsif ( $type eq 'backup' ) {
607
$template_type = 'backup';
610
$template_type = 'system';
612
$row->{use_cache} = ( ($obj->cache_expire_type || 0) != 0 ) ? 1 : 0;
613
$row->{template_type} = $template_type;
614
$row->{type} = 'entry' if $type eq 'individual';
615
my $published_url = $obj->published_url;
616
$row->{published_url} = $published_url if $published_url;
620
my $filter = $app->param('filter_key');
621
my $template_type = $filter || '';
622
$template_type =~ s/_templates//;
624
$params->{screen_class} = "list-template";
625
$params->{listing_screen} = 1;
627
$app->load_list_actions( 'template', $params );
628
$params->{page_actions} = $app->page_actions('list_templates');
629
$params->{search_label} = $app->translate("Templates");
630
$params->{object_type} = 'template';
631
$params->{blog_view} = 1;
632
$params->{refreshed} = $app->param('refreshed');
633
$params->{published} = $app->param('published');
634
$params->{saved_copied} = $app->param('saved_copied');
635
$params->{saved_deleted} = $app->param('saved_deleted');
636
$params->{saved} = $app->param('saved');
638
# determine list of system template types:
642
$set = $blog->template_set;
646
$scope = 'global:system';
648
my @tmpl_path = ( $set && ($set ne 'mt_blog')) ? ("template_sets", $set, 'templates', $scope) : ("default_templates", $scope);
649
my $sys_tmpl = MT->registry(@tmpl_path) || {};
653
if ($template_type ne 'backup') {
655
# blog template listings
658
label => $app->translate("Index Templates"),
663
label => $app->translate("Archive Templates"),
664
type => ['archive', 'individual', 'page', 'category'],
668
label => $app->translate("Template Modules"),
673
label => $app->translate("System Templates"),
674
type => [ keys %$sys_tmpl ],
679
# global template listings
682
label => $app->translate("Template Modules"),
687
label => $app->translate("Email Templates"),
692
label => $app->translate("System Templates"),
693
type => [ keys %$sys_tmpl ],
699
# global template listings
702
label => $app->translate("Template Backups"),
708
my @types = sort { $types{$a}->{order} <=> $types{$b}->{order} } keys %types;
709
if ($template_type) {
710
@types = ( $template_type );
712
$app->delete_param('filter_key') if $filter;
713
foreach my $tmpl_type (@types) {
714
if ( $tmpl_type eq 'index' ) {
715
$app->param( 'filter_key', 'index_templates' );
717
elsif ( $tmpl_type eq 'archive' ) {
718
$app->param( 'filter_key', 'archive_templates' );
720
elsif ( $tmpl_type eq 'system' ) {
721
$app->param( 'filter_key', 'system_templates' );
723
elsif ( $tmpl_type eq 'email' ) {
724
$app->param( 'filter_key', 'email_templates' );
726
elsif ( $tmpl_type eq 'module' ) {
727
$app->param( 'filter_key', 'module_templates' );
730
unless ( exists($types{$tmpl_type}->{type})
731
&& 'ARRAY' eq ref($types{$tmpl_type}->{type})
732
&& 0 == scalar(@{$types{$tmpl_type}->{type}}) )
734
$terms->{type} = $types{$tmpl_type}->{type};
735
$tmpl_param = $app->listing(
746
my $template_type_label = $types{$tmpl_type}->{label};
747
$tmpl_param->{template_type} = $tmpl_type;
748
$tmpl_param->{template_type_label} = $template_type_label;
749
push @tmpl_loop, $tmpl_param;
752
$params->{filter_key} = $filter;
753
$params->{filter_label} = $types{$template_type}{label}
754
if exists $types{$template_type};
755
$app->param('filter_key', $filter);
757
# restore filter_key param (we modified it for the
758
# sake of the individual table listings)
759
$app->delete_param('filter_key');
762
$params->{template_type_loop} = \@tmpl_loop;
763
$params->{screen_id} = "list-template";
765
return $app->load_tmpl('list_template.tmpl', $params);
771
my $blog_id = $q->param('blog_id');
772
my $blog = $app->blog;
773
my $id = $q->param('id');
775
my $user_id = $app->user->id;
777
# We can only do previews on blog templates. Have to publish
778
# the preview file somewhere!
779
return $app->errtrans("Invalid request.") unless $blog;
781
require MT::Template;
783
$tmpl = MT::Template->load( { id => $id, blog_id => $blog_id } )
784
or return $app->errtrans( "Invalid request." );
787
$tmpl = MT::Template->new;
789
$tmpl->blog_id($blog_id);
792
my $names = $tmpl->column_names;
793
my %values = map { $_ => scalar $app->param($_) } @$names;
794
delete $values{'id'} unless $q->param('id');
796
## Strip linefeed characters.
797
for my $col (qw( text )) {
798
$values{$col} =~ tr/\r//d if $values{$col};
800
$tmpl->set_values( \%values );
802
my $preview_basename = $app->preview_object_basename;
804
my $type = $tmpl->type;
805
my $preview_tmpl = $tmpl;
809
my $blog_path = $blog->site_path;
810
my $blog_url = $blog->site_url;
812
if (($type eq 'custom') || ($type eq 'widget')) {
813
# determine 'host' template
814
$preview_tmpl = MT::Template->load({ blog_id => $blog_id, identifier => 'main_index' });
815
if (!$preview_tmpl) {
816
return $app->errtrans("Can't locate host template to preview module/widget.");
818
my $req = $app->request;
819
# stash this module so that it is selected through a
820
# MTInclude tag instead of the one in the database:
821
my $tmpl_name = $tmpl->name;
822
$tmpl_name =~ s/^Widget: // if $type eq 'widget';
823
my $stash_id = 'template_' . $type . '::' . $blog_id . '::' . $tmpl_name;
824
$req->stash($stash_id, [ $tmpl, $tmpl->tokens ]);
825
} elsif (($type eq 'individual') || ($type eq 'page')) {
826
my $ctx = $preview_tmpl->context;
827
my $entry_type = $type eq 'individual' ? 'entry' : 'page';
828
my ($obj) = create_preview_content($app, $blog, $entry_type, 1);
829
$obj->basename( $preview_basename );
830
$ctx->stash('entry', $obj);
831
$ctx->{current_archive_type} = $type eq 'individual' ? 'Individual' : 'Page';
832
if (($type eq 'individual') && $blog->archive_path) {
833
$blog_path = $blog->archive_path;
834
$blog_url = $blog->archive_url;
836
$archive_file = File::Spec->catfile( $blog_path, $obj->archive_file );
837
$archive_url = $obj->archive_url;
839
my $archiver = MT->publisher->archiver( $ctx->{current_archive_type} );
840
my $tparams = $archiver->template_params;
842
$ctx->var( $_, $tparams->{$_} ) for keys %$tparams;
844
} elsif ($type eq 'archive') {
845
# some variety of archive template
846
my $ctx = $preview_tmpl->context;
847
require MT::TemplateMap;
848
my $map = MT::TemplateMap->load( { template_id => $id, is_preferred => 1 });
850
return $app->error("Cannot preview without a template map!");
852
$ctx->{current_archive_type} = $map->archive_type;
853
my $archiver = MT->publisher->archiver( $map->archive_type );
854
my $tparams = $archiver->template_params;
856
$ctx->var( $_, $tparams->{$_} ) for keys %$tparams;
858
my @entries = create_preview_content($app, $blog, $archiver->entry_class, 10);
859
if ($archiver->date_based) {
860
$ctx->{current_timestamp} = $entries[0]->authored_on;
861
$ctx->{current_timestamp_end} = $entries[$#entries]->authored_on;
863
if ($archiver->author_based) {
864
$ctx->stash('author', $app->user);
867
if ($archiver->category_based) {
868
$cat = new MT::Category;
869
$cat->label($app->translate("Preview"));
870
$cat->basename("preview");
872
$ctx->stash('archive_category', $cat);
874
$ctx->stash('entries', \@entries);
876
my $file = MT->publisher->archive_file_for( $entries[0], $blog, $map->archive_type, $cat, $map, $ctx->{current_timestamp}, $app->user);
877
$archive_file = File::Spec->catfile( $blog_path, $file );
878
$archive_url = MT::Util::caturl( $blog_url, $file );
879
} elsif ($type eq 'index') {
881
# for now, only index templates can be previewed
882
return $app->errtrans("Invalid request.");
888
# Default case; works for index templates (other template types should
889
# have defined $archive_file by now).
890
$archive_file = File::Spec->catfile( $blog_path, $preview_tmpl->outfile )
891
unless defined $archive_file;
893
( $orig_file, $path ) = File::Basename::fileparse( $archive_file );
895
$archive_url = MT::Util::caturl( $blog_url, $orig_file )
896
unless defined $archive_url;
899
require File::Basename;
900
$file_ext = $archive_file;
901
if ($file_ext =~ m/\.[a-z]+$/) {
902
$file_ext =~ s!.+\.!.!;
906
$archive_file = File::Spec->catfile( $path, $preview_basename . $file_ext );
909
$app->run_callbacks( 'cms_pre_preview.template', $app, $preview_tmpl, \@data );
911
my $has_hires = eval 'require Time::HiRes; 1' ? 1 : 0;
912
my $start_time = $has_hires ? Time::HiRes::time() : time;
914
my $ctx = $preview_tmpl->context;
915
$ctx->var('preview_template', 1);
916
my $html = $preview_tmpl->output;
918
$param{build_time} = $has_hires ? sprintf("%.3f", Time::HiRes::time() - $start_time ) : "~" . ( time - $start_time );
920
unless ( defined($html) ) {
921
return $app->error( $app->translate( "Publish error: [_1]",
922
MT::Util::encode_html( $preview_tmpl->errstr ) ) );
925
# If MT is configured to do 'local' previews, convert all
926
# the normal blog URLs into the domain used by MT itself (ie,
927
# blog is published to www.example.com, which is a different
928
# server from where MT runs, mt.example.com; previews therefore
929
# should occur locally, so replace all http://www.example.com/
930
# with http://mt.example.com/).
931
my ($old_url, $new_url);
932
if ($app->config('LocalPreviews')) {
933
$old_url = $blog_url;
934
$old_url =~ s!^(https?://[^/]+?/)(.*)?!$1!;
935
$new_url = $app->base . '/';
936
$html =~ s!\Q$old_url\E!$new_url!g;
939
my $fmgr = $blog->file_mgr;
941
## Determine if we need to build directory structure,
942
## and build it if we do. DirUmask determines
943
## directory permissions.
944
require File::Basename;
946
unless $path eq '/'; ## OS X doesn't like / at the end in mkdir().
947
unless ( $fmgr->exists($path) ) {
948
$fmgr->mkpath($path);
951
if ( $fmgr->exists($path) && $fmgr->can_write($path) ) {
952
$param{preview_file} = $preview_basename;
953
my $preview_url = $archive_url;
954
$preview_url =~ s! / \Q$orig_file\E ( /? ) $!/$preview_basename$file_ext$1!x;
956
# We also have to translate the URL used for the
957
# published file to be on the MT app domain.
958
if (defined $new_url) {
959
$preview_url =~ s!^\Q$old_url\E!$new_url!;
962
$param{preview_url} = $preview_url;
964
$fmgr->put_data( $html, $archive_file );
966
# we have to make a record of this preview just in case it
967
# isn't cleaned up by re-editing, saving or cancelling on
970
my $sess_obj = MT::Session->get_by_key(
972
id => $preview_basename,
973
kind => 'TF', # TF = Temporary File
974
name => $archive_file,
977
$sess_obj->start(time);
981
return $app->error( $app->translate(
982
"Unable to create preview file in this location: [_1]", $path ) );
985
$param{id} = $id if $id;
986
$param{new_object} = $param{id} ? 0 : 1;
987
$param{name} = $tmpl->name;
988
$q->param( 'build_dynamic', $tmpl->build_dynamic );
989
my $cols = $tmpl->column_names;
990
for my $col (@$cols) {
994
data_value => scalar $q->param($col)
997
$param{template_loop} = \@data;
998
$param{object_type} = $type;
999
return $app->load_tmpl( 'preview_template_strip.tmpl', \%param );
1002
sub create_preview_content {
1003
my ($app, $blog, $type, $number) = @_;
1005
my $blog_id = $blog->id;
1006
my $entry_class = $app->model($type);
1007
my @obj = $entry_class->load({
1008
blog_id => $blog_id,
1009
status => MT::Entry::RELEASE()
1011
limit => $number || 1,
1012
direction => 'descend',
1013
'sort' => 'authored_on'
1016
# create a dummy object
1017
my $obj = $entry_class->new;
1018
$obj->blog_id($blog_id);
1020
$obj->author_id( $app->user->id );
1021
$obj->authored_on( $blog->current_timestamp );
1022
$obj->status( MT::Entry::RELEASE() );
1023
$obj->title($app->translate("Lorem ipsum"));
1024
my $preview_text = $app->translate('LOREM_IPSUM_TEXT');
1025
if ($preview_text eq 'LOREM_IPSUM_TEXT') {
1026
$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.};
1028
my $preview_more = $app->translate('LORE_IPSUM_TEXT_MORE');
1029
if ($preview_text eq 'LOREM_IPSUM_TEXT_MORE') {
1030
$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;
1032
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.
1034
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.};
1036
$obj->text($preview_text);
1037
$obj->text_more($preview_more);
1038
$obj->keywords(MT->translate("sample, entry, preview"));
1039
$obj->tags(qw( lorem ipsum sample preview ));
1045
sub reset_blog_templates {
1047
my $q = $app->param;
1048
my $perms = $app->permissions
1049
or return $app->error( $app->translate("No permissions") );
1050
return $app->error( $app->translate("Permission denied.") )
1051
unless $perms->can_edit_templates;
1052
$app->validate_magic() or return;
1053
my $blog = MT::Blog->load( $perms->blog_id )
1054
or return $app->error($app->translate('Can\'t load blog #[_1].', $perms->blog_id));
1055
require MT::Template;
1056
my @tmpl = MT::Template->load( { blog_id => $blog->id } );
1058
for my $tmpl (@tmpl) {
1059
$tmpl->remove or return $app->error( $tmpl->errstr );
1061
my $set = $blog ? $blog->template_set : undef;
1062
require MT::DefaultTemplates;
1063
my $tmpl_list = MT::DefaultTemplates->templates($set) || [];
1065
for my $val (@$tmpl_list) {
1066
$val->{text} = $app->translate_templatized( $val->{text} );
1067
my $tmpl = MT::Template->new;
1068
if ( ( 'widgetset' eq $val->{type} )
1069
&& ( exists $val->{modulesets} ) ) {
1070
my $modulesets = delete $val->{modulesets};
1071
$tmpl->modulesets( join ',', @$modulesets );
1073
$tmpl->set_values($val);
1074
$tmpl->build_dynamic(0);
1075
$tmpl->blog_id( $blog->id );
1077
or return $app->error(
1079
"Populating blog with default templates failed: [_1]",
1084
# FIXME: enumeration of types
1085
if ( $val->{type} eq 'archive'
1086
|| $val->{type} eq 'category'
1087
|| $val->{type} eq 'page'
1088
|| $val->{type} eq 'individual' )
1090
push @arch_tmpl, $tmpl;
1094
## Set up mappings from new templates to archive types.
1095
for my $tmpl (@arch_tmpl) {
1098
# FIXME: enumeration of types
1099
if ( $tmpl->type eq 'archive' ) {
1100
@at = qw( Daily Weekly Monthly Category );
1102
elsif ( $tmpl->type eq 'page' ) {
1105
elsif ( $tmpl->type eq 'individual' ) {
1106
@at = qw( Individual );
1108
require MT::TemplateMap;
1110
my $map = MT::TemplateMap->new;
1111
$map->archive_type($at);
1112
$map->is_preferred(1);
1113
$map->template_id( $tmpl->id );
1114
$map->blog_id( $tmpl->blog_id );
1116
or return $app->error(
1118
"Setting up mappings failed: [_1]",
1128
{ '_type' => 'template', blog_id => $blog->id, 'reset' => 1 }
1133
sub _generate_map_table {
1135
my ( $blog_id, $template_id ) = @_;
1137
require MT::Template;
1139
my $blog = MT::Blog->load($blog_id);
1140
my $template = MT::Template->load($template_id);
1141
my $tmpl = $app->load_tmpl('include/archive_maps.tmpl');
1142
my $maps = _populate_archive_loop( $app, $blog, $template );
1143
$tmpl->param( object_type => 'templatemap' );
1144
$tmpl->param( publish_queue_available => eval 'require List::Util; require Scalar::Util; 1;' );
1145
$tmpl->param( object_loop => $maps ) if @$maps;
1146
my $html = $tmpl->output();
1148
if ( $html =~ m/<__trans / ) {
1149
$html = $app->translate_templatized($html);
1154
sub _populate_archive_loop {
1156
my ( $blog, $obj ) = @_;
1158
my $index = $app->config('IndexBasename');
1159
my $ext = $blog->file_extension || '';
1160
$ext = '.' . $ext if $ext ne '';
1162
require MT::TemplateMap;
1163
my @tmpl_maps = MT::TemplateMap->load( { template_id => $obj->id } );
1166
foreach my $map_obj (@tmpl_maps) {
1168
$map->{map_id} = $map_obj->id;
1169
$map->{map_is_preferred} = $map_obj->is_preferred;
1171
$map->{map_build_type} = $map_obj->build_type;
1172
$map->{ 'map_build_type_' . ( $map_obj->build_type || 0 ) } = 1;
1173
my ( $period, $interval ) = _get_schedule( $map_obj->build_interval );
1174
$map->{ 'map_schedule_period_' . $period } = 1
1176
$map->{map_schedule_interval} = $interval
1177
if defined $interval;
1179
my $at = $map->{archive_type} = $map_obj->archive_type;
1181
$map->{ 'archive_type_preferred_' . $blog->archive_type_preferred } = 1
1182
if $blog->archive_type_preferred;
1183
$map->{file_template} = $map_obj->file_template
1184
if $map_obj->file_template;
1186
my $archiver = $app->publisher->archiver($at);
1187
next unless $archiver;
1188
$map->{archive_label} = $archiver->archive_label;
1189
my $tmpls = $archiver->default_archive_templates;
1192
my $name = $_->{label};
1193
$name =~ s/\.html$/$ext/;
1194
$name =~ s/index$ext$/$index$ext/;
1198
value => $_->{template},
1199
default => ( $_->{default} || 0 ),
1205
foreach (@$tmpl_loop) {
1206
if ( ( !$map->{file_template} && $_->{default} )
1207
|| ( $map->{file_template} eq $_->{value} ) )
1211
$map->{file_template} = $_->{value}
1212
if !$map->{file_template};
1216
unshift @$tmpl_loop,
1218
name => $map->{file_template},
1219
value => $map->{file_template},
1224
$map->{archive_tmpl_loop} = $tmpl_loop;
1226
1 < MT::TemplateMap->count(
1227
{ archive_type => $at, blog_id => $obj->blog_id }
1231
$map->{has_multiple_archives} = 1;
1236
@maps = sort { MT::App::CMS::archive_type_sorter( $a, $b ) } @maps;
1242
$app->validate_magic() or return;
1243
my $perms = $app->{perms}
1244
or return $app->error( $app->translate("No permissions") );
1245
my $q = $app->param;
1246
my $id = $q->param('id');
1248
require MT::TemplateMap;
1249
MT::TemplateMap->remove( { id => $id } );
1251
_generate_map_table( $app, $q->param('blog_id'),
1252
$q->param('template_id') );
1253
$app->{no_print_body} = 1;
1254
$app->send_http_header("text/plain");
1260
$app->validate_magic() or return;
1261
my $perms = $app->{perms}
1262
or return $app->error( $app->translate("No permissions") );
1264
my $q = $app->param;
1266
require MT::TemplateMap;
1267
my $blog_id = $q->param('blog_id');
1268
my $at = $q->param('new_archive_type');
1269
my $exist = MT::TemplateMap->exist(
1271
blog_id => $blog_id,
1275
my $map = MT::TemplateMap->new;
1276
$map->is_preferred( $exist ? 0 : 1 );
1277
$map->template_id( scalar $q->param('template_id') );
1278
$map->blog_id($blog_id);
1279
$map->archive_type($at);
1281
or return $app->error(
1282
$app->translate( "Saving map failed: [_1]", $map->errstr ) );
1284
_generate_map_table( $app, $blog_id, scalar $q->param('template_id') );
1285
$app->{no_print_body} = 1;
1286
$app->send_http_header("text/plain");
1291
my ( $eh, $app, $id ) = @_;
1292
my $perms = $app->permissions;
1293
return !$id || ($perms && $perms->can_edit_templates) || (!$app->blog && $app->user->can_edit_templates);
1297
my ( $eh, $app, $id ) = @_;
1298
my $perms = $app->permissions;
1299
return ($perms && $perms->can_edit_templates) || (!$perms && $app->user->can_edit_templates);
1303
my ( $eh, $app, $obj ) = @_;
1304
return 1 if $app->user->is_superuser();
1305
my $perms = $app->permissions;
1306
return ($perms && $perms->can_edit_templates) || (!$perms && $app->user->can_edit_templates);
1311
my ( $app, $obj ) = @_;
1313
## Strip linefeed characters.
1314
( my $text = $obj->text ) =~ tr/\r//d;
1316
if ($text =~ m/<(MT|_)_trans/i) {
1317
$text = $app->translate_templatized($text);
1322
# update text heights if necessary
1323
if ( my $perms = $app->permissions ) {
1324
my $prefs = $perms->template_prefs || '';
1325
my $text_height = $app->param('text_height');
1326
if ( defined $text_height ) {
1327
my ($pref_text_height) = $prefs =~ m/\btext:(\d+)\b/;
1328
$pref_text_height ||= 0;
1329
if ( $text_height != $pref_text_height ) {
1330
if ( $prefs =~ m/\btext\b/ ) {
1331
$prefs =~ s/\btext(:\d+)\b/text:$text_height/;
1334
$prefs = 'text:' . $text_height . ',' . $prefs;
1339
if ( $prefs ne ( $perms->template_prefs || '' ) ) {
1340
$perms->template_prefs($prefs);
1346
$obj->include_with_ssi( $app->param('include_with_ssi') ? 1 : 0 );
1347
$obj->cache_path( $app->param('cache_path'));
1348
my $cache_expire_type = defined $app->param('cache_expire_type')
1349
? $app->param('cache_expire_type')
1351
$obj->cache_expire_type($cache_expire_type);
1352
my $period = $app->param('cache_expire_period');
1353
my $interval = $app->param('cache_expire_interval');
1354
my $sec = _get_interval( $period, $interval );
1355
$obj->cache_expire_interval($sec) if defined $sec;
1356
my $q = $app->param;
1359
foreach my $name ( $q->param('cache_expire_event') ) {
1360
push @events, $name;
1362
$obj->cache_expire_event( join ',', @events ) if $#events >= 0;
1363
if ( $cache_expire_type == 1 ) {
1365
$app->translate("You should not be able to enter 0 as the time.") )
1368
elsif ( $cache_expire_type == 2 ) {
1370
$app->translate("You must select at least one event checkbox.") )
1374
require MT::PublishOption;
1375
my $build_type = $app->param('build_type');
1377
if ( $build_type == MT::PublishOption::SCHEDULED() ) {
1378
my $period = $app->param('schedule_period');
1379
my $interval = $app->param('schedule_interval');
1380
my $sec = _get_interval( $period, $interval );
1381
$obj->build_interval($sec);
1384
if ( $build_type == MT::PublishOption::DISABLED()
1385
|| $build_type == MT::PublishOption::MANUALLY() )
1389
$obj->rebuild_me($rebuild_me);
1395
my ( $app, $obj, $original ) = @_;
1397
my $sess_obj = $app->autosave_session_obj;
1398
$sess_obj->remove if $sess_obj;
1401
my $q = $app->param;
1402
my $type = $q->param('type');
1403
# FIXME: enumeration of types
1404
if ( $type eq 'custom'
1406
|| $type eq 'widget'
1407
|| $type eq 'widgetset' )
1409
$dynamic = $obj->build_dynamic;
1413
# archive template specific post_save tasks
1414
require MT::TemplateMap;
1419
if ( $p =~ /^archive_tmpl_preferred_(\w+)_(\d+)$/ ) {
1422
$map = MT::TemplateMap->load($map_id)
1424
$map->prefer( $q->param($p) ); # prefer method saves in itself
1426
elsif ( $p =~ /^archive_file_tmpl_(\d+)$/ ) {
1428
$map = MT::TemplateMap->load($map_id)
1430
$map->file_template( $q->param($p) );
1433
elsif ( $p =~ /^map_build_type_(\d+)$/ ) {
1435
$map = MT::TemplateMap->load($map_id)
1437
my $build_type = $q->param($p);
1438
require MT::PublishOption;
1439
# Populate maps that are changed from static to dynamic
1440
# This should capture new map as well
1441
push @static_maps, $map->id
1442
if ( ( $build_type ne $map->build_type )
1443
&& ( MT::PublishOption::DYNAMIC() eq $build_type ) );
1444
$map->build_type($build_type);
1445
if ( $build_type == MT::PublishOption::SCHEDULED() ) {
1446
my $period = $q->param( 'map_schedule_period_' . $map_id );
1447
my $interval = $q->param( 'map_schedule_interval_' . $map_id );
1448
my $sec = _get_interval( $period, $interval );
1449
$map->build_interval($sec);
1454
&& $map && $map->build_type == MT::PublishOption::DYNAMIC() )
1459
$app->{static_dynamic_maps} = @static_maps ? \@static_maps : 0;
1462
if ( !$original->id ) {
1465
message => $app->translate(
1466
"Template '[_1]' (ID:[_2]) created by '[_3]'",
1467
$obj->name, $obj->id, $app->user->name
1469
level => MT::Log::INFO(),
1470
class => 'template',
1477
if ( $obj->type eq 'index' ) {
1478
$app->rebuild_indexes(
1479
BlogID => $obj->blog_id,
1482
) or return $app->publish_error(); # XXXX
1484
if ( my $blog = $app->blog ) {
1485
require MT::CMS::Blog;
1487
if ( $obj->type eq 'index' ) {
1488
$path = $blog->site_path;
1489
$url = $blog->site_url;
1492
# must be archive since other types can't be dynamic
1493
if ( $path = $blog->archive_path ) {
1494
$url = $blog->archive_url;
1497
$path = $blog->site_path;
1498
$url = $blog->site_url;
1501
# specific arguments so not to overwrite mtview and htaccess
1502
MT::CMS::Blog::prepare_dynamic_publishing(
1516
my ( $eh, $app, $obj ) = @_;
1520
message => $app->translate(
1521
"Template '[_1]' (ID:[_2]) deleted by '[_3]'",
1522
$obj->name, $obj->id, $app->user->name
1524
level => MT::Log::INFO(),
1526
category => 'delete'
1531
sub build_template_table {
1535
my $perms = $app->permissions;
1536
my $list_pref = $app->list_pref('template');
1537
my $limit = $args{limit};
1538
my $param = $args{param} || {};
1540
if ( $args{load_args} ) {
1541
my $class = $app->model('template');
1542
$iter = $class->load_iter( @{ $args{load_args} } );
1544
elsif ( $args{iter} ) {
1545
$iter = $args{iter};
1547
elsif ( $args{items} ) {
1548
$iter = sub { pop @{ $args{items} } };
1549
$limit = scalar @{ $args{items} };
1551
return [] unless $iter;
1556
while ( my $tmpl = $iter->() ) {
1557
my $blog = $blogs{ $tmpl->blog_id } ||=
1558
MT::Blog->load( $tmpl->blog_id ) if $tmpl->blog_id;
1560
my $row = $tmpl->column_values;
1561
$row->{name} = '' if !defined $row->{name};
1562
$row->{name} =~ s/^\s+|\s+$//g;
1563
$row->{name} = "(" . $app->translate("No Name") . ")"
1564
if $row->{name} eq '';
1565
my $published_url = $tmpl->published_url;
1566
$row->{published_url} = $published_url if $published_url;
1567
$row->{use_cache} = ( ($tmpl->cache_expire_type || 0) != 0 ) ? 1 : 0;
1569
# FIXME: enumeration of types
1570
$row->{can_delete} = 1
1571
if $tmpl->type =~ m/(custom|index|archive|page|individual|category|widget)/;
1573
$row->{weblog_name} = $blog->name;
1575
elsif ($tmpl->blog_id) {
1576
$row->{weblog_name} = '* ' . $app->translate('Orphaned') . ' *';
1579
$row->{weblog_name} = '* ' . $app->translate('Global Templates') . ' *';
1581
$row->{object} = $tmpl;
1583
last if defined($limit) && (@data > $limit);
1585
return [] unless @data;
1587
$param->{template_table}[0] = {%$list_pref};
1588
$param->{template_table}[0]{object_loop} = \@data;
1589
$param->{template_table}[0]{object_type} = 'template';
1590
$app->load_list_actions( 'template', $param );
1591
$param->{object_loop} = \@data;
1595
sub dialog_publishing_profile {
1597
$app->validate_magic or return;
1599
my $blog = $app->blog;
1600
$app->assert( $blog ) or return;
1603
my $perms = $app->permissions;
1604
return $app->errtrans("Permission denied.")
1605
unless $app->user->is_superuser ||
1606
$perms->can_administer_blog ||
1607
$perms->can_edit_templates;
1610
$param->{dynamicity} = $blog->custom_dynamic_templates || 'none';
1611
$param->{screen_id} = "publishing-profile-dialog";
1612
$param->{return_args} = $app->param('return_args');
1614
$app->build_page('dialog/publishing_profile.tmpl',
1618
sub dialog_refresh_templates {
1620
$app->validate_magic or return;
1623
my $perms = $app->permissions;
1624
return $app->errtrans("Permission denied.")
1625
unless $app->user->is_superuser ||
1626
$perms->can_administer_blog ||
1627
$perms->can_edit_templates;
1630
my $blog = $app->blog;
1631
$param->{return_args} = $app->param('return_args');
1634
$param->{blog_id} = $blog->id;
1636
my $sets = $app->registry("template_sets");
1637
$sets->{$_}{key} = $_ for keys %$sets;
1638
$sets = $app->filter_conditional_list([ values %$sets ]);
1640
no warnings; # some sets may not define an order
1641
@$sets = sort { $a->{order} <=> $b->{order} } @$sets;
1642
$param->{'template_set_loop'} = $sets;
1644
my $existing_set = $blog->template_set || 'mt_blog';
1646
if ($_->{key} eq $existing_set) {
1650
$param->{'template_set_index'} = $#$sets;
1651
$param->{'template_set_count'} = scalar @$sets;
1653
$param->{template_sets} = $sets;
1654
$param->{screen_id} = "refresh-templates-dialog";
1657
# load template sets
1658
$app->build_page('dialog/refresh_templates.tmpl',
1662
sub refresh_all_templates {
1666
if ($app->param('backup')) {
1667
# refresh templates dialog uses a 'backup' field
1671
my $template_set = $app->param('template_set');
1672
my $refresh_type = $app->param('refresh_type') || 'refresh';
1677
if ($app->param('blog_id')) {
1678
@id = ( scalar $app->param('blog_id') );
1681
@id = $app->param('id');
1683
# refresh global templates
1688
require MT::Template;
1689
require MT::DefaultTemplates;
1691
require MT::Permission;
1694
foreach my $blog_id (@id) {
1697
$blog = MT::Blog->load($blog_id);
1700
if ( !$app->user->is_superuser() ) {
1701
my $perms = MT::Permission->load(
1702
{ blog_id => $blog_id, author_id => $app->user->id } );
1705
|| ( !$perms->can_edit_templates()
1706
&& !$perms->can_administer_blog() )
1716
if ($refresh_type eq 'clean') {
1717
# the user wants to back up all templates and
1718
# install the new ones
1720
my @ts = MT::Util::offset_time_list( $t, $blog_id );
1721
my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d",
1722
$ts[5] + 1900, $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1724
my $tmpl_iter = MT::Template->load_iter({
1725
blog_id => $blog_id,
1726
type => { not => 'backup' },
1729
while (my $tmpl = $tmpl_iter->()) {
1731
# zap all template maps
1732
require MT::TemplateMap;
1733
MT::TemplateMap->remove({
1734
template_id => $tmpl->id,
1736
$tmpl->type('backup');
1738
$tmpl->name . ' (Backup from ' . $ts . ')' );
1739
$tmpl->identifier(undef);
1740
$tmpl->rebuild_me(0);
1741
$tmpl->linked_file(undef);
1749
# This also creates our template mappings
1750
$blog->create_default_templates( $template_set ||
1751
$blog->template_set || 'mt_blog' );
1753
if ($template_set) {
1754
$blog->template_set( $template_set );
1756
$app->run_callbacks( 'blog_template_set_change', { blog => $blog } );
1762
$tmpl_list = MT::DefaultTemplates->templates($template_set || $blog->template_set) || MT::DefaultTemplates->templates();
1765
$tmpl_list = MT::DefaultTemplates->templates();
1768
foreach my $val (@$tmpl_list) {
1770
# when refreshing blog templates,
1771
# skip over global templates which
1772
# specify a blog_id of 0...
1773
next if $val->{global};
1776
next unless exists $val->{global};
1779
if ( !$val->{orig_name} ) {
1780
$val->{orig_name} = $val->{name};
1781
$val->{text} = $app->translate_templatized( $val->{text} );
1784
my $orig_name = $val->{orig_name};
1786
my @ts = MT::Util::offset_time_list( $t, ( $blog_id ? $blog_id : undef ) );
1787
my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d", $ts[5] + 1900,
1788
$ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1791
$terms->{blog_id} = $blog_id;
1792
$terms->{type} = $val->{type};
1793
if ( $val->{type} =~
1794
m/^(archive|individual|page|category|index|custom|widget|widgetset)$/ )
1796
$terms->{name} = $val->{name};
1799
$terms->{identifier} = $val->{identifier};
1802
# this should only return 1 template; we're searching
1803
# within a given blog for a specific type of template (for
1804
# "system" templates; or for a type + name, which should be
1805
# unique for that blog.
1806
my $tmpl = MT::Template->load($terms);
1807
if ($tmpl && $backup) {
1809
# check for default template text...
1810
# if it is a default template, then outright replace it
1811
my $text = $tmpl->text;
1814
my $def_text = $val->{text};
1815
$def_text =~ s/\s+//g;
1817
# if it has been customized, back it up to a new tmpl record
1818
if ($def_text ne $text) {
1819
my $backup = $tmpl->clone;
1820
delete $backup->{column_values}
1821
->{id}; # make sure we don't overwrite original
1822
delete $backup->{changed_cols}->{id};
1824
$backup->name . $app->translate( ' (Backup from [_1])', $ts ) );
1825
$backup->type('backup');
1826
# if ( $backup->type !~
1827
# m/^(archive|individual|page|category|index|custom|widget)$/ )
1829
# $backup->type('custom')
1830
# ; # system templates can't be created
1832
$backup->outfile('');
1833
$backup->linked_file( $tmpl->linked_file );
1834
$backup->identifier(undef);
1835
$backup->rebuild_me(0);
1836
$backup->build_dynamic(0);
1841
# we found that the previous template had not been
1842
# altered, so replace it with new default template...
1843
if ( ( 'widgetset' eq $val->{type} )
1844
&& ( exists $val->{widgets} ) ) {
1845
my $modulesets = delete $val->{widgets};
1846
$tmpl->modulesets( MT::Template->widgets_to_modulesets($modulesets, $blog_id) );
1848
$tmpl->text( $val->{text} );
1849
$tmpl->identifier( $val->{identifier} );
1850
$tmpl->type( $val->{type} )
1851
; # fixes mismatch of types for cases like "archive" => "individual"
1852
$tmpl->linked_file('');
1856
# create this one...
1857
my $tmpl = new MT::Template;
1858
if ( ( 'widgetset' eq $val->{type} )
1859
&& ( exists $val->{widgets} ) ) {
1860
my $modulesets = delete $val->{widgets};
1861
$tmpl->modulesets( MT::Template->widgets_to_modulesets($modulesets, $blog_id) );
1863
$tmpl->build_dynamic(0);
1866
text => $val->{text},
1867
name => $val->{name},
1868
type => $val->{type},
1869
identifier => $val->{identifier},
1870
outfile => $val->{outfile},
1871
rebuild_me => $val->{rebuild_me},
1874
$tmpl->blog_id($blog_id);
1876
or return $app->error(
1877
$app->translate("Error creating new template: ")
1883
$app->add_return_arg( 'refreshed' => 1 );
1887
sub refresh_individual_templates {
1892
my $user = $app->user;
1893
my $perms = $app->permissions;
1896
"Permission denied.")
1898
#TODO: system level-designer permission
1899
unless $user->is_superuser() || $user->can_edit_templates()
1901
&& ( $perms->can_edit_templates()
1902
|| $perms->can_administer_blog ) );
1905
if ( my $blog_id = $app->param('blog_id') ) {
1906
my $blog = $app->model('blog')->load($blog_id)
1907
or return $app->error($app->translate('Can\'t load blog #[_1].', $blog_id));
1908
$set = $blog->template_set()
1912
require MT::DefaultTemplates;
1913
my $tmpl_list = MT::DefaultTemplates->templates($set) or return;
1915
my $tmpl_types = {};
1918
foreach my $tmpl (@$tmpl_list) {
1919
$tmpl->{text} = $app->translate_templatized( $tmpl->{text} );
1920
$tmpl_ids->{ $tmpl->{identifier} } = $tmpl
1921
if $tmpl->{identifier};
1922
if ( $tmpl->{type} !~ m/^(archive|individual|page|category|index|custom|widget)$/ )
1924
$tmpl_types->{ $tmpl->{type} } = $tmpl;
1927
$tmpls->{ $tmpl->{type} }{ $tmpl->{name} } = $tmpl;
1934
my @id = $app->param('id');
1935
require MT::Template;
1936
foreach my $tmpl_id (@id) {
1937
my $tmpl = MT::Template->load($tmpl_id);
1939
my $blog_id = $tmpl->blog_id;
1941
# FIXME: permission check -- for this blog_id
1943
my @ts = MT::Util::offset_time_list( $t, $blog_id );
1944
my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d", $ts[5] + 1900,
1945
$ts[4] + 1, @ts[ 3, 2, 1, 0 ];
1947
my $val = ( $tmpl->identifier ? $tmpl_ids->{ $tmpl->identifier() } : undef )
1948
|| $tmpl_types->{ $tmpl->type() }
1949
|| $tmpls->{ $tmpl->type() }{ $tmpl->name };
1953
"Skipping template '[_1]' since it appears to be a custom template.",
1959
my $text = $tmpl->text;
1962
my $def_text = $val->{text};
1963
$def_text =~ s/\s+//g;
1965
if ($text ne $def_text) {
1966
# if it has been customized, back it up to a new tmpl record
1967
my $backup = $tmpl->clone;
1968
delete $backup->{column_values}
1969
->{id}; # make sure we don't overwrite original
1970
delete $backup->{changed_cols}->{id};
1971
$backup->name( $backup->name . ' (Backup from ' . $ts . ')' );
1972
$backup->type('backup');
1973
$backup->outfile('');
1974
$backup->linked_file( $tmpl->linked_file );
1975
$backup->rebuild_me(0);
1976
$backup->build_dynamic(0);
1977
$backup->identifier(undef);
1981
'Refreshing template <strong>[_3]</strong> with <a href="?__mode=view&blog_id=[_1]&_type=template&id=[_2]">backup</a>',
1982
$blog_id, $backup->id, $tmpl->name );
1984
# we found that the previous template had not been
1985
# altered, so replace it with new default template...
1986
$tmpl->text( $val->{text} );
1987
$tmpl->identifier( $val->{identifier} );
1988
$tmpl->linked_file('');
1991
push @msg, $app->translate("Skipping template '[_1]' since it has not been changed.", $tmpl->name);
1995
push @msg_loop, { message => $_ } foreach @msg;
1997
$app->build_page( 'refresh_results.tmpl',
1998
{ message_loop => \@msg_loop, return_url => $app->return_uri } );
2001
sub clone_templates {
2004
my $user = $app->user;
2005
my $perms = $app->permissions;
2008
"Permission denied.")
2010
#TODO: system level-designer permission
2011
unless $user->is_superuser() || $user->can_edit_templates()
2013
&& ( $perms->can_edit_templates()
2014
|| $perms->can_administer_blog ) );
2016
my @id = $app->param('id');
2017
require MT::Template;
2018
foreach my $tmpl_id (@id) {
2019
my $tmpl = MT::Template->load($tmpl_id);
2022
my $new_tmpl = $tmpl->clone({
2030
my $new_basename = $app->translate("Copy of [_1]", $tmpl->name);
2031
my $new_name = $new_basename;
2033
while (MT::Template->exist({ name => $new_name, blog_id => $tmpl->blog_id })) {
2034
$new_name = $new_basename . ' (' . ++$i . ')';
2037
$new_tmpl->name($new_name);
2041
$app->add_return_arg( 'saved_copied' => 1 );
2045
sub publish_index_templates {
2047
$app->validate_magic or return;
2050
my $perms = $app->permissions;
2051
return $app->errtrans("Permission denied.")
2052
unless $app->user->is_superuser ||
2053
$perms->can_administer_blog ||
2054
$perms->can_rebuild;
2056
my $blog = $app->blog;
2057
my $templates = MT->model('template')->lookup_multi([ $app->param('id') ]);
2058
TEMPLATE: for my $tmpl (@$templates) {
2059
next TEMPLATE if !defined $tmpl;
2060
next TEMPLATE if $tmpl->blog_id != $blog->id;
2061
next TEMPLATE unless $tmpl->build_type;
2063
$app->rebuild_indexes(
2070
$app->call_return( published => 1 );
2073
sub publish_archive_templates {
2075
$app->validate_magic or return;
2078
my $perms = $app->permissions;
2079
return $app->errtrans("Permission denied.")
2080
unless $app->user->is_superuser
2081
|| $perms->can_administer_blog
2082
|| $perms->can_rebuild;
2084
my @ids = $app->param('id');
2085
if (scalar @ids == 1) {
2086
# we also support a list of comma-delimited ids like this
2087
@ids = split /,/, $ids[0];
2089
return $app->error($app->translate("Invalid request."))
2094
require MT::TemplateMap;
2095
while (!$tmpl_id && @ids) {
2096
$tmpl_id = shift @ids;
2097
my @tmpl_maps = MT::TemplateMap->load( { template_id => $tmpl_id } );
2098
foreach my $map (@tmpl_maps) {
2099
next unless $map->build_type;
2100
$ats{ $map->archive_type } = 1;
2102
undef $tmpl_id unless keys %ats;
2105
# we have a template and archive types to publish!
2107
require MT::CMS::Blog;
2109
my $reedit = $app->param('reedit');
2111
# we have more to do after this, so save the list
2112
# of remaining archive templates...
2113
$return_args = $app->uri_params(
2114
mode => 'publish_archive_templates',
2116
magic_token => $app->current_magic,
2117
blog_id => scalar $app->param('blog_id'),
2118
id => join(",", @ids),
2123
my $mode = $reedit ? 'view' : 'list';
2124
$return_args = $app->uri_params(
2127
_type => 'template',
2128
blog_id => scalar $app->param('blog_id'),
2130
( $reedit ? ( saved => 1 ) : () ),
2131
( $reedit ? ( id => $reedit ) : () ),
2135
$return_args =~ s/^\?//;
2137
$app->return_args( $return_args );
2138
$app->param( 'template_id', $tmpl_id );
2139
$app->param( 'single_template', 1 ); # forces fullscreen mode
2140
$app->param( 'type', join(",", keys %ats) );
2141
return MT::CMS::Blog::start_rebuild_pages($app);
2146
my $q = $app->param;
2148
$app->validate_magic() or return;
2149
my $author = $app->user;
2151
my $id = $q->param('id');
2153
if ( !$author->is_superuser ) {
2154
$app->run_callbacks( 'cms_save_permission_filter.template', $app, $id )
2155
|| return $app->error(
2156
$app->translate( "Permission denied: [_1]", $app->errstr() ) );
2159
my $filter_result = $app->run_callbacks( 'cms_save_filter.widgetset', $app );
2161
if ( !$filter_result ) {
2162
return edit_widget( $app, { error => $app->translate( "Save failed: [_1]", $app->errstr ) } );
2165
my $class = $app->model('template');
2168
$obj = $class->load($id)
2169
or return $app->error($app->translate("Invalid ID [_1]", $id));
2175
my $original = $obj->clone();
2176
$obj->name($q->param('name'));
2177
$obj->type('widgetset');
2178
$obj->blog_id( $q->param('blog_id') || 0 );
2179
$obj->modulesets($q->param('modules'));
2182
$app->run_callbacks( 'cms_pre_save.template', $app, $obj, $original ) )
2184
return edit_widget( $app, { error => $app->translate( "Save failed: [_1]", $app->errstr ) } );
2188
or return $app->error(
2189
$app->translate( "Saving object failed: [_1]", $obj->errstr ) );
2191
$app->run_callbacks( 'cms_post_save.template', $app, $obj, $original )
2192
or return $app->error( $app->errstr() );
2196
'mode' => 'edit_widget',
2198
{ blog_id => $obj->blog_id, 'saved' => 1, rebuild => 1, id => $obj->id }
2207
my $q = $app->param();
2208
my $id = scalar($q->param('id')) || $opt{id};
2209
my $name = scalar($q->param('name'));
2210
my $blog_id = scalar $q->param('blog_id') || 0;
2212
my $tmpl_class = $app->model('template');
2213
require MT::Promise;
2214
my $obj_promise = MT::Promise::delay(
2216
return $tmpl_class->load($id) || undef;
2220
if ( !$app->user->is_superuser ) {
2221
$app->run_callbacks( 'cms_view_permission_filter.template',
2222
$app, $id, $obj_promise )
2223
|| return $app->error(
2224
$app->translate( "Permission denied: [_1]", $app->errstr() ) );
2228
blog_id => $blog_id,
2229
search_type => "template",
2230
search_label => MT::Template->class_label_plural,
2231
exists($opt{rebuild}) ? ( rebuild => $opt{rebuild} ) : (),
2232
exists($opt{error}) ? ( error => $opt{error} ) : (),
2233
exists($opt{saved}) ? ( saved => $opt{saved} ) : (),
2241
my $blog = $app->blog;
2242
# include_system/include_cache are only applicable
2243
# to blog-level templates
2244
$param->{include_system} = $blog->include_system;
2245
$param->{include_cache} = $blog->include_cache;
2246
$param->{include_with_ssi} = 0;
2247
$param->{cache_path} = '';
2248
$param->{cache_enabled} = 0;
2249
$param->{cache_expire_type} = 0;
2250
$param->{cache_expire_period} = '';
2251
$param->{cache_expire_interval} = 0;
2252
$param->{ssi_type} = uc $blog->include_system;
2255
my $iter = $tmpl_class->load_iter(
2256
{ type => 'widget', blog_id => $blog_id ? [ $blog_id, 0 ] : 0 },
2257
{ sort => 'name', direction => 'ascend' }
2261
while (my $m = $iter->()) {
2263
$all_widgets{ $m->id }{name} = $m->name;
2264
$all_widgets{ $m->id }{blog_id} = $m->blog_id;
2270
$wtmpl = $obj_promise->force()
2271
or return $app->error(
2273
"Load failed: [_1]",
2274
$tmpl_class->errstr || $app->translate("(no reason given)")
2277
return $app->return_to_dashboard( redirect => 1 )
2278
if $wtmpl->blog_id ne $blog_id;
2279
$param->{name} = $wtmpl->name;
2280
$param->{include_with_ssi} = $wtmpl->include_with_ssi
2281
if defined $wtmpl->include_with_ssi;
2282
$param->{cache_path} = $wtmpl->cache_path
2283
if defined $wtmpl->cache_path;
2284
$param->{cache_expire_type} = $wtmpl->cache_expire_type
2285
if defined $wtmpl->cache_expire_type;
2286
my ( $period, $interval ) =
2287
_get_schedule( $wtmpl->cache_expire_interval );
2288
$param->{cache_expire_period} = $period if defined $period;
2289
$param->{cache_expire_interval} = $interval if defined $interval;
2290
my @events = split ',', $wtmpl->cache_expire_event;
2291
foreach my $name (@events) {
2292
$param->{ 'cache_expire_event_' . $name } = 1;
2294
my $modulesets = $wtmpl->modulesets;
2295
if ( $modulesets ) {
2296
my @modules = split ',', $modulesets;
2297
foreach my $mid ( @modules ) {
2298
push @inst_modules, {
2300
name => $all_widgets{$mid}{name},
2301
blog_id => $all_widgets{$mid}{blog_id},
2303
delete $all_widgets{$mid};
2307
$param->{installed} = \@inst_modules if @inst_modules;
2308
my @avail_modules = map { {
2309
id => $_, name => $all_widgets{$_}{name}, blog_id => $all_widgets{$_}{blog_id}
2310
} } keys %all_widgets;
2311
$param->{available} = \@avail_modules;
2313
my $res = $app->run_callbacks('cms_edit.widgetset', $app, $id, $wtmpl, $param);
2315
return $app->error($app->callback_errstr());
2318
$app->load_tmpl('edit_widget.tmpl', $param);
2324
my $q = $app->param;
2326
my $perms = $app->blog ? $app->permissions : $app->user->permissions;
2327
return $app->return_to_dashboard( redirect => 1 )
2328
unless $perms || $app->user->is_superuser;
2329
if ( $perms && !$perms->can_edit_templates ) {
2330
return $app->return_to_dashboard( permission => 1 );
2332
my $blog_id = $q->param('blog_id') || 0;
2334
my $widget_loop = &build_template_table( $app,
2336
{ type => 'widget', blog_id => $blog_id ? [ $blog_id, 0 ] : 0 },
2337
{ sort => 'name', direction => 'ascend' }
2341
my $iter = $app->model('template')->load_iter(
2342
{ type => 'widgetset', blog_id => $blog_id ? $blog_id : 0 },
2343
{ sort => 'name', direction => 'ascend' }
2346
while ( my $widgetset = $iter->() ) {
2347
next unless $widgetset;
2349
id => $widgetset->id,
2350
widgetmanager => $widgetset->name,
2352
if ( my $modulesets = $widgetset->modulesets ) {
2353
$ws->{widgets} = $modulesets;
2355
foreach my $module ( split ',', $modulesets ) {
2356
my ( $widget ) = grep { $_->{id} eq $module } @$widget_loop;
2357
push @names, $widget->{name} if $widget;
2359
$ws->{names} = join(', ', @names) if @names;
2361
push @widgetmanagers, $ws;
2366
# Remove system level widgets from the listing
2367
@widget_loop = grep { $_->{blog_id} == $blog_id } @$widget_loop;
2370
@widget_loop = @$widget_loop;
2374
@widgetmanagers ? ( object_loop => \@widgetmanagers ) : (),
2375
@widget_loop ? ( widget_table => \@widget_loop ) : (),
2376
object_type => "template",
2377
search_type => "template",
2378
search_label => MT::Template->class_label_plural,
2379
listing_screen => 1,
2380
screen_id => "list-widget-set",
2381
$blog_id ? ( blog_view => 1, blog_id => $blog_id ) : (),
2382
exists($opt{rebuild}) ? ( rebuild => $opt{rebuild} ) : (),
2383
exists($opt{error}) ? ( error => $opt{error} ) : (),
2384
exists($opt{deleted}) ? ( saved => $opt{deleted} ) : ()
2387
$app->load_tmpl('list_widget.tmpl', $param);
2392
my $q = $app->param;
2393
my $type = $q->param('_type');
2395
return $app->errtrans("Invalid request.")
2398
return $app->error( $app->translate("Invalid request.") )
2399
if $app->request_method() ne 'POST';
2401
$app->validate_magic() or return;
2403
my $tmpl_class = $app->model('template');
2405
for my $id ( $q->param('id') ) {
2406
next unless $id; # avoid 'empty' ids
2408
my $obj = $tmpl_class->load($id);
2410
$app->run_callbacks( 'cms_delete_permission_filter.template',
2412
|| return $app->error(
2413
$app->translate( "Permission denied: [_1]", $app->errstr() ) );
2416
or return $app->errtrans(
2417
'Removing [_1] failed: [_2]',
2418
$app->translate('template'),
2421
$app->run_callbacks( 'cms_post_delete.template', $app, $obj );
2426
sub restore_widgetmanagers {
2427
my ($cb, $objects, $deferred, $errors, $callback) = @_;
2428
my @keys = grep { $_ =~ /^MT::Template#/ } keys( %$objects );
2429
foreach my $key ( @keys ) {
2430
my $tmpl = $objects->{$key};
2431
next unless 'widgetset' eq $tmpl->type;
2432
my $modulesets = $tmpl->modulesets;
2433
next unless $modulesets;
2434
$callback->( MT->translate( 'Restoring widget set [_1]... ', $tmpl->name ) );
2436
my @tmpl_ids = split ',', $modulesets;
2438
foreach my $id ( @tmpl_ids ) {
2439
my $new_tmpl = $objects->{"MT::Template#$id"};
2440
next unless $new_tmpl;
2441
push @new_ids, $new_tmpl->id;
2444
$tmpl->modulesets( join(',', @new_ids) );
2446
$callback->( MT->translate("Done.") . "\n" );
2449
$callback->( MT->translate("Failed.") . "\n" );
2456
my @period_options = (
2467
expr => 24 * 60 * 60,
2473
return unless defined $sec;
2474
my ( $period, $interval );
2475
for (@period_options) {
2476
last if $sec % $_->{expr};
2477
$period = $_->{name};
2478
$interval = $sec / $_->{expr};
2480
( $period, $interval );
2484
my ( $period, $interval ) = @_;
2485
return unless defined $period;
2487
for (@period_options) {
2488
if ( $_->{name} eq $period ) {
2489
$sec = $interval * $_->{expr};