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

Revision 1770, 120.7 kB (checked in by takayama, 20 months ago)

Resolved BugId:68759
* Implemented new copy template feature.

  • 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                code => "${pkg}Template::refresh_individual_templateE",
626                permission => 'edit_templates',
627            },
628            publish_index_templates => {
629                label => "Publish Template(s)",
630                code => "${pkg}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            copy_templates => {
644                label => "Clone Template(s)",
645                code => "${pkg}Template::clone_templates",
646                permission => 'edit_templates',
647            },
648        },
649    };
650}
651
652sub _entry_label {
653    my $app = MT->instance;
654    my $type = $app->param('type') || 'entry';
655    $app->model($type)->class_label_plural;
656}
657
658sub core_list_filters {
659    my $app = shift;
660    return {
661        asset => sub {
662            require MT::CMS::Asset;
663            return MT::CMS::Asset::asset_list_filters($app, @_);
664        },
665        entry => {
666            published => {
667                label => sub {
668                    $app->translate( 'Published [_1]', _entry_label );
669                },
670                order   => 100,
671                handler => sub {
672                    my ( $terms, $args ) = @_;
673                    $terms->{status} = 2;
674                },
675            },
676            unpublished => {
677                label => sub {
678                    $app->translate( 'Unpublished [_1]', _entry_label );
679                },
680                order   => 200,
681                handler => sub {
682                    my ( $terms, $args ) = @_;
683                    $terms->{status} = 1;
684                },
685            },
686            scheduled => {
687                label => sub {
688                    $app->translate( 'Scheduled [_1]', _entry_label );
689                },
690                order   => 300,
691                handler => sub {
692                    my ( $terms, $args ) = @_;
693                    $terms->{status} = 4;
694                },
695            },
696            my_posts => {
697                label => sub {
698                    $app->translate( 'My [_1]', _entry_label );
699                },
700                order   => 400,
701                handler => sub {
702                    my ( $terms, $args ) = @_;
703                    $terms->{author_id} = $app->user->id;
704                },
705            },
706            received_comments_in_last_7_days => {
707                label => sub {
708                    $app->translate( '[_1] with comments in the last 7 days',
709                        _entry_label );
710                },
711                order   => 500,
712                handler => sub {
713                    my ( $terms, $args ) = @_;
714                    my $ts = time - 10 * 24 * 60 * 60;
715                    $ts = epoch2ts( MT->app->blog, $ts );
716                    $args->{join} = MT::Comment->join_on(
717                        'entry_id',
718                        {
719                            created_on  => [ $ts, undef ],
720                            junk_status => [ 0,   1 ]
721                        },
722                        { range_incl => { created_on => 1 }, unique => 1 }
723                    );
724                    # Since we're selecting content from the mt_entry
725                    # table, but we want to sort by the joined
726                    # 'comment_created_on' column, we have to specify the
727                    # sort column as a reference and a full field name,
728                    # to prevent MT from adding a 'entry_' prefix to
729                    # the column name.
730                    $args->{sort}      = \'comment_created_on';
731                    $args->{direction} = 'descend';
732                },
733            },
734            _by_date => {
735                label => sub {
736                    my $app = MT->instance;
737                    my $val = $app->param('filter_val');
738                    my ( $from, $to ) = split /-/, $val;
739                    $from = undef unless $from =~ m/^\d{8}$/;
740                    $to   = undef unless $to   =~ m/^\d{8}$/;
741                    my $format = '%x';
742                    $from = format_ts(
743                        $format, $from . '000000',
744                        undef,   MT->current_language
745                    ) if $from;
746                    $to = format_ts(
747                        $format, $to . '000000',
748                        undef,   MT->current_language
749                    ) if $to;
750                    my $label = _entry_label;
751
752                    if ( $from && $to ) {
753                        return $app->translate(
754                            '[_1] posted between [_2] and [_3]',
755                            $label, $from, $to );
756                    }
757                    elsif ($from) {
758                        return $app->translate( "[_1] posted since [_2]",
759                            $label, $from );
760                    }
761                    elsif ($to) {
762                        return $app->translate( "[_1] posted on or before [_2]",
763                            $label, $to );
764                    }
765                },
766                handler => sub {
767                    my ( $terms, $args ) = @_;
768                    my $val = $app->param('filter_val');
769                    my ( $from, $to ) = split /-/, $val;
770                    $from = undef unless $from =~ m/^\d{8}$/;
771                    $from .= '000000';
772                    $to = undef unless $to =~ m/^\d{8}$/;
773                    $to .= '235959';
774                    $terms->{authored_on} =
775                      [ MT::Object::ts2db($from), MT::Object::ts2db($to) ];
776                    $args->{range_incl}{authored_on} = 1;
777                },
778            },
779        },
780        ping => {
781            default => {
782                label   => 'Non-spam TrackBacks',
783                order   => 100,
784                handler => sub {
785                    my ( $terms, $args ) = @_;
786                    $terms->{junk_status} = [ 0, 1 ];
787                },
788            },
789            my_posts => {
790                label   => 'TrackBacks on my entries',
791                order   => 200,
792                handler => sub {
793                    my ( $terms, $args ) = @_;
794                    require MT::Entry;
795                    my $app = MT->instance;
796                    $terms->{junk_status} = [ 0, 1 ];
797                    require MT::Trackback;
798                    $args->{join} = MT::Trackback->join_on(
799                        undef,
800                        {
801                            id => \'= tbping_tb_id',
802                        },
803                        {
804                            join => MT::Entry->join_on(
805                                undef,
806                                {
807                                    id => \'= trackback_entry_id',
808                                    author_id => $app->user->id
809                                }
810                            )
811                        },
812                    );
813                },
814            },
815            published => {
816                label   => 'Published TrackBacks',
817                order   => 200,
818                handler => sub {
819                    my ( $terms, $args ) = @_;
820                    $terms->{visible} = 1;
821                },
822            },
823            unpublished => {
824                label   => 'Unpublished TrackBacks',
825                order   => 300,
826                handler => sub {
827                    my ( $terms, $args ) = @_;
828                    $terms->{junk_status} = [ 0, 1 ];
829                    $terms->{visible} = 0;
830                },
831            },
832            spam => {
833                label   => 'TrackBacks marked as Spam',
834                order   => 400,
835                handler => sub {
836                    my ( $terms, $args ) = @_;
837                    $terms->{junk_status} = -1;
838                },
839            },
840            last_7_days => {
841                label   => 'All TrackBacks in the last 7 days',
842                order   => 700,
843                handler => sub {
844                    my ( $terms, $args ) = @_;
845                    my $ts = time - 7 * 24 * 60 * 60;
846                    $ts = epoch2ts( MT->app->blog, $ts );
847                    $terms->{created_on} = [ $ts, undef ];
848                    $args->{range_incl}{created_on} = 1;
849                    $terms->{junk_status} = [ 0, 1 ];
850                },
851            },
852        },
853        comment => {
854            default => {
855                label   => 'Non-spam Comments',
856                order   => 100,
857                handler => sub {
858                    my ( $terms, $args ) = @_;
859                    $terms->{junk_status} = [ 0, 1 ];
860                },
861            },
862            my_posts => {
863                label   => 'Comments on my entries',
864                order   => 200,
865                handler => sub {
866                    my ( $terms, $args ) = @_;
867                    require MT::Entry;
868                    my $app = MT->instance;
869                    $terms->{junk_status} = [ 0, 1 ];
870                    # This join syntax employs a hack that allows us
871                    # to do joins on abitrary columns. Typically,
872                    # objectdriver joins are applied with the primary
873                    # key column of the driving table (here,
874                    # mt_comment.comment_id), but we actually want to
875                    # join to the mt_entry table where the entry_id
876                    # matches to mt_comment.comment_entry_id (not the
877                    # primary key). So by specifying NO 'join' column
878                    # (which is always compared with the primary key),
879                    # we specify the actual join conditions in the
880                    # terms. And using a reference for the
881                    # 'comment_entry_id' column name, to pass that
882                    # on directly to the SQL statement that is generated.
883                    $args->{join} = MT::Entry->join_on(
884                        undef,
885                        {
886                            id        => \'= comment_entry_id',
887                            author_id => $app->user->id
888                        }
889                    );
890                },
891            },
892            unpublished => {
893                label   => 'Pending comments',
894                order   => 300,
895                handler => sub {
896                    my ( $terms, $args ) = @_;
897                    $terms->{junk_status} = [ 0, 1 ];
898                    $terms->{visible} = 0;
899                },
900            },
901            spam => {
902                label   => 'Spam Comments',
903                order   => 400,
904                handler => sub {
905                    my ( $terms, $args ) = @_;
906                    $terms->{junk_status} = -1;
907                },
908            },
909            published => {
910                label   => 'Published comments',
911                order   => 500,
912                handler => sub {
913                    my ( $terms, $args ) = @_;
914                    $terms->{visible} = 1;
915                },
916            },
917            #            my_comments => {
918            #                label   => 'My comments',
919            #                order   => 600,
920            #                handler => sub {
921            #                    my ( $terms, $args ) = @_;
922            #                    $terms->{commenter_id} = $app->user->id;
923            #                },
924            #            },
925            last_7_days => {
926                label   => 'Comments in the last 7 days',
927                order   => 700,
928                handler => sub {
929                    my ( $terms, $args ) = @_;
930                    my $ts = time - 7 * 24 * 60 * 60;
931                    $ts = epoch2ts( MT->app->blog, $ts );
932                    $terms->{created_on} = [ $ts, undef ];
933                    $args->{range_incl}{created_on} = 1;
934                    $terms->{junk_status} = [ 0, 1 ];
935                },
936            },
937            #           last_24_hours => {
938            #               label   => 'All comments in the last 24 hours',
939            #               order   => 800,
940            #               handler => sub {
941            #                   my ( $terms, $args ) = @_;
942            #                   my $ts = time - 24 * 60 * 60;
943            #                   $ts = epoch2ts( MT->app->blog, $ts );
944            #                   $terms->{created_on} = [ $ts, undef ];
945            #                   $args->{range_incl}{created_on} = 1;
946            #                   $terms->{junk_status} = [ 0, 1 ];
947            #               },
948            #           },
949            _comments_by_user => {
950                label => sub {
951                    my $app     = MT->app;
952                    my $user_id = $app->param('filter_val');
953                    my $user    = MT::Author->load($user_id);
954                    require MT::Author;
955                    return $app->translate(
956                        "All comments by [_1] '[_2]'",
957                        (
958                              $user->type == MT::Author::COMMENTER()
959                            ? $app->translate("Commenter")
960                            : $app->translate("Author")
961                        ),
962                        (
963                              $user->nickname
964                            ? $user->nickname . ' (' . $user->name . ')'
965                            : $user->name
966                        )
967                    );
968                },
969                handler => sub {
970                    my ( $terms, $args ) = @_;
971                    my $cmtr_id = int( MT->app->param('filter_val') );
972                    $terms->{commenter_id} = $cmtr_id;
973                },
974            },
975            _comments_by_entry => {
976                label => sub {
977                    my $entry_id = MT->app->param('filter_val');
978                    my $entry    = MT::Entry->load($entry_id);
979                    return MT->translate( "All comments for [_1] '[_2]'",
980                        $entry->class_label, $entry->title );
981                },
982                handler => sub {
983                    my ( $terms, $args ) = @_;
984                    my $entry_id = int( MT->app->param('filter_val') );
985                    $terms->{entry_id} = $entry_id;
986                    $terms->{junk_status} = [ 0, 1 ];
987                },
988            },
989            _by_date => {
990                label => sub {
991                    my $app = MT->instance;
992                    my $val = $app->param('filter_val');
993                    my ( $from, $to ) = split /-/, $val;
994                    $from = undef unless $from =~ m/^\d{8}$/;
995                    $to   = undef unless $to   =~ m/^\d{8}$/;
996                    my $format = '%x';
997                    $from = format_ts(
998                        $format, $from . '000000',
999                        undef,   MT->current_language
1000                    ) if $from;
1001                    $to = format_ts(
1002                        $format, $to . '000000',
1003                        undef,   MT->current_language
1004                    ) if $to;
1005
1006                    if ( $from && $to ) {
1007                        return $app->translate(
1008                            'Comments posted between [_1] and [_2]',
1009                            $from, $to );
1010                    }
1011                    elsif ($from) {
1012                        return $app->translate( "Comments posted since [_1]",
1013                            $from );
1014                    }
1015                    elsif ($to) {
1016                        return $app->translate(
1017                            "Comments posted on or before [_1]", $to );
1018                    }
1019                },
1020                handler => sub {
1021                    my ( $terms, $args ) = @_;
1022                    my $val = $app->param('filter_val');
1023                    my ( $from, $to ) = split /-/, $val;
1024                    $from = undef unless $from =~ m/^\d{8}$/;
1025                    $from .= '000000';
1026                    $to = undef unless $to =~ m/^\d{8}$/;
1027                    $to .= '235959';
1028                    $terms->{junk_status} = [ 0, 1 ];
1029                    $terms->{created_on} =
1030                      [ MT::Object::ts2db($from), MT::Object::ts2db($to) ];
1031                    $args->{range_incl}{created_on} = 1;
1032                },
1033            },
1034        },
1035        template => {
1036            index_templates => {
1037                label   => "Index Templates",
1038                order   => 100,
1039                handler => sub {
1040                    my ( $terms, $args ) = @_;
1041                    # FIXME: enumeration of types
1042                    $terms->{type} = 'index';
1043                },
1044                condition => sub {
1045                    $app->param('blog_id');
1046                },
1047            },
1048            archive_templates => {
1049                label   => "Archive Templates",
1050                order   => 200,
1051                handler => sub {
1052                    my ( $terms, $args ) = @_;
1053                    $terms->{type} =
1054                      [ 'individual', 'page', 'archive', 'category' ];
1055                },
1056                condition => sub {
1057                    $app->param('blog_id');
1058                },
1059            },
1060            module_templates => {
1061                label   => "Template Modules",
1062                order   => 400,
1063                handler => sub {
1064                    my ($terms) = @_;
1065                    $terms->{type} = 'custom';
1066                },
1067            },
1068            email_templates => {
1069                label   => "E-mail Templates",
1070                order   =>  300,
1071                handler => sub {
1072                    my ($terms) = @_;
1073                    $terms->{type} = 'email';
1074                },
1075                condition => sub {
1076                    !$app->param('blog_id');
1077                },
1078            },
1079            system_templates => {
1080                label   => "System Templates",
1081                order   => 200,
1082                handler => sub {
1083                    my ($terms) = @_;
1084                    my $scope;
1085                    my $set;
1086                    if ( my $blog_id = $app->param('blog_id') ) {
1087                        my $blog  = $app->model('blog')->load($blog_id);
1088                        $set   = $blog->template_set;
1089                        $scope .= 'system';
1090                    }
1091                    else {
1092                        $terms->{blog_id} = 0;
1093                        $scope = 'global:system';
1094                    }
1095                    my @tmpl_path = ( $set && ($set ne 'mt_blog')) ? ("template_sets", $set, 'templates', $scope) : ("default_templates", $scope);
1096                    my $sys_tmpl = MT->registry(@tmpl_path) || {};
1097                    $terms->{type} = [ keys %$sys_tmpl ];
1098                },
1099                condition => sub {
1100                    $app->param('blog_id');
1101                },
1102            },
1103        },
1104        tag => {
1105            entry => {
1106                label => 'Tags with entries',
1107                order => 100,
1108            },
1109            page => {
1110                label => 'Tags with pages',
1111                order => 200,
1112            },
1113            asset => {
1114                label => 'Tags with assets',
1115                order => 300,
1116            },
1117        },
1118        sys_user => {
1119            enabled => {
1120                label => 'Enabled Users',
1121                order => 100,
1122                handler => sub {
1123                    my ($terms) = @_;
1124                    $terms->{status} = 1;
1125                },
1126            },
1127            disabled => {
1128                label => "Disabled Users",
1129                order => 200,
1130                handler => sub {
1131                    my ($terms) = @_;
1132                    $terms->{status} = 2;
1133                },
1134            },
1135            pending => {
1136                label => "Pending Users",
1137                order => 300,
1138                handler => sub {
1139                    my ($terms) = @_;
1140                    $terms->{status} = 3;
1141                },
1142            },
1143        },
1144        user => {
1145            author => {
1146                label   => 'Authors',
1147                order   => 100,
1148                handler => sub {
1149                    my ($terms) = @_;
1150                    require MT::Author;
1151                    $terms->{type} = MT::Author::AUTHOR();
1152                },
1153            },
1154            commenter => {
1155                label   => 'Commenters',
1156                order   => 200,
1157                handler => sub {
1158                    my ($terms) = @_;
1159                    require MT::Author;
1160                    $terms->{type} = MT::Author::COMMENTER();
1161                },
1162            },
1163        },
1164    };
1165}
1166
1167sub core_menus {
1168    my $app = shift;
1169    return {
1170        'create' => {
1171            label => "Create",
1172            order => 100,
1173        },
1174        'manage' => {
1175            label => "Manage",
1176            order => 200,
1177        },
1178        'design' => {
1179            label => "Design",
1180            order => 300,
1181        },
1182        'prefs' => {
1183            label => "Preferences",
1184            order => 400,
1185        },
1186        'tools' => {
1187            label => "Tools",
1188            order => 500,
1189        },
1190
1191        'create:blog' => {
1192            label => "Blog",
1193            order => 100,
1194            view  => "system",
1195        },
1196        'create:blog' => {
1197            label      => "Blog",
1198            order      => 200,
1199            view       => "system",
1200            mode       => "view",
1201            args       => { _type => "blog" },
1202            permission => "create_blog",
1203        },
1204        'create:user' => {
1205            label      => "User",
1206            order      => 200,
1207            view       => "system",
1208            mode       => "view",
1209            args       => { _type => "author" },
1210            permission => "administer",
1211            condition  => sub {
1212                return !MT->config->ExternalUserManagement;
1213            },
1214        },
1215        'create:entry' => {
1216            label      => "Entry",
1217            order      => 100,
1218            mode       => 'view',
1219            args       => { _type => 'entry' },
1220            permission => 'create_post',
1221            view       => "blog",
1222        },
1223        'create:page' => {
1224            label      => "Page",
1225            order      => 200,
1226            mode       => 'view',
1227            args       => { _type => 'page' },
1228            permission => 'manage_pages',
1229            view       => "blog",
1230        },
1231        'create:file' => {
1232            label      => "Upload File",
1233            order      => 300,
1234            dialog     => 'start_upload',
1235            permission => 'upload,edit_assets',
1236            view       => "blog",
1237        },
1238
1239        'manage:blog' => {
1240            label => "Blogs",
1241            mode  => "list_blog",
1242            order => 100,
1243            view  => "system",
1244        },
1245        'manage:user' => {
1246            label      => "Users",
1247            mode       => "list_user",
1248            order      => 200,
1249            permission => "administer",
1250            view       => "system",
1251        },
1252        'manage:entry' => {
1253            label      => "Entries",
1254            mode       => 'list_entry',
1255            order      => 1000,
1256            condition  => sub {
1257                return 1 if $app->user->is_superuser;
1258                if ( $app->param('blog_id') ) {
1259                    my $perms = $app->user->permissions($app->param('blog_id'));
1260                    return 1 if $perms->can_create_post || $perms->can_publish_post || $perms->can_edit_all_posts;
1261                }
1262                else {
1263                    require MT::Permission;
1264                    my @blogs = 
1265                        map { $_->blog_id }
1266                          grep { $_->can_create_post || $_->can_publish_post || $_->can_edit_all_posts }
1267                          MT::Permission->load( { author_id => $app->user->id } );
1268                    return 1 if @blogs;
1269                }
1270                return 0;
1271            },
1272            #permission => 'create_post,publish_post,edit_all_posts',
1273        },
1274        'manage:comment' => {
1275            label      => "Comments",
1276            mode       => 'list_comment',
1277            order      => 2000,
1278            condition  => sub {
1279                return 1 if $app->user->is_superuser;
1280                if ( $app->param('blog_id') ) {
1281                    my $perms = $app->user->permissions($app->param('blog_id'));
1282                    return 1 if $perms->can_create_post || $perms->can_manage_feedback || $perms->can_edit_all_posts || $perms->can_comment;
1283                }
1284                else {
1285                    require MT::Permission;
1286                    my @blogs = 
1287                        map { $_->blog_id }
1288                          grep { $_->can_create_post || $_->can_manage_feedback || $_->can_edit_all_posts || $_->can_comment }
1289                          MT::Permission->load( { author_id => $app->user->id } );
1290                    return 1 if @blogs;
1291                }
1292                return 0;
1293            },
1294            #permission => 'create_post,edit_all_posts,manage_feedback,comment',
1295        },
1296        'manage:asset' => {
1297            label      => "Assets",
1298            mode       => 'list_asset',
1299            order      => 3000,
1300            permission => 'edit_assets',
1301        },
1302        'manage:page' => {
1303            label      => "Pages",
1304            mode       => 'list_pages',
1305            order      => 4000,
1306            permission => 'manage_pages',
1307        },
1308        'manage:ping' => {
1309            label      => "TrackBacks",
1310            mode       => 'list_pings',
1311            order      => 5000,
1312            permission => 'create_post,edit_all_posts,manage_feedback',
1313        },
1314        'manage:category' => {
1315            label      => "Categories",
1316            mode       => 'list_cat',
1317            order      => 6000,
1318            permission => 'edit_categories',
1319            view       => "blog",
1320        },
1321        'manage:folder' => {
1322            label      => "Folders",
1323            mode       => 'list_cat',
1324            args       => { _type => 'folder' },
1325            order      => 7000,
1326            permission => 'manage_pages',
1327            view       => "blog",
1328        },
1329        'manage:tag' => {
1330            label             => "Tags",
1331            mode              => 'list_tag',
1332            order             => 8000,
1333            permission        => 'edit_tags',
1334            system_permission => 'administer',
1335        },
1336        'manage:blog_user' => {
1337            label             => "Users",
1338            mode              => 'list_member',
1339            order             => 9000,
1340            view              => "blog",
1341            permission        => 'administer_blog',
1342            system_permission => 'administer',
1343        },
1344        'manage:notification' => {
1345            label             => "Address Book",
1346            mode              => 'list',
1347            args              => { _type => 'notification' },
1348            order             => 10000,
1349            permission        => 'edit_notifications',
1350            view              => "blog",
1351            condition         => sub {
1352                return $app->config->EnableAddressBook;
1353            },
1354        },
1355
1356        'design:template' => {
1357            label         => "Templates",
1358            mode          => 'list',
1359            args          => { _type => 'template' },
1360            order         => 100,
1361            permission    => 'edit_templates',
1362            system_permission    => 'edit_templates',
1363        },
1364
1365        'prefs:general' => {
1366            label      => "General",
1367            order      => 100,
1368            mode       => "cfg_system",
1369            view       => "system",
1370            permission => "administer",
1371        },
1372        'prefs:user' => {
1373            label      => "User",
1374            order      => 200,
1375            mode       => "cfg_system_users",
1376            view       => "system",
1377            permission => "administer",
1378        },
1379        'prefs:feedback' => {
1380            label      => "Feedback",
1381            order      => 300,
1382            mode       => "cfg_system_feedback",
1383            view       => "system",
1384            permission => "administer",
1385        },
1386        'prefs:settings' => {
1387            label             => "General",
1388            mode              => 'cfg_prefs',
1389            order             => 100,
1390            permission        => 'administer_blog,edit_config,set_publish_paths',
1391            system_permission => 'administer',
1392            view              => "blog",
1393        },
1394        'prefs:publishing' => {
1395            label             => "Publishing",
1396            mode              => 'cfg_archives',
1397            order             => 110,
1398            permission        => 'administer_blog,edit_config,set_publish_paths',
1399            system_permission => 'administer',
1400            view              => "blog",
1401        },
1402        'prefs:entry' => {
1403            label             => "Entry",
1404            mode              => 'cfg_entry',
1405            order             => 120,
1406            permission        => 'administer_blog,edit_config,set_publish_paths',
1407            system_permission => 'administer',
1408            view              => "blog",
1409        },
1410        'prefs:comment' => {
1411            label             => "Comment",
1412            mode              => 'cfg_comments',
1413            order             => 130,
1414            permission        => 'administer_blog,edit_config,set_publish_paths',
1415            system_permission => 'administer',
1416            view              => "blog",
1417        },
1418        'prefs:trackback' => {
1419            label             => "TrackBack",
1420            mode              => 'cfg_trackbacks',
1421            order             => 140,
1422            permission        => 'administer_blog,edit_config,set_publish_paths',
1423            system_permission => 'administer',
1424            view              => "blog",
1425        },
1426        'prefs:registration' => {
1427            label             => "Registration",
1428            mode              => 'cfg_registration',
1429            order             => 150,
1430            permission        => 'administer_blog,edit_config,set_publish_paths',
1431            system_permission => 'administer',
1432            view              => "blog",
1433        },
1434        'prefs:spam' => {
1435            label             => "Spam",
1436            mode              => 'cfg_spam',
1437            order             => 160,
1438            permission        => 'administer_blog,edit_config,set_publish_paths',
1439            system_permission => 'administer',
1440            view              => "blog",
1441        },
1442        'prefs:web_services' => {
1443            label             => "Web Services",
1444            mode              => 'cfg_web_services',
1445            order             => 170,
1446            permission        => 'administer_blog,edit_config,set_publish_paths',
1447            system_permission => 'administer',
1448            view              => "blog",
1449        },
1450
1451        'tools:plugins' => {
1452            label             => "Plugins",
1453            order             => 100,
1454            mode              => "cfg_plugins",
1455            permission        => "administer_blog",
1456            system_permission => "manage_plugins",
1457        },
1458        'tools:activity_log' => {
1459            label             => "Activity Log",
1460            order             => 200,
1461            mode              => "view_log",
1462            permission        => "view_blog_log",
1463            system_permission => "view_log",
1464        },
1465        'tools:import' => {
1466            label             => "Import",
1467            order             => 300,
1468            mode              => "start_import",
1469            view              => "blog",
1470            permission        => "administer_blog",
1471        },                   
1472        'tools:export' => {
1473            label             => "Export",
1474            order             => 400,
1475            mode              => "start_export",
1476            view              => "blog",
1477            permission        => "administer_blog",
1478        },                   
1479        'tools:backup' => {
1480            label             => "Backup",
1481            order             => 500,
1482            mode              => "start_backup",
1483            permission        => "administer_blog",
1484        },
1485        'tools:restore' => {
1486            label             => "Restore",
1487            order             => 600,
1488            mode              => "start_restore",
1489            permission        => "administer_blog",
1490            view              => "system",
1491        },
1492        'tools:system_information' => {
1493            label             => "System Information",
1494            order             => 700,
1495            mode              => "tools",
1496            view              => "system",
1497        },
1498        'tools:ip_info' => {
1499            label             => "IP Banning",
1500            mode              => 'list',
1501            args              => { _type => 'banlist' },
1502            order             => 900,
1503            permission        => 'manage_feedback',
1504            condition         => sub {
1505                $app->config->ShowIPInformation;
1506            },
1507            view              => "blog",
1508        },
1509
1510        # System menu which is actually separate
1511        # in the CMS navigation
1512        'system' => {
1513            label => "System Overview",
1514            mode  => 'dashboard',
1515            order => 10000,
1516        },
1517        'system:user' => {
1518            label             => "Users",
1519            mode              => 'list_authors',
1520            order             => 100,
1521            permission        => 'administer_blog',
1522            system_permission => 'administer',
1523        },
1524        'system:blog' => {
1525            label => "Blogs",
1526            mode  => 'list_blogs',
1527            order => 200,
1528        },
1529        'system:template' => {
1530            label             => "Global Templates",
1531            mode              => 'list_template',
1532            order             => 250,
1533            system_permission => 'edit_templates',
1534        },
1535        'system:settings' => {
1536            label             => "Settings",
1537            mode              => 'cfg_system',
1538            order             => 300,
1539            system_permission => 'administer',
1540        },
1541        'system:plugins' => {
1542            label             => "Plugins",
1543            mode              => 'cfg_plugins',
1544            order             => 400,
1545            system_permission => 'manage_plugins',
1546        },
1547        'system:log' => {
1548            label             => "Activity Log",
1549            mode              => 'view_log',
1550            order             => 500,
1551            system_permission => 'view_log',
1552        },
1553        'system:tools' => {
1554            label             => "Tools",
1555            mode              => 'tools',
1556            order             => 600,
1557            system_permission => 'administer',
1558        },
1559    };
1560}
1561
1562sub init_core_callbacks {
1563    my $app = shift;
1564    my $pkg = 'cms_';
1565    my $pfx = '$Core::MT::CMS::';
1566    $app->_register_core_callbacks(
1567        {
1568            # notification callbacks
1569            $pkg . 'save_permission_filter.notification' => "${pfx}AddressBook::can_save",
1570            $pkg . 'save_filter.notification' => "${pfx}AddressBook::save_filter",
1571            $pkg . 'post_delete.notification' => "${pfx}AddressBook::post_delete",
1572
1573            # banlist callbacks
1574            $pkg . 'save_permission_filter.banlist' => "${pfx}BanList::can_save",
1575            $pkg . 'save_filter.banlist' => "${pfx}BanList::save_filter",
1576
1577            # associations
1578            $pkg . 'delete_permission_filter.association' => "${pfx}User::can_delete_association",
1579
1580            # user callbacks
1581            $pkg . 'edit.author' => "${pfx}User::edit",
1582            $pkg . 'view_permission_filter.author' => "${pfx}User::can_view",
1583            $pkg . 'save_permission_filter.author' => "${pfx}User::can_save",
1584            $pkg . 'delete_permission_filter.author' => "${pfx}User::can_delete",
1585            $pkg . 'save_filter.author' => "${pfx}User::save_filter",
1586            $pkg . 'pre_save.author'    => "${pfx}User::pre_save",
1587            $pkg . 'post_save.author'   => "${pfx}User::post_save",
1588            $pkg . 'post_delete.author' => "${pfx}User::post_delete",
1589
1590            # blog callbacks
1591            $pkg . 'edit.blog' => "${pfx}Blog::edit",
1592            $pkg
1593              . 'view_permission_filter.blog' => "${pfx}Blog::can_view",
1594            $pkg
1595              . 'save_permission_filter.blog' => "${pfx}Blog::can_save",
1596            $pkg
1597              . 'delete_permission_filter.blog' => "${pfx}Blog::can_delete",
1598            $pkg . 'pre_save.blog'    => "${pfx}Blog::pre_save",
1599            $pkg . 'post_save.blog'   => "${pfx}Blog::post_save",
1600            $pkg . 'save_filter.blog' => "${pfx}Blog::save_filter",
1601            $pkg . 'post_delete.blog' => "${pfx}Blog::post_delete",
1602
1603            # folder callbacks
1604            $pkg . 'edit.folder' => "${pfx}Folder::edit",
1605            $pkg . 'view_permission_filter.folder' => "${pfx}Folder::can_view",
1606            $pkg . 'save_permission_filter.folder' => "${pfx}Folder::can_save",
1607            $pkg . 'delete_permission_filter.folder' => "${pfx}Folder::can_delete",
1608            $pkg . 'pre_save.folder'    => "${pfx}Folder::pre_save",
1609            $pkg . 'post_save.folder'   => "${pfx}Folder::post_save",
1610            $pkg . 'save_filter.folder' => "${pfx}Folder::save_filter",
1611            $pkg . 'post_delete.folder' => "${pfx}Folder::post_delete",
1612
1613            # category callbacks
1614            $pkg . 'edit.category' => "${pfx}Category::edit",
1615            $pkg . 'view_permission_filter.category' => "${pfx}Category::can_view",
1616            $pkg . 'save_permission_filter.category' => "${pfx}Category::can_save",
1617            $pkg . 'delete_permission_filter.category' => "${pfx}Category::can_delete",
1618            $pkg . 'pre_save.category'    => "${pfx}Category::pre_save",
1619            $pkg . 'post_save.category'   => "${pfx}Category::post_save",
1620            $pkg . 'save_filter.category' => "${pfx}Category::save_filter",
1621            $pkg . 'post_delete.category' => "${pfx}Category::post_delete",
1622
1623            # comment callbacks
1624            $pkg . 'edit.comment' => "${pfx}Comment::edit",
1625            $pkg . 'view_permission_filter.comment' => "${pfx}Comment::can_view",
1626            $pkg . 'save_permission_filter.comment' => "${pfx}Comment::can_save",
1627            $pkg . 'delete_permission_filter.comment' => "${pfx}Comment::can_delete",
1628            $pkg . 'pre_save.comment'    => "${pfx}Comment::pre_save",
1629            $pkg . 'post_save.comment'   => "${pfx}Comment::post_save",
1630            $pkg . 'post_delete.comment' => "${pfx}Comment::post_delete",
1631
1632            # commenter callbacks
1633            $pkg . 'edit.commenter' => "${pfx}Comment::edit_commenter",
1634            $pkg . 'view_permission_filter.commenter' => "${pfx}Comment::can_view_commenter",
1635            $pkg . 'delete_permission_filter.commenter' => "${pfx}Comment::can_delete_commenter",
1636
1637            # entry callbacks
1638            $pkg . 'edit.entry' => "${pfx}Entry::edit",
1639            $pkg . 'view_permission_filter.entry' => "${pfx}Entry::can_view",
1640            $pkg . 'delete_permission_filter.entry' => "${pfx}Entry::can_delete",
1641            $pkg . 'pre_save.entry'    => "${pfx}Entry::pre_save",
1642            $pkg . 'post_save.entry'   => "${pfx}Entry::post_save",
1643            $pkg . 'post_delete.entry' => "${pfx}Entry::post_delete",
1644
1645            # page callbacks
1646            $pkg . 'edit.page' => "${pfx}Page::edit",
1647            $pkg . 'view_permission_filter.page' => "${pfx}Page::can_view",
1648            $pkg . 'delete_permission_filter.page' => "${pfx}Page::can_delete",
1649            $pkg . 'pre_save.page'    => "${pfx}Page::pre_save",
1650            $pkg . 'post_save.page'   => "${pfx}Page::post_save",
1651            $pkg . 'post_delete.page' => "${pfx}Page::post_delete",
1652
1653            # ping callbacks
1654            $pkg . 'edit.ping' => "${pfx}TrackBack::edit",
1655            $pkg . 'view_permission_filter.ping' => "${pfx}TrackBack::can_view",
1656            $pkg . 'save_permission_filter.ping' => "${pfx}TrackBack::can_save",
1657            $pkg . 'delete_permission_filter.ping' => "${pfx}TrackBack::can_delete",
1658            $pkg . 'pre_save.ping'    => "${pfx}TrackBack::pre_save",
1659            $pkg . 'post_save.ping'   => "${pfx}TrackBack::post_save",
1660            $pkg . 'post_delete.ping' => "${pfx}TrackBack::post_delete",
1661
1662            # template callbacks
1663            $pkg . 'edit.template' => "${pfx}Template::edit",
1664            $pkg . 'view_permission_filter.template' => "${pfx}Template::can_view",
1665            $pkg . 'save_permission_filter.template' => "${pfx}Template::can_save",
1666            $pkg . 'delete_permission_filter.template' => "${pfx}Template::can_delete",
1667            $pkg . 'pre_save.template'    => "${pfx}Template::pre_save",
1668            $pkg . 'post_save.template'   => "${pfx}Template::post_save",
1669            $pkg . 'post_delete.template' => "${pfx}Template::post_delete",
1670
1671            # tags
1672            $pkg . 'delete_permission_filter.tag' => "${pfx}Tag::can_delete",
1673            $pkg . 'post_delete.tag' => "${pfx}Tag::post_delete",
1674
1675            # junk-related callbacks
1676            #'HandleJunk' => \&_builtin_spam_handler,
1677            #'HandleNotJunk' => \&_builtin_spam_unhandler,
1678            $pkg . 'not_junk_test' => "${pfx}Common::not_junk_test",
1679
1680            # assets
1681            $pkg . 'edit.asset' => "${pfx}Asset::edit",
1682            $pkg . 'view_permission_filter.asset' => "${pfx}Asset::can_view",
1683            $pkg . 'delete_permission_filter.asset' => "${pfx}Asset::can_delete",
1684            $pkg . 'pre_save.asset'    => "${pfx}Asset::pre_save",
1685            $pkg . 'post_save.asset'   => "${pfx}Asset::post_save",
1686            $pkg . 'post_delete.asset' => "${pfx}Asset::post_delete",
1687            'template_param.edit_asset' => "${pfx}Asset::template_param_edit",
1688        }
1689    );
1690}
1691
1692sub user_can_admin_commenters {
1693    my $app   = shift;
1694    my $perms = $app->permissions;
1695    $app->user->is_superuser()
1696      || ( $perms
1697        && ( $perms->can_administer_blog || $perms->can_manage_feedback ) );
1698}
1699
1700sub validate_magic {
1701    my $app = shift;
1702    if ( my $feed_token = $app->param('feed_token') ) {
1703        return unless $app->user;
1704        my $pw = $app->user->api_password;
1705        return undef if ( $pw || '' ) eq '';
1706        my $auth_token = perl_sha1_digest_hex( 'feed:' . $pw );
1707        return $feed_token eq $auth_token;
1708    }
1709    else {
1710        return $app->SUPER::validate_magic(@_);
1711    }
1712}
1713
1714sub is_authorized {
1715    my $app     = shift;
1716    my $blog_id = $app->param('blog_id');
1717    $app->permissions(undef);
1718    return 1 unless $blog_id;
1719    return unless my $user = $app->user;
1720    my $perms = $app->permissions( $user->permissions($blog_id) );
1721    $perms
1722      ? 1
1723      : $app->error(
1724        $app->translate("You are not authorized to log in to this blog.") );
1725}
1726
1727sub set_default_tmpl_params {
1728    my $app = shift;
1729    $app->SUPER::set_default_tmpl_params(@_);
1730    my ($tmpl) = @_;
1731    my $param = {};
1732
1733    my $mode      = $app->mode;
1734    my $auth_mode = $app->config('AuthenticationModule');
1735    my ($pref) = split /\s+/, $auth_mode;
1736
1737    # TODO - remove after testing or after new IA is defined
1738    $param->{app_layout_fixed} = 0;
1739    $param->{athena_nav}       = 1;
1740
1741    $param->{"auth_mode_$pref"} = 1;
1742    $param->{mt_news}           = $app->config('NewsURL');
1743    $param->{mt_support}        = $app->config('SupportURL');
1744    my $lang = lc MT->current_language || 'en_us';
1745    $param->{language_id} = ( $lang !~ /en[_-]us/ ) ? $lang : '';
1746    $param->{mode} = $app->mode;
1747
1748    my $blog_id = $app->param('blog_id') || 0;
1749    my $blog;
1750    my $blog_class = $app->model('blog');
1751    $blog ||= $blog_class->load($blog_id) if $blog_id;
1752    if ( my $auth = $app->user ) {
1753        $param->{is_administrator} = $auth->is_superuser;
1754        $param->{can_create_blog}  = $auth->can_create_blog;
1755        $param->{can_view_log} ||= $auth->can_view_log;
1756        $param->{can_manage_plugins}    = $auth->can_manage_plugins;
1757        $param->{can_edit_templates}    = $auth->can_edit_templates;
1758        $param->{can_publish_feedbacks} = $auth->is_superuser;
1759        $param->{can_search_replace}    = $auth->is_superuser;
1760        $param->{has_authors_button}    = $auth->is_superuser;
1761        $param->{author_id}             = $auth->id;
1762        $param->{author_name}           = $auth->name;
1763        $param->{author_display_name}   = $auth->nickname || $auth->name;
1764    }
1765
1766    if ( my $perms = $app->permissions ) {
1767        my $perm_hash = $perms->to_hash;
1768        foreach my $perm_name ( keys %$perm_hash ) {
1769            my $perm_token = $perm_name;
1770            $perm_token =~ s/^permission\.//;
1771            $param->{$perm_token} = $perm_hash->{$perm_name}
1772              if defined $perm_hash->{$perm_name};
1773        }
1774        $param->{can_edit_entries} = $param->{can_create_post}
1775          || $param->{can_edit_all_entries}
1776          || $param->{can_publish_post};
1777        $param->{can_search_replace} = $param->{can_edit_all_posts};
1778        $param->{can_edit_authors}   = $param->{can_administer_blog};
1779        $param->{can_access_assets}  = $param->{can_create_post}
1780          || $param->{can_edit_all_posts}
1781          || $param->{can_edit_assets};
1782        $param->{can_edit_commenters}   = $param->{can_manage_feedback};
1783             $param->{has_manage_label} = $param->{can_edit_templates}
1784          || $param->{can_administer_blog}
1785          || $param->{can_edit_categories}
1786          || $param->{can_edit_config}
1787          || $param->{can_edit_tags}
1788          || $param->{can_set_publish_paths}
1789          || $param->{show_ip_info};
1790        $param->{has_posting_label} = $param->{can_create_post}
1791          || $param->{can_edit_entries}
1792          || $param->{can_access_assets};
1793        $param->{has_community_label} = $param->{can_edit_entries}
1794          || $param->{can_edit_notifications};
1795        $param->{can_publish_feedbacks} = $param->{can_manage_feedback}
1796          || $param->{can_publish_post}
1797          || $param->{can_edit_all_posts};
1798        $param->{can_view_log}     = $param->{can_view_blog_log};
1799        $param->{can_publish_post} = $param->{can_publish_post}
1800          || $param->{can_edit_all_posts};
1801        $param->{show_ip_info} = $app->config->ShowIPInformation
1802          && $param->{can_manage_feedback};
1803    }
1804
1805    my $static_app_url = $app->static_path;
1806    $param->{help_url} = $app->help_url() || $static_app_url . 'docs/';
1807
1808    $param->{show_ip_info} ||= $app->config('ShowIPInformation');
1809    my $type = $app->param('_type') || '';
1810
1811    $param->{ "mode_$mode" . ( $type ? "_$type" : '' ) } = 1;
1812    $param->{return_args} ||= $app->make_return_args;
1813    $tmpl->param($param);
1814}
1815
1816sub build_page {
1817    my $app = shift;
1818    my ( $page, $param ) = @_;
1819    $param ||= {};
1820
1821    my $blog_id = $app->param('blog_id') || 0;
1822    my $blog;
1823    my $blog_class = $app->model('blog');
1824    $blog ||= $blog_class->load($blog_id) if $blog_id;
1825    if ( $blog_id && $page ne 'login.tmpl' ) {
1826        if ($blog) {
1827            $param->{blog_name} = $blog->name;
1828            $param->{blog_id}   = $blog->id;
1829            $param->{blog_url}  = $blog->site_url;
1830            $param->{blog_template_set} = $blog->template_set;
1831        }
1832        else {
1833            $app->error( $app->translate( "No such blog [_1]", $blog_id ) );
1834        }
1835    }
1836    if ( $page ne 'login.tmpl' ) {
1837        if ( ref $app eq 'MT::App::CMS' ) {
1838            $param->{system_overview_nav} = 1
1839              unless $blog_id
1840              || exists $param->{system_overview_nav}
1841              || $param->{no_breadcrumbs};
1842            $param->{quick_search} = 1 unless defined $param->{quick_search};
1843        }
1844    }
1845
1846    $app->build_blog_selector($param);
1847    $app->build_menus($param);
1848    if (!ref($page) || ($page->isa('MT::Template') && !$page->param('page_actions'))) {
1849        $param->{page_actions} ||= $app->page_actions( $app->mode );
1850    }
1851
1852    $app->SUPER::build_page( $page, $param );
1853}
1854
1855sub build_blog_selector {
1856    my $app = shift;
1857    my ($param) = @_;
1858
1859    return if exists $param->{top_blog_loop};
1860
1861    my $blog = $app->blog;
1862    my $blog_id = $blog->id if $blog;
1863    $param->{dynamic_all} = $blog->custom_dynamic_templates eq 'all' if $blog;
1864
1865    my $blog_class = $app->model('blog');
1866    my $auth = $app->user or return;
1867
1868    # Any access to a blog will put it on the top of your
1869    # recently used blogs list (the blog selector)
1870    $app->add_to_favorite_blogs($blog_id) if $blog_id;
1871
1872    my %args;
1873    $args{join} =
1874      MT::Permission->join_on( 'blog_id',
1875          { author_id => $auth->id, permissions => { not => "'comment'" } } );
1876    $args{limit} = 11;    # don't load more than 11
1877    my @blogs = $blog_class->load( undef, \%args );
1878
1879    my @fav_blogs = @{ $auth->favorite_blogs || [] };
1880    @fav_blogs = grep { $_ != $blog_id } @fav_blogs if $blog_id;
1881
1882    # Special case for when a user only has access to a single blog.
1883    if ( (!defined($app->param('blog_id'))) && ( @blogs == 1 ) && ( scalar @fav_blogs <= 1 ) ) {
1884
1885        # User only has visibility to a single blog. Don't
1886        # bother giving them a dashboard link for 'all blogs', or
1887        # to 'select a blog'.
1888        $param->{single_blog_mode} = 1;
1889        my $blog = $blogs[0];
1890        $blog_id = $blog->id;
1891        my $perms = MT::Permission->load(
1892            {
1893                blog_id   => $blog_id,
1894                author_id => $auth->id
1895            }
1896        );
1897        if ( !$app->blog ) {
1898            if ( $app->mode eq 'dashboard' ) {
1899                $app->param( 'blog_id', $blog_id );
1900                $param->{blog_id}   = $blog_id;
1901                $param->{blog_name} = $blog->name;
1902                $app->permissions($perms);
1903                $app->blog($blog);
1904            }
1905            else {
1906                @fav_blogs = ($blog_id);
1907                $blog_id   = undef;
1908            }
1909        }
1910    }
1911    elsif ( @blogs && ( @blogs <= 10 ) ) {
1912
1913        # This user only has visibility to 10 or fewer blogs;
1914        # no need to reference their 'favorite' blogs list.
1915        my @ids = map { $_->id } @blogs;
1916        if ($blog_id) {
1917            @ids = grep { $_ != $blog_id } @ids;
1918        }
1919        @fav_blogs = @ids;
1920        if ( $auth->is_superuser ) {
1921
1922            # Better check to see if there are more than
1923            # 10 blogs in the system; if so, a superuser
1924            # will still want the 'Select a blog...' chooser.
1925            # Otherwise, hide it.
1926            my $all_blog_count = $blog_class->count();
1927            if ( $all_blog_count < 11 ) {
1928                $param->{selector_hide_chooser} = 1;
1929            }
1930        }
1931        else {
1932
1933            # This user is not a superuser and only has
1934            # 10 blogs, so they don't need a 'select blog'
1935            # link...
1936            $param->{selector_hide_chooser} = 1;
1937        }
1938    }
1939    $param->{selector_hide_chooser} ||= 0;
1940
1941    # Logic for populating the blog selector control
1942    #   * Pull list of 'favorite blogs' from user record
1943    #   * Load all of those blogs so we can display them
1944    #   * Exclude the current blog from the favorite list so it isn't
1945    #     shown twice.
1946    @blogs = $blog_class->load( { id => \@fav_blogs } ) if @fav_blogs;
1947    my %blogs = map { $_->id => $_ } @blogs;
1948    @blogs = ();
1949    foreach my $id (@fav_blogs) {
1950        push @blogs, $blogs{$id} if $blogs{$id};
1951    }
1952
1953    my @data;
1954    if (@blogs) {
1955        my @perms = grep { !$_->is_empty } MT::Permission->load(
1956            {
1957                author_id => $auth->id,
1958                blog_id   => \@fav_blogs,
1959            }
1960        );
1961        my %perms = map { $_->blog_id => $_ } @perms;
1962        for my $blog (@blogs) {
1963            my $perm = $perms{ $blog->id };
1964            next
1965              unless $auth->is_superuser || ( $perm && !$perm->is_empty );
1966            push @data,
1967              {
1968                top_blog_id   => $blog->id,
1969                top_blog_name => $blog->name
1970              };
1971            $data[-1]{top_blog_selected} = 1
1972              if $blog_id && ( $blog->id == $blog_id );
1973        }
1974    }
1975    $param->{top_blog_loop} = \@data;
1976    if ( !$app->user->can_create_blog
1977        && ( $param->{single_blog_mode} || scalar(@data) <= 1 ) )
1978    {
1979        $param->{no_submenu} = 1;
1980    }
1981}
1982
1983sub build_menus {
1984    my $app = shift;
1985    my ($param) = @_;
1986    return if exists $param->{top_nav_loop};
1987
1988    my $menus   = $app->registry('menus');
1989    my $blog    = $app->blog;
1990    my $blog_id = $blog->id if $blog;
1991
1992    my @top_ids = grep { !/:/ } keys %$menus;
1993    my @top;
1994    my @sys;
1995    my $user = $app->user
1996      or return;
1997    my $perms = $app->permissions || $user->permissions;
1998    my $view = $blog_id ? "blog" : "system";
1999
2000    my $hide_disabled_options = $app->config('HideDisabledMenus') || 0;
2001
2002    my $admin =
2003      $user->is_superuser();    # || ($perms && $perms->has('administer_blog'));
2004
2005    foreach my $id (@top_ids) {
2006        my $menu = $menus->{$id};
2007        next if $menu->{view} && $menu->{view} ne $view;
2008        if (my $cond = $menu->{condition}) {
2009            if (!ref($cond)) {
2010                $cond = $menu->{condition} = $app->handler_to_coderef($cond);
2011            }
2012            next unless $cond->();
2013        }
2014
2015        $menu->{allowed} = 1;
2016        $menu->{'id'} = $id;
2017
2018        my @sub_ids = grep { m/^$id:/ } keys %$menus;
2019        my @sub;
2020        foreach my $sub_id (@sub_ids) {
2021            my $sub = $menus->{$sub_id};
2022            next
2023              if $sub->{view}
2024              && ( $sub->{view} ne $view );
2025            $sub->{'id'} = $sub_id;
2026            if (my $cond = $sub->{condition}) {
2027                if (!ref($cond)) {
2028                    $cond = $sub->{condition} = $app->handler_to_coderef($cond);
2029                }
2030                next unless $cond->();
2031            }
2032            push @sub, $sub;
2033        }
2034
2035        if (
2036            my $p =
2037              $blog_id
2038            ? $menu->{permission}
2039            || $menu->{system_permission}
2040            : $menu->{system_permission}
2041            || $menu->{permission}
2042          )
2043        {
2044            my $allowed = 0;
2045            my @p = split /,/, $p;
2046            foreach my $p (@p) {
2047                my $perm = 'can_' . $p;
2048                $allowed = 1, last if ( $perms && $perms->$perm() ) || $admin;
2049            }
2050            $menu->{allowed} = $allowed;
2051        }
2052        elsif ( !$perms && !$blog_id ) {
2053            $menu->{allowed} = 0 if $menu->{system_permission} && !$admin;
2054        }
2055
2056        next if $hide_disabled_options && (! $menu->{allowed});
2057
2058        if (@sub) {
2059            if ( $id eq 'system' ) {
2060                push @sys, $menu;
2061            }
2062            else {
2063                push @top, $menu;
2064            }
2065
2066            my $has_sub = 0;
2067            foreach my $sub (@sub) {
2068                my $sys_only = $sub->{id} =~ m/^system:/;
2069                $sub->{allowed} = 1;
2070                if ( $sub->{mode} ) {
2071                    $sub->{link} = $app->uri(
2072                        mode => $sub->{mode},
2073                        args => {
2074                            %{ $sub->{args} || {} },
2075                            (
2076                                $blog_id
2077                                  && !$sys_only ? ( blog_id => $blog_id ) : ()
2078                            )
2079                        }
2080                    );
2081                }
2082                $sub->{allowed} = 0
2083                  if $sub->{view} && ( $sub->{view} ne $view );
2084                if ( $sub->{allowed} ) {
2085                    my $sub_perms =
2086                      ( $sys_only || ( $sub->{view} || '' ) eq 'system' )
2087                      ? $app->user->permissions(0)
2088                      : $perms;
2089                    if (
2090                        $sub_perms
2091                        && (
2092                            my $p =
2093                              $blog_id
2094                            ? $sub->{permission}
2095                            || $sub->{system_permission}
2096                            : $sub->{system_permission}
2097                            || $sub->{permission}
2098                        )
2099                      )
2100                    {
2101                        my $allowed = 0;
2102                        my @p = split /,/, $p;
2103                        foreach my $p (@p) {
2104                            my $perm = 'can_' . $p;
2105                            $allowed = 1, last
2106                              if ( $sub_perms && $sub_perms->$perm() )
2107                              || $admin;
2108                        }
2109                        $sub->{allowed} = $allowed;
2110                    }
2111                    elsif ( !$sub_perms && !$blog_id ) {
2112                        $sub->{allowed} = 0
2113                          if ( $sub->{system_permission} || $sub->{permission} )
2114                          && !$admin;
2115                    }
2116                    $has_sub = 1 if $sub->{allowed};
2117                }
2118            }
2119            if ( $menu->{mode} ) {
2120                my $sys_only = 1 if $menu->{id} eq 'system';
2121                $menu->{link} = $app->uri(
2122                    mode => $menu->{mode},
2123                    args => {
2124                        %{ $menu->{args} || {} },
2125                        (
2126                            $blog_id
2127                              && !$sys_only ? ( blog_id => $blog_id ) : ()
2128                        )
2129                    }
2130                );
2131            }
2132            @sub = sort { $a->{order} <=> $b->{order} } @sub;
2133
2134            @sub = grep { $_->{allowed} } @sub if $hide_disabled_options;
2135            if ( !$menu->{mode} ) {
2136                $menu->{link} = $sub[0]->{link};
2137            }
2138            $menu->{sub_nav_loop} = \@sub;
2139
2140            if ( $menu->{allowed} ) {
2141                $menu->{allowed} = 0 unless $has_sub;
2142            }
2143        }
2144    }
2145
2146    @top = grep { $_->{allowed} } @top if $hide_disabled_options;
2147    @sys = grep { $_->{allowed} } @sys if $hide_disabled_options;
2148    @top = sort { $a->{order} <=> $b->{order} } @top;
2149    $param->{top_nav_loop} = \@top;
2150    $param->{sys_nav_loop} = \@sys;
2151}
2152
2153sub return_to_dashboard {
2154    my $app = shift;
2155    my (%param) = @_;
2156    $param{redirect} = 1 unless %param;
2157    my $blog_id = $app->param('blog_id');
2158    $param{blog_id} = $blog_id if $blog_id;
2159    return $app->redirect( $app->uri( mode => 'dashboard', args => \%param ) );
2160}
2161
2162sub list_pref {
2163    my $app      = shift;
2164    my ($list)   = @_;
2165    my $updating = $app->mode eq 'update_list_prefs';
2166    unless ($updating) {
2167        my $pref = $app->request("list_pref_$list");
2168        return $pref if defined $pref;
2169    }
2170
2171    my $cookie = $app->cookie_val('mt_list_pref') || '';
2172    my $mode = $app->mode;
2173
2174    # defaults:
2175    my $d = $app->config->DefaultListPrefs || {};
2176    my %default = (
2177        Rows       => 25,
2178        Format     => 'Compact',
2179        SortOrder  => 'Ascend',      # Ascend|Descend
2180        Button     => 'Above',       # Above|Below|Both
2181        DateFormat => 'Relative',    # Relative|Full
2182    );
2183    if ( ( $list eq 'comment' ) || ( $list eq 'ping' ) ) {
2184        $default{Format} = 'expanded';
2185    }
2186    $default{$_} = lc( $d->{$_} ) for keys %$d;
2187    my $list_pref;
2188    if ( $list eq 'main_menu' ) {
2189        $list_pref = {
2190            'sort' => 'name',
2191            order  => $default{SortOrder} || 'ascend',
2192            view   => $default{Format} || 'compact',
2193            dates  => $default{DateFormat} || 'relative',
2194        };
2195    }
2196    else {
2197        $list_pref = {
2198            rows  => $default{Rows}       || 25,
2199            view  => $default{Format}     || 'compact',
2200            bar   => $default{Button}     || 'above',
2201            dates => $default{DateFormat} || 'relative',
2202        };
2203    }
2204    my @list_prefs = split /;/, $cookie;
2205    my $new_cookie = '';
2206    foreach my $pref (@list_prefs) {
2207        my ( $name, $prefs ) = $pref =~ m/^(\w+):(.*)$/;
2208        next unless $name && $prefs;
2209        if ( $name eq $list ) {
2210            my @prefs = split /,/, $prefs;
2211            foreach (@prefs) {
2212                my ( $k, $v ) = split /=/;
2213                $list_pref->{$k} = $v if exists $list_pref->{$k};
2214            }
2215        }
2216        else {
2217            $new_cookie .= ( $new_cookie ne '' ? ';' : '' ) . $pref;
2218        }
2219    }
2220
2221    if ($updating) {
2222        my $updated = 0;
2223        if ( my $limit = $app->param('limit') ) {
2224            $limit = 20 if $limit eq 'none';
2225            $list_pref->{rows} = $limit > 0 ? $limit : 20;
2226            $updated = 1;
2227        }
2228        if ( my $view = $app->param('verbosity') ) {
2229            if ( $view =~ m!^compact|expanded$! ) {
2230                $list_pref->{view} = $view;
2231                $updated = 1;
2232            }
2233        }
2234        if ( my $bar = $app->param('actions') ) {
2235            if ( $bar =~ m!^above|below|both$! ) {
2236                $list_pref->{bar} = $bar;
2237                $updated = 1;
2238            }
2239        }
2240        if ( my $ord = $app->param('order') ) {
2241            if ( $ord =~ m!^ascend|descend$! ) {
2242                $list_pref->{order} = $ord;
2243                $updated = 1;
2244            }
2245        }
2246        if ( my $sort = $app->param('sort') ) {
2247            if ( $sort =~ m!^name|created|updated$! ) {
2248                $list_pref->{'sort'} = $sort;
2249                $updated = 1;
2250            }
2251        }
2252        if ( my $dates = $app->param('dates') ) {
2253            if ( $dates =~ m!^relative|full$! ) {
2254                $list_pref->{'dates'} = $dates;
2255                $updated = 1;
2256            }
2257        }
2258
2259        if ($updated) {
2260            my @list_prefs;
2261            foreach ( keys %$list_pref ) {
2262                push @list_prefs, $_ . '=' . $list_pref->{$_};
2263            }
2264            my $prefs = join ',', @list_prefs;
2265            $new_cookie .=
2266              ( $new_cookie ne '' ? ';' : '' ) . $list . ':' . $prefs;
2267            $app->bake_cookie(
2268                -name    => 'mt_list_pref',
2269                -value   => $new_cookie,
2270                -expires => '+10y'
2271            );
2272        }
2273    }
2274
2275    if ( $list_pref->{rows} ) {
2276        $list_pref->{ "limit_" . $list_pref->{rows} } = $list_pref->{rows};
2277    }
2278    if ( $list_pref->{view} ) {
2279        $list_pref->{ "view_" . $list_pref->{view} } = 1;
2280    }
2281    if ( $list_pref->{dates} ) {
2282        $list_pref->{ "dates_" . $list_pref->{dates} } = 1;
2283    }
2284    if ( $list_pref->{bar} ) {
2285        if ( lc $list_pref->{bar} eq 'both' ) {
2286            $list_pref->{"position_actions_both"}   = 1;
2287            $list_pref->{"position_actions_top"}    = 1;
2288            $list_pref->{"position_actions_bottom"} = 1;
2289        }
2290        elsif ( lc $list_pref->{bar} eq 'below' ) {
2291            $list_pref->{"position_actions_bottom"} = 1;
2292        }
2293        elsif ( lc $list_pref->{bar} eq 'above' ) {
2294            $list_pref->{"position_actions_top"} = 1;
2295        }
2296    }
2297    if ( $list_pref->{'sort'} ) {
2298        $list_pref->{ 'sort_' . $list_pref->{'sort'} } = 1;
2299    }
2300    if ( $list_pref->{'order'} ) {
2301        $list_pref->{ 'order_' . $list_pref->{'order'} } = 1;
2302    }
2303    $app->request( "list_pref_$list", $list_pref );
2304}
2305
2306sub make_feed_link {
2307    my $app = shift;
2308    my ( $view, $params ) = @_;
2309    my $user = $app->user;
2310    return if ( $user->api_password || '' ) eq '';
2311
2312    $params ||= {};
2313    $params->{view}     = $view;
2314    $params->{username} = $user->name;
2315    $params->{token}    = perl_sha1_digest_hex( 'feed:' . $user->api_password );
2316    $app->base
2317      . $app->mt_path
2318      . $app->config('ActivityFeedScript')
2319      . $app->uri_params( args => $params );
2320}
2321
2322sub show_error {
2323    my $app  = shift;
2324    my ($param) = @_;
2325
2326    # handle legacy scalar error string signature
2327    $param = { error => $param } unless ref($param) eq 'HASH';
2328
2329    my $mode = $app->mode;
2330    if ( $mode eq 'rebuild' ) {
2331
2332        my $r = MT::Request->instance;
2333        if (my $tmpl = $r->cache('build_template')) {
2334            # this is the template that likely caused the rebuild error
2335            push @{ $param->{button_loop} ||= [] }, {
2336                link => $app->uri( mode => 'view', args => { blog_id => $tmpl->blog_id, '_type' => 'template', id => $tmpl->id }),
2337                label => $app->translate("Edit Template"),
2338            };
2339        }
2340
2341        my $blog_id = $app->param('blog_id');
2342        my $url     = $app->uri(
2343            mode => 'rebuild_confirm',
2344            args => { blog_id => $blog_id }
2345        );
2346        $param->{goback} ||= qq{window.location='$url'};
2347        $param->{value}  ||= $app->translate('Go Back');
2348    }
2349
2350    return $app->SUPER::show_error($param);
2351}
2352
2353sub load_default_entry_prefs {
2354    my $app = shift;
2355    my $prefs;
2356    require MT::Permission;
2357    my $blog_id;
2358    $blog_id = $app->blog->id if $app->blog;
2359    my $perm = MT::Permission->load( { blog_id => $blog_id, author_id => 0 } );
2360    my %default = %{ $app->config->DefaultEntryPrefs };
2361    if ( $perm && $perm->entry_prefs ) {
2362        $prefs = $perm->entry_prefs;
2363    }
2364    else {
2365        if ( lc( $default{type} ) eq 'custom' ) {
2366            my %map = (
2367                Category   => 'category',
2368                Excerpt    => 'excerpt',
2369                Keywords   => 'keywords',
2370                Tags       => 'tags',
2371                Publishing => 'publishing',
2372                Feedback   => 'feedback',
2373            );
2374            my @p;
2375            foreach my $p ( keys %map ) {
2376                push @p, $map{$p} . ':' . ( $default{$p} || $default{ lc $p } )
2377                  if ( $default{$p} || $default{ lc $p } );
2378            }
2379            $prefs = join ',', @p;
2380            $prefs ||= 'Custom';
2381        }
2382        elsif ( lc( $default{type} ) ne 'default' ) {
2383            $prefs = 'Advanced';
2384        }
2385        $default{button} = 'Bottom' if lc( $default{button} ) eq 'below';
2386        $default{button} = 'Top'    if lc( $default{button} ) eq 'above';
2387        $prefs .= '|' . $default{button} if $prefs;
2388    }
2389    $prefs ||= 'Default|Bottom';
2390    return $prefs;
2391}
2392
2393sub load_template_prefs {
2394    my $app = shift;
2395    my ($prefs) = @_;
2396    my %param;
2397
2398    if ( !$prefs ) {
2399        $prefs = '';
2400    }
2401    my @p = split /,/, $prefs;
2402    for my $p (@p) {
2403        if ( $p =~ m/^(.+?):(\d+)$/ ) {
2404            $param{ 'disp_prefs_height_' . $1 } = $2;
2405        }
2406    }
2407    \%param;
2408}
2409
2410sub _parse_entry_prefs {
2411    my $app = shift;
2412    my ( $prefs, $param ) = @_;
2413
2414    my @p = split /,/, $prefs;
2415    for my $p (@p) {
2416        if ( $p =~ m/^(.+?):(\d+)$/ ) {
2417            my ( $name, $num ) = ( $1, $2 );
2418            if ($num) {
2419                $param->{ 'disp_prefs_height_' . $name } = $num;
2420            }
2421            $param->{ 'disp_prefs_show_' . $name } = 1;
2422        }
2423        else {
2424            $p = 'Default' if lc($p) eq 'basic';
2425            if ( ( lc($p) eq 'advanced' ) || ( lc($p) eq 'default' ) ) {
2426                $param->{ 'disp_prefs_' . $p } = 1;
2427                foreach my $def (
2428                    qw(title body category tags keywords feedback publishing ))
2429                {
2430                    $param->{ 'disp_prefs_show_' . $def } = 1;
2431                }
2432                if ( lc($p) eq 'advanced' ) {
2433                    foreach my $def (qw(excerpt feedback)) {
2434                        $param->{ 'disp_prefs_show_' . $def } = 1;
2435                    }
2436                }
2437            }
2438            else {
2439                $param->{ 'disp_prefs_show_' . $p } = 1;
2440            }
2441        }
2442    }
2443}
2444
2445sub load_entry_prefs {
2446    my $app = shift;
2447    my ($prefs) = @_;
2448    my %param;
2449    my $pos;
2450    my $is_from_db = 1;
2451
2452    # defaults:
2453    if ( !$prefs ) {
2454        $prefs = $app->load_default_entry_prefs;
2455        ( $prefs, $pos ) = split /\|/, $prefs;
2456        $is_from_db = 0;
2457        $app->_parse_entry_prefs( $prefs, \%param );
2458        my @fields;
2459        foreach ( keys %param ) {
2460            if (m/^disp_prefs_show_(.+)/) {
2461                push @fields, { name => $1 };
2462            }
2463        }
2464        $param{disp_prefs_default_fields} = \@fields;
2465    }
2466    else {
2467        ( $prefs, $pos ) = split /\|/, $prefs;
2468    }
2469    $app->_parse_entry_prefs( $prefs, \%param );
2470    if ($is_from_db) {
2471        my $default_prefs = $app->load_default_entry_prefs;
2472        ( $default_prefs, my ($default_pos) ) = split /\|/, $default_prefs;
2473        $pos ||= $default_pos;
2474        $app->_parse_entry_prefs( $default_prefs, \my %def_param );
2475        my @fields;
2476        foreach ( keys %def_param ) {
2477            if (m/^disp_prefs_show_(.+)/) {
2478                push @fields, { name => $1 };
2479            }
2480        }
2481        if ( $prefs eq 'Default' ) {
2482            foreach my $p ( keys %param ) {
2483                delete $param{$p} if $p =~ m/^disp_prefs_show_/;
2484            }
2485        }
2486        if ( $param{disp_prefs_Default} ) {
2487
2488            # apply default settings
2489            %param = ( %def_param, %param );
2490        }
2491        $param{disp_prefs_default_fields} = \@fields;
2492    }
2493    $pos ||= 'Bottom';
2494    if (   !exists $param{'disp_prefs_Default'}
2495        && !exists $param{'disp_prefs_Advanced'} )
2496    {
2497        $param{'disp_prefs_Custom'} = 1;
2498    }
2499    if ( lc $pos eq 'both' ) {
2500        $param{'position_actions_top'}    = 1;
2501        $param{'position_actions_bottom'} = 1;
2502        $param{'position_actions_both'}   = 1;
2503    }
2504    else {
2505        $param{ 'position_actions_' . $pos } = 1;
2506    }
2507    \%param;
2508}
2509
2510sub _convert_word_chars {
2511    my ( $app, $s ) = @_;
2512    return '' unless $s;
2513    return $s if 'utf-8' ne lc( $app->charset );
2514
2515    my $blog = $app->blog;
2516    my $smart_replace = $blog ? $blog->smart_replace
2517        : MT->config->NwcSmartReplace;
2518    return $s if $smart_replace == 2;
2519
2520    if ($smart_replace) {
2521
2522        # html character entity replacements
2523        $s =~ s/\342\200\231/&#8217;/g;
2524        $s =~ s/\342\200\230/&#8216;/g;
2525        $s =~ s/\342\200\246/&#133;/g;
2526        $s =~ s/\342\200\223/-/g;
2527        $s =~ s/\342\200\224/&#8212;/g;
2528        $s =~ s/\342\200\234/&#8220;/g;
2529        $s =~ s/\342\200\235/&#8221;/g;
2530    }
2531    else {
2532
2533        # ascii equivalent replacements
2534        $s =~ s/\342\200[\230\231]/'/g;
2535        $s =~ s/\342\200\246/.../g;
2536        $s =~ s/\342\200\223/-/g;
2537        $s =~ s/\342\200\224/--/g;
2538        $s =~ s/\342\200[\234\235]/"/g;
2539    }
2540
2541    # While we're fixing Word, remove processing instructions with
2542    # colons, as they can break PHP.
2543    $s =~ s{ <\? xml:namespace [^>]*> }{}ximsg;
2544
2545    $s;
2546}
2547
2548sub _translate_naughty_words {
2549    my $app = shift;
2550    my ($entry) = @_;
2551    my $blog = $app->blog;
2552    return if $blog->smart_replace == 2;
2553
2554    my $fields = $blog->smart_replace_fields;
2555    return unless $fields;
2556
2557    my @fields