root/branches/release-38/lib/MT/CMS/Common.pm @ 2382

Revision 2382, 38.1 kB (checked in by auno, 19 months ago)

Fixed to redirect same screen after publishing for archive template. BugzID:79797

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