root/branches/release-33/lib/MT/App/CMS.pm @ 1715

Revision 1715, 120.5 kB (checked in by fumiakiy, 20 months ago)

Webmaster can save pages in batch. BugId:67906

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