root/branches/release-36/lib/MT/App/CMS.pm @ 2121

Revision 2121, 113.4 kB (checked in by bchoate, 19 months ago)

Prefix preview filenames with 'mt-preview-' so any left-behind preview files are easier to find.

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