root/branches/release-35/lib/MT/CMS/Common.pm @ 1980

Revision 1980, 37.7 kB (checked in by fumiakiy, 20 months ago)

Create required files and directories for dynamic publishing according to the selected publishing profile.

Also "rebuild" the necessary files to rename those files and append .static to the file name. BugId:79364

  • 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        $q->param( 'type',            'index-' . $obj->id );
373        $q->param( 'tmpl_id',         $obj->id );
374        $q->param( 'single_template', 1 );
375        require MT::CMS::Blog;
376        return MT::CMS::Blog::start_rebuild_pages($app);
377    }
378    elsif ( $type eq 'blog' ) {
379        return $app->redirect(
380            $app->uri(
381                'mode' => 'cfg_prefs',
382                args   => { blog_id => $blog_id, saved => 1 }
383            )
384        );
385    }
386    elsif ( $type eq 'author' ) {
387        # Delete the author's userpic thumb (if any); it'll be regenerated.
388        if ($original->userpic_asset_id != $obj->userpic_asset_id) {
389            my $thumb_file = $original->userpic_file();
390            my $fmgr = MT::FileMgr->new('Local');
391            if ($fmgr->exists($thumb_file)) {
392                $fmgr->delete($thumb_file);
393            }
394        }
395    }
396
397    $app->add_return_arg( 'id' => $obj->id ) if !$original->id;
398    $app->add_return_arg( 'saved' => 1 );
399    $app->call_return;
400}
401
402sub edit {
403    my $app  = shift;
404    my $q    = $app->param;
405    my $type = $q->param('_type');
406
407    return $app->errtrans("Invalid request.")
408      unless $type;
409
410    # being a general-purpose method, lets look for a mode handler
411    # that is specifically for editing this type. if we find it,
412    # reroute to it.
413
414    my $edit_mode = $app->mode . '_' . $type;
415    if ( my $hdlrs = $app->handlers_for_mode($edit_mode) ) {
416        return $app->forward($edit_mode, @_);
417    }
418
419    my %param = eval { $_[0] ? %{ $_[0] } : (); };
420    die Carp::longmess if $@;
421    my $class = $app->model($type) or return;
422    my $blog_id = $q->param('blog_id');
423
424    if ( defined($blog_id) && $blog_id ) {
425        return $app->error( $app->translate("Invalid parameter") )
426          unless ( $blog_id =~ m/\d+/ );
427    }
428
429    $app->remove_preview_file;
430
431    my $enc = $app->config->PublishCharset;
432    if ( $q->param('_recover') ) {
433        my $sess_obj = $app->autosave_session_obj;
434        if ($sess_obj) {
435            my $data = $sess_obj->thaw_data;
436            if ($data) {
437
438                # XMLHttpRequest always send text in UTF-8... right?
439                if ( 'utf-8' eq lc($enc) ) {
440                    $q->param( $_, $data->{$_} ) for keys %$data;
441                }
442                else {
443                    foreach ( keys %$data ) {
444                        my $encoded =
445                          MT::I18N::encode_text( $data->{$_}, 'utf-8', $enc );
446                        $q->param( $_, $encoded );
447                    }
448                }
449                $param{'recovered_object'} = 1;
450            }
451            else {
452                $param{'recovered_failed'} = 1;
453            }
454        }
455        else {
456            $param{'recovered_failed'} = 1;
457        }
458    }
459    elsif ( $q->param('qp') ) {
460        foreach (qw( title text )) {
461            my $data = $q->param($_);
462            my $encoded = MT::I18N::encode_text( $data, undef, $enc )
463              if $data;
464            $q->param( $_, $encoded );
465        }
466    }
467
468    $param{autosave_frequency} = $app->config->AutoSaveFrequency;
469
470    my $id     = $q->param('id');
471    my $perms  = $app->permissions;
472    my $author = $app->user;
473    my $cfg    = $app->config;
474    $param{styles} = '';
475    if ( $type eq 'author' ) {
476        if ( $perms || $blog_id ) {
477            return $app->return_to_dashboard( redirect => 1 );
478        }
479    }
480    else {
481        if ( ( !$perms || !$blog_id )
482            && ( $type eq 'entry' || $type eq 'page'
483                 || $type eq 'category' || $type eq 'folder'
484                 || $type eq 'comment'  || $type eq 'commenter'
485                 || $type eq 'ping' ) ) {
486            return $app->return_to_dashboard( redirect => 1 );
487        }
488    }
489
490    my $cols = $class->column_names;
491    require MT::Promise;
492    my $obj_promise = MT::Promise::delay(
493        sub {
494            return $class->load($id) || undef;
495        }
496    );
497
498    if ( !$author->is_superuser ) {
499        $app->run_callbacks( 'cms_view_permission_filter.' . $type,
500            $app, $id, $obj_promise )
501          || return $app->error(
502            $app->translate( "Permission denied: [_1]", $app->errstr() ) );
503    }
504    my $obj;
505    my $blog;
506    my $blog_class = $app->model('blog');
507    if ($blog_id) {
508        $blog = $blog_class->load($blog_id);
509    }
510    else {
511        $blog_id = 0;
512    }
513
514    if ($id) {    # object exists, we're just editing it.
515          # Stash the object itself so we don't have to keep forcing the promise
516        $obj = $obj_promise->force()
517          or return $app->error(
518            $app->translate(
519                "Load failed: [_1]",
520                $class->errstr || $app->translate("(no reason given)")
521            )
522          );
523
524        # Populate the param hash with the object's own values
525        for my $col (@$cols) {
526            $param{$col} =
527              defined $q->param($col) ? $q->param($col) : $obj->$col();
528        }
529
530        # Make certain any blog-specific element matches the blog we're
531        # dealing with. If not, call shenanigans.
532        if (   ( exists $param{blog_id} )
533            && ( $blog_id != ($obj->blog_id || 0) ) )
534        {
535            return $app->return_to_dashboard( redirect => 1 );
536        }
537
538        if ( $class->properties->{audit} ) {
539            my $creator = MT::Author->load(
540                {
541                    id   => $obj->created_by(),
542                    type => MT::Author::AUTHOR()
543                }
544            );
545            if ($creator) {
546                $param{created_by} = $creator->name;
547            }
548            if ( my $mod_by = $obj->modified_by() ) {
549                my $modified = MT::Author->load(
550                    {
551                        id   => $mod_by,
552                        type => MT::Author::AUTHOR()
553                    }
554                );
555                if ($modified) {
556                    $param{modified_by} = $modified->name;
557                }
558                else {
559                    $param{modified_by} = $app->translate("(user deleted)");
560                }
561
562                # Since legacy MT installs will still have a
563                # timestamp type for their modified_on fields,
564                # we cannot reliably disaply a modified on date
565                # by default; we must only show the modification
566                # date IF there is also a modified_by value.
567                if ( my $ts = $obj->modified_on ) {
568                    $param{modified_on_ts} = $ts;
569                    $param{modified_on_formatted} =
570                      format_ts( MT::App::CMS::LISTING_DATETIME_FORMAT(), $ts, undef, $app->user ? $app->user->preferred_language : undef );
571                }
572            }
573            if ( my $ts = $obj->created_on ) {
574                $param{created_on_ts} = $ts;
575                $param{created_on_formatted} =
576                  format_ts( MT::App::CMS::LISTING_DATETIME_FORMAT(), $ts, undef, $app->user ? $app->user->preferred_language : undef );
577            }
578        }
579
580        $param{new_object} = 0;
581    }
582    else {    # object is new
583        $param{new_object} = 1;
584        for my $col (@$cols) {
585            $param{$col} = $q->param($col);
586        }
587    }
588
589    my $res = $app->run_callbacks('cms_edit.' . $type, $app, $id, $obj, \%param);
590    if (!$res) {
591        return $app->error($app->callback_errstr());
592    }
593
594    if ($param{autosave_support}) {
595        # autosave support, but don't bother if we're reediting
596        if ( !$app->param('reedit') ) {
597            my $sess_obj = $app->autosave_session_obj;
598            if ($sess_obj) {
599                $param{autosaved_object_exists} = 1;
600                $param{autosaved_object_ts} =
601                  MT::Util::epoch2ts( $blog, $sess_obj->start );
602            }
603        }
604    }
605
606    if ( ( $q->param('msg') || "" ) eq 'nosuch' ) {
607        $param{nosuch} = 1;
608    }
609    for my $p ( $q->param ) {
610        $param{$p} = $q->param($p) if $p =~ /^saved/;
611    }
612    $param{page_actions} = $app->page_actions($type, $obj);
613    if ( $class->can('class_label') ) {
614        $param{object_label} = $class->class_label;
615    }
616    if ( $class->can('class_label_plural') ) {
617        $param{object_label_plural} = $class->class_label_plural;
618    }
619
620    my $tmpl_file = $param{output} || "edit_${type}.tmpl";
621    $param{object_type} ||= $type;
622    $param{screen_id} ||= "edit-$type";
623    $param{screen_class} .= " edit-$type";
624    return $app->load_tmpl( $tmpl_file, \%param );
625}
626
627sub list {
628    my $app  = shift;
629    my $q    = $app->param;
630    my $type = $q->param('_type');
631
632    return $app->errtrans("Invalid request.")
633      unless $type;
634
635    # being a general-purpose method, lets look for a mode handler
636    # that is specifically for editing this type. if we find it,
637    # reroute to it.
638
639    my $list_mode = 'list_' . $type;
640    if ( my $hdlrs = $app->handlers_for_mode($list_mode) ) {
641        return $app->forward($list_mode);
642    }
643
644    my %param = $_[0] ? %{ $_[0] } : ();
645
646    my $perms = $app->permissions;
647    return $app->return_to_dashboard( redirect => 1 )
648      unless $perms;
649    if (
650        $perms
651        && (   ( $type eq 'blog' && !$perms->can_edit_config )
652            || ( $type eq 'template'     && !$perms->can_edit_templates )
653            || ( $type eq 'notification' && !$perms->can_edit_notifications ) )
654      )
655    {
656        return $app->return_to_dashboard( permission => 1 );
657    }
658    my $id        = $q->param('id');
659    my $class     = $app->model($type) or return;
660    my $blog_id   = $q->param('blog_id');
661    my $list_pref = $app->list_pref($type);
662    my ( %terms, %args );
663    %param = ( %param, %$list_pref );
664    my $cols   = $class->column_names;
665    my $limit  = $list_pref->{rows};
666    my $offset = $app->param('offset') || 0;
667
668    for my $name (@$cols) {
669        $terms{blog_id} = $blog_id, last
670          if $name eq 'blog_id';
671    }
672    if ( $type eq 'notification' ) {
673        $args{direction} = 'descend';
674        $args{offset}    = $offset;
675        $args{limit}     = $limit + 1;
676    }
677    elsif ( $type eq 'banlist' ) {
678        $param{use_plugins} = $app->config->UsePlugins;
679        $limit = 0;
680    }
681    my $iter = $class->load_iter( \%terms, \%args );
682
683    my (
684        @data,         @index_data,  @custom_data,
685        @archive_data, @system_data, @widget_data
686    );
687    my (%authors);
688    my $blog_class = $app->model('blog');
689    my $blog       = $blog_class->load($blog_id);
690    my $set        = $blog ? $blog->template_set : undef;
691    require MT::DefaultTemplates;
692    my $dtmpl = MT::DefaultTemplates->templates($set) || [];
693    my %dtmpl = map { $_->{type} => $_ } @$dtmpl;
694
695    while ( my $obj = $iter->() ) {
696        my $row = $obj->column_values;
697        if ( my $ts = $obj->created_on ) {
698            $row->{created_on_formatted} =
699              format_ts( MT::App::CMS::LISTING_DATE_FORMAT(), $ts, $blog, $app->user ? $app->user->preferred_language : undef );
700            $row->{created_on_time_formatted} =
701              format_ts( MT::App::CMS::LISTING_DATETIME_FORMAT(), $ts, $blog, $app->user ? $app->user->preferred_language : undef );
702            $row->{created_on_relative} = relative_date( $ts, time, $blog );
703        }
704        if ( $type eq 'template' ) {
705            $row->{name} = '' if !defined $row->{name};
706            $row->{name} =~ s/^\s+|\s+$//g;
707            $row->{name} = "(" . $app->translate("No Name") . ")"
708              if $row->{name} eq '';
709
710            if ( $obj->type eq 'index' ) {
711                push @index_data, $row;
712                $row->{rebuild_me} =
713                  defined $row->{rebuild_me} ? $row->{rebuild_me} : 1;
714                my $published_url = $obj->published_url;
715                $row->{published_url} = $published_url if $published_url;
716            }
717            elsif ( $obj->type eq 'custom' ) {
718                push @custom_data, $row;
719            }
720            elsif ( $obj->type eq 'widget' ) {
721                push @widget_data, $row;
722            }
723            elsif ($obj->type eq 'archive'
724                || $obj->type eq 'category'
725                || $obj->type eq 'page'
726                || $obj->type eq 'individual' )
727            {
728
729                # FIXME: enumeration of types
730                push @archive_data, $row;
731            }
732            else {
733                if ( my $def_tmpl = $dtmpl{ $obj->type } ) {
734                    $row->{description} = $def_tmpl->{description_label};
735                }
736                else {
737
738                    # unknown system template; skip over it
739                    # or should we change it to a custom template
740                    # right now?
741                    next;
742                }
743                push @system_data, $row;
744            }
745            $param{search_label} = $app->translate('Templates');
746        }
747        else {
748            if ( $limit && ( scalar @data == $limit ) ) {
749                $param{next_offset} = 1;
750                last;
751            }
752            push @data, $row;
753        }
754        if ( $type eq 'ping' ) {
755            return $app->list_pings();
756            require MT::Trackback;
757            require MT::Entry;
758            my $tb_center = MT::Trackback->load( $obj->tb_id );
759            my $entry     = MT::Entry->load( $tb_center->entry_id )
760                or return $app->error($app->translate('Can\'t load entry #[_1].', $tb_center->entry_id));
761            if ( my $ts = $obj->created_on ) {
762                $row->{created_on_time_formatted} =
763                  format_ts( MT::App::CMS::LISTING_DATETIME_FORMAT(), $ts, $blog, $app->user ? $app->user->preferred_language : undef );
764                $row->{has_edit_access} = $perms->can_edit_all_posts
765                  || $app->user->id == $entry->author_id;
766            }
767        }
768    }    # end loop over the set of objects;
769         # NOW transform the @data array
770    if ( $type eq 'notification' ) {
771        $app->add_breadcrumb( $app->translate('Notification List') );
772        $param{nav_notifications} = 1;
773
774        #@data = sort { $a->{email} cmp $b->{email} } @data;
775        $param{object_type}        = 'notification';
776        $param{list_noncron}       = 1;
777        $param{notification_count} = scalar @data;
778        $param{search_type} = 'entry';
779    }
780    if ( $type eq 'template' ) {
781        $app->add_breadcrumb( $app->translate('Templates') );
782        $param{nav_templates} = 1;
783        for my $ref ( \@index_data, \@custom_data, \@archive_data ) {
784            @$ref = sort { $a->{name} cmp $b->{name} } @$ref;
785        }
786        my $tab = $app->param('tab') || 'index';
787        $param{template_group}      = $tab;
788        $param{"tab_$tab"}          = 1;
789        $param{object_index_loop}   = \@index_data;
790        $param{object_custom_loop}  = \@custom_data;
791        $param{object_widget_loop}  = \@widget_data;
792        $param{object_archive_loop} = \@archive_data;
793        $param{object_system_loop}  = \@system_data;
794        $param{object_type}         = 'template';
795    }
796    else {
797        $param{object_loop} = \@data;
798    }
799
800    # add any breadcrumbs
801    if ( $type eq 'banlist' ) {
802        $app->add_breadcrumb( $app->translate('IP Banning') );
803        $param{nav_config}                       = 1;
804        $param{object_type}                      = 'banlist';
805        $param{show_ip_info}                     = 1;
806        $param{list_noncron}                     = 1;
807        $param{search_type} = 'entry';
808        $param{can_edit_config_or_publish_paths} = $perms->can_edit_config
809          || $perms->can_set_publish_paths;
810    }
811    elsif ( $type eq 'ping' ) {
812        $app->add_breadcrumb( $app->translate('TrackBacks') );
813        $param{nav_trackbacks} = 1;
814        $param{object_type}    = 'ping';
815    }
816    $param{object_count} = scalar @data;
817
818    if ( $type ne 'template' ) {
819        $param{offset}     = $offset;
820        $param{list_start} = $offset + 1;
821        delete $args{limit};
822        delete $args{offset};
823        $param{list_total} = $class->count( \%terms, \%args );
824        $param{list_end}        = $offset + ( scalar @data );
825        $param{next_offset_val} = $offset + ( scalar @data );
826
827    #$param{next_offset} = $param{next_offset_val} < $param{list_total} ? 1 : 0;
828        $param{next_max} = $param{list_total} - $limit;
829        $param{next_max} = 0 if ( $param{next_max} || 0 ) < $offset + 1;
830        if ( $offset > 0 ) {
831            $param{prev_offset}     = 1;
832            $param{prev_offset_val} = $offset - $limit;
833            $param{prev_offset_val} = 0 if $param{prev_offset_val} < 0;
834        }
835    }
836
837    $app->load_list_actions( $type, \%param );
838
839    $param{saved}         = $q->param('saved');
840    $param{saved_deleted} = $q->param('saved_deleted');
841    $param{page_actions}  = $app->page_actions( 'list_' . $type );
842    unless ( $param{screen_class} ) {
843        $param{screen_class} = "list-$type";
844    }
845    $app->load_tmpl( "list_${type}.tmpl", \%param );
846}
847
848sub delete {
849    my $app  = shift;
850    my $q    = $app->param;
851    my $type = $q->param('_type');
852
853    return $app->errtrans("Invalid request.")
854      unless $type;
855
856    return $app->error( $app->translate("Invalid request.") )
857      if $app->request_method() ne 'POST';
858
859    # being a general-purpose method, lets look for a mode handler
860    # that is specifically for editing this type. if we find it,
861    # reroute to it.
862
863    my $delete_mode = 'delete_' . $type;
864    if ( my $hdlrs = $app->handlers_for_mode($delete_mode) ) {
865        return $app->forward($delete_mode);
866    }
867
868    my $parent  = $q->param('parent');
869    my $blog_id = $q->param('blog_id');
870    my $class   = $app->model($type) or return;
871    my $perms   = $app->permissions;
872    my $author  = $app->user;
873
874    $app->validate_magic() or return;
875
876    my ( $entry_id, $cat_id, $author_id ) = ( "", "", "" );
877    my %rebuild_entries;
878    my @rebuild_cats;
879    my $required_items = 0;
880    for my $id ( $q->param('id') ) {
881        next unless $id;    # avoid 'empty' ids
882        if ( ( $type eq 'association' ) && ( $id =~ /PSEUDO-/ ) ) {
883            $app->_delete_pseudo_association($id);
884            next;
885        }
886
887        my $obj = $class->load($id);
888        next unless $obj;
889        $app->run_callbacks( 'cms_delete_permission_filter.' . $type,
890            $app, $obj )
891          || return $app->error(
892            $app->translate( "Permission denied: [_1]", $app->errstr() ) );
893
894        if ( $type eq 'comment' ) {
895            $entry_id = $obj->entry_id;
896            $rebuild_entries{$entry_id} = 1 if $obj->visible;
897        }
898        elsif ( $type eq 'ping' || $type eq 'ping_cat' ) {
899            require MT::Trackback;
900            my $tb = MT::Trackback->load( $obj->tb_id );
901            if ($tb) {
902                $entry_id = $tb->entry_id;
903                $cat_id   = $tb->category_id;
904                if ( $obj->visible ) {
905                    $rebuild_entries{$entry_id} = 1 if $entry_id;
906                    push @rebuild_cats, $cat_id if $cat_id;
907                }
908            }
909        }
910        elsif ( $type eq 'tag' ) {
911
912            # if we're in a blog context, remove ONLY tags from that weblog
913            if ($blog_id) {
914                my $ot_class  = $app->model('objecttag');
915                my $obj_type  = $q->param('__type') || 'entry';
916                my $obj_class = $app->model($obj_type);
917                my $iter      = $ot_class->load_iter(
918                    {
919                        blog_id           => $blog_id,
920                        object_datasource => $obj_class->datasource,
921                        tag_id            => $id
922                    },
923                    {
924                        'join' => $obj_class->join_on(
925                            undef,
926                            {
927                                id => \'= objecttag_object_id',
928                                (
929                                    $obj_class =~ m/asset/i
930                                    ? ()
931                                    : ( class => $obj_class->class_type )
932                                )
933                            }
934                        )
935                    }
936                );
937
938                if ($iter) {
939                    my @ot;
940                    while ( my $obj = $iter->() ) {
941                        push @ot, $obj->id;
942                    }
943                    foreach (@ot) {
944                        my $obj = $ot_class->load($_);
945                        next unless $obj;
946                        $obj->remove
947                          or return $app->errtrans( 'Removing tag failed: [_1]',
948                            $obj->errstr );
949                    }
950                }
951
952                $app->run_callbacks( 'cms_post_delete.' . $type, $app, $obj );
953                next;
954
955            }
956        }
957        elsif ( $type eq 'category' ) {
958            my @kids = MT::Category->load( { parent => $id } );
959            return $app->errtrans(
960"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."
961            ) if @kids;
962            if ( $app->config('DeleteFilesAtRebuild') ) {
963                require MT::Blog;
964                require MT::Entry;
965                require MT::Placement;
966                my $blog = MT::Blog->load($blog_id)
967                    or return $app->error($app->translate('Can\'t load blog #[_1].', $blog_id));
968                my $at   = $blog->archive_type;
969                if ( $at && $at ne 'None' ) {
970                    my @at = split /,/, $at;
971                    for my $target (@at) {
972                        my $archiver = $app->publisher->archiver($target);
973                        next unless $archiver;
974                        if ( $archiver->category_based ) {
975                            if ( $archiver->date_based ) {
976                                my @entries = MT::Entry->load(
977                                    { status => MT::Entry::RELEASE() },
978                                    {
979                                        join => MT::Placement->join_on(
980                                            'entry_id',
981                                            { category_id => $id },
982                                            { unqiue      => 1 }
983                                        )
984                                    }
985                                );
986                                for (@entries) {
987                                    $app->publisher->remove_entry_archive_file(
988                                        Category    => $obj,
989                                        ArchiveType => $target,
990                                        Entry       => $_
991                                    );
992                                }
993                            }
994                            else {
995                                $app->publisher->remove_entry_archive_file(
996                                    Category    => $obj,
997                                    ArchiveType => $target
998                                );
999                            }
1000                        }
1001                    }
1002                }
1003            }
1004        }
1005        elsif ( $type eq 'page' ) {
1006            if ( $app->config('DeleteFilesAtRebuild') ) {
1007                $app->publisher->remove_entry_archive_file(
1008                    Entry       => $obj,
1009                    ArchiveType => 'Page'
1010                );
1011            }
1012        }
1013        elsif ( $type eq 'author' ) {
1014            if ( $app->config->ExternalUserManagement ) {
1015                require MT::LDAP;
1016                my $ldap = MT::LDAP->new
1017                  or return $app->error(
1018                    MT->translate(
1019                        "Loading MT::LDAP failed: [_1].",
1020                        MT::LDAP->errstr
1021                    )
1022                  );
1023                my $dn = $ldap->get_dn( $obj->name );
1024                if ($dn) {
1025                    $app->add_return_arg( author_ldap_found => 1 );
1026                }
1027            }
1028        }
1029
1030        # FIXME: enumeration of types
1031        if (   $type eq 'template'
1032            && $obj->type !~
1033            /(custom|index|archive|page|individual|category|widget|backup)/o )
1034        {
1035            $required_items++;
1036        }
1037        else {
1038            $obj->remove
1039              or return $app->errtrans(
1040                'Removing [_1] failed: [_2]',
1041                $app->translate($type),
1042                $obj->errstr
1043              );
1044            $app->run_callbacks( 'cms_post_delete.' . $type, $app, $obj );
1045        }
1046    }
1047    require MT::Entry;
1048    for my $entry_id ( keys %rebuild_entries ) {
1049        my $entry = MT::Entry->load($entry_id);
1050        $app->rebuild_entry( Entry => $entry, BuildDependencies => 1 )
1051            or return $app->publish_error();
1052    }
1053    for my $cat_id (@rebuild_cats) {
1054
1055        # FIXME: What about other category-based archives?
1056        # What if user is not publishing category archives?
1057        my $cat = MT::Category->load($cat_id);
1058        $app->rebuild(
1059            Category    => $cat,
1060            BlogID      => $blog_id,
1061            ArchiveType => 'Category'
1062        ) or return $app->publish_error();
1063    }
1064    $app->run_callbacks( 'rebuild', MT::Blog->load($blog_id) );
1065    $app->add_return_arg(
1066        $type eq 'ping'
1067        ? ( saved_deleted_ping => 1 )
1068        : ( saved_deleted => 1 )
1069    );
1070    if ( $q->param('is_power_edit') ) {
1071        $app->add_return_arg( is_power_edit => 1 );
1072    }
1073    if ($required_items) {
1074        $app->add_return_arg(
1075            error => $app->translate("System templates can not be deleted.") );
1076    }
1077    $app->call_return;
1078}
1079
1080sub not_junk_test {
1081    my ( $eh, $app, $obj ) = @_;
1082    require MT::JunkFilter;
1083    MT::JunkFilter->filter($obj);
1084    $obj->is_junk ? 0 : 1;
1085}
1086
10871;
Note: See TracBrowser for help on using the browser.