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

Revision 1786, 122.1 kB (checked in by auno, 20 months ago)

Added publish button and pulldown option on template listing. BugzID:75100

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