root/branches/release-30/lib/MT/App/CMS.pm @ 1396

Revision 1396, 117.3 kB (checked in by bchoate, 21 months ago)

Fixed test for custom DefaultEntryPrefs settings. BugId:66635

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