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

Revision 1858, 122.8 kB (checked in by bchoate, 20 months ago)

Fixed filter for entries with comments received in last 7 days.

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