root/branches/release-39/lib/MT/App/CMS.pm @ 2425

Revision 2425, 113.6 kB (checked in by fumiakiy, 18 months ago)

Merged the latest checkin to r38 to r39. svn merge -r2421:2424 http://code.sixapart.com/svn/movabletype/branches/release-38 .

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