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

Revision 1852, 122.8 kB (checked in by bsmith, 20 months ago)

bugzid:76495 - Move Publishing Profiles to modal triggered by action on template listing

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