root/branches/release-30/lib/MT/CMS/Common.pm @ 1386

Revision 1386, 37.9 kB (checked in by bchoate, 21 months ago)

Fix for start_rebuild_pages call.

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