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

Revision 2115, 38.9 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 
1package MT::CMS::Common;
2
3use strict;
4
5use MT::Util qw( format_ts offset_time_list relative_date );
6
7sub save {
8    my $app  = shift;
9    my $q    = $app->param;
10    my $type = $q->param('_type');
11
12    return $app->errtrans("Invalid request.")
13      unless $type;
14
15    # being a general-purpose method, lets look for a mode handler
16    # that is specifically for editing this type. if we find it,
17    # reroute to it.
18
19    my $save_mode = 'save_' . $type;
20    if ( my $hdlrs = $app->handlers_for_mode($save_mode) ) {
21        return $app->forward($save_mode);
22    }
23
24    my $id = $q->param('id');
25    $q->param( 'allow_pings', 0 )
26      if ( $type eq 'category' ) && !defined( $q->param('allow_pings') );
27
28    $app->validate_magic() or return;
29    my $author = $app->user;
30
31    # Check permissions
32    my $perms = $app->permissions;
33
34    if ( !$author->is_superuser ) {
35        if ( ($type ne 'author') && ($type ne 'template') )
36        {    # for authors, blog-ctx $perms is not relevant
37            return $app->errtrans("Permisison denied.")
38              if !$perms && $id;
39        }
40
41        $app->run_callbacks( 'cms_save_permission_filter.' . $type, $app, $id )
42          || return $app->error(
43            $app->translate( "Permission denied: [_1]", $app->errstr() ) );
44    }
45
46    my $param = {};
47    if ( $type eq 'author' ) {
48        if ( my $delim = $q->param('tag_delim') ) {
49            $param->{ 'auth_pref_tag_delim_' . $delim } = 1;
50            $param->{'auth_pref_tag_delim'} = $delim;
51        }
52        $param->{languages} =
53          $app->languages_list( $q->param('preferred_language') )
54          if $q->param('preferred_language');
55        $param->{create_personal_weblog} =
56          $q->param('create_personal_weblog') ? 1 : 0;
57        require MT::Permission;
58        my $sys_perms = MT::Permission->perms('system');
59        foreach (@$sys_perms) {
60            $param->{ 'perm_can_' . $_->[0] } = 1
61              if $q->param( 'can_' . $_->[0] );
62        }
63    }
64
65    my $filter_result = $app->run_callbacks( 'cms_save_filter.' . $type, $app );
66
67    if ( !$filter_result ) {
68        my %param = (%$param);
69        $param{error}       = $app->errstr;
70        $param{return_args} = $app->param('return_args');
71
72        if ( ( $type eq 'notification' ) || ( $type eq 'banlist' ) ) {
73            return list( $app, \%param );
74        }
75        elsif ( ( $app->param('cfg_screen') || '' ) eq 'cfg_archives' ) {
76            return edit( $app, \%param );
77        }
78        else {
79            return $app->forward( 'view', \%param );
80        }
81    }
82
83    return $app->errtrans(
84        'The Template Name and Output File fields are required.')
85      if $type eq 'template' && !$q->param('name') && !$q->param('outfile');
86
87    if ( $type eq 'author' ) {
88        ## If we are saving a user profile, we need to do some
89        ## password maintenance. First make sure that the two
90        ## passwords match...
91        my $editing_other_profile;
92        if ( $id && ( $author->id != $id ) && ( $author->is_superuser ) ) {
93            $editing_other_profile = 1;
94        }
95        my %param = (%$param);
96        unless ($editing_other_profile) {
97            require MT::Auth;
98            my $error = MT::Auth->sanity_check($app);
99            if ($error) {
100                $param{error} = $error;
101                require MT::Log;
102                $app->log(
103                    {
104                        message  => $error,
105                        level    => MT::Log::ERROR(),
106                        class    => 'system',
107                        category => 'save_author_profile'
108                    }
109                );
110            }
111            if ( $param{error} ) {
112                $param{return_args} = $app->param('return_args');
113                my $qual = $id ? '' : 'author_state_';
114                for my $f (qw( name nickname email url state )) {
115                    $param{ $qual . $f } = $q->param($f);
116                }
117                return edit( $app, \%param );
118            }
119        }
120    }
121
122    if ( $type eq 'template' ) {
123
124        # check for autosave
125        if ( $q->param('_autosave') ) {
126            return $app->autosave_object();
127        }
128    }
129
130    my $class = $app->model($type)
131      or return $app->errtrans( "Invalid type [_1]", $type );
132    my ($obj);
133    if ($id) {
134        $obj = $class->load($id)
135            or return $app->error($app->translate("Invalid ID [_1]", $id));
136    }
137    else {
138        $obj = $class->new;
139    }
140
141    my $original = $obj->clone();
142    my $names    = $obj->column_names;
143    my %values   = map { $_ => ( scalar $q->param($_) ) } @$names;
144
145    if ( $type eq 'blog' ) {
146        unless ( $author->is_superuser
147            || ( $perms && $perms->can_administer_blog ) )
148        {
149            if ( $id && !( $perms->can_set_publish_paths ) ) {
150                delete $values{site_url};
151                delete $values{site_path};
152                delete $values{archive_url};
153                delete $values{archive_path};
154            }
155            if ( $id && !( $perms->can_edit_config ) ) {
156                delete $values{$_} foreach grep {
157                         $_ ne 'site_path'
158                      && $_ ne 'site_url'
159                      && $_ ne 'archive_path'
160                      && $_ ne 'archive_url'
161                } @$names;
162            }
163        }
164    }
165
166    if ( $type eq 'author' ) {
167
168        #FIXME: Legacy columns - remove them
169        my @cols = qw(is_superuser can_create_blog can_view_log can_edit_templates);
170        if ( !$author->is_superuser ) {
171            delete $values{$_} for @cols;
172        }
173        else {
174            if ( !$id || ( $author->id != $id ) ) {
175                # Assign the auth_type unless it was assigned
176                # through the form.
177                $obj->auth_type($app->config->AuthenticationModule)
178                    unless $obj->auth_type;
179                if ( $values{'status'} == MT::Author::ACTIVE() ) {
180                    my $sys_perms = MT::Permission->perms('system');
181                    if ( defined($q->param('is_superuser'))
182                      && $q->param('is_superuser')) {
183                        $obj->is_superuser(1);
184                    }
185                    else {
186                        foreach (@$sys_perms) {
187                            my $name = 'can_' . $_->[0];
188                            $name = 'is_superuser' if $name eq 'can_administer';
189                            if ( defined $q->param($name) ) {
190                                $obj->$name( $q->param($name) );
191                                delete $values{$name};
192                            }
193                            else {
194                                $obj->$name(0);
195                            }
196                        }
197                    }
198                }
199            }
200        }
201        delete $values{'password'};
202    }
203
204    if ( $type eq 'blog' ) {
205        # If this is a new blog, set the preferences, archive settings
206        # and template set to the defaults.
207        if ( !$obj->id ) {
208            $obj->language( $app->user->preferred_language );
209            $obj->nofollow_urls(1);
210            $obj->follow_auth_links(1);
211            $obj->page_layout('layout-wtt');
212            my @authenticators = qw( MovableType );
213            foreach my $auth (qw( Vox LiveJournal )) {
214                my $a = MT->commenter_authenticator($auth);
215                if ( !defined $a
216                    || ( exists $a->{condition} && ( !$a->{condition}->() ) ) )
217                {
218                    next;
219                }
220                push @authenticators, $auth;
221            }
222            $obj->commenter_authenticators( join ',', @authenticators );
223            my $set = $app->param('template_set') || 'mt_blog';
224            $obj->template_set( $set );
225        }
226
227        if ( $values{file_extension} ) {
228            $values{file_extension} =~ s/^\.*//
229              if ( $q->param('file_extension') || '' ) ne '';
230        }
231
232        unless ( $values{site_url} =~ m!/$! ) {
233            my $url = $values{site_url};
234            $values{site_url} = $url;
235        }
236    }
237
238    if ( $type eq 'entry' || $type eq 'page' ) {
239
240        # This has to happen prior to callbacks since callbacks may
241        # be affected by the translation...
242
243        # translates naughty words when PublishCharset is NOT UTF-8
244        $app->_translate_naughty_words($obj);
245    }
246
247    if ( $type eq 'template' ) {
248        if (   $q->param('type') eq 'archive'
249            && $q->param('archive_type') )
250        {
251            $values{type} = $q->param('archive_type');
252        }
253    }
254
255    delete $values{'id'} if exists( $values{'id'} ) && !$values{'id'};
256    $obj->set_values( \%values );
257
258    if ( $obj->properties->{audit} ) {
259        $obj->created_by( $author->id ) unless $obj->id;
260        $obj->modified_by( $author->id ) if $obj->id;
261    }
262
263    unless (
264        $app->run_callbacks( 'cms_pre_save.' . $type, $app, $obj, $original ) )
265    {
266        if ( 'blog' eq $type ) {
267            my $meth = $q->param('cfg_screen');
268            if ( $meth && $app->handlers_for_mode($meth) ) {
269                $app->error(
270                    $app->translate( "Save failed: [_1]", $app->errstr ) );
271                return $app->$meth;
272            }
273        }
274        return edit( $app,
275            {
276                %$param,
277                error => $app->translate( "Save failed: [_1]", $app->errstr )
278            }
279        );
280    }
281
282    # Done pre-processing the record-to-be-saved; now save it.
283
284    $obj->touch() if ( $type eq 'blog' );
285
286    $obj->save
287      or return $app->error(
288        $app->translate( "Saving object failed: [_1]", $obj->errstr ) );
289
290    # Now post-process it.
291    $app->run_callbacks( 'cms_post_save.' . $type, $app, $obj, $original )
292      or return $app->error( $app->errstr() );
293
294    # Save NWC settings
295    my $screen = $q->param('cfg_screen') || '';
296    if ( $type eq 'blog' && $screen eq 'cfg_entry' ) {
297        my @fields;
298        push( @fields, 'title' )     if $q->param('nwc_title');
299        push( @fields, 'text' )      if $q->param('nwc_text');
300        push( @fields, 'text_more' ) if $q->param('nwc_text_more');
301        push( @fields, 'keywords' )  if $q->param('nwc_keywords');
302        push( @fields, 'excerpt' )   if $q->param('nwc_excerpt');
303        push( @fields, 'tags' )      if $q->param('nwc_tags');
304        my $fields = @fields ? join( ',', @fields ) : 0;
305        $obj->smart_replace_fields( $fields );
306        $obj->smart_replace( $q->param('nwc_smart_replace') );
307        $obj->save;
308    }
309
310    # Finally, decide where to go next, depending on the object type.
311    my $blog_id = $q->param('blog_id');
312    if ( $type eq 'blog' ) {
313        $blog_id = $obj->id;
314    }
315
316    # TODO: convert this to use $app->call_return();
317    # then templates can determine the page flow.
318    if ( $type eq 'notification' ) {
319        return $app->redirect(
320            $app->uri(
321                'mode' => 'list',
322                args   => {
323                    '_type' => 'notification',
324                    blog_id => $blog_id,
325                    saved   => $obj->email
326                }
327            )
328        );
329    }
330    elsif ( my $cfg_screen = $q->param('cfg_screen') ) {
331        if ( $cfg_screen eq 'cfg_publish_profile' ) {
332            my $dcty = $obj->custom_dynamic_templates || 'none';
333            if ( ( $dcty eq 'all' ) || ( $dcty eq 'archives' ) ) {
334                require MT::CMS::Blog;
335                my %param = ();
336                MT::CMS::Blog::_create_build_order( $app, $obj, \%param );
337                $q->param( 'single_template', 1 ); # to show tmpl full-screen
338                if ( $dcty eq 'all' ) {
339                    $q->param( 'type', $param{build_order} );
340                }
341                elsif ( $dcty eq 'archives' ) {
342                    my @ats = map { $_->{archive_type} } @{ $param{archive_type_loop} };
343                    $q->param( 'type', join( ',', @ats ) );
344                }
345                return MT::CMS::Blog::start_rebuild_pages($app);
346            }
347        }
348        if ( $cfg_screen eq 'cfg_templatemaps' ) {
349            $cfg_screen = 'cfg_archives';
350        }
351        my $site_path = $obj->site_path;
352        my $fmgr      = $obj->file_mgr;
353        unless ( $fmgr->exists($site_path) ) {
354            $fmgr->mkpath($site_path);
355        }
356        $app->add_return_arg( no_writedir => 1 )
357          unless $fmgr->exists($site_path) && $fmgr->can_write($site_path);
358    }
359    elsif ( $type eq 'banlist' ) {
360        return $app->redirect(
361            $app->uri(
362                'mode' => 'list',
363                args   => {
364                    '_type' => 'banlist',
365                    blog_id => $blog_id,
366                    saved   => $obj->ip
367                }
368            )
369        );
370    }
371    elsif ( $type eq 'template' && $q->param('rebuild') ) {
372        if ( !$id ) {
373            # add return argument for newly created templates
374            $app->add_return_arg( id => $obj->id );
375        }
376        if ( $obj->type eq 'index' ) {
377            $q->param( 'type',            'index-' . $obj->id );
378            $q->param( 'tmpl_id',         $obj->id );
379            $q->param( 'single_template', 1 );
380            return $app->forward( 'start_rebuild' );
381        } else {
382            # archive rebuild support
383            $q->param( 'id', $obj->id );
384            return $app->forward( 'publish_archive_templates' );
385        }
386    }
387    elsif ( $type eq 'template' ) {
388        if (   $obj->type eq 'archive'
389            || $obj->type eq 'category'
390            || $obj->type eq 'page'
391            || $obj->type eq 'individual' )
392        {
393            require MT::TemplateMap;
394            my @maps = MT::TemplateMap->load(
395                {
396                    template_id => $obj->id,
397                    build_type  => MT::PublishOption::DYNAMIC()
398                }
399            );
400            my @ats = map { $_->archive_type } @maps;
401            if ($#ats >= 0) {
402                $q->param( 'type', join( ',', @ats ) );
403                $q->param( 'with_indexes', 1 );
404                $q->param( 'no_static', 1 );
405                $q->param( 'template_id', $obj->id );
406                $q->param( 'single_template', 1 );
407                require MT::CMS::Blog;
408                return MT::CMS::Blog::start_rebuild_pages($app);
409            }
410        }
411    }
412    elsif ( $type eq 'blog' ) {
413        return $app->redirect(
414            $app->uri(
415                'mode' => 'cfg_prefs',
416                args   => { blog_id => $blog_id, saved => 1 }
417            )
418        );
419    }
420    elsif ( $type eq 'author' ) {
421        # Delete the author's userpic thumb (if any); it'll be regenerated.
422        if ($original->userpic_asset_id != $obj->userpic_asset_id) {
423            my $thumb_file = $original->userpic_file();
424            my $fmgr = MT::FileMgr->new('Local');
425            if ($fmgr->exists($thumb_file)) {
426                $fmgr->delete($thumb_file);
427            }
428        }
429    }
430
431    $app->add_return_arg( 'id' => $obj->id ) if !$original->id;
432    $app->add_return_arg( 'saved' => 1 );
433    $app->call_return;
434}
435
436sub edit {
437    my $app  = shift;
438    my $q    = $app->param;
439    my $type = $q->param('_type');
440
441    return $app->errtrans("Invalid request.")
442      unless $type;
443
444    # being a general-purpose method, lets look for a mode handler
445    # that is specifically for editing this type. if we find it,
446    # reroute to it.
447
448    my $edit_mode = $app->mode . '_' . $type;
449    if ( my $hdlrs = $app->handlers_for_mode($edit_mode) ) {
450        return $app->forward($edit_mode, @_);
451    }
452
453    my %param = eval { $_[0] ? %{ $_[0] } : (); };
454    die Carp::longmess if $@;
455    my $class = $app->model($type) or return;
456    my $blog_id = $q->param('blog_id');
457
458    if ( defined($blog_id) && $blog_id ) {
459        return $app->error( $app->translate("Invalid parameter") )
460          unless ( $blog_id =~ m/\d+/ );
461    }
462
463    $app->remove_preview_file;
464
465    my $enc = $app->config->PublishCharset;
466    if ( $q->param('_recover') ) {
467        my $sess_obj = $app->autosave_session_obj;
468        if ($sess_obj) {
469            my $data = $sess_obj->thaw_data;
470            if ($data) {
471
472                # XMLHttpRequest always send text in UTF-8... right?
473                if ( 'utf-8' eq lc($enc) ) {
474                    $q->param( $_, $data->{$_} ) for keys %$data;
475                }
476                else {
477                    foreach ( keys %$data ) {
478                        my $encoded =
479                          MT::I18N::encode_text( $data->{$_}, 'utf-8', $enc );
480                        $q->param( $_, $encoded );
481                    }
482                }
483                $param{'recovered_object'} = 1;
484            }
485            else {
486                $param{'recovered_failed'} = 1;
487            }
488        }
489        else {
490            $param{'recovered_failed'} = 1;
491        }
492    }
493    elsif ( $q->param('qp') ) {
494        foreach (qw( title text )) {
495            my $data = $q->param($_);
496            my $encoded = MT::I18N::encode_text( $data, undef, $enc )
497              if $data;
498            $q->param( $_, $encoded );
499        }
500    }
501
502    $param{autosave_frequency} = $app->config->AutoSaveFrequency;
503
504    my $id     = $q->param('id');
505    my $perms  = $app->permissions;
506    my $author = $app->user;
507    my $cfg    = $app->config;
508    $param{styles} = '';
509    if ( $type eq 'author' ) {
510        if ( $perms || $blog_id ) {
511            return $app->return_to_dashboard( redirect => 1 );
512        }
513    }
514    else {
515        if ( ( !$perms || !$blog_id )
516            && ( $type eq 'entry' || $type eq 'page'
517                 || $type eq 'category' || $type eq 'folder'
518                 || $type eq 'comment'  || $type eq 'commenter'
519                 || $type eq 'ping' ) ) {
520            return $app->return_to_dashboard( redirect => 1 );
521        }
522    }
523
524    my $cols = $class->column_names;
525    require MT::Promise;
526    my $obj_promise = MT::Promise::delay(
527        sub {
528            return $class->load($id) || undef;
529        }
530    );
531
532    if ( !$author->is_superuser ) {
533        $app->run_callbacks( 'cms_view_permission_filter.' . $type,
534            $app, $id, $obj_promise )
535          || return $app->error(
536            $app->translate( "Permission denied: [_1]", $app->errstr() ) );
537    }
538    my $obj;
539    my $blog;
540    my $blog_class = $app->model('blog');
541    if ($blog_id) {
542        $blog = $blog_class->load($blog_id);
543    }
544    else {
545        $blog_id = 0;
546    }
547
548    if ($id) {    # object exists, we're just editing it.
549          # Stash the object itself so we don't have to keep forcing the promise
550        $obj = $obj_promise->force()
551          or return $app->error(
552            $app->translate(
553                "Load failed: [_1]",
554                $class->errstr || $app->translate("(no reason given)")
555            )
556          );
557
558        # Populate the param hash with the object's own values
559        for my $col (@$cols) {
560            $param{$col} =
561              defined $q->param($col) ? $q->param($col) : $obj->$col();
562        }
563
564        # Make certain any blog-specific element matches the blog we're
565        # dealing with. If not, call shenanigans.
566        if (   ( exists $param{blog_id} )
567            && ( $blog_id != ($obj->blog_id || 0) ) )
568        {
569            return $app->return_to_dashboard( redirect => 1 );
570        }
571
572        if ( $class->properties->{audit} ) {
573            my $creator = MT::Author->load(
574                {
575                    id   => $obj->created_by(),
576                    type => MT::Author::AUTHOR()
577                }
578            );
579            if ($creator) {
580                $param{created_by} = $creator->name;
581            }
582            if ( my $mod_by = $obj->modified_by() ) {
583                my $modified = MT::Author->load(
584                    {
585                        id   => $mod_by,
586                        type => MT::Author::AUTHOR()
587                    }
588                );
589                if ($modified) {
590                    $param{modified_by} = $modified->name;
591                }
592                else {
593                    $param{modified_by} = $app->translate("(user deleted)");
594                }
595
596                # Since legacy MT installs will still have a
597                # timestamp type for their modified_on fields,
598                # we cannot reliably disaply a modified on date
599                # by default; we must only show the modification
600                # date IF there is also a modified_by value.
601                if ( my $ts = $obj->modified_on ) {
602                    $param{modified_on_ts} = $ts;
603                    $param{modified_on_formatted} =
604                      format_ts( MT::App::CMS::LISTING_DATETIME_FORMAT(), $ts, undef, $app->user ? $app->user->preferred_language : undef );
605                }
606            }
607            if ( my $ts = $obj->created_on ) {
608                $param{created_on_ts} = $ts;
609                $param{created_on_formatted} =
610                  format_ts( MT::App::CMS::LISTING_DATETIME_FORMAT(), $ts, undef, $app->user ? $app->user->preferred_language : undef );
611            }
612        }
613
614        $param{new_object} = 0;
615    }
616    else {    # object is new
617        $param{new_object} = 1;
618        for my $col (@$cols) {
619            $param{$col} = $q->param($col);
620        }
621    }
622
623    my $res = $app->run_callbacks('cms_edit.' . $type, $app, $id, $obj, \%param);
624    if (!$res) {
625        return $app->error($app->callback_errstr());
626    }
627
628    if ($param{autosave_support}) {
629        # autosave support, but don't bother if we're reediting
630        if ( !$app->param('reedit') ) {
631            my $sess_obj = $app->autosave_session_obj;
632            if ($sess_obj) {
633                $param{autosaved_object_exists} = 1;
634                $param{autosaved_object_ts} =
635                  MT::Util::epoch2ts( $blog, $sess_obj->start );
636            }
637        }
638    }
639
640    if ( ( $q->param('msg') || "" ) eq 'nosuch' ) {
641        $param{nosuch} = 1;
642    }
643    for my $p ( $q->param ) {
644        $param{$p} = $q->param($p) if $p =~ /^saved/;
645    }
646    $param{page_actions} = $app->page_actions($type, $obj);
647    if ( $class->can('class_label') ) {
648        $param{object_label} = $class->class_label;
649    }
650    if ( $class->can('class_label_plural') ) {
651        $param{object_label_plural} = $class->class_label_plural;
652    }
653
654    my $tmpl_file = $param{output} || "edit_${type}.tmpl";
655    $param{object_type} ||= $type;
656    $param{screen_id} ||= "edit-$type";
657    $param{screen_class} .= " edit-$type";
658    return $app->load_tmpl( $tmpl_file, \%param );
659}
660
661sub list {
662    my $app  = shift;
663    my $q    = $app->param;
664    my $type = $q->param('_type');
665
666    return $app->errtrans("Invalid request.")
667      unless $type;
668
669    # being a general-purpose method, lets look for a mode handler
670    # that is specifically for editing this type. if we find it,
671    # reroute to it.
672
673    my $list_mode = 'list_' . $type;
674    if ( my $hdlrs = $app->handlers_for_mode($list_mode) ) {
675        return $app->forward($list_mode);
676    }
677
678    my %param = $_[0] ? %{ $_[0] } : ();
679
680    my $perms = $app->permissions;
681    return $app->return_to_dashboard( redirect => 1 )
682      unless $perms;
683    if (
684        $perms
685        && (   ( $type eq 'blog' && !$perms->can_edit_config )
686            || ( $type eq 'template'     && !$perms->can_edit_templates )
687            || ( $type eq 'notification' && !$perms->can_edit_notifications ) )
688      )
689    {
690        return $app->return_to_dashboard( permission => 1 );
691    }
692    my $id        = $q->param('id');
693    my $class     = $app->model($type) or return;
694    my $blog_id   = $q->param('blog_id');
695    my $list_pref = $app->list_pref($type);
696    my ( %terms, %args );
697    %param = ( %param, %$list_pref );
698    my $cols   = $class->column_names;
699    my $limit  = $list_pref->{rows};
700    my $offset = $app->param('offset') || 0;
701
702    for my $name (@$cols) {
703        $terms{blog_id} = $blog_id, last
704          if $name eq 'blog_id';
705    }
706    if ( $type eq 'notification' ) {
707        $args{direction} = 'descend';
708        $args{offset}    = $offset;
709        $args{limit}     = $limit + 1;
710    }
711    elsif ( $type eq 'banlist' ) {
712        $param{use_plugins} = $app->config->UsePlugins;
713        $limit = 0;
714    }
715    my $iter = $class->load_iter( \%terms, \%args );
716
717    my (
718        @data,         @index_data,  @custom_data,
719        @archive_data, @system_data, @widget_data
720    );
721    my (%authors);
722    my $blog_class = $app->model('blog');
723    my $blog       = $blog_class->load($blog_id);
724    my $set        = $blog ? $blog->template_set : undef;
725    require MT::DefaultTemplates;
726    my $dtmpl = MT::DefaultTemplates->templates($set) || [];
727    my %dtmpl = map { $_->{type} => $_ } @$dtmpl;
728
729    while ( my $obj = $iter->() ) {
730        my $row = $obj->column_values;
731        if ( my $ts = $obj->created_on ) {
732            $row->{created_on_formatted} =
733              format_ts( MT::App::CMS::LISTING_DATE_FORMAT(), $ts, $blog, $app->user ? $app->user->preferred_language : undef );
734            $row->{created_on_time_formatted} =
735              format_ts( MT::App::CMS::LISTING_DATETIME_FORMAT(), $ts, $blog, $app->user ? $app->user->preferred_language : undef );
736            $row->{created_on_relative} = relative_date( $ts, time, $blog );
737        }
738        if ( $type eq 'template' ) {
739            $row->{name} = '' if !defined $row->{name};
740            $row->{name} =~ s/^\s+|\s+$//g;
741            $row->{name} = "(" . $app->translate("No Name") . ")"
742              if $row->{name} eq '';
743
744            if ( $obj->type eq 'index' ) {
745                push @index_data, $row;
746                $row->{rebuild_me} =
747                  defined $row->{rebuild_me} ? $row->{rebuild_me} : 1;
748                my $published_url = $obj->published_url;
749                $row->{published_url} = $published_url if $published_url;
750            }
751            elsif ( $obj->type eq 'custom' ) {
752                push @custom_data, $row;
753            }
754            elsif ( $obj->type eq 'widget' ) {
755                push @widget_data, $row;
756            }
757            elsif ($obj->type eq 'archive'
758                || $obj->type eq 'category'
759                || $obj->type eq 'page'
760                || $obj->type eq 'individual' )
761            {
762
763                # FIXME: enumeration of types
764                push @archive_data, $row;
765            }
766            else {
767                if ( my $def_tmpl = $dtmpl{ $obj->type } ) {
768                    $row->{description} = $def_tmpl->{description_label};
769                }
770                else {
771
772                    # unknown system template; skip over it
773                    # or should we change it to a custom template
774                    # right now?
775                    next;
776                }
777                push @system_data, $row;
778            }
779            $param{search_label} = $app->translate('Templates');
780        }
781        else {
782            if ( $limit && ( scalar @data == $limit ) ) {
783                $param{next_offset} = 1;
784                last;
785            }
786            push @data, $row;
787        }
788        if ( $type eq 'ping' ) {
789            return $app->list_pings();
790            require MT::Trackback;
791            require MT::Entry;
792            my $tb_center = MT::Trackback->load( $obj->tb_id );
793            my $entry     = MT::Entry->load( $tb_center->entry_id )
794                or return $app->error($app->translate('Can\'t load entry #[_1].', $tb_center->entry_id));
795            if ( my $ts = $obj->created_on ) {
796                $row->{created_on_time_formatted} =
797                  format_ts( MT::App::CMS::LISTING_DATETIME_FORMAT(), $ts, $blog, $app->user ? $app->user->preferred_language : undef );
798                $row->{has_edit_access} = $perms->can_edit_all_posts
799                  || $app->user->id == $entry->author_id;
800            }
801        }
802    }    # end loop over the set of objects;
803         # NOW transform the @data array
804    if ( $type eq 'notification' ) {
805        $app->add_breadcrumb( $app->translate('Notification List') );
806        $param{nav_notifications} = 1;
807
808        #@data = sort { $a->{email} cmp $b->{email} } @data;
809        $param{object_type}        = 'notification';
810        $param{list_noncron}       = 1;
811        $param{notification_count} = scalar @data;
812        $param{search_type} = 'entry';
813    }
814    if ( $type eq 'template' ) {
815        $app->add_breadcrumb( $app->translate('Templates') );
816        $param{nav_templates} = 1;
817        for my $ref ( \@index_data, \@custom_data, \@archive_data ) {
818            @$ref = sort { $a->{name} cmp $b->{name} } @$ref;
819        }
820        my $tab = $app->param('tab') || 'index';
821        $param{template_group}      = $tab;
822        $param{"tab_$tab"}          = 1;
823        $param{object_index_loop}   = \@index_data;
824        $param{object_custom_loop}  = \@custom_data;
825        $param{object_widget_loop}  = \@widget_data;
826        $param{object_archive_loop} = \@archive_data;
827        $param{object_system_loop}  = \@system_data;
828        $param{object_type}         = 'template';
829    }
830    else {
831        $param{object_loop} = \@data;
832    }
833
834    # add any breadcrumbs
835    if ( $type eq 'banlist' ) {
836        $app->add_breadcrumb( $app->translate('IP Banning') );
837        $param{nav_config}                       = 1;
838        $param{object_type}                      = 'banlist';
839        $param{show_ip_info}                     = 1;
840        $param{list_noncron}                     = 1;
841        $param{search_type} = 'entry';
842        $param{can_edit_config_or_publish_paths} = $perms->can_edit_config
843          || $perms->can_set_publish_paths;
844    }
845    elsif ( $type eq 'ping' ) {
846        $app->add_breadcrumb( $app->translate('TrackBacks') );
847        $param{nav_trackbacks} = 1;
848        $param{object_type}    = 'ping';
849    }
850    $param{object_count} = scalar @data;
851
852    if ( $type ne 'template' ) {
853        $param{offset}     = $offset;
854        $param{list_start} = $offset + 1;
855        delete $args{limit};
856        delete $args{offset};
857        $param{list_total} = $class->count( \%terms, \%args );
858        $param{list_end}        = $offset + ( scalar @data );
859        $param{next_offset_val} = $offset + ( scalar @data );
860
861    #$param{next_offset} = $param{next_offset_val} < $param{list_total} ? 1 : 0;
862        $param{next_max} = $param{list_total} - $limit;
863        $param{next_max} = 0 if ( $param{next_max} || 0 ) < $offset + 1;
864        if ( $offset > 0 ) {
865            $param{prev_offset}     = 1;
866            $param{prev_offset_val} = $offset - $limit;
867            $param{prev_offset_val} = 0 if $param{prev_offset_val} < 0;
868        }
869    }
870
871    $app->load_list_actions( $type, \%param );
872
873    $param{saved}         = $q->param('saved');
874    $param{saved_deleted} = $q->param('saved_deleted');
875    $param{page_actions}  = $app->page_actions( 'list_' . $type );
876    unless ( $param{screen_class} ) {
877        $param{screen_class} = "list-$type";
878    }
879    $app->load_tmpl( "list_${type}.tmpl", \%param );
880}
881
882sub delete {
883    my $app  = shift;
884    my $q    = $app->param;
885    my $type = $q->param('_type');
886
887    return $app->errtrans("Invalid request.")
888      unless $type;
889
890    return $app->error( $app->translate("Invalid request.") )
891      if $app->request_method() ne 'POST';
892
893    # being a general-purpose method, lets look for a mode handler
894    # that is specifically for editing this type. if we find it,
895    # reroute to it.
896
897    my $delete_mode = 'delete_' . $type;
898    if ( my $hdlrs = $app->handlers_for_mode($delete_mode) ) {
899        return $app->forward($delete_mode);
900    }
901
902    my $parent  = $q->param('parent');
903    my $blog_id = $q->param('blog_id');
904    my $class   = $app->model($type) or return;
905    my $perms   = $app->permissions;
906    my $author  = $app->user;
907
908    $app->validate_magic() or return;
909
910    my ( $entry_id, $cat_id, $author_id ) = ( "", "", "" );
911    my %rebuild_entries;
912    my @rebuild_cats;
913    my $required_items = 0;
914    for my $id ( $q->param('id') ) {
915        next unless $id;    # avoid 'empty' ids
916        if ( ( $type eq 'association' ) && ( $id =~ /PSEUDO-/ ) ) {
917            $app->_delete_pseudo_association($id);
918            next;
919        }
920
921        my $obj = $class->load($id);
922        next unless $obj;
923        $app->run_callbacks( 'cms_delete_permission_filter.' . $type,
924            $app, $obj )
925          || return $app->error(
926            $app->translate( "Permission denied: [_1]", $app->errstr() ) );
927
928        if ( $type eq 'comment' ) {
929            $entry_id = $obj->entry_id;
930            $rebuild_entries{$entry_id} = 1 if $obj->visible;
931        }
932        elsif ( $type eq 'ping' || $type eq 'ping_cat' ) {
933            require MT::Trackback;
934            my $tb = MT::Trackback->load( $obj->tb_id );
935            if ($tb) {
936                $entry_id = $tb->entry_id;
937                $cat_id   = $tb->category_id;
938                if ( $obj->visible ) {
939                    $rebuild_entries{$entry_id} = 1 if $entry_id;
940                    push @rebuild_cats, $cat_id if $cat_id;
941                }
942            }
943        }
944        elsif ( $type eq 'tag' ) {
945
946            # if we're in a blog context, remove ONLY tags from that weblog
947            if ($blog_id) {
948                my $ot_class  = $app->model('objecttag');
949                my $obj_type  = $q->param('__type') || 'entry';
950                my $obj_class = $app->model($obj_type);
951                my $iter      = $ot_class->load_iter(
952                    {
953                        blog_id           => $blog_id,
954                        object_datasource => $obj_class->datasource,
955                        tag_id            => $id
956                    },
957                    {
958                        'join' => $obj_class->join_on(
959                            undef,
960                            {
961                                id => \'= objecttag_object_id',
962                                (
963                                    $obj_class =~ m/asset/i
964                                    ? ()
965                                    : ( class => $obj_class->class_type )
966                                )
967                            }
968                        )
969                    }
970                );
971
972                if ($iter) {
973                    my @ot;
974                    while ( my $obj = $iter->() ) {
975                        push @ot, $obj->id;
976                    }
977                    foreach (@ot) {
978                        my $obj = $ot_class->load($_);
979                        next unless $obj;
980                        $obj->remove
981                          or return $app->errtrans( 'Removing tag failed: [_1]',
982                            $obj->errstr );
983                    }
984                }
985
986                $app->run_callbacks( 'cms_post_delete.' . $type, $app, $obj );
987                next;
988
989            }
990        }
991        elsif ( $type eq 'category' ) {
992            my @kids = MT::Category->load( { parent => $id } );
993            return $app->errtrans(
994"You can't delete that category because it has sub-categories. Move or delete the sub-categories first if you want to delete this one."
995            ) if @kids;
996            if ( $app->config('DeleteFilesAtRebuild') ) {
997                require MT::Blog;
998                require MT::Entry;
999                require MT::Placement;
1000                my $blog = MT::Blog->load($blog_id)
1001                    or return $app->error($app->translate('Can\'t load blog #[_1].', $blog_id));
1002                my $at   = $blog->archive_type;
1003                if ( $at && $at ne 'None' ) {
1004                    my @at = split /,/, $at;
1005                    for my $target (@at) {
1006                        my $archiver = $app->publisher->archiver($target);
1007                        next unless $archiver;
1008                        if ( $archiver->category_based ) {
1009                            if ( $archiver->date_based ) {
1010                                my @entries = MT::Entry->load(
1011                                    { status => MT::Entry::RELEASE() },
1012                                    {
1013                                        join => MT::Placement->join_on(
1014                                            'entry_id',
1015                                            { category_id => $id },
1016                                            { unqiue      => 1 }
1017                                        )
1018                                    }
1019                                );
1020                                for (@entries) {
1021                                    $app->publisher->remove_entry_archive_file(
1022                                        Category    => $obj,
1023                                        ArchiveType => $target,
1024                                        Entry       => $_
1025                                    );
1026                                }
1027                            }
1028                            else {
1029                                $app->publisher->remove_entry_archive_file(
1030                                    Category    => $obj,
1031                                    ArchiveType => $target
1032                                );
1033                            }
1034                        }
1035                    }
1036                }
1037            }
1038        }
1039        elsif ( $type eq 'page' ) {
1040            if ( $app->config('DeleteFilesAtRebuild') ) {
1041                $app->publisher->remove_entry_archive_file(
1042                    Entry       => $obj,
1043                    ArchiveType => 'Page'
1044                );
1045            }
1046        }
1047        elsif ( $type eq 'author' ) {
1048            if ( $app->config->ExternalUserManagement ) {
1049                require MT::LDAP;
1050                my $ldap = MT::LDAP->new
1051                  or return $app->error(
1052                    MT->translate(
1053                        "Loading MT::LDAP failed: [_1].",
1054                        MT::LDAP->errstr
1055                    )
1056                  );
1057                my $dn = $ldap->get_dn( $obj->name );
1058                if ($dn) {
1059                    $app->add_return_arg( author_ldap_found => 1 );
1060                }
1061            }
1062        }
1063
1064        # FIXME: enumeration of types
1065        if (   $type eq 'template'
1066            && $obj->type !~
1067            /(custom|index|archive|page|individual|category|widget|backup)/o )
1068        {
1069            $required_items++;
1070        }
1071        else {
1072            $obj->remove
1073              or return $app->errtrans(
1074                'Removing [_1] failed: [_2]',
1075                $app->translate($type),
1076                $obj->errstr
1077              );
1078            $app->run_callbacks( 'cms_post_delete.' . $type, $app, $obj );
1079        }
1080    }
1081    require MT::Entry;
1082    for my $entry_id ( keys %rebuild_entries ) {
1083        my $entry = MT::Entry->load($entry_id);
1084        $app->rebuild_entry( Entry => $entry, BuildDependencies => 1 )
1085            or return $app->publish_error();
1086    }
1087    for my $cat_id (@rebuild_cats) {
1088
1089        # FIXME: What about other category-based archives?
1090        # What if user is not publishing category archives?
1091        my $cat = MT::Category->load($cat_id);
1092        $app->rebuild(
1093            Category    => $cat,
1094            BlogID      => $blog_id,
1095            ArchiveType => 'Category'
1096        ) or return $app->publish_error();
1097    }
1098    $app->run_callbacks( 'rebuild', MT::Blog->load($blog_id) );
1099    $app->add_return_arg(
1100        $type eq 'ping'
1101        ? ( saved_deleted_ping => 1 )
1102        : ( saved_deleted => 1 )
1103    );
1104    if ( $q->param('is_power_edit') ) {
1105        $app->add_return_arg( is_power_edit => 1 );
1106    }
1107    if ($required_items) {
1108        $app->add_return_arg(
1109            error => $app->translate("System templates can not be deleted.") );
1110    }
1111    $app->call_return;
1112}
1113
1114sub not_junk_test {
1115    my ( $eh, $app, $obj ) = @_;
1116    require MT::JunkFilter;
1117    MT::JunkFilter->filter($obj);
1118    $obj->is_junk ? 0 : 1;
1119}
1120
11211;
Note: See TracBrowser for help on using the browser.