root/branches/release-34/lib/MT/App/CMS.pm @ 1828

Revision 1828, 122.4 kB (checked in by auno, 20 months ago)

Removed 'Clone Template' action for system and email templates. BugzID:78613

  • Property svn:keywords set to Author Date Id Revision
Line 
1# Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd.
2# This program is distributed under the terms of the
3# GNU General Public License, version 2.
4#
5# $Id$
6
7package MT::App::CMS;
8
9use strict;
10use base qw( MT::App );
11
12use MT::Util qw( format_ts epoch2ts perl_sha1_digest_hex perl_sha1_digest
13    remove_html );
14
15sub LISTING_DATE_FORMAT ()      { '%b %e, %Y' }
16sub LISTING_DATETIME_FORMAT ()  { '%b %e, %Y' }
17sub LISTING_TIMESTAMP_FORMAT () { "%Y-%m-%d %I:%M:%S%p" }
18sub NEW_PHASE () { 1 }
19
20sub id { 'cms' }
21
22sub init {
23    my $app = shift;
24    $app->SUPER::init(@_) or return;
25    $app->{state_params} = [
26        '_type',  'id',         'tab',     'offset',
27        'filter', 'filter_val', 'blog_id', 'is_power_edit',
28        'filter_key', 'type'
29    ];
30    $app->{template_dir}         = 'cms';
31    $app->{plugin_template_path} = '';
32    $app->{is_admin}             = 1;
33    $app->{default_mode}         = 'dashboard';
34    $app;
35}
36
37sub core_methods {
38    my $app = shift;
39    my $pkg = '$Core::MT::CMS::';
40    return {
41        'tools'     => "${pkg}Tools::system_check",
42        'dashboard' => "${pkg}Dashboard::dashboard",
43        'menu'      => '${pkg}Dashboard::dashboard',
44        'admin'     => '${pkg}Dashboard::dashboard',
45
46        ## Generic handlers
47        'save'           => "${pkg}Common::save",
48        'edit'           => "${pkg}Common::edit",
49        'view'           => "${pkg}Common::edit",
50        'list'           => "${pkg}Common::list",
51        'delete'         => "${pkg}Common::delete",
52        'search_replace' => "${pkg}Search::search_replace",
53
54        ## Edit methods
55        'edit_role'  => "${pkg}User::edit_role",
56
57        ## Listing methods
58        'list_ping'     => "${pkg}TrackBack::list",
59        'list_entry'    => "${pkg}Entry::list",
60        'list_template' => "${pkg}Template::list",
61        'list_page'     => "${pkg}Page::list",
62        'list_comment'  => {
63            handler    => "${pkg}Comment::list",
64            permission => 'view_feedback',
65        },
66        'list_member'      => "${pkg}User::list_member",
67        'list_user'        => "${pkg}User::list",
68        'list_author'      => "${pkg}User::list",
69        'list_commenter'   => "${pkg}Comment::list_commenter",
70        'list_asset'       => "${pkg}Asset::list",
71        'list_blog'        => "${pkg}Blog::list",
72        'list_category'    => "${pkg}Category::list",
73        'list_tag'         => "${pkg}Tag::list",
74        'list_association' => "${pkg}User::list_association",
75        'list_role'        => "${pkg}User::list_role",
76
77        'asset_insert'        => "${pkg}Asset::insert",
78        'asset_userpic'       => "${pkg}User::asset_userpic",
79        'save_commenter_perm' => "${pkg}Comment::save_commenter_perm",
80        'trust_commenter'     => "${pkg}Comment::trust_commenter",
81        'ban_commenter'       => "${pkg}Comment::ban_commenter",
82        'approve_item'        => "${pkg}Comment::approve_item",
83        'unapprove_item'      => "${pkg}Comment::unapprove_item",
84        'preview_entry'       => "${pkg}Entry::preview",
85
86        ## Blog configuration screens
87        'cfg_archives'     => "${pkg}Blog::cfg_archives",
88        'cfg_prefs'        => "${pkg}Blog::cfg_prefs",
89        'cfg_plugins'      => "${pkg}Plugin::cfg_plugins",
90        'cfg_comments'     => "${pkg}Comment::cfg_comments",
91        'cfg_trackbacks'   => "${pkg}TrackBack::cfg_trackbacks",
92        'cfg_registration' => "${pkg}Comment::cfg_registration",
93        'cfg_spam'         => "${pkg}Comment::cfg_spam",
94        'cfg_entry'        => "${pkg}Entry::cfg_entry",
95        'cfg_web_services' => "${pkg}Blog::cfg_web_services",
96
97        ## Save
98        'save_cat'     => "${pkg}Category::save",
99        'save_entries' => "${pkg}Entry::save_entries",
100        'save_pages'   => "${pkg}Page::save_pages",
101        'save_entry'   => "${pkg}Entry::save",
102        'save_role'    => "${pkg}User::save_role",
103
104        ## Delete
105        'delete_entry'   => "${pkg}Entry::delete",
106
107        ## List actions
108        'enable_object'  => "${pkg}User::enable",
109        'disable_object' => "${pkg}User::disable",
110        'list_action'    => "${pkg}Tools::do_list_action",
111        'empty_junk'     => "${pkg}Comment::empty_junk",
112        'handle_junk'    => "${pkg}Comment::handle_junk",
113        'not_junk'       => "${pkg}Comment::not_junk",
114
115        'ping'               => "${pkg}Entry::send_pings",
116        'rebuild_phase'      => "${pkg}Blog::rebuild_phase",
117        'rebuild'            => "${pkg}Blog::rebuild_pages",
118        'rebuild_new_phase'  => "${pkg}Blog::rebuild_new_phase",
119        'start_rebuild'      => "${pkg}Blog::start_rebuild_pages",
120        'rebuild_confirm'    => "${pkg}Blog::rebuild_confirm",
121        'entry_notify'       => "${pkg}AddressBook::entry_notify",
122        'send_notify'        => "${pkg}AddressBook::send_notify",
123        'start_upload'       => "${pkg}Asset::start_upload",
124        'upload_file'        => "${pkg}Asset::upload_file",
125        'upload_userpic'     => "${pkg}User::upload_userpic",
126        'complete_insert'    => "${pkg}Asset::complete_insert",
127        'complete_upload'    => "${pkg}Asset::complete_upload",
128        'start_upload_entry' => "${pkg}Asset::start_upload_entry",
129        'logout'             => {
130            code           => sub { $_[0]->SUPER::logout(@_) },
131            requires_login => 0,
132        },
133        'start_recover' => {
134            code           => "${pkg}Tools::start_recover",
135            requires_login => 0,
136        },
137        'recover' => {
138            code           => "${pkg}Tools::recover_password",
139            requires_login => 0,
140        },
141
142        'view_log'            => "${pkg}Log::view",
143        'list_log'            => "${pkg}Log::view",
144        'reset_log'           => "${pkg}Log::reset",
145        'export_log'          => "${pkg}Log::export",
146        'export_notification' => "${pkg}AddressBook::export",
147        'start_import'        => "${pkg}Import::start_import",
148        'start_export'        => "${pkg}Export::start_export",
149        'export'              => "${pkg}Export::export",
150        'import'              => "${pkg}Import::do_import",
151        'pinged_urls'         => "${pkg}Entry::pinged_urls",
152        'save_entry_prefs'    => "${pkg}Entry::save_entry_prefs",
153        'save_favorite_blogs' => "${pkg}Blog::save_favorite_blogs",
154        'folder_add'               => "${pkg}Category::category_add",
155        'category_add'             => "${pkg}Category::category_add",
156        'category_do_add'          => "${pkg}Category::category_do_add",
157        'cc_return'                => "${pkg}Blog::cc_return",
158        'reset_blog_templates'     => "${pkg}Template::reset_blog_templates",
159        'handshake'                => "${pkg}Blog::handshake",
160        'itemset_action'           => "${pkg}Tools::do_list_action",
161        'page_action'              => "${pkg}Tools::do_page_action",
162        'cfg_system'               => "${pkg}Tools::cfg_system_general",
163        'cfg_system_users'         => "${pkg}User::cfg_system_users",
164        'cfg_system_feedback'      => "${pkg}Comment::cfg_system_feedback",
165        'save_plugin_config'       => "${pkg}Plugin::save_config",
166        'reset_plugin_config'      => "${pkg}Plugin::reset_config",
167        'save_cfg_system_feedback' => "${pkg}Comment::save_cfg_system_feedback",
168        'save_cfg_system_general'  => "${pkg}Tools::save_cfg_system_general",
169        'save_cfg_system_users'    => "${pkg}User::save_cfg_system_users",
170        'update_welcome_message'   => "${pkg}Blog::update_welcome_message",
171        'upgrade'                  => {
172            code           => "${pkg}Tools::upgrade",
173            requires_login => 0,
174        },
175        'plugin_control'           => "${pkg}Plugin::plugin_control",
176        'recover_profile_password' => "${pkg}User::recover_profile_password",
177        'rename_tag'               => "${pkg}Tag::rename_tag",
178        'remove_user_assoc'        => "${pkg}User::remove_user_assoc",
179        'revoke_role'              => "${pkg}User::revoke_role",
180        'grant_role'               => "${pkg}User::grant_role",
181        'start_backup'             => "${pkg}Tools::start_backup",
182        'start_restore'            => "${pkg}Tools::start_restore",
183        'backup'                   => "${pkg}Tools::backup",
184        'backup_download'          => "${pkg}Tools::backup_download",
185        'restore'                  => "${pkg}Tools::restore",
186        'restore_premature_cancel' => "${pkg}Tools::restore_premature_cancel",
187        'adjust_sitepath'          => "${pkg}Tools::adjust_sitepath",
188        'system_check'             => "${pkg}Tools::system_check",
189        'dialog_refresh_templates' => "${pkg}Template::dialog_refresh_templates",
190        'refresh_all_templates'    => "${pkg}Template::refresh_all_templates",
191        'preview_template'         => "${pkg}Template::preview",
192        'publish_index_templates'  => "${pkg}Template::publish_index_templates",
193        'publish_archive_templates'=> "${pkg}Template::publish_archive_templates",
194
195        ## Comment Replies
196        reply         => "${pkg}Comment::reply",
197        do_reply      => "${pkg}Comment::do_reply",
198        reply_preview => "${pkg}Comment::reply_preview",
199
200        ## Dialogs
201        'dialog_restore_upload'  => "${pkg}Tools::dialog_restore_upload",
202        'dialog_adjust_sitepath' => "${pkg}Tools::dialog_adjust_sitepath",
203        'dialog_post_comment'    => "${pkg}Comment::dialog_post_comment",
204        'dialog_select_weblog'   => "${pkg}Blog::dialog_select_weblog",
205        'dialog_select_sysadmin' => "${pkg}User::dialog_select_sysadmin",
206        'dialog_grant_role'      => "${pkg}User::dialog_grant_role",
207        'dialog_select_author'   => "${pkg}User::dialog_select_author",
208
209        ## AJAX handlers
210        'delete_map'        => "${pkg}Template::delete_map",
211        'add_map'           => "${pkg}Template::add_map",
212        'js_tag_check'      => "${pkg}Tag::js_tag_check",
213        'js_tag_list'       => "${pkg}Tag::js_tag_list",
214        'convert_to_html'   => "${pkg}Tools::convert_to_html",
215        'update_list_prefs' => "${pkg}Tools::update_list_prefs",
216        'js_add_category'   => "${pkg}Category::js_add_category",
217        'remove_userpic'    => "${pkg}User::remove_userpic",
218
219        # declared in MT::App
220        'update_widget_prefs' => sub { return shift->update_widget_prefs(@_) },
221
222        'js_recent_entries_for_tag' => "${pkg}Tag::js_recent_entries_for_tag",
223
224        ## DEPRECATED ##
225        'list_pings'    => "${pkg}TrackBack::list",
226        'list_entries'  => "${pkg}Entry::list",
227        'list_pages'    => "${pkg}Page::list",
228        'list_comments' => {
229            handler    => "${pkg}Comment::list",
230            permission => 'view_feedback',
231        },
232        'list_authors'      => "${pkg}User::list",
233        'list_assets'       => "${pkg}Asset::list",
234        'list_cat'          => "${pkg}Category::list",
235        'list_blogs'        => "${pkg}Blog::list",
236        'list_associations' => "${pkg}User::list_association",
237        'list_roles'        => "${pkg}User::list_role",
238    };
239}
240
241sub core_widgets {
242    my $app = shift;
243    my $pkg = '$Core::MT::CMS::';
244    return {
245        new_install => {
246            template => 'widget/new_install.tmpl',
247            set      => 'main',    # forces this widget to the main group
248            singular => 1,
249        },
250        new_user => {
251            template => 'widget/new_user.tmpl',
252            set      => 'main',    # forces this widget to the main group
253            singular => 1,
254        },
255        new_version => {
256            template => 'widget/new_version.tmpl',
257            set      => 'main',
258            singular => 1,
259            handler  => "${pkg}Dashboard::new_version_widget",
260        },
261        this_is_you => {
262            label    => 'This is You',
263            template => 'widget/this_is_you.tmpl',
264            handler  => "${pkg}Dashboard::this_is_you_widget",
265            set      => 'sidebar',
266            singular => 1,
267        },
268        mt_shortcuts => {
269            label    => 'Handy Shortcuts',
270            template => 'widget/mt_shortcuts.tmpl',
271            singular => 1,
272            set      => 'sidebar',
273        },
274        mt_news => {
275            label    => 'Movable Type News',
276            template => 'widget/mt_news.tmpl',
277            handler  => "${pkg}Dashboard::mt_news_widget",
278            singular => 1,
279            set      => 'sidebar',
280        },
281        blog_stats => {
282            label    => 'Blog Stats',
283            template => 'widget/blog_stats.tmpl',
284            handler  => "${pkg}Dashboard::mt_blog_stats_widget",
285            singular => 1,
286            set      => 'main',
287        },
288    };
289}
290
291sub core_blog_stats_tabs {
292    my $app = shift;
293    my $pkg = '$Core::MT::CMS::';
294    return {
295        entry => {
296            label    => 'Entries',
297            template => 'widget/blog_stats_entry.tmpl',
298            handler  => "${pkg}Dashboard::mt_blog_stats_widget_entry_tab",
299            stats    => "${pkg}Dashboard::generate_dashboard_stats_entry_tab",
300        },
301        comment => {
302            label    => 'Comments',
303            template => 'widget/blog_stats_comment.tmpl',
304            handler  => "${pkg}Dashboard::mt_blog_stats_widget_comment_tab",
305            stats    => "${pkg}Dashboard::generate_dashboard_stats_comment_tab",
306        },
307        tag_cloud => {
308            label    => 'Tag Cloud',
309            template => 'widget/blog_stats_tag_cloud.tmpl',
310        },
311    };
312}
313
314sub core_page_actions {
315    return {
316        list_templates => {
317            refresh_all_blog_templates => {
318                label => "Refresh Blog Templates",
319                dialog => 'dialog_refresh_templates',
320                condition => sub {
321                    MT->app->blog,
322                },
323                order => 1000,
324            },
325            refresh_global_templates => {
326                label => "Refresh Global Templates",
327                handler => '$Core::MT::CMS::Template::refresh_all_templates',
328                condition => sub {
329                    ! MT->app->blog,
330                },
331                order => 1000,
332                continue_prompt => MT->translate('This action will restore your global templates to factory settings without creating a backup. Click OK to continue or Cancel to abort.'),
333            },
334        },
335    };
336}
337
338sub init_plugins {
339    my $app = shift;
340
341    # This has to be done prior to plugin initialization since we
342    # may have plugins that register themselves using some of the
343    # older callback names. The callback aliases are declared
344    # in init_core_callbacks.
345    $app->init_core_callbacks();
346    $app->SUPER::init_plugins(@_);
347}
348
349sub init_request {
350    my $app = shift;
351    $app->SUPER::init_request(@_);
352    $app->set_no_cache;
353    $app->{requires_login} = 1
354      unless exists $app->{requires_login};    # by default, we require login
355
356    my $mode = $app->mode;
357
358    # Global 'blog_id' parameter check; if we get something
359    # other than an integer, die
360    if (my $blog_id = $app->param('blog_id')) {
361        if ($blog_id ne int($blog_id)) {
362            die $app->translate("Invalid request");
363        }
364    }
365
366    unless (defined $app->{upgrade_required}) {
367        $app->{upgrade_required} = 0;
368        if (   ( $mode ne 'logout' )
369            && ( $mode ne 'start_recover' )
370            && ( $mode ne 'recover' )
371            && ( $mode ne 'upgrade' ) )
372        {
373            my $schema  = $app->config('SchemaVersion');
374            my $version = $app->config('MTVersion');
375            if ( !$schema  || ( $schema  < $app->schema_version )
376              || ( ( !$version || ( $version < $app->version_number ) ) 
377                && $app->config->NotifyUpgrade ) ) {
378                $app->{upgrade_required} = 1;
379            }
380            else {
381                foreach my $plugin (@MT::Components) {
382                    if ( $plugin->needs_upgrade ) {
383                        $app->{upgrade_required} = 1;
384                        last;
385                    }
386                }
387            }
388        }
389    }
390
391    if ($app->{upgrade_required}) {
392        $app->{requires_login} = 0;
393        $app->mode('upgrade');
394    }
395}
396
397sub core_list_actions {
398    my $app = shift;
399    my $pkg = '$Core::MT::CMS::';
400    return {
401        'entry' => {
402            'set_published' => {
403                label      => "Publish Entries",
404                order      => 100,
405                code       => "${pkg}Entry::publish_entries",
406                permission => 'edit_all_posts,publish_post',
407                condition  => sub {
408                    return 0 if $app->mode eq 'view';
409                    return $app->blog && $app->blog->site_path ? 1 : 0;
410                  }
411            },
412            'set_draft' => {
413                label      => "Unpublish Entries",
414                order      => 200,
415                code       => "${pkg}Entry::draft_entries",
416                permission => 'edit_all_posts,publish_post',
417                condition  => sub {
418                    return 0 if $app->mode eq 'view';
419                    return $app->blog && $app->blog->site_path ? 1 : 0;
420                  }
421            },
422            'add_tags' => {
423                label       => "Add Tags...",
424                order       => 300,
425                code        => "${pkg}Tag::add_tags_to_entries",
426                input       => 1,
427                input_label => 'Tags to add to selected entries',
428                permission  => 'edit_all_posts',
429                condition   => sub {
430                    return $app->mode ne 'view';
431                  }
432            },
433            'remove_tags' => {
434                label       => "Remove Tags...",
435                order       => 400,
436                code        => "${pkg}Tag::remove_tags_from_entries",
437                input       => 1,
438                input_label => 'Tags to remove from selected entries',
439                permission  => 'edit_all_posts',
440                condition   => sub {
441                    return $app->mode ne 'view';
442                  }
443            },
444            'open_batch_editor' => {
445                label     => "Batch Edit Entries",
446                code      => "${pkg}Entry::open_batch_editor",
447                order     => 500,
448                condition => sub {
449                    return 0 if $app->mode eq 'view';
450                    $app->param('blog_id')
451                      && ( $app->user->is_superuser()
452                        || $app->permissions->can_edit_all_posts );
453                },
454            },
455        },
456        'page' => {
457            'set_published' => {
458                label      => "Publish Pages",
459                order      => 100,
460                code       => "${pkg}Entry::publish_entries",
461                permission => 'manage_pages',
462                condition  => sub {
463                    return 0 if $app->mode eq 'view';
464                    return $app->blog && $app->blog->site_path ? 1 : 0;
465                },
466            },
467            'set_draft' => {
468                label      => "Unpublish Pages",
469                order      => 200,
470                code       => "${pkg}Entry::draft_entries",
471                permission => 'manage_pages',
472                condition  => sub {
473                    return 0 if $app->mode eq 'view';
474                    return $app->blog && $app->blog->site_path ? 1 : 0;
475                },
476            },
477            'add_tags' => {
478                label       => "Add Tags...",
479                order       => 300,
480                code        => "${pkg}Tag::add_tags_to_entries",
481                input       => 1,
482                input_label => 'Tags to add to selected pages',
483                permission  => 'manage_pages',
484                condition   => sub {
485                    return $app->mode ne 'view';
486                },
487            },
488            'remove_tags' => {
489                label       => "Remove Tags...",
490                order       => 400,
491                code        => "${pkg}Tag::remove_tags_from_entries",
492                input       => 1,
493                input_label => 'Tags to remove from selected pages',
494                permission  => 'manage_pages',
495                condition   => sub {
496                    return $app->mode ne 'view';
497                },
498            },
499            'open_batch_editor' => {
500                label     => "Batch Edit Pages",
501                code      => "${pkg}Entry::open_batch_editor",
502                order     => 500,
503                condition => sub {
504                    return 0 if $app->mode eq 'view';
505                    $app->param('blog_id')
506                      && ( $app->user->is_superuser()
507                        || $app->permissions->can_manage_pages );
508                },
509            },
510        },
511        'asset' => {
512            'add_tags' => {
513                label       => "Add Tags...",
514                order       => 100,
515                code        => "${pkg}Tag::add_tags_to_assets",
516                input       => 1,
517                input_label => 'Tags to add to selected assets',
518                permission  => 'edit_assets',
519            },
520            'remove_tags' => {
521                label       => "Remove Tags...",
522                order       => 200,
523                code        => "${pkg}Tag::remove_tags_from_assets",
524                input       => 1,
525                input_label => 'Tags to remove from selected assets',
526                permission  => 'edit_assets',
527            },
528        },
529        'ping' => {
530            'unapprove_ping' => {
531                label      => "Unpublish TrackBack(s)",
532                order      => 100,
533                code       => "${pkg}Comment::unapprove_item",
534                permission => 'manage_feedback,publish_post',
535            },
536        },
537        'comment' => {
538            'unapprove_comment' => {
539                label      => "Unpublish Comment(s)",
540                order      => 100,
541                code       => "${pkg}Comment::unapprove_item",
542                permission => 'manage_feedback,publish_post',
543                condition  => sub {
544                    return 1;
545                },
546            },
547            'trust_commenter' => {
548                label      => "Trust Commenter(s)",
549                order      => 200,
550                code       => "${pkg}Comment::trust_commenter_by_comment",
551                permission => 'manage_feedback',
552            },
553            'untrust_commenter' => {
554                label      => "Untrust Commenter(s)",
555                order      => 300,
556                code       => "${pkg}Comment::untrust_commenter_by_comment",
557                permission => 'manage_feedback',
558            },
559            'ban_commenter' => {
560                label      => "Ban Commenter(s)",
561                order      => 400,
562                code       => "${pkg}Comment::ban_commenter_by_comment",
563                permission => 'manage_feedback',
564            },
565            'unban_commenter' => {
566                label      => "Unban Commenter(s)",
567                order      => 500,
568                code       => "${pkg}Comment::unban_commenter_by_comment",
569                permission => 'manage_feedback',
570            },
571        },
572        'commenter' => {
573            'untrust' => {
574                label      => "Untrust Commenter(s)",
575                order      => 100,
576                code       => "{$pkg}Comment::untrust_commenter",
577                permission => 'manage_feedback',
578            },
579            'unban' => {
580                label      => "Unban Commenter(s)",
581                order      => 200,
582                code       => "${pkg}Comment::unban_commenter",
583                permission => 'manage_feedback',
584            },
585        },
586        'author' => {
587            'recover_passwords' => {
588                label => "Recover Password(s)",
589                order => 100,
590                continue_prompt_handler => sub {
591                    MT->translate("_WARNING_PASSWORD_RESET_MULTI");
592                },
593                code      => "${pkg}Tools::recover_passwords",
594                condition => sub {
595                    ( $app->user->is_superuser()
596                          && MT::Auth->can_recover_password );
597                },
598            },
599            'delete_user' => {
600                label           => "Delete",
601                order           => 200,
602                continue_prompt_handler => sub {
603                    $app->config->ExternalUserManagement
604                        ? MT->translate("_WARNING_DELETE_USER_EUM")
605                        : MT->translate("_WARNING_DELETE_USER");
606                },
607                code      => "${pkg}Common::delete",
608                condition => sub {
609                    $app->user->is_superuser();
610                },
611            },
612        },
613        'blog' => {
614            refresh_blog_templates => {
615                label => "Refresh Template(s)",
616                code => sub {
617                    my $app = MT->app;
618                    $app->param('backup', 1);
619                    require MT::CMS::Template;
620                    MT::CMS::Template::refresh_all_templates($app, @_)
621                },
622            },
623        },
624        'template' => {
625            refresh_tmpl_templates => {
626                label => "Refresh Template(s)",
627                code => "${pkg}Template::refresh_individual_templates",
628                permission => 'edit_templates',
629                order => 100,
630            },
631            publish_index_templates => {
632                label => "Publish Template(s)",
633                code => "${pkg}Template::publish_index_templates",
634                permission => 'rebuild',
635                condition => sub {
636                    my $app = MT->app;
637                    my $tmpl_type = $app->param('filter_key');
638                    return $app->mode eq 'itemset_action'  ? 1
639                         : !$app->blog                     ? 0
640                         : !$tmpl_type                     ? 0
641                         : $tmpl_type eq 'index_templates' ? 1
642                         :                                   0
643                         ;
644                },
645                order => 200,
646            },
647            publish_archive_templates => {
648                label      => "Publish Template(s)",
649                code       => "${pkg}Template::publish_archive_templates",
650                permission => 'rebuild',
651                condition  => sub {
652                    my $app       = MT->app;
653                    my $tmpl_type = $app->param('filter_key');
654                    return $app->mode eq 'itemset_action' ? 1
655                      : !$app->blog ? 0
656                      : !$tmpl_type ? 0
657                      : $tmpl_type eq 'archive_templates' ? 1
658                      :                                     0;
659                },
660                order => 300,
661            },
662            copy_templates => {
663                label => "Clone Template(s)",
664                code => "${pkg}Template::clone_templates",
665                permission => 'edit_templates',
666                condition => sub {
667                    my $app       = MT->app;
668                    my $tmpl_type = $app->param('filter_key');
669                    return $tmpl_type eq 'system_templates' ? 0
670                      : $tmpl_type    eq 'email_templates'  ? 0
671                      :                                       1;
672                },
673                order => 400,
674            },
675        },
676    };
677}
678
679sub _entry_label {
680    my $app = MT->instance;
681    my $type = $app->param('type') || 'entry';
682    $app->model($type)->class_label_plural;
683}
684
685sub core_list_filters {
686    my $app = shift;
687    return {
688        asset => sub {
689            require MT::CMS::Asset;
690            return MT::CMS::Asset::asset_list_filters($app, @_);
691        },
692        entry => {
693            published => {
694                label => sub {
695                    $app->translate( 'Published [_1]', _entry_label );
696                },
697                order   => 100,
698                handler => sub {
699                    my ( $terms, $args ) = @_;
700                    $terms->{status} = 2;
701                },
702            },
703            unpublished => {
704                label => sub {
705                    $app->translate( 'Unpublished [_1]', _entry_label );
706                },
707                order   => 200,
708                handler => sub {
709                    my ( $terms, $args ) = @_;
710                    $terms->{status} = 1;
711                },
712            },
713            scheduled => {
714                label => sub {
715                    $app->translate( 'Scheduled [_1]', _entry_label );
716                },
717                order   => 300,
718                handler => sub {
719                    my ( $terms, $args ) = @_;
720                    $terms->{status} = 4;
721                },
722            },
723            my_posts => {
724                label => sub {
725                    $app->translate( 'My [_1]', _entry_label );
726                },
727                order   => 400,
728                handler => sub {
729                    my ( $terms, $args ) = @_;
730                    $terms->{author_id} = $app->user->id;
731                },
732            },
733            received_comments_in_last_7_days => {
734                label => sub {
735                    $app->translate( '[_1] with comments in the last 7 days',
736                        _entry_label );
737                },
738                order   => 500,
739                handler => sub {
740                    my ( $terms, $args ) = @_;
741                    my $ts = time - 10 * 24 * 60 * 60;
742                    $ts = epoch2ts( MT->app->blog, $ts );
743                    $args->{join} = MT::Comment->join_on(
744                        'entry_id',
745                        {
746                            created_on  => [ $ts, undef ],
747                            junk_status => [ 0,   1 ]
748                        },
749                        { range_incl => { created_on => 1 }, unique => 1 }
750                    );
751                    # Since we're selecting content from the mt_entry
752                    # table, but we want to sort by the joined
753                    # 'comment_created_on' column, we have to specify the
754                    # sort column as a reference and a full field name,
755                    # to prevent MT from adding a 'entry_' prefix to
756                    # the column name.
757                    $args->{sort}      = \'comment_created_on';
758                    $args->{direction} = 'descend';
759                },
760            },
761            _by_date => {
762                label => sub {
763                    my $app = MT->instance;
764                    my $val = $app->param('filter_val');
765                    my ( $from, $to ) = split /-/, $val;
766                    $from = undef unless $from =~ m/^\d{8}$/;
767                    $to   = undef unless $to   =~ m/^\d{8}$/;
768                    my $format = '%x';
769                    $from = format_ts(
770                        $format, $from . '000000',
771                        undef,   MT->current_language
772                    ) if $from;
773                    $to = format_ts(
774                        $format, $to . '000000',
775                        undef,   MT->current_language
776                    ) if $to;
777                    my $label = _entry_label;
778
779                    if ( $from && $to ) {
780                        return $app->translate(
781                            '[_1] posted between [_2] and [_3]',
782                            $label, $from, $to );
783                    }
784                    elsif ($from) {
785                        return $app->translate( "[_1] posted since [_2]",
786                            $label, $from );
787                    }
788                    elsif ($to) {
789                        return $app->translate( "[_1] posted on or before [_2]",
790                            $label, $to );
791                    }
792                },
793                handler => sub {
794                    my ( $terms, $args ) = @_;
795                    my $val = $app->param('filter_val');
796                    my ( $from, $to ) = split /-/, $val;
797                    $from = undef unless $from =~ m/^\d{8}$/;
798                    $from .= '000000';
799                    $to = undef unless $to =~ m/^\d{8}$/;
800                    $to .= '235959';
801                    $terms->{authored_on} =
802                      [ MT::Object::ts2db($from), MT::Object::ts2db($to) ];
803                    $args->{range_incl}{authored_on} = 1;
804                },
805            },
806        },
807        ping => {
808            default => {
809                label   => 'Non-spam TrackBacks',
810                order   => 100,
811                handler => sub {
812                    my ( $terms, $args ) = @_;
813                    $terms->{junk_status} = [ 0, 1 ];
814                },
815            },
816            my_posts => {
817                label   => 'TrackBacks on my entries',
818                order   => 200,
819                handler => sub {
820                    my ( $terms, $args ) = @_;
821                    require MT::Entry;
822                    my $app = MT->instance;
823                    $terms->{junk_status} = [ 0, 1 ];
824                    require MT::Trackback;
825                    $args->{join} = MT::Trackback->join_on(
826                        undef,
827                        {
828                            id => \'= tbping_tb_id',
829                        },
830                        {
831                            join => MT::Entry->join_on(
832                                undef,
833                                {
834                                    id => \'= trackback_entry_id',
835                                    author_id => $app->user->id
836                                }
837                            )
838                        },
839                    );
840                },
841            },
842            published => {
843                label   => 'Published TrackBacks',
844                order   => 200,
845                handler => sub {
846                    my ( $terms, $args ) = @_;
847                    $terms->{visible} = 1;
848                },
849            },
850            unpublished => {
851                label   => 'Unpublished TrackBacks',
852                order   => 300,
853                handler => sub {
854                    my ( $terms, $args ) = @_;
855                    $terms->{junk_status} = [ 0, 1 ];
856                    $terms->{visible} = 0;
857                },
858            },
859            spam => {
860                label   => 'TrackBacks marked as Spam',
861                order   => 400,
862                handler => sub {
863                    my ( $terms, $args ) = @_;
864                    $terms->{junk_status} = -1;
865                },
866            },
867            last_7_days => {
868                label   => 'All TrackBacks in the last 7 days',
869                order   => 700,
870                handler => sub {
871                    my ( $terms, $args ) = @_;
872                    my $ts = time - 7 * 24 * 60 * 60;
873                    $ts = epoch2ts( MT->app->blog, $ts );
874                    $terms->{created_on} = [ $ts, undef ];
875                    $args->{range_incl}{created_on} = 1;
876                    $terms->{junk_status} = [ 0, 1 ];
877                },
878            },
879        },
880        comment => {
881            default => {
882                label   => 'Non-spam Comments',
883                order   => 100,
884                handler => sub {
885                    my ( $terms, $args ) = @_;
886                    $terms->{junk_status} = [ 0, 1 ];
887                },
888            },
889            my_posts => {
890                label   => 'Comments on my entries',
891                order   => 200,
892                handler => sub {
893                    my ( $terms, $args ) = @_;
894                    require MT::Entry;
895                    my $app = MT->instance;
896                    $terms->{junk_status} = [ 0, 1 ];
897                    # This join syntax employs a hack that allows us
898                    # to do joins on abitrary columns. Typically,
899                    # objectdriver joins are applied with the primary
900                    # key column of the driving table (here,
901                    # mt_comment.comment_id), but we actually want to
902                    # join to the mt_entry table where the entry_id
903                    # matches to mt_comment.comment_entry_id (not the
904                    # primary key). So by specifying NO 'join' column
905                    # (which is always compared with the primary key),
906                    # we specify the actual join conditions in the
907                    # terms. And using a reference for the
908                    # 'comment_entry_id' column name, to pass that
909                    # on directly to the SQL statement that is generated.
910                    $args->{join} = MT::Entry->join_on(
911                        undef,
912                        {
913                            id        => \'= comment_entry_id',
914                            author_id => $app->user->id
915                        }
916                    );
917                },
918            },
919            unpublished => {
920                label   => 'Pending comments',
921                order   => 300,
922                handler => sub {
923                    my ( $terms, $args ) = @_;
924                    $terms->{junk_status} = [ 0, 1 ];
925                    $terms->{visible} = 0;
926                },
927            },
928            spam => {
929                label   => 'Spam Comments',
930                order   => 400,
931                handler => sub {
932                    my ( $terms, $args ) = @_;
933                    $terms->{junk_status} = -1;
934                },
935            },
936            published => {
937                label   => 'Published comments',
938                order   => 500,
939                handler => sub {
940                    my ( $terms, $args ) = @_;
941                    $terms->{visible} = 1;
942                },
943            },
944            #            my_comments => {
945            #                label   => 'My comments',
946            #                order   => 600,
947            #                handler => sub {
948            #                    my ( $terms, $args ) = @_;
949            #                    $terms->{commenter_id} = $app->user->id;
950            #                },
951            #            },
952            last_7_days => {
953                label   => 'Comments in the last 7 days',
954                order   => 700,
955                handler => sub {
956                    my ( $terms, $args ) = @_;
957                    my $ts = time - 7 * 24 * 60 * 60;
958                    $ts = epoch2ts( MT->app->blog, $ts );
959                    $terms->{created_on} = [ $ts, undef ];
960                    $args->{range_incl}{created_on} = 1;
961                    $terms->{junk_status} = [ 0, 1 ];
962                },
963            },
964            #           last_24_hours => {
965            #               label   => 'All comments in the last 24 hours',
966            #               order   => 800,
967            #               handler => sub {
968            #                   my ( $terms, $args ) = @_;
969            #                   my $ts = time - 24 * 60 * 60;
970            #                   $ts = epoch2ts( MT->app->blog, $ts );
971            #                   $terms->{created_on} = [ $ts, undef ];
972            #                   $args->{range_incl}{created_on} = 1;
973            #                   $terms->{junk_status} = [ 0, 1 ];
974            #               },
975            #           },
976            _comments_by_user => {
977                label => sub {
978                    my $app     = MT->app;
979                    my $user_id = $app->param('filter_val');
980                    my $user    = MT::Author->load($user_id);
981                    require MT::Author;
982                    return $app->translate(
983                        "All comments by [_1] '[_2]'",
984                        (
985                              $user->type == MT::Author::COMMENTER()
986                            ? $app->translate("Commenter")
987                            : $app->translate("Author")
988                        ),
989                        (
990                              $user->nickname
991                            ? $user->nickname . ' (' . $user->name . ')'
992                            : $user->name
993                        )
994                    );
995                },
996                handler => sub {
997                    my ( $terms, $args ) = @_;
998                    my $cmtr_id = int( MT->app->param('filter_val') );
999                    $terms->{commenter_id} = $cmtr_id;
1000                },
1001            },
1002            _comments_by_entry => {
1003                label => sub {
1004                    my $entry_id = MT->app->param('filter_val');
1005                    my $entry    = MT::Entry->load($entry_id);
1006                    return MT->translate( "All comments for [_1] '[_2]'",
1007                        $entry->class_label, $entry->title );
1008                },
1009                handler => sub {
1010                    my ( $terms, $args ) = @_;
1011                    my $entry_id = int( MT->app->param('filter_val') );
1012                    $terms->{entry_id} = $entry_id;
1013                    $terms->{junk_status} = [ 0, 1 ];
1014                },
1015            },
1016            _by_date => {
1017                label => sub {
1018                    my $app = MT->instance;
1019                    my $val = $app->param('filter_val');
1020                    my ( $from, $to ) = split /-/, $val;
1021                    $from = undef unless $from =~ m/^\d{8}$/;
1022                    $to   = undef unless $to   =~ m/^\d{8}$/;
1023                    my $format = '%x';
1024                    $from = format_ts(
1025                        $format, $from . '000000',
1026                        undef,   MT->current_language
1027                    ) if $from;
1028                    $to = format_ts(
1029                        $format, $to . '000000',
1030                        undef,   MT->current_language
1031                    ) if $to;
1032
1033                    if ( $from && $to ) {
1034                        return $app->translate(
1035                            'Comments posted between [_1] and [_2]',
1036                            $from, $to );
1037                    }
1038                    elsif ($from) {
1039                        return $app->translate( "Comments posted since [_1]",
1040                            $from );
1041                    }
1042                    elsif ($to) {
1043                        return $app->translate(
1044                            "Comments posted on or before [_1]", $to );
1045                    }
1046                },
1047                handler => sub {
1048                    my ( $terms, $args ) = @_;
1049                    my $val = $app->param('filter_val');
1050                    my ( $from, $to ) = split /-/, $val;
1051                    $from = undef unless $from =~ m/^\d{8}$/;
1052                    $from .= '000000';
1053                    $to = undef unless $to =~ m/^\d{8}$/;
1054                    $to .= '235959';
1055                    $terms->{junk_status} = [ 0, 1 ];
1056                    $terms->{created_on} =
1057                      [ MT::Object::ts2db($from), MT::Object::ts2db($to) ];
1058                    $args->{range_incl}{created_on} = 1;
1059                },
1060            },
1061        },
1062        template => {
1063            index_templates => {
1064                label   => "Index Templates",
1065                order   => 100,
1066                handler => sub {
1067                    my ( $terms, $args ) = @_;
1068                    # FIXME: enumeration of types
1069                    $terms->{type} = 'index';
1070                },
1071                condition => sub {
1072                    $app->param('blog_id');
1073                },
1074            },
1075            archive_templates => {
1076                label   => "Archive Templates",
1077                order   => 200,
1078                handler => sub {
1079                    my ( $terms, $args ) = @_;
1080                    $terms->{type} =
1081                      [ 'individual', 'page', 'archive', 'category' ];
1082                },
1083                condition => sub {
1084                    $app->param('blog_id');
1085                },
1086            },
1087            module_templates => {
1088                label   => "Template Modules",
1089                order   => 400,
1090                handler => sub {
1091                    my ($terms) = @_;
1092                    $terms->{type} = 'custom';
1093                },
1094            },
1095            email_templates => {
1096                label   => "E-mail Templates",
1097                order   =>  300,
1098                handler => sub {
1099                    my ($terms) = @_;
1100                    $terms->{type} = 'email';
1101                },
1102                condition => sub {
1103                    !$app->param('blog_id');
1104                },
1105            },
1106            system_templates => {
1107                label   => "System Templates",
1108                order   => 200,
1109                handler => sub {
1110                    my ($terms) = @_;
1111                    my $scope;
1112                    my $set;
1113                    if ( my $blog_id = $app->param('blog_id') ) {
1114                        my $blog  = $app->model('blog')->load($blog_id);
1115                        $set   = $blog->template_set;
1116                        $scope .= 'system';
1117                    }
1118                    else {
1119                        $terms->{blog_id} = 0;
1120                        $scope = 'global:system';
1121                    }
1122                    my @tmpl_path = ( $set && ($set ne 'mt_blog')) ? ("template_sets", $set, 'templates', $scope) : ("default_templates", $scope);
1123                    my $sys_tmpl = MT->registry(@tmpl_path) || {};
1124                    $terms->{type} = [ keys %$sys_tmpl ];
1125                },
1126                condition => sub {
1127                    $app->param('blog_id');
1128                },
1129            },
1130        },
1131        tag => {
1132            entry => {
1133                label => 'Tags with entries',
1134                order => 100,
1135            },
1136            page => {
1137                label => 'Tags with pages',
1138                order => 200,
1139            },
1140            asset => {
1141                label => 'Tags with assets',
1142                order => 300,
1143            },
1144        },
1145        sys_user => {
1146            enabled => {
1147                label => 'Enabled Users',
1148                order => 100,
1149                handler => sub {
1150                    my ($terms) = @_;
1151                    $terms->{status} = 1;
1152                },
1153            },
1154            disabled => {
1155                label => "Disabled Users",
1156                order => 200,
1157                handler => sub {
1158                    my ($terms) = @_;
1159                    $terms->{status} = 2;
1160                },
1161            },
1162            pending => {
1163                label => "Pending Users",
1164                order => 300,
1165                handler => sub {
1166                    my ($terms) = @_;
1167                    $terms->{status} = 3;
1168                },
1169            },
1170        },
1171        user => {
1172            author => {
1173                label   => 'Authors',
1174                order   => 100,
1175                handler => sub {
1176                    my ($terms) = @_;
1177                    require MT::Author;
1178                    $terms->{type} = MT::Author::AUTHOR();
1179                },
1180            },
1181            commenter => {
1182                label   => 'Commenters',
1183                order   => 200,
1184                handler => sub {
1185                    my ($terms) = @_;
1186                    require MT::Author;
1187                    $terms->{type} = MT::Author::COMMENTER();
1188                },
1189            },
1190        },
1191    };
1192}
1193
1194sub core_menus {
1195    my $app = shift;
1196    return {
1197        'create' => {
1198            label => "Create",
1199            order => 100,
1200        },
1201        'manage' => {
1202            label => "Manage",
1203            order => 200,
1204        },
1205        'design' => {
1206            label => "Design",
1207            order => 300,
1208        },
1209        'prefs' => {
1210            label => "Preferences",
1211            order => 400,
1212        },
1213        'tools' => {
1214            label => "Tools",
1215            order => 500,
1216        },
1217
1218        'create:blog' => {
1219            label => "Blog",
1220            order => 100,
1221            view  => "system",
1222        },
1223        'create:blog' => {
1224            label      => "Blog",
1225            order      => 200,
1226            view       => "system",
1227            mode       => "view",
1228            args       => { _type => "blog" },
1229            permission => "create_blog",
1230        },
1231        'create:user' => {
1232            label      => "User",
1233            order      => 200,
1234            view       => "system",
1235            mode       => "view",
1236            args       => { _type => "author" },
1237            permission => "administer",
1238            condition  => sub {
1239                return !MT->config->ExternalUserManagement;
1240            },
1241        },
1242        'create:entry' => {
1243            label      => "Entry",
1244            order      => 100,
1245            mode       => 'view',
1246            args       => { _type => 'entry' },
1247            permission => 'create_post',
1248            view       => "blog",
1249        },
1250        'create:page' => {
1251            label      => "Page",
1252            order      => 200,
1253            mode       => 'view',
1254            args       => { _type => 'page' },
1255            permission => 'manage_pages',
1256            view       => "blog",
1257        },
1258        'create:file' => {
1259            label      => "Upload File",
1260            order      => 300,
1261            dialog     => 'start_upload',
1262            permission => 'upload,edit_assets',
1263            view       => "blog",
1264        },
1265
1266        'manage:blog' => {
1267            label => "Blogs",
1268            mode  => "list_blog",
1269            order => 100,
1270            view  => "system",
1271        },
1272        'manage:user' => {
1273            label      => "Users",
1274            mode       => "list_user",
1275            order      => 200,
1276            permission => "administer",
1277            view       => "system",
1278        },
1279        'manage:entry' => {
1280            label      => "Entries",
1281            mode       => 'list_entry',
1282            order      => 1000,
1283            condition  => sub {
1284                return 1 if $app->user->is_superuser;
1285                if ( $app->param('blog_id') ) {
1286                    my $perms = $app->user->permissions($app->param('blog_id'));
1287                    return 1 if $perms->can_create_post || $perms->can_publish_post || $perms->can_edit_all_posts;
1288                }
1289                else {
1290                    require MT::Permission;
1291                    my @blogs = 
1292                        map { $_->blog_id }
1293                          grep { $_->can_create_post || $_->can_publish_post || $_->can_edit_all_posts }
1294                          MT::Permission->load( { author_id => $app->user->id } );
1295                    return 1 if @blogs;
1296                }
1297                return 0;
1298            },
1299            #permission => 'create_post,publish_post,edit_all_posts',
1300        },
1301        'manage:comment' => {
1302            label      => "Comments",
1303            mode       => 'list_comment',
1304            order      => 2000,
1305            condition  => sub {
1306                return 1 if $app->user->is_superuser;
1307                if ( $app->param('blog_id') ) {
1308                    my $perms = $app->user->permissions($app->param('blog_id'));
1309                    return 1 if $perms->can_create_post || $perms->can_manage_feedback || $perms->can_edit_all_posts || $perms->can_comment;
1310                }
1311                else {
1312                    require MT::Permission;
1313                    my @blogs = 
1314                        map { $_->blog_id }
1315                          grep { $_->can_create_post || $_->can_manage_feedback || $_->can_edit_all_posts || $_->can_comment }
1316                          MT::Permission->load( { author_id => $app->user->id } );
1317                    return 1 if @blogs;
1318                }
1319                return 0;
1320            },
1321            #permission => 'create_post,edit_all_posts,manage_feedback,comment',
1322        },
1323        'manage:asset' => {
1324            label      => "Assets",
1325            mode       => 'list_asset',
1326            order      => 3000,
1327            permission => 'edit_assets',
1328        },
1329        'manage:page' => {
1330            label      => "Pages",
1331            mode       => 'list_pages',
1332            order      => 4000,
1333            permission => 'manage_pages',
1334        },
1335        'manage:ping' => {
1336            label      => "TrackBacks",
1337            mode       => 'list_pings',
1338            order      => 5000,
1339            permission => 'create_post,edit_all_posts,manage_feedback',
1340        },
1341        'manage:category' => {
1342            label      => "Categories",
1343            mode       => 'list_cat',
1344            order      => 6000,
1345            permission => 'edit_categories',
1346            view       => "blog",
1347        },
1348        'manage:folder' => {
1349            label      => "Folders",
1350            mode       => 'list_cat',
1351            args       => { _type => 'folder' },
1352            order      => 7000,
1353            permission => 'manage_pages',
1354            view       => "blog",
1355        },
1356        'manage:tag' => {
1357            label             => "Tags",
1358            mode              => 'list_tag',
1359            order             => 8000,
1360            permission        => 'edit_tags',
1361            system_permission => 'administer',
1362        },
1363        'manage:blog_user' => {
1364            label             => "Users",
1365            mode              => 'list_member',
1366            order             => 9000,
1367            view              => "blog",
1368            permission        => 'administer_blog',
1369            system_permission => 'administer',
1370        },
1371        'manage:notification' => {
1372            label             => "Address Book",
1373            mode              => 'list',
1374            args              => { _type => 'notification' },
1375            order             => 10000,
1376            permission        => 'edit_notifications',
1377            view              => "blog",
1378            condition         => sub {
1379                return $app->config->EnableAddressBook;
1380            },
1381        },
1382
1383        'design:template' => {
1384            label         => "Templates",
1385            mode          => 'list',
1386            args          => { _type => 'template' },
1387            order         => 100,
1388            permission    => 'edit_templates',
1389            system_permission    => 'edit_templates',
1390        },
1391
1392        'prefs:general' => {
1393            label      => "General",
1394            order      => 100,
1395            mode       => "cfg_system",
1396            view       => "system",
1397            permission => "administer",
1398        },
1399        'prefs:user' => {
1400            label      => "User",
1401            order      => 200,
1402            mode       => "cfg_system_users",
1403            view       => "system",
1404            permission => "administer",
1405        },
1406        'prefs:feedback' => {
1407            label      => "Feedback",
1408            order      => 300,
1409            mode       => "cfg_system_feedback",
1410            view       => "system",
1411            permission => "administer",
1412        },
1413        'prefs:settings' => {
1414            label             => "General",
1415            mode              => 'cfg_prefs',
1416            order             => 100,
1417            permission        => 'administer_blog,edit_config,set_publish_paths',
1418            system_permission => 'administer',
1419            view              => "blog",
1420        },
1421        'prefs:publishing' => {
1422            label             => "Publishing",
1423            mode              => 'cfg_archives',
1424            order             => 110,
1425            permission        => 'administer_blog,edit_config,set_publish_paths',
1426            system_permission => 'administer',
1427            view              => "blog",
1428        },
1429        'prefs:entry' => {
1430            label             => "Entry",
1431            mode              => 'cfg_entry',
1432            order             => 120,
1433            permission        => 'administer_blog,edit_config,set_publish_paths',
1434            system_permission => 'administer',
1435            view              => "blog",
1436        },
1437        'prefs:comment' => {
1438            label             => "Comment",
1439            mode              => 'cfg_comments',
1440            order             => 130,
1441            permission        => 'administer_blog,edit_config,set_publish_paths',
1442            system_permission => 'administer',
1443            view              => "blog",
1444        },
1445        'prefs:trackback' => {
1446            label             => "TrackBack",
1447            mode              => 'cfg_trackbacks',
1448            order             => 140,
1449            permission        => 'administer_blog,edit_config,set_publish_paths',
1450            system_permission => 'administer',
1451            view              => "blog",
1452        },
1453        'prefs:registration' => {
1454            label             => "Registration",
1455            mode              => 'cfg_registration',
1456            order             => 150,
1457            permission        => 'administer_blog,edit_config,set_publish_paths',
1458            system_permission => 'administer',
1459            view              => "blog",
1460        },
1461        'prefs:spam' => {
1462            label             => "Spam",
1463            mode              => 'cfg_spam',
1464            order             => 160,
1465            permission        => 'administer_blog,edit_config,set_publish_paths',
1466            system_permission => 'administer',
1467            view              => "blog",
1468        },
1469        'prefs:web_services' => {
1470            label             => "Web Services",
1471            mode              => 'cfg_web_services',
1472            order             => 170,
1473            permission        => 'administer_blog,edit_config,set_publish_paths',
1474            system_permission => 'administer',
1475            view              => "blog",
1476        },
1477
1478        'tools:plugins' => {
1479            label             => "Plugins",
1480            order             => 100,
1481            mode              => "cfg_plugins",
1482            permission        => "administer_blog",
1483            system_permission => "manage_plugins",
1484        },
1485        'tools:activity_log' => {
1486            label             => "Activity Log",
1487            order             => 200,
1488            mode              => "view_log",
1489            permission        => "view_blog_log",
1490            system_permission => "view_log",
1491        },
1492        'tools:import' => {
1493            label             => "Import",
1494            order             => 300,
1495            mode              => "start_import",
1496            view              => "blog",
1497            permission        => "administer_blog",
1498        },                   
1499        'tools:export' => {
1500            label             => "Export",
1501            order             => 400,
1502            mode              => "start_export",
1503            view              => "blog",
1504            permission        => "administer_blog",
1505        },                   
1506        'tools:backup' => {
1507            label             => "Backup",
1508            order             => 500,
1509            mode              => "start_backup",
1510            permission        => "administer_blog",
1511        },
1512        'tools:restore' => {
1513            label             => "Restore",
1514            order             => 600,
1515            mode              => "start_restore",
1516            permission        => "administer_blog",
1517            view              => "system",
1518        },
1519        'tools:system_information' => {
1520            label             => "System Information",
1521            order             => 700,
1522            mode              => "tools",
1523            view              => "system",
1524        },
1525        'tools:ip_info' => {
1526            label             => "IP Banning",
1527            mode              => 'list',
1528            args              => { _type => 'banlist' },
1529            order             => 900,
1530            permission        => 'manage_feedback',
1531            condition         => sub {
1532                $app->config->ShowIPInformation;
1533            },
1534            view              => "blog",
1535        },
1536
1537        # System menu which is actually separate
1538        # in the CMS navigation
1539        'system' => {
1540            label => "System Overview",
1541            mode  => 'dashboard',
1542            order => 10000,
1543        },
1544        'system:user' => {
1545            label             => "Users",
1546            mode              => 'list_authors',
1547            order             => 100,
1548            permission        => 'administer_blog',
1549            system_permission => 'administer',
1550        },
1551        'system:blog' => {
1552            label => "Blogs",
1553            mode  => 'list_blogs',
1554            order => 200,
1555        },
1556        'system:template' => {
1557            label             => "Global Templates",
1558            mode              => 'list_template',
1559            order             => 250,
1560            system_permission => 'edit_templates',
1561        },
1562        'system:settings' => {
1563            label             => "Settings",
1564            mode              => 'cfg_system',
1565            order             => 300,
1566            system_permission => 'administer',
1567        },
1568        'system:plugins' => {
1569            label             => "Plugins",
1570            mode              => 'cfg_plugins',
1571            order             => 400,
1572            system_permission => 'manage_plugins',
1573        },
1574        'system:log' => {
1575            label             => "Activity Log",
1576            mode              => 'view_log',
1577            order             => 500,
1578            system_permission => 'view_log',
1579        },
1580        'system:tools' => {
1581            label             => "Tools",
1582            mode              => 'tools',
1583            order             => 600,
1584            system_permission => 'administer',
1585        },
1586    };
1587}
1588
1589sub init_core_callbacks {
1590    my $app = shift;
1591    my $pkg = 'cms_';
1592    my $pfx = '$Core::MT::CMS::';
1593    $app->_register_core_callbacks(
1594        {
1595            # notification callbacks
1596            $pkg . 'save_permission_filter.notification' => "${pfx}AddressBook::can_save",
1597            $pkg . 'save_filter.notification' => "${pfx}AddressBook::save_filter",
1598            $pkg . 'post_delete.notification' => "${pfx}AddressBook::post_delete",
1599
1600            # banlist callbacks
1601            $pkg . 'save_permission_filter.banlist' => "${pfx}BanList::can_save",
1602            $pkg . 'save_filter.banlist' => "${pfx}BanList::save_filter",
1603
1604            # associations
1605            $pkg . 'delete_permission_filter.association' => "${pfx}User::can_delete_association",
1606
1607            # user callbacks
1608            $pkg . 'edit.author' => "${pfx}User::edit",
1609            $pkg . 'view_permission_filter.author' => "${pfx}User::can_view",
1610            $pkg . 'save_permission_filter.author' => "${pfx}User::can_save",
1611            $pkg . 'delete_permission_filter.author' => "${pfx}User::can_delete",
1612            $pkg . 'save_filter.author' => "${pfx}User::save_filter",
1613            $pkg . 'pre_save.author'    => "${pfx}User::pre_save",
1614            $pkg . 'post_save.author'   => "${pfx}User::post_save",
1615            $pkg . 'post_delete.author' => "${pfx}User::post_delete",
1616
1617            # blog callbacks
1618            $pkg . 'edit.blog' => "${pfx}Blog::edit",
1619            $pkg
1620              . 'view_permission_filter.blog' => "${pfx}Blog::can_view",
1621            $pkg
1622              . 'save_permission_filter.blog' => "${pfx}Blog::can_save",
1623            $pkg
1624              . 'delete_permission_filter.blog' => "${pfx}Blog::can_delete",
1625            $pkg . 'pre_save.blog'    => "${pfx}Blog::pre_save",
1626            $pkg . 'post_save.blog'   => "${pfx}Blog::post_save",
1627            $pkg . 'save_filter.blog' => "${pfx}Blog::save_filter",
1628            $pkg . 'post_delete.blog' => "${pfx}Blog::post_delete",
1629
1630            # folder callbacks
1631            $pkg . 'edit.folder' => "${pfx}Folder::edit",
1632            $pkg . 'view_permission_filter.folder' => "${pfx}Folder::can_view",
1633            $pkg . 'save_permission_filter.folder' => "${pfx}Folder::can_save",
1634            $pkg . 'delete_permission_filter.folder' => "${pfx}Folder::can_delete",
1635            $pkg . 'pre_save.folder'    => "${pfx}Folder::pre_save",
1636            $pkg . 'post_save.folder'   => "${pfx}Folder::post_save",
1637            $pkg . 'save_filter.folder' => "${pfx}Folder::save_filter",
1638            $pkg . 'post_delete.folder' => "${pfx}Folder::post_delete",
1639
1640            # category callbacks
1641            $pkg . 'edit.category' => "${pfx}Category::edit",
1642            $pkg . 'view_permission_filter.category' => "${pfx}Category::can_view",
1643            $pkg . 'save_permission_filter.category' => "${pfx}Category::can_save",
1644            $pkg . 'delete_permission_filter.category' => "${pfx}Category::can_delete",
1645            $pkg . 'pre_save.category'    => "${pfx}Category::pre_save",
1646            $pkg . 'post_save.category'   => "${pfx}Category::post_save",
1647            $pkg . 'save_filter.category' => "${pfx}Category::save_filter",
1648            $pkg . 'post_delete.category' => "${pfx}Category::post_delete",
1649
1650            # comment callbacks
1651            $pkg . 'edit.comment' => "${pfx}Comment::edit",
1652            $pkg . 'view_permission_filter.comment' => "${pfx}Comment::can_view",
1653            $pkg . 'save_permission_filter.comment' => "${pfx}Comment::can_save",
1654            $pkg . 'delete_permission_filter.comment' => "${pfx}Comment::can_delete",
1655            $pkg . 'pre_save.comment'    => "${pfx}Comment::pre_save",
1656            $pkg . 'post_save.comment'   => "${pfx}Comment::post_save",
1657            $pkg . 'post_delete.comment' => "${pfx}Comment::post_delete",
1658
1659            # commenter callbacks
1660            $pkg . 'edit.commenter' => "${pfx}Comment::edit_commenter",
1661            $pkg . 'view_permission_filter.commenter' => "${pfx}Comment::can_view_commenter",
1662            $pkg . 'delete_permission_filter.commenter' => "${pfx}Comment::can_delete_commenter",
1663
1664            # entry callbacks
1665            $pkg . 'edit.entry' => "${pfx}Entry::edit",
1666            $pkg . 'view_permission_filter.entry' => "${pfx}Entry::can_view",
1667            $pkg . 'delete_permission_filter.entry' => "${pfx}Entry::can_delete",
1668            $pkg . 'pre_save.entry'    => "${pfx}Entry::pre_save",
1669            $pkg . 'post_save.entry'   => "${pfx}Entry::post_save",
1670            $pkg . 'post_delete.entry' => "${pfx}Entry::post_delete",
1671
1672            # page callbacks
1673            $pkg . 'edit.page' => "${pfx}Page::edit",
1674            $pkg . 'view_permission_filter.page' => "${pfx}Page::can_view",
1675            $pkg . 'delete_permission_filter.page' => "${pfx}Page::can_delete",
1676            $pkg . 'pre_save.page'    => "${pfx}Page::pre_save",
1677            $pkg . 'post_save.page'   => "${pfx}Page::post_save",
1678            $pkg . 'post_delete.page' => "${pfx}Page::post_delete",
1679
1680            # ping callbacks
1681            $pkg . 'edit.ping' => "${pfx}TrackBack::edit",
1682            $pkg . 'view_permission_filter.ping' => "${pfx}TrackBack::can_view",
1683            $pkg . 'save_permission_filter.ping' => "${pfx}TrackBack::can_save",
1684            $pkg . 'delete_permission_filter.ping' => "${pfx}TrackBack::can_delete",
1685            $pkg . 'pre_save.ping'    => "${pfx}TrackBack::pre_save",
1686            $pkg . 'post_save.ping'   => "${pfx}TrackBack::post_save",
1687            $pkg . 'post_delete.ping' => "${pfx}TrackBack::post_delete",
1688
1689            # template callbacks
1690            $pkg . 'edit.template' => "${pfx}Template::edit",
1691            $pkg . 'view_permission_filter.template' => "${pfx}Template::can_view",
1692            $pkg . 'save_permission_filter.template' => "${pfx}Template::can_save",
1693            $pkg . 'delete_permission_filter.template' => "${pfx}Template::can_delete",
1694            $pkg . 'pre_save.template'    => "${pfx}Template::pre_save",
1695            $pkg . 'post_save.template'   => "${pfx}Template::post_save",
1696            $pkg . 'post_delete.template' => "${pfx}Template::post_delete",
1697
1698            # tags
1699            $pkg . 'delete_permission_filter.tag' => "${pfx}Tag::can_delete",
1700            $pkg . 'post_delete.tag' => "${pfx}Tag::post_delete",
1701
1702            # junk-related callbacks
1703            #'HandleJunk' => \&_builtin_spam_handler,
1704            #'HandleNotJunk' => \&_builtin_spam_unhandler,
1705            $pkg . 'not_junk_test' => "${pfx}Common::not_junk_test",
1706
1707            # assets
1708            $pkg . 'edit.asset' => "${pfx}Asset::edit",
1709            $pkg . 'view_permission_filter.asset' => "${pfx}Asset::can_view",
1710            $pkg . 'delete_permission_filter.asset' => "${pfx}Asset::can_delete",
1711            $pkg . 'pre_save.asset'    => "${pfx}Asset::pre_save",
1712            $pkg . 'post_save.asset'   => "${pfx}Asset::post_save",
1713            $pkg . 'post_delete.asset' => "${pfx}Asset::post_delete",
1714            'template_param.edit_asset' => "${pfx}Asset::template_param_edit",
1715        }
1716    );
1717}
1718
1719sub user_can_admin_commenters {
1720    my $app   = shift;
1721    my $perms = $app->permissions;
1722    $app->user->is_superuser()
1723      || ( $perms
1724        && ( $perms->can_administer_blog || $perms->can_manage_feedback ) );
1725}
1726
1727sub validate_magic {
1728    my $app = shift;
1729    if ( my $feed_token = $app->param('feed_token') ) {
1730        return unless $app->user;
1731        my $pw = $app->user->api_password;
1732        return undef if ( $pw || '' ) eq '';
1733        my $auth_token = perl_sha1_digest_hex( 'feed:' . $pw );
1734        return $feed_token eq $auth_token;
1735    }
1736    else {
1737        return $app->SUPER::validate_magic(@_);
1738    }
1739}
1740
1741sub is_authorized {
1742    my $app     = shift;
1743    my $blog_id = $app->param('blog_id');
1744    $app->permissions(undef);
1745    return 1 unless $blog_id;
1746    return unless my $user = $app->user;
1747    my $perms = $app->permissions( $user->permissions($blog_id) );
1748    $perms
1749      ? 1
1750      : $app->error(
1751        $app->translate("You are not authorized to log in to this blog.") );
1752}
1753
1754sub set_default_tmpl_params {
1755    my $app = shift;
1756    $app->SUPER::set_default_tmpl_params(@_);
1757    my ($tmpl) = @_;
1758    my $param = {};
1759
1760    my $mode      = $app->mode;
1761    my $auth_mode = $app->config('AuthenticationModule');
1762    my ($pref) = split /\s+/, $auth_mode;
1763
1764    # TODO - remove after testing or after new IA is defined
1765    $param->{app_layout_fixed} = 0;
1766    $param->{athena_nav}       = 1;
1767
1768    $param->{"auth_mode_$pref"} = 1;
1769    $param->{mt_news}           = $app->config('NewsURL');
1770    $param->{mt_support}        = $app->config('SupportURL');
1771    my $lang = lc MT->current_language || 'en_us';
1772    $param->{language_id} = ( $lang !~ /en[_-]us/ ) ? $lang : '';
1773    $param->{mode} = $app->mode;
1774
1775    my $blog_id = $app->param('blog_id') || 0;
1776    my $blog;
1777    my $blog_class = $app->model('blog');
1778    $blog ||= $blog_class->load($blog_id) if $blog_id;
1779    if ( my $auth = $app->user ) {
1780        $param->{is_administrator} = $auth->is_superuser;
1781        $param->{can_create_blog}  = $auth->can_create_blog;
1782        $param->{can_view_log} ||= $auth->can_view_log;
1783        $param->{can_manage_plugins}    = $auth->can_manage_plugins;
1784        $param->{can_edit_templates}    = $auth->can_edit_templates;
1785        $param->{can_publish_feedbacks} = $auth->is_superuser;
1786        $param->{can_search_replace}    = $auth->is_superuser;
1787        $param->{has_authors_button}    = $auth->is_superuser;
1788        $param->{author_id}             = $auth->id;
1789        $param->{author_name}           = $auth->name;
1790        $param->{author_display_name}   = $auth->nickname || $auth->name;
1791    }
1792
1793    if ( my $perms = $app->permissions ) {
1794        my $perm_hash = $perms->to_hash;
1795        foreach my $perm_name ( keys %$perm_hash ) {
1796            my $perm_token = $perm_name;
1797            $perm_token =~ s/^permission\.//;
1798            $param->{$perm_token} = $perm_hash->{$perm_name}
1799              if defined $perm_hash->{$perm_name};
1800        }
1801        $param->{can_edit_entries} = $param->{can_create_post}
1802          || $param->{can_edit_all_entries}
1803          || $param->{can_publish_post};
1804        $param->{can_search_replace} = $param->{can_edit_all_posts};
1805        $param->{can_edit_authors}   = $param->{can_administer_blog};
1806        $param->{can_access_assets}  = $param->{can_create_post}
1807          || $param->{can_edit_all_posts}
1808          || $param->{can_edit_assets};
1809        $param->{can_edit_commenters}   = $param->{can_manage_feedback};
1810             $param->{has_manage_label} = $param->{can_edit_templates}
1811          || $param->{can_administer_blog}
1812          || $param->{can_edit_categories}
1813          || $param->{can_edit_config}
1814          || $param->{can_edit_tags}
1815          || $param->{can_set_publish_paths}
1816          || $param->{show_ip_info};
1817        $param->{has_posting_label} = $param->{can_create_post}
1818          || $param->{can_edit_entries}
1819          || $param->{can_access_assets};
1820        $param->{has_community_label} = $param->{can_edit_entries}
1821          || $param->{can_edit_notifications};
1822        $param->{can_publish_feedbacks} = $param->{can_manage_feedback}
1823          || $param->{can_publish_post}
1824          || $param->{can_edit_all_posts};
1825        $param->{can_view_log}     = $param->{can_view_blog_log};
1826        $param->{can_publish_post} = $param->{can_publish_post}
1827          || $param->{can_edit_all_posts};
1828        $param->{show_ip_info} = $app->config->ShowIPInformation
1829          && $param->{can_manage_feedback};
1830    }
1831
1832    my $static_app_url = $app->static_path;
1833    $param->{help_url} = $app->help_url() || $static_app_url . 'docs/';
1834
1835    $param->{show_ip_info} ||= $app->config('ShowIPInformation');
1836    my $type = $app->param('_type') || '';
1837
1838    $param->{ "mode_$mode" . ( $type ? "_$type" : '' ) } = 1;
1839    $param->{return_args} ||= $app->make_return_args;
1840    $tmpl->param($param);
1841}
1842
1843sub build_page {
1844    my $app = shift;
1845    my ( $page, $param ) = @_;
1846    $param ||= {};
1847
1848    my $blog_id = $app->param('blog_id') || 0;
1849    my $blog;
1850    my $blog_class = $app->model('blog');
1851    $blog ||= $blog_class->load($blog_id) if $blog_id;
1852    if ( $blog_id && $page ne 'login.tmpl' ) {
1853        if ($blog) {
1854            $param->{blog_name} = $blog->name;
1855            $param->{blog_id}   = $blog->id;
1856            $param->{blog_url}  = $blog->site_url;
1857            $param->{blog_template_set} = $blog->template_set;
1858        }
1859        else {
1860            $app->error( $app->translate( "No such blog [_1]", $blog_id ) );
1861        }
1862    }
1863    if ( $page ne 'login.tmpl' ) {
1864        if ( ref $app eq 'MT::App::CMS' ) {
1865            $param->{system_overview_nav} = 1
1866              unless $blog_id
1867              || exists $param->{system_overview_nav}
1868              || $param->{no_breadcrumbs};
1869            $param->{quick_search} = 1 unless defined $param->{quick_search};
1870        }
1871    }
1872
1873    $app->build_blog_selector($param);
1874    $app->build_menus($param);
1875    if (!ref($page) || ($page->isa('MT::Template') && !$page->param('page_actions'))) {
1876        $param->{page_actions} ||= $app->page_actions( $app->mode );
1877    }
1878
1879    $app->SUPER::build_page( $page, $param );
1880}
1881
1882sub build_blog_selector {
1883    my $app = shift;
1884    my ($param) = @_;
1885
1886    return if exists $param->{top_blog_loop};
1887
1888    my $blog = $app->blog;
1889    my $blog_id = $blog->id if $blog;
1890    $param->{dynamic_all} = $blog->custom_dynamic_templates eq 'all' if $blog;
1891
1892    my $blog_class = $app->model('blog');
1893    my $auth = $app->user or return;
1894
1895    # Any access to a blog will put it on the top of your
1896    # recently used blogs list (the blog selector)
1897    $app->add_to_favorite_blogs($blog_id) if $blog_id;
1898
1899    my %args;
1900    $args{join} =
1901      MT::Permission->join_on( 'blog_id',
1902          { author_id => $auth->id, permissions => { not => "'comment'" } } );
1903    $args{limit} = 11;    # don't load more than 11
1904    my @blogs = $blog_class->load( undef, \%args );
1905
1906    my @fav_blogs = @{ $auth->favorite_blogs || [] };
1907    @fav_blogs = grep { $_ != $blog_id } @fav_blogs if $blog_id;
1908
1909    # Special case for when a user only has access to a single blog.
1910    if ( (!defined($app->param('blog_id'))) && ( @blogs == 1 ) && ( scalar @fav_blogs <= 1 ) ) {
1911
1912        # User only has visibility to a single blog. Don't
1913        # bother giving them a dashboard link for 'all blogs', or
1914        # to 'select a blog'.
1915        $param->{single_blog_mode} = 1;
1916        my $blog = $blogs[0];
1917        $blog_id = $blog->id;
1918        my $perms = MT::Permission->load(
1919            {
1920                blog_id   => $blog_id,
1921                author_id => $auth->id
1922            }
1923        );
1924        if ( !$app->blog ) {
1925            if ( $app->mode eq 'dashboard' ) {
1926                $app->param( 'blog_id', $blog_id );
1927                $param->{blog_id}   = $blog_id;
1928                $param->{blog_name} = $blog->name;
1929                $app->permissions($perms);
1930                $app->blog($blog);
1931            }
1932            else {
1933                @fav_blogs = ($blog_id);
1934                $blog_id   = undef;
1935            }
1936        }
1937    }
1938    elsif ( @blogs && ( @blogs <= 10 ) ) {
1939
1940        # This user only has visibility to 10 or fewer blogs;
1941        # no need to reference their 'favorite' blogs list.
1942        my @ids = map { $_->id } @blogs;
1943        if ($blog_id) {
1944            @ids = grep { $_ != $blog_id } @ids;
1945        }
1946        @fav_blogs = @ids;
1947        if ( $auth->is_superuser ) {
1948
1949            # Better check to see if there are more than
1950            # 10 blogs in the system; if so, a superuser
1951            # will still want the 'Select a blog...' chooser.
1952            # Otherwise, hide it.
1953            my $all_blog_count = $blog_class->count();
1954            if ( $all_blog_count < 11 ) {
1955                $param->{selector_hide_chooser} = 1;
1956            }
1957        }
1958        else {
1959
1960            # This user is not a superuser and only has
1961            # 10 blogs, so they don't need a 'select blog'
1962            # link...
1963            $param->{selector_hide_chooser} = 1;
1964        }
1965    }
1966    $param->{selector_hide_chooser} ||= 0;
1967
1968    # Logic for populating the blog selector control
1969    #   * Pull list of 'favorite blogs' from user record
1970    #   * Load all of those blogs so we can display them
1971    #   * Exclude the current blog from the favorite list so it isn't
1972    #     shown twice.
1973    @blogs = $blog_class->load( { id => \@fav_blogs } ) if @fav_blogs;
1974    my %blogs = map { $_->id => $_ } @blogs;
1975    @blogs = ();
1976    foreach my $id (@fav_blogs) {
1977        push @blogs, $blogs{$id} if $blogs{$id};
1978    }
1979
1980    my @data;
1981    if (@blogs) {
1982        my @perms = grep { !$_->is_empty } MT::Permission->load(
1983            {
1984                author_id => $auth->id,
1985                blog_id   => \@fav_blogs,
1986            }
1987        );
1988        my %perms = map { $_->blog_id => $_ } @perms;
1989        for my $blog (@blogs) {
1990            my $perm = $perms{ $blog->id };
1991            next
1992              unless $auth->is_superuser || ( $perm && !$perm->is_empty );
1993            push @data,
1994              {
1995                top_blog_id   => $blog->id,
1996                top_blog_name => $blog->name
1997              };
1998            $data[-1]{top_blog_selected} = 1
1999              if $blog_id && ( $blog->id == $blog_id );
2000        }
2001    }
2002    $param->{top_blog_loop} = \@data;
2003    if ( !$app->user->can_create_blog
2004        && ( $param->{single_blog_mode} || scalar(@data) <= 1 ) )
2005    {
2006        $param->{no_submenu} = 1;
2007    }
2008}
2009
2010sub build_menus {
2011    my $app = shift;
2012    my ($param) = @_;
2013    return if exists $param->{top_nav_loop};
2014
2015    my $menus   = $app->registry('menus');
2016    my $blog    = $app->blog;
2017    my $blog_id = $blog->id if $blog;
2018
2019    my @top_ids = grep { !/:/ } keys %$menus;
2020    my @top;
2021    my @sys;
2022    my $user = $app->user
2023      or return;
2024    my $perms = $app->permissions || $user->permissions;
2025    my $view = $blog_id ? "blog" : "system";
2026
2027    my $hide_disabled_options = $app->config('HideDisabledMenus') || 0;
2028
2029    my $admin =
2030      $user->is_superuser();    # || ($perms && $perms->has('administer_blog'));
2031
2032    foreach my $id (@top_ids) {
2033        my $menu = $menus->{$id};
2034        next if $menu->{view} && $menu->{view} ne $view;
2035        if (my $cond = $menu->{condition}) {
2036            if (!ref($cond)) {
2037                $cond = $menu->{condition} = $app->handler_to_coderef($cond);
2038            }
2039            next unless $cond->();
2040        }
2041
2042        $menu->{allowed} = 1;
2043        $menu->{'id'} = $id;
2044
2045        my @sub_ids = grep { m/^$id:/ } keys %$menus;
2046        my @sub;
2047        foreach my $sub_id (@sub_ids) {
2048            my $sub = $menus->{$sub_id};
2049            next
2050              if $sub->{view}
2051              && ( $sub->{view} ne $view );
2052            $sub->{'id'} = $sub_id;
2053            if (my $cond = $sub->{condition}) {
2054                if (!ref($cond)) {
2055                    $cond = $sub->{condition} = $app->handler_to_coderef($cond);
2056                }
2057                next unless $cond->();
2058            }
2059            push @sub, $sub;
2060        }
2061
2062        if (
2063            my $p =
2064              $blog_id
2065            ? $menu->{permission}
2066            || $menu->{system_permission}
2067            : $menu->{system_permission}
2068            || $menu->{permission}
2069          )
2070        {
2071            my $allowed = 0;
2072            my @p = split /,/, $p;
2073            foreach my $p (@p) {
2074                my $perm = 'can_' . $p;
2075                $allowed = 1, last if ( $perms && $perms->$perm() ) || $admin;
2076            }
2077            $menu->{allowed} = $allowed;
2078        }
2079        elsif ( !$perms && !$blog_id ) {
2080            $menu->{allowed} = 0 if $menu->{system_permission} && !$admin;
2081        }
2082
2083        next if $hide_disabled_options && (! $menu->{allowed});
2084
2085        if (@sub) {
2086            if ( $id eq 'system' ) {
2087                push @sys, $menu;
2088            }
2089            else {
2090                push @top, $menu;
2091            }
2092
2093            my $has_sub = 0;
2094            foreach my $sub (@sub) {
2095                my $sys_only = $sub->{id} =~ m/^system:/;
2096                $sub->{allowed} = 1;
2097                if ( $sub->{mode} ) {
2098                    $sub->{link} = $app->uri(
2099                        mode => $sub->{mode},
2100                        args => {
2101                            %{ $sub->{args} || {} },
2102                            (
2103                                $blog_id
2104                                  && !$sys_only ? ( blog_id => $blog_id ) : ()
2105                            )
2106                        }
2107                    );
2108                }
2109                $sub->{allowed} = 0
2110                  if $sub->{view} && ( $sub->{view} ne $view );
2111                if ( $sub->{allowed} ) {
2112                    my $sub_perms =
2113                      ( $sys_only || ( $sub->{view} || '' ) eq 'system' )
2114                      ? $app->user->permissions(0)
2115                      : $perms;
2116                    if (
2117                        $sub_perms
2118                        && (
2119                            my $p =
2120                              $blog_id
2121                            ? $sub->{permission}
2122                            || $sub->{system_permission}
2123                            : $sub->{system_permission}
2124                            || $sub->{permission}
2125                        )
2126                      )
2127                    {
2128                        my $allowed = 0;
2129                        my @p = split /,/, $p;
2130                        foreach my $p (@p) {
2131                            my $perm = 'can_' . $p;
2132                            $allowed = 1, last
2133                              if ( $sub_perms && $sub_perms->$perm() )
2134                              || $admin;
2135                        }
2136                        $sub->{allowed} = $allowed;
2137                    }
2138                    elsif ( !$sub_perms && !$blog_id ) {
2139                        $sub->{allowed} = 0
2140                          if ( $sub->{system_permission} || $sub->{permission} )
2141                          && !$admin;
2142                    }
2143                    $has_sub = 1 if $sub->{allowed};
2144                }
2145            }
2146            if ( $menu->{mode} ) {
2147                my $sys_only = 1 if $menu->{id} eq 'system';
2148                $menu->{link} = $app->uri(
2149                    mode => $menu->{mode},
2150                    args => {
2151                        %{ $menu->{args} || {} },
2152                        (
2153                            $blog_id
2154                              && !$sys_only ? ( blog_id => $blog_id ) : ()
2155                        )
2156                    }
2157                );
2158            }
2159            @sub = sort { $a->{order} <=> $b->{order} } @sub;
2160
2161            @sub = grep { $_->{allowed} } @sub if $hide_disabled_options;
2162            if ( !$menu->{mode} ) {
2163                $menu->{link} = $sub[0]->{link};
2164            }
2165            $menu->{sub_nav_loop} = \@sub;
2166
2167            if ( $menu->{allowed} ) {
2168                $menu->{allowed} = 0 unless $has_sub;
2169            }
2170        }
2171    }
2172
2173    @top = grep { $_->{allowed} } @top if $hide_disabled_options;
2174    @sys = grep { $_->{allowed} } @sys if $hide_disabled_options;
2175    @top = sort { $a->{order} <=> $b->{order} } @top;
2176    $param->{top_nav_loop} = \@top;
2177    $param->{sys_nav_loop} = \@sys;
2178}
2179
2180sub return_to_dashboard {
2181    my $app = shift;
2182    my (%param) = @_;
2183    $param{redirect} = 1 unless %param;
2184    my $blog_id = $app->param('blog_id');
2185    $param{blog_id} = $blog_id if $blog_id;
2186    return $app->redirect( $app->uri( mode => 'dashboard', args => \%param ) );
2187}
2188
2189sub list_pref {
2190    my $app      = shift;
2191    my ($list)   = @_;
2192    my $updating = $app->mode eq 'update_list_prefs';
2193    unless ($updating) {
2194        my $pref = $app->request("list_pref_$list");
2195        return $pref if defined $pref;
2196    }
2197
2198    my $cookie = $app->cookie_val('mt_list_pref') || '';
2199    my $mode = $app->mode;
2200
2201    # defaults:
2202    my $d = $app->config->DefaultListPrefs || {};
2203    my %default = (
2204        Rows       => 25,
2205        Format     => 'Compact',
2206        SortOrder  => 'Ascend',      # Ascend|Descend
2207        Button     => 'Above',       # Above|Below|Both
2208        DateFormat => 'Relative',    # Relative|Full
2209    );
2210    if ( ( $list eq 'comment' ) || ( $list eq 'ping' ) ) {
2211        $default{Format} = 'expanded';
2212    }
2213    $default{$_} = lc( $d->{$_} ) for keys %$d;
2214    my $list_pref;
2215    if ( $list eq 'main_menu' ) {
2216        $list_pref = {
2217            'sort' => 'name',
2218            order  => $default{SortOrder} || 'ascend',
2219            view   => $default{Format} || 'compact',
2220            dates  => $default{DateFormat} || 'relative',
2221        };
2222    }
2223    else {
2224        $list_pref = {
2225            rows  => $default{Rows}       || 25,
2226            view  => $default{Format}     || 'compact',
2227            bar   => $default{Button}     || 'above',
2228            dates => $default{DateFormat} || 'relative',
2229        };
2230    }
2231    my @list_prefs = split /;/, $cookie;
2232    my $new_cookie = '';
2233    foreach my $pref (@list_prefs) {
2234        my ( $name, $prefs ) = $pref =~ m/^(\w+):(.*)$/;
2235        next unless $name && $prefs;
2236        if ( $name eq $list ) {
2237            my @prefs = split /,/, $prefs;
2238            foreach (@prefs) {
2239                my ( $k, $v ) = split /=/;
2240                $list_pref->{$k} = $v if exists $list_pref->{$k};
2241            }
2242        }
2243        else {
2244            $new_cookie .= ( $new_cookie ne '' ? ';' : '' ) . $pref;
2245        }
2246    }
2247
2248    if ($updating) {
2249        my $updated = 0;
2250        if ( my $limit = $app->param('limit') ) {
2251            $limit = 20 if $limit eq 'none';
2252            $list_pref->{rows} = $limit > 0 ? $limit : 20;
2253            $updated = 1;
2254        }
2255        if ( my $view = $app->param('verbosity') ) {
2256            if ( $view =~ m!^compact|expanded$! ) {
2257                $list_pref->{view} = $view;
2258                $updated = 1;
2259            }
2260        }
2261        if ( my $bar = $app->param('actions') ) {
2262            if ( $bar =~ m!^above|below|both$! ) {
2263                $list_pref->{bar} = $bar;
2264                $updated = 1;
2265            }
2266        }
2267        if ( my $ord = $app->param('order') ) {
2268            if ( $ord =~ m!^ascend|descend$! ) {
2269                $list_pref->{order} = $ord;
2270                $updated = 1;
2271            }
2272        }
2273        if ( my $sort = $app->param('sort') ) {
2274            if ( $sort =~ m!^name|created|updated$! ) {
2275                $list_pref->{'sort'} = $sort;
2276                $updated = 1;
2277            }
2278        }
2279        if ( my $dates = $app->param('dates') ) {
2280            if ( $dates =~ m!^relative|full$! ) {
2281                $list_pref->{'dates'} = $dates;
2282                $updated = 1;
2283            }
2284        }
2285
2286        if ($updated) {
2287            my @list_prefs;
2288            foreach ( keys %$list_pref ) {
2289                push @list_prefs, $_ . '=' . $list_pref->{$_};
2290            }
2291            my $prefs = join ',', @list_prefs;
2292            $new_cookie .=
2293              ( $new_cookie ne '' ? ';' : '' ) . $list . ':' . $prefs;
2294            $app->bake_cookie(
2295                -name    => 'mt_list_pref',
2296                -value   => $new_cookie,
2297                -expires => '+10y'
2298            );
2299        }
2300    }
2301
2302    if ( $list_pref->{rows} ) {
2303        $list_pref->{ "limit_" . $list_pref->{rows} } = $list_pref->{rows};
2304    }
2305    if ( $list_pref->{view} ) {
2306        $list_pref->{ "view_" . $list_pref->{view} } = 1;
2307    }
2308    if ( $list_pref->{dates} ) {
2309        $list_pref->{ "dates_" . $list_pref->{dates} } = 1;
2310    }
2311    if ( $list_pref->{bar} ) {
2312        if ( lc $list_pref->{bar} eq 'both' ) {
2313            $list_pref->{"position_actions_both"}   = 1;
2314            $list_pref->{"position_actions_top"}    = 1;
2315            $list_pref->{"position_actions_bottom"} = 1;
2316        }
2317        elsif ( lc $list_pref->{bar} eq 'below' ) {
2318            $list_pref->{"position_actions_bottom"} = 1;
2319        }
2320        elsif ( lc $list_pref->{bar} eq 'above' ) {
2321            $list_pref->{"position_actions_top"} = 1;
2322        }
2323    }
2324    if ( $list_pref->{'sort'} ) {
2325        $list_pref->{ 'sort_' . $list_pref->{'sort'} } = 1;
2326    }
2327    if ( $list_pref->{'order'} ) {
2328        $list_pref->{ 'order_' . $list_pref->{'order'} } = 1;
2329    }
2330    $app->request( "list_pref_$list", $list_pref );
2331}
2332
2333sub make_feed_link {
2334    my $app = shift;
2335    my ( $view, $params ) = @_;
2336    my $user = $app->user;
2337    return if ( $user->api_password || '' ) eq '';
2338
2339    $params ||= {};
2340    $params->{view}     = $view;
2341    $params->{username} = $user->name;
2342    $params->{token}    = perl_sha1_digest_hex( 'feed:' . $user->api_password );
2343    $app->base
2344      . $app->mt_path
2345      . $app->config('ActivityFeedScript')
2346      . $app->uri_params( args => $params );
2347}
2348
2349sub show_error {
2350    my $app  = shift;
2351    my ($param) = @_;
2352
2353    # handle legacy scalar error string signature
2354    $param = { error => $param } unless ref($param) eq 'HASH';
2355
2356    my $mode = $app->mode;
2357    if ( $mode eq 'rebuild' ) {
2358
2359        my $r = MT::Request->instance;
2360        if (my $tmpl = $r->cache('build_template')) {
2361            # this is the template that likely caused the rebuild error
2362            push @{ $param->{button_loop} ||= [] }, {
2363                link => $app->uri( mode => 'view', args => { blog_id => $tmpl->blog_id, '_type' => 'template', id => $tmpl->id }),
2364                label => $app->translate("Edit Template"),
2365            };
2366        }
2367
2368        my $blog_id = $app->param('blog_id');
2369        my $url     = $app->uri(
2370            mode => 'rebuild_confirm',
2371            args => { blog_id => $blog_id }
2372        );
2373        $param->{goback} ||= qq{window.location='$url'};
2374        $param->{value}  ||= $app->translate('Go Back');
2375    }
2376
2377    return $app->SUPER::show_error($param);
2378}
2379
2380sub load_default_entry_prefs {
2381    my $app = shift;
2382    my $prefs;
2383    require MT::Permission;
2384    my $blog_id;
2385    $blog_id = $app->blog->id if $app->blog;
2386    my $perm = MT::Permission->load( { blog_id => $blog_id, author_id => 0 } );
2387    my %default = %{ $app->config->DefaultEntryPrefs };
2388    if ( $perm && $perm->entry_prefs ) {
2389        $prefs = $perm->entry_prefs;
2390    }
2391    else {
2392        if ( lc( $default{type} ) eq 'custom' ) {
2393            my %map = (
2394                Category   => 'category',
2395                Excerpt    => 'excerpt',
2396                Keywords   => 'keywords',
2397                Tags       => 'tags',
2398                Publishing => 'publishing',
2399                Feedback   => 'feedback',
2400            );
2401            my @p;
2402            foreach my $p ( keys %map ) {
2403                push @p, $map{$p} . ':' . ( $default{$p} || $default{ lc $p } )
2404                  if ( $default{$p} || $default{ lc $p } );
2405            }
2406            $prefs = join ',', @p;
2407            $prefs ||= 'Custom';
2408        }
2409        elsif ( lc( $default{type} ) ne 'default' ) {
2410            $prefs = 'Advanced';
2411        }
2412        $default{button} = 'Bottom' if lc( $default{button} ) eq 'below';
2413        $default{button} = 'Top'    if lc( $default{button} ) eq 'above';
2414        $prefs .= '|' . $default{button} if $prefs;
2415    }
2416    $prefs ||= 'Default|Bottom';
2417    return $prefs;
2418}
2419
2420sub load_template_prefs {
2421    my $app = shift;
2422    my ($prefs) = @_;
2423    my %param;
2424
2425    if ( !$prefs ) {
2426        $prefs = '';
2427    }
2428    my @p = split /,/, $prefs;
2429    for my $p (@p) {
2430        if ( $p =~ m/^(.+?):(\d+)$/ ) {
2431            $param{ 'disp_prefs_height_' . $1 } = $2;
2432        }
2433    }
2434    \%param;
2435}
2436
2437sub _parse_entry_prefs {
2438    my $app = shift;
2439    my ( $prefs, $param ) = @_;
2440
2441    my @p = split /,/, $prefs;
2442    for my $p (@p) {
2443        if ( $p =~ m/^(.+?):(\d+)$/ ) {
2444            my ( $name, $num ) = ( $1, $2 );
2445            if ($num) {
2446                $param->{ 'disp_prefs_height_' . $name } = $num;
2447            }
2448            $param->{ 'disp_prefs_show_' . $name } = 1;
2449        }
2450        else {
2451            $p = 'Default' if lc($p) eq 'basic';
2452            if ( ( lc($p) eq 'advanced' ) || ( lc($p) eq 'default' ) ) {
2453                $param->{ 'disp_prefs_' . $p } = 1;
2454                foreach my $def (
2455                    qw(title body category tags keywords feedback publishing ))
2456                {
2457                    $param->{ 'disp_prefs_show_' . $def } = 1;
2458                }
2459                if ( lc($p) eq 'advanced' ) {
2460                    foreach my $def (qw(excerpt feedback)) {
2461                        $param->{ 'disp_prefs_show_' . $def } = 1;
2462                    }
2463                }
2464            }
2465            else {
2466                $param->{ 'disp_prefs_show_' . $p } = 1;
2467            }
2468        }
2469    }
2470}
2471
2472sub load_entry_prefs {
2473    my $app = shift;
2474    my ($prefs) = @_;
2475    my %param;
2476    my $pos;
2477    my $is_from_db = 1;
2478
2479    # defaults:
2480    if ( !$prefs ) {
2481        $prefs = $app->load_default_entry_prefs;
2482        ( $prefs, $pos ) = split /\|/, $prefs;
2483        $is_from_db = 0;
2484        $app->_parse_entry_prefs( $prefs, \%param );
2485        my @fields;
2486        foreach ( keys %param ) {
2487            if (m/^disp_prefs_show_(.+)/) {
2488                push @fields, { name => $1 };
2489            }
2490        }
2491        $param{disp_prefs_default_fields} = \@fields;
2492    }
2493    else {
2494        ( $prefs, $pos ) = split /\|/, $prefs;
2495    }
2496    $app->_parse_entry_prefs( $prefs, \%param );
2497    if ($is_from_db) {
2498        my $default_prefs = $app->load_default_entry_prefs;
2499        ( $default_prefs, my ($default_pos) ) = split /\|/, $default_prefs;
2500        $pos ||= $default_pos;
2501        $app->_parse_entry_prefs( $default_prefs, \my %def_param );
2502        my @fields;
2503        foreach ( keys %def_param ) {
2504            if (m/^disp_prefs_show_(.+)/) {
2505                push @fields, { name => $1 };
2506            }
2507        }
2508        if ( $prefs eq 'Default' ) {
2509            foreach my $p ( keys %param ) {
2510                delete $param{$p} if $p =~ m/^disp_prefs_show_/;
2511            }
2512        }
2513        if ( $param{disp_prefs_Default} ) {
2514
2515            # apply default settings
2516            %param = ( %def_param, %param );
2517        }
2518        $param{disp_prefs_default_fields} = \@fields;
2519    }
2520    $pos ||= 'Bottom';
2521    if (   !exists $param{'disp_prefs_Default'}
2522        && !exists $param{'disp_prefs_Advanced'} )
2523    {
2524        $param{'disp_prefs_Custom'} = 1;
2525    }
2526    if ( lc $pos eq 'both' ) {
2527        $param{'position_actions_top'}    = 1;
2528        $param{'position_actions_bottom'} = 1;
2529        $param{'position_actions_both'}   = 1;
2530    }
2531    else {
2532        $param{ 'position_actions_' . $pos } = 1;
2533    }
2534    \%param;
2535}
2536
2537sub _convert_word_chars {
2538    my ( $app, $s ) = @_;
2539    return '' unless $s;
2540    return $s if 'utf-8' ne lc( $app->charset );
2541
2542    my $blog = $app->blog;
2543    my $smart_replace = $blog ? $blog->smart_replace
2544        : MT->config->NwcSmartReplace;
2545    return $s if $smart_replace == 2;
2546
2547    if ($smart_replace)<