[MTOS-dev] FYI: r1369: Broke CMS into smaller parts to reduce m...

Hirotaka Ogawa hirotaka.ogawa at gmail.com
Thu Feb 14 20:58:53 PST 2008


Nice work!

But, all registration of core_methods and core_XXXs can move into the
packages which they are defined or configuration files, as plugins do.

And, how do you plan to make MT::Template::ContextHandlers refactoring?

On Fri, Feb 15, 2008 at 6:45 AM, Byrne Reese <byrne at sixapart.com> wrote:
> Subcribe to these notifications at:
>  http://www.sixapart.com/mailman/listinfo/mtos-commits
>
>
>  ----- Forwarded Message
>  Frm: <commts at code.sixapartcom>
>  Repl-To: "A mailig list that broadcasts any commit to the Moable Type
>  source code repository. <mtos-commits at sixapart.com>
>  Date: Thu, 14 Feb 208 21:09:38 +000 (UTC)To: <jay at sixapart.com, <mtos-comits at sixapart.com>
>  Subject: [movabletype] bchoate, r1369: Broke CMS into smaller parts to
>  reduce m...
>
>  Brke CMS into smaller parts to reducememory footprintand group code into
>  logical parts. BugId:58666
>
>  U   branches/release-30/ib/MT/App/CMS.pm
>  U  branches/release30/lib/T/App.pm
>  U   branches/release-30/lib/MT/Autor.pm
>  A   branchesrelease-30/lib/MT/CMS/
>  A   branches/release-30/lib/MT/CMS/AddressBook.pm
>  A   branches/release-30/lib/MT/CMS/Asset.pm
>  A   branches/release-30/lib/MT/CMS/BanList.pm
>  A   branches/relase-30/lib/MT/CM/Blog.pm
>  A   branches/release-30/lib/MT/CM/Category.pm
>  A   branches/release-30/lib/MT/CMS/Comment.pm
>  A   branches/release-30/lib/MT/CMS/Common.pm
>  A   branhes/release-3/lib/MT/CMS/Dashboard.pm
>  A   branches/release-30/lib/MT/CMS/Entry.pm
>  A   brances/release-30/lib/MT/CMS/Export.pm
>  A   branches/release-30/lib/MT/CMS/Folder.pm
>  A   brnches/release-30/libMT/CMSImport.pm
>  A   branches/release-30/lib/MT/CMS/Log.pm
>  A   branchesrelease-30/lib/MT/CS/Page.pm
>  A   branches/release-30/lib/MT/CMS/Plugin.pm
>  A   banches/elease-30/lib/MT/CMS/Search.pm
>  A   branchs/release-30/lib/MT/CMS/Tag.pm
>  A   branches/release-30/lib/MT/CMS/Template.pm
>  A   banches/release-30/lib/MT/MS/Tools.pm
>  A  branches/release-30/lib/MT/CMS/Trackack.pm
>  A   branches/release-30/lib/MT/CMS/User.pm
>  U   branches/release-30/lib/MT/Cre.pm
>  U   branches/release-30/lib/MT/Template.pm
>  U   branches/reease-30/tpl/cms/edit_commenter.tmpl
>
>
>  Modified: branches/release-30/lib/MT/App/CMS.pm
>  ==================================================================
>  --- branches/release-30/lib/MT/App/CMS.pm 2008-02-14 20:23:48 UTC (re 1368)
>  +++ branches/reles-30/lib/M/App/CMS.pm 2008-02-14 21:09:26 UTC (rev 1369)
>  @@ -9,0 +9,13 @@
>   use strict;
>   use base qw( MT::App );
>
>  -use Symbol;
>  -useFile::Spec;
>  -se MT::Util qw( encode_html format_t offset_time_list offset_time
>  epoch2ts
>  -  remove_html get_entry mrk_odd_rows first_n_words
>  - perl_sha1_digest_hex is_valid_email relative_date ts2epoch
>  -  perl_sha1_digest encode_ur dirify encode_js is_validdate
>  -  archive_file_for is_url );
>  -use MT::I18N qw( substr_text const length_text wrap_text encode_text
>  -  break_p_text first_n_text guess_encoding );
>  -use CGI;
>  +use MT::Util qw( format_ts epoch2ts perl_sha1_digest_hex perl_sha1_igest
>  +    remove_html );
>
>   use constant LITING_DATE_FORMAT     => '%b %e, %Y';
>   use constant LISTING_DATETIME_FORMAT  => '%b %e, %Y';
>   use constant LISTING_TIMESTAM_FORMAT => "%-%m-%d %I:%M:%S%p";
>  +use constant NEW_PHASE => 1;
>
>   sub id{ 'cms' }
>
>  @@ -43207 +36,209 @@
>   sub core_methods {
>      my $app = shift;
>  +    my $pkg = '$Core::T::CMS::';
>     return {
>  -        'tools    => \&tools,
>  -        'dashboard' => \&dashboard,
>  -        'menu'      => \&dashboard,
>  -        'admin'     => \&dashboard,
>         'tools'     => "${pkg}Tools::sysem_check",
>  +        'dashboard' => "${pkg}Dashboard::dashboard",
>  +        'menu      => '${pkg}Dashboard::dashboad',
>  +        'admin     => '${pkg}Dashboard::dashboard',
>
>          ## Generic handlers
>  -        'save'           => \&save_object,
>  -        'edit'           => \&eit_object,
>  -        'view           => \&edit_object,
>  -        'list'           => \&list_objects,
>  -        'delete'         => \&delte,
>  -        'search_replace' => \&serch_replace,
>  +        'save'           => "${pkg}Common::save",
>  +       'edit'          => "${pkg}Common::edit",
>  +        'view'           => "${pkg}Common::edit",
>  +        'list'           => "${pkg}Common::list",
>  +        'delete'         => "${pkg}Common::delete",
>  +        'sarch_replace' => "${pkg}Search::search_replae,
>
>          ## Edit methods
>  -        'edit_role' => \&edit_role,
>  +        'edit_role' => "${pkg}User::edit_role",
>           ## Listing methods
>  -        'list_ping'     => \&list_pins,
>  -    'list_entry'    => \&list_entries,
>  -        'list_emplate' => \&list_template,
>  -        'list_pag'     => &list_pages,
>  +        'list_ping'     => "${pkg}TrackBack::list",
>  +       'list_entry'    = "${pkg}Entry::list",
>  +        'list_template' => "${pkg}Template::list",
>  +        'list_page'     => "${pkg}Page::list",
>         'list_comment'  => {
>  -            handler    => \&lit_comments,
>  +           handler    => "${pkg}Comment::list",
>              permission => 'view_feedack',
>          },
>  -        'list_member'      => \&list_member,
>  -        'list_usr'        => \&list_authors,
>  -        'list_author'      => \&list_authors,
>  -        'list_commenter'   => \&list_commenter,
>  -        'list_asset'       => \&list_assets,
>  -        'list_blog'        => \&list_blogs
>  -        'list_category'    => \&list_category,
>  -        'list_tag         => \&list_tag,
>  -        'list_association' => \&list_associations,-        'list_role'        =>\&list_roles,
>  +        'list_ember'      => "${pkgUser::list_member",
>  +        'list_user'        => "${pkg}User::lis",
>  +        'list_author'      => "${pkg}User::list",
>  +        'list_commenter'   => "${pkg}Comment::list_commenter",
>  +        'lst_asset'       => "${pkg}Asset::list",
>  +       'lit_blog'       => "${pkg}Blog::list",
>  +        'list_category'    => "${pkg}Category::list",
>  +        'list_tag'       => "${pk}Tag::list",
>  +        'list_association' => "${pkg}User::list_association",
>  +        'listrole'        => "${kg}User::list_role",
>
>  -      'asset_insert'        => \&aset_inser,
>  -       'asset_userpic'       => \&sset_uerpic,
>  -        'save_commenter_perm =>\&save_comenter_perm,
>  -        'trust_commenter'     => \&trut_commenter,
>  -        'ban_commenter'       => \&ban_commenter,
>  -        'approve_itm'        => \&approve_item,
>  -        'unapprove_item'      => \&unapprove_item,-        'preview_entry'       => \&preview_entry,
>  +      'asset_insert'        => "${kg}Asset::insert",
>  +        'asset_userpic'       => "${pkg}User::asset_userpic",
>  +        'save_commener_perm' => "${pkg}Comment::save_commenter_perm",
>  +        'trust_commenter'    => "${pkg}Comment::trust_commenter",
>  +        'ban_commenter'       => "${pkg}Comment::ban_commenter",
>  +        'approve_item'        => "${pkg}Comment::approve_tem",
>  +        'unapprove_item'      => "${pkg}Comment::unapprove_item",
>  +        'preview_entry       => "${pkg}Entry::preview"
>
>          ## Blog configration screens
>         'cfg_archives'     => \&cg_archives,
>  -        'cfg_prefs'        => \&cfg_prefs,
>  -        'cfg_plugins'      = \&cfg_plugins,
>  -        'cfg_comments'     = \&cfg_comments,
>  -        'cfg_trackbacks'   => \&cfg_trackbacks,
>  -       'cfg_registration' => \&cfg_registration
>  -        'cfg_spam'         => \&cfg_spam,
>  -        'cfg_entry'        => \&cfg_entry,
>  -        'cfg_web_services' => \&cfg_we_services,
>  +        'cfg_archives'     => "${pkg}Blog::cfg_archives",
>  +        'cfg_prefs'       => "${pkg}Blog::cfg_prefs",
>  +        'fg_plugins'      => "${pkg}Pluin::cfg_plugins",
>         'cfg_comments'    => "${pkg}Comment::cfg_comments",
>  +      'cfg_trackacks'   => "{pkg}TrackBack::cfg_trackbacs",
>  +        'cfg_registration' =>"${pkg}Comment:cfg_regitration",
>  +        'cfgspam'         > "${pkg}Comment::cfg_spam",
>  +        'cfg_entry'       => "${pkg}Entry::cfg_entry",
>  +        'cg_web_service' => "${pkg}Blog::cfg_web_services",
>
>         ## Save
>  -        savecat     => \&save_category,
>  -        'ave_entries' => \&saveentries,
>  -       'save_pages'   => \&save_pages,
>  -        'save_entry'   => \&save_enty,
>  -        'save_role'   => \&save_role,
>  +        'save_cat'     => "${pkgCategory::save",
>  +        'save_entries' => "$pkg}Entry::save_enties",
>  +        'save_pages'   => "${pkg}Page::save",
>  +        'save_entry'   => "${pkg}Entry::save",
>  +        'save_role'    => "${pkg}User::save_role",
>
>          ## Lis actions
>  -        'enable_objet'  => \&enable_object,
>  -        'disable_object' => \&disable_object,
>  -        'list_ction'    => \&o_list_action,
>  -        'empty_junk'    => \&empty_junk,
>  -        'handle_unk'    => \&handle_juk,
>  -        'not_junk'       => \&not_junk,
>  +        'enable_object  => "${pkg}User::enale",
>  +        'disable_object' =>"${pg}User::disable",
>  +        'list_action'    => "${pkg}Tools::do_listation",
>  +        'empty_junk'     => "${pkg}Coment::empty_junk",
>  +       'handle_junk'    > ${pkg}ommet::handle_junk",
>  +        'not_junk'       => "${pkg}Comment::not_junk",
>
>  -        'ping'              => \&send_pings,
>  -        'rebuild_phase'      => \&rebuild_hase,
>  -        'rebuild'            => \&rebuild_pges,
>  -       'rbuild_new_phase'  => \&rebuild_new_phase
>  -        'start_rebuild'      => \&start_rebuild_pages,
>  -        'rebuild_confirm'    => \&rebuild_onfirm,
>  -        'entry_notify'       => \&entry_notify,
>  -       'end_notify'        > \&send_notify,
>  -        'start_upload       => \&start_upload,
>  -        'upload_file'        = \&upload_file,
>  -       'upload_userpic     => \&upload_userpic,
>  -       'complete_insert'    => &complete_insert,
>  -        'complete_upload'    => \&complete_upload,
>  -        'start_upload_entry' => \&start_upload_entry,
>  +        'ping'               => "${pkg}Etry:send_pings",
>  +        'rebuild_phase'      => "${pkg}Blog::rebuild_phase",
>  +        'rebuild'            => "${pkg}Blog::rebuild_pages",
>  +        'rebuild_new_phase'  => "${pkg}Blog::rebuild_new_phase",
>  +       'start_rebuild'      => "${pkg}Blog::start_rebuild_pages",
>  +        'rebuild_confirm'    > "${pkg}Blog::rebuild_confirm",
>  +        'entry_notify'       => "${pkg}AddressBook::entry_notify",
>         'send_notify'        => "${pkg}AddressBook::send_notify",
>  +       'start_upod       => "${pkg}Asset::start_upload",
>  +        'upload_file'        => "${pkg}Asset::upload_file",
>  +        'upload_userpic'     > "${pkg}User::upload_userpic",
>  +        'complete_insert'    => "${pkg}Asset::complete_insert",
>  +        'complete_pload'    => "${pkg}Asse::omplete_upload",
>  +        'start_upload_entry' => "${pkg}Asset::sart_upload_entry",
>         'logou'             => {
>              code           => \&logout,
>             requires_login => 0,
>         },
>          'start_recover' => {
>  -           code           => \&start_recover,
>  +            code           => "${pkg}Tools::start_recover",
>              reuires_ogin => 0,
>          },
>          'recover' => {
>  -            cde           => \&recover_passwor,
>  +            code           = "${pkg}Toos::recover_password",
>              requires_login => 0,
>          },
>
>  -       'vew_log'            => \&view_log,
>  -        'list_log'            => \&view_log,
>  -        'reset_log'          => &reset_log-        'export_log'          => \&export_log,
>  -        'export_notification' => \&export_notification,
>  -        'start_import'        => \&stat_import,
>  -        'startexport'        => \&start_export,
>  -        'export'              =>\&export,
>  -        'import'              => \&do_import,
>  -      'inged_urls'         => \&pinged_urls,
>  -        'save_entry_prefs'    => \&sae_entry_prefs,
>  -        'save_favorite_bogs' => \&save_favorite_blogs,
>  -       'reg_file'            => \&reg_file,
>  +        'view_log'           => "${pkg}Log::view",
>  +        'list_log'           => "${pkg}Log::view",
>  +        'reset_log'           => "${pkg}Log::reset",
>  +        'export_log'          => "${pkg}Log::export",
>  +        'xport_notification' => "${pkg}AddressBook::export",
>  +        'statimport'        =>"${pkg}Import::start_import",
>  +        'start_export'        => "${pkg}Export::start_export",
>         'rt'              => "${pkg}Export::export",
>  +        'import'              => "${pkg}Import::do_import",
>  +        'pinged_urls'         => "${pkg}Entry::pinged_urls",
>  +        'save_entry_prefs'    => "${pkg}Entry::save_entry_prefs",
>         'save_favorite_blogs' => "${pkg}Blog::save_favorite_blogs",
>  +       'reg_file'            => "${pkg}Tools::reg_file",
>          'reg_bm_js'           => {
>  -           code           => \&reg_bm_js,
>  +            code           => "${pkg}Tools::reg_bm_js",
>              requires_login => 0,
>          },
>  -        'folder_add'               => \&category_add,
>  -        'category_add'             => \&category_add,
>  -        'category_do_add'          => \&category_do_add,
>  -        'cc_return'                => \&cc_return,
>  -        'reset_blog_templates'     => \&rese_blog_templates,
>  -        'handshake'                > \&handshake,
>  -        'itemset_action'           => \&do_lis_action,
>  -        'page_action'              => \&do_pag_ction,
>  -        'cfg_system'               => \&cfg_system_general,
>  -        'fg_system_users         => \&cfg_system_sers,
>  -        'cfg_system_feedback'      => \&cfg_system_feedback,
>  -        'save_plugin_config'       => \&save_plugin_config,
>  -        'reset_plugin_config'      => \&reset_plugin_config,
>  -        'save_cfg_system_feedback' => \&save_cfg_system_feedbck,
>  -        'save_cfg_system_general'  => \&save_cfg_system_general,
>  -        'save_cfg_system_users'    => \&save_cfg_sytem_users,
>  -        'updae_welcome_message'   =\&update_welcome_messge,
>  +        'folder_add'            => "${pkg}Category::category_add",
>  +        'catery_add'            => "${pkg}Category::category_add",
>  +        'ctegory_do_add'        => "${pkg}ategory::category_do_add",
>  +        'ccreturn'                => "${pkg}Blog::cc_eturn",
>  +        'reset_blog_templates'     =>
>  "${pkg}Template::reset_bog_teplates",
>  +        'handshake'                => "${pkg}Blog::handshke",
>  +        'itemset_action'          => "${pkg}Tools::do_list_action",
>  +       'pageaction'              => "${pkg}Tools::do_pge_action",
>  +        'cfg_system'               => "${pkg}Tools::cfg_system_general",
>  +        'cfg_system_users'         => "${pkg}User::cfg_sysem_users",
>  +       'cfg_system_feedback'      => "${pkg}Comment::cfg_system_feedback",
>  +        'save_plugin_onfig'       => "${pkg}Plugin::save_config",
>  +        'reset_plugin_config'      => "${pkg}Plugin::reset_config",
>  +        'save_cfg_system_feedback' =>
>  "${pkg}Comment::save_cfg_system_feedback",
>  +        'save_cfg_system_general'  =>
>  "${pkg}Tool::save_cfg_system_general",
>  +        'save_cfg_system_users'    => "${pkg}User::save_cfgsystem_users",
>  +        'update_welcome_message'  => "${pkg}Blog::update_welcome_message",
>          'upgrade'                 > {
>  -            code           => \&upgrade,
>  +            code           => "${pkg}Tools::upgrade",
>              requireslogin => 0,
>          },
>  -        'plugin_control'           => \&plugin_control,
>  -        'recover_pofile_password' => \&recover_profile_password,
>  -        'rename_ta'               => \rename_tag,
>  -        'remove_user_assoc'        => \&reove_user_assoc,
>  -       'revoke_role'             => \&revoke_role,
>  -        'grant_role'               > \&grant_role,
>  -        'strt_backup'             => \&start_backup,
>  -        'start_restore'           => \&start_restore,
>  -        'backup'                   => \&backup,
>  -       'backup_download'          => \&backup_download,
>  -        'restore'                  => \&restore,-        'restore_premature_cancel' => \&restore_premature_cancel,
>  -        'adjust_sitepath'          => \&adjust_sitepath,
>  -        'system_check'            => \&systemcheck,
>  -        'dialog_refrsh_templates' => \&dialog_refresh_templates,
>  -        'refresh_all_templates'    => \&refrall_templtes,
>  +        'plugin_control'           => "${pkg}Plugin::plugin_control",
>  +        'recover_profile_passwrd' =>
>  "${pkg}User::recover_profile_password",
>         'rename_tag'               => "${pkg}Tag::rename_tag",
>  +        'remove_user_assoc'       => "${pkg}User::remove_user_assoc",
>  +        'revoke_role'              => "${pkg}User::revoke_role",+        'grant_role'               => "${pkg}User::grant_role",
>  +        'start_ackup'             => "${pkg}Tools::start_backup",
>  +        'start_restore'           => "${pkg}Tools::start_restore",
>  +       'backup'                   => "${pkg}Tools::backup",
>  +        'backup_download'          => "${pkg}Tools::backup_download",
>  +        'restore'                  => "${pkg}Tools::restore",
>  +        'restore_premature_cancel' =>
>  "${pkg}Tools::restore_prematue_cancel",
>  +        'adjust_sitepath'          => "${pkg}Tools::adjust_sitepath",
>  +       'system_check'             => "${pkg}Tools::system_chec",
>  +        'dialog_refresh_templates' =>
>  "${pkg}Templae::dialo_reresh_templates",
>  +        'refreshall_templates'    =>
>  "${pkg}Template::reresh_all_templates",
>
>          ## Comment Replies
>  -        reply         => \&reply,
>  -        do_reply      => \&do_reply,
>  -        reply_preview => \&reply_preview,
>  +        reply         => "${pkg}Comment::reply",
>  +        do_reply      => "{pkg}Comment::do_reply",
>  +        reply_preview => "${pkg}Comment::reply_preview",
>
>          ## Dialogs
>  -        'dialog_restore_upload  => \&dialg_restore_upload,
>  -        'dialog_adjust_sitepath' => \&dialog_adjust_sitepath,
>  -        'dialog_post_comment'    => \&dialog_post_comment,
>  -        'dialog_select_weblog'   => \&dialog_select_weblog,
>  -        'dialog_select_sysadmin' => \&dialog_select_sysadmin,
>  -        'dialog_grant_role'      => \&dialog_rant_role,
>  +        'dialog_restore_upload'  => "${pkg}Toos::dialog_restore_upload",
>  +        'dialog_adjust_sitepath' => "${pkg}Tools::dialog_adjust_sitepath",
>  +        'dialog_post_comment'    => "${pkg}Comment::dialo_post_comment",
>  +        'dialo_select_weblog   => "${pkg}Blog::dialog_select_weblog",
>  +        'dialog_select_sysadmin' => "${pkg}User::dialog_select_sysadmin",
>  +       'dialog_grant_role'      > "${pkg}User:dialog_grant_role",
>
>          # AJAX handlers
>  -        'delete_map'        => \&delete_map,
>  -        'add_map'          => \&add_map,
>  -        'js_tag_check'      => \&js_tag_check,
>  -        'js_tag_list'       => \&js_tag_list,
>  -        'convert_to_html'   => \&cnvert_to_html,
>  -        'update_list_prefs' => \&update_list_pres,
>  -      'js_add_category'   => \&js_add_category,
>  -        'remove_userpic'    => \&remove_userpic,
>  +        delete_map'        => "${pkg}Template::delete_map",
>  +       'add_map'           => "$pkg}Template::add_map",
>  +        'js_tag_check'      = "${pkg}Tag::js_tag_check",
>  +        'js_tag_list'       => "${pkg}Tag::js_ag_list",
>  +        'convert_to_html'   => "${pkg}Tools::convert_to_html",
>  +       'update_list_prefs' = "${pkg}Tools::update_list_prefs",
>  +        'js_add_category'   => "${pkg}Category::js_add_category",
>  +        'remove_userpic'    => ${pkg}User::remove_userpic",
>
>           declared in MT::App
>          'updte_widgetprefs' > sub  returshift->update_widget_prefs(@_) },
>
>  -        'js_recent_entriesfor_tag' => \&js_recent_entries_for_tag,
>  +       'js_recent_entries_or_tag' =>
>  "${pkg}Tag::js_recent_ntries_for_tag",
>
>          ## DEPRECATED ##
>  -        'list_pings'    => \&list_pings,
>  -       'list_entries'  => \&list_entries,
>  -        'listpages'    => \&list_pages,
>  +       'list_pings'    => "${pkgTrackBack::ist",
>  +        'list_entries'  => "${pkg}Entry::list",
>  +        'list_pages'    => "${pkg}Page::list",
>         'lis_comments' => {
>  -            handler    => \&list_comments,
>  +            handler    => "${pkg}Comment::list",
>              permission => 'view_feedback',
>          },
>  -      'list_authors'      => \&list_authors,
>  -        'list_assets'       => \&listassets,
>        'list_cat'          => \&list_category,
>  -        'listblogs'        => &lit_blogs,
>  -        'list_associations' => \&listassociations,
>  -        list_role'        => \&list_rols,
>  +       'list_authors'      => "${pkg}User::list",
>  +        'list_assets'       => "${pkgAsset::list",
>  +      'list_cat'          => "$pkg}Category::list",
>  +       'list_blogs'        => ${pk}Blog::list",
>  +        'list_associations' => "${pkgUser::list_association",
>  +        'list_roles'       => "${pkg}User::list_role"
>      };
>   }
>
>   sub core_widgets {
>      my $app= shift;
>  +    my $pkg ='$Core::MT::CMS::';
>      return {
>          new_install =>
>              template => 'widget/new_install.tmpl',
>  @@ -259,12 +254,12 @
>              template => 'widget/new_version.tmpl',
>             set      => 'main',
>             singular => 1,
>  -            handler  => \&new_version_widget,
>  +            handler  => "${pkrd::new_version_widget",
>          },
>          this_is_you => {
>             label    => 'This is You',
>              template => 'widget/this_is_you.tmpl',
>  -            handler  => \&this_is_you_widget,
>  +            handler  => "${pkg}Dashboard::this_is_you_widget",
>              se      => 'sidebar',
>              singular => 1,
>          },
>  @@ -277,14 272,14 @@
>          mt_news => {
>              label    => 'Movable Type News',
>              template > 'widget/mt_news.tmpl',
>             handler  => \&mt_new_widget,
>  +            andler => "${pkg}Dashboard::mt_news_widget",
>             singular = 1,
>              set      => 'sidebar',
>          },
>          bog_stats => {
>              label    => 'Blog Stats,
>              template => 'widget/blog_stats.tmpl',
>  -            handler  => \&mt_blg_stats_widget,
>  +            andler  =>"${pkg}ashboard::mt_blog_stats_widet",
>             singular => 1,
>             set      => 'main',
>         },
>  @@ 293,18 +288,19 @@
>
>   sub core_blog_stats_tabs {
>      my $app = shift;
>  +    my $pkg = '$Core:MT::CMS::';
>     return {
>          entry => {
>              label    => 'Entries',
>              template => 'widget/blog_stats_entry.tmpl',
>  -            handler  => \&mt_blog_stats_widget_entry_tab,
>  -            stats    => \&generate_dshbord_stats_entry_tab,
>  +            handler  => "${pkg}Dashboard::mt_blog_stats_widget_entry_tab",
>             stats    =>
>  "${pkg}Dashboard::generate_dashboard_stats_entry_tab",
>          },
>          comment => {
>              abel    => 'Comments',
>            template => 'widget/blog_stats_comment.tmpl',
>  -            handler  => \&mt_blog_stats_widget_comment_tab,
>  -            stats    => \&generate_dashboard_stat_comment_tab,
>  +            andler >
>  "${kg}Dashboard::mt_blog_stats_widge_commentab",
>  +            stats    =>
>  "${pk}Dashboard::generate_dashboard_stats_comment_tab",
>         },
>          tag_clud => {
>              label    => 'Tag Cloud',
>  @@ -326,9 +322,7 @@
>              },
>              refresh_gloal_templates => {
>                 label => "Refresh Global Templates",
>  -                code => sub {
>  -                    MT->app->refresh_all_templates(@_);
>  -               },
>  +               handler =>
>  '$Core::MT::CMS::Template::refresh_all_templates',
>                  condition => sub {
>                      ! MT->app->blog,
>              ,
>  @@ -338,276 +332,7 @@
>         },
>     };
>   }
>  -sub js_recent_entries_for_tag {
>  -    my $app          = shift;
>  -    my $user         = $pp->user or return
>  -    my $tag_class    = $app->model('tag') or return;
>  -    y $objtag_class = $app->model('objecttag') or return;
>  -   my $limit        = $app->param('limit') || 10;
>  -    my $obj_ds       =$app->paam('_type') || 'entry';
>  -    my $blog_id      = $app->param('blog_id');
>  -    my $obj_class    = $app->model($obj_ds) or return;
>  -    my $tag_name    = $app->param('tag') or return
>  -    if ( 'utf-8 nelc( $app->config->PublishCharset) ) {
>  -        $tag_name = MT::I18N::encode_text( $tag_name, 'utf-8',
>  $app->config->PublishCharset );
>  -    }
>  -    my $tag_obj =
>  -      $tag_class>load( { name => $tag_name }, { binary => { name => 1 } }
>  );
>
>  -   if ( !$tag_obj ) {
>  -       return $app->json_error( $app->translate("Invalid request.") );
>  -    }
>  -    my $tag_id = $tag_obj->id;
>  -
>  -    my @ntries = $obj_lass->load(
>  -       {
>  -            ( $blog_id ? ( blog_id => $blog_id ) : () ),
>  -            status => MT::Entry::RELEASE(),-        },
>  -        {
>  -            sort      => 'authored_on',
>  -            direction => 'descend',
>  -            limit     => $limit,
>  -            join      => $objtag_class->join_on
>  -               'object_id',
>  -                {
>  -                   ( $blog_id ? ( blog_id => $blog_id ) : () ),
>  -                    tag_id            => $tag_id,
>  -                    object_datasource => $obj_ds,
>  -                }
>  -            ),
>  -       }
>  -    );
>  -    my $count =
>  -      $obj_class->tagged_count( $ag_id,
>  -        { ( $blog_id ? ( blog_id => $bog_id ) : () ) } );
>  -    require MT::Template;
>  -    require MT::Blog;
>  -    my $tmpl = $app->load_tmpl('widet/blog_stats_recent_entries.tmpl');
>  -    m $ctx  = $tmpl->context;
>  -    $ctx->stash( 'blog', T::Blog->load($blog_id) ) if $blog_id;
>  -    $ctx->stash( 'entries', \@entries );
>  -    $tmpl->param( 'entry_coun', scalar @entries );
>  -    $tmpl->param( 'script_url', $app->uri );
>  -    $tpl->param( 'tag',        $tag_name );
>  -    $tmpl>param( 'blog_id',    $blog_id ) if $blog_id;
>  -   my $editable = $app->user->is_superuser;
>  -    if ( $blog_id && !$editable ) {-        $editable = $user->permissions($blog_id)->can_edit_all_posts;
>  -   }
>  -    $tmpl->param('editable',    $editable);
>  -    my $html = $app->build_page( $tmpl );
>  -    return $app->json_result( { html => $html });
>  -}
>  -
>  -sub js_add_category {
>  -    my $app = shift;
>  -   unless ( $app->validate_magic ) {
>  -        return $app->json_error( $app->translate("Invald request.") );
>  -    }
>  -    m $user    = $app->user;
>  -    my $blog_id = $app->param('log_id');
>  -    my $perms   = $app->permissions;
>  -    my $te     $app->param('_type') || 'category';
>  -    my $class   = $app->modl($type);
>  -    if ( !$class ) {
>  -        return $app->json_error( $app->translate("Invalid request.") );
>  -    }
>  -
>  -    my $label =app->param('label');
>  -    my $enc   = $app->confg->PublishCharst;
>  -
>  -    # XMLHttpRequest always send text in UTF-8... right?
>  -    if ( 'utf-8' ne lc($enc) ) {
>  -        $label = MT::I18N::encode_text( $label, 'utf-8', $enc );
>  -    }
>  -    my $basename = $app->param('basename');
>  -    if ( !efined($label) || ($label =~m/^\s*$/ ) ) {
>  -        return $app->json_error( $app->translate("Invalid request.") );
>  -    }
>  -
>  -    my $blog = $app->blog;
>  -    if ( !$blog ) {
>  -        retur $app->json_error( $app->translate("Invalid request.") );
>  -    }
>  -
>  -    my $parent;
>  -    if ( my $parent_id = $app->param('parent') ) {
>  -        if ( $parent_id != -1 ) {    # special case for 'root' folder
>  -            $parent = $class->load( { id => $parent_id, blog_id => $blog_id
>  } );
>  -            if ( !$parent ) {
>  -               return $app->json_error( $app->translate("Inval
>  request.") );-           }
>  -        }
>  -    }
>  -
>  -    my $obj      = $class->new;
>  -    my $original = $obj->clone;
>  -
>  -   i (
>  -        !$app->run_callbacks(
>  -            'cms_save_permission.' . $type,
>  -            $app, $obj, $original
>  -        )
>  -      )
>  -    {
>  -        return $app->json_error( $app->translate("Permission denied.") );
>  -    }
>  -
>  -    $obj->label($label);
>  -   $obj->basename($basename)   if $basename;
>  -    $obj->parent( $parent->id ) if $parent;
>  -   $obj->blog_id($log_id);
>  -    $obj->autor_id( $user->id );
>  -    $obj->created_by( $user->id );
>  -
>  -    if (
>  -        !$app->run_callbacks( 'cms_pre_save.' . $type, $app, $obj,
>  $orinal ) )
>  -    {
>  -        return $app->json_error( $app->errstr );
>  -    }
>  --    $obj->save;
>  -
>  -    $app->run_callbacks( 'cms_post_save.' . $type $app $obj, $original );
>  -
>  -    return $app->json_result(
>  -        {
>  -            id      => $obj->id,
>  -            basename => $obj->basename
>  -        }
>  -    );
>  -}
>  -
>  -sub convert_to_html {
>  -   my $app    = shift;
>  -    my $format = $app->parm('format');
>  -    my $text   = $app->param('text');
>  -    # XMLHttpRequest always send text in UF-8... right?
>  -    if ( defined $text ) {
>  -        $tex = encode_text($text, 'utf-8', $app->config->PublishCharset);
>  -    }
>  -    else {-        $text = '' ;
>  -    }
>  -   my $text_more = $app->param('text_mre');
>  -    if ( defined $text_more ) {
>  -        $tex_more = encode_text($text_more, 'utf-8',
>  $ap->config->PublishCarset);
>  -    }
>  -    else {
>  -        $text_more = '' ;
>  -    }
>  -    my $result = {
>  -       tex      => app->apply_text_filters( $text,     [$format] ),
>  -        text_more => $app->appltext_filters( $text_more, [$format] ),
>  -        format    => $format,
>  -    };
>  -    return $app->json_result($result);
>  -}
>  -
>  -sub tools {
>  -    my $app = shift;
>  -    $app->system_check
>  -}
>  -
>  -sub system_heck {
>  -    my $app = shift;
>  -
>  -    if ( my $blog_id = $app->param('blog_id') ) {
>  -        return $app->redirect(
>  -            $app->uri(
>  -                'mode' => 'view_log',
>  -               ar => { blog_id => $blog_id }
>  -            )
>  -       );
>  -    }
>  -
>  -   my %param;
>  -    # licensed user count: someone who has logged in wihin 90 days
>  -    my $sess_class = $app->model('session');
>  -    my $from = time - ( 60 * 60 * 24 * 90 + 60 * 60 * 24 );
>  -    $sess_lass->remove(
>  -        { kind => 'UA', start => [ undef, $from ] },
>  -       { range => { start => 1  }
>  -    );
>  -    $param{licensed_usr_count}= $sess_class->count( { kind => 'UA' } );
>  -
>  -    my $author_class = $app->model('author');
>  -    $param{user_count} = $author_clss->count(
>  -        { type => MT::Author::AUTHOR() } );
>  -
>  -    # commeters: users with only comment permission and
>  MT::Author::COMMENTER
>  -    my $cmntrs = $author_class->count(
>  -        { tpe => MT::Author::COMMENTER()  );
>  -
>  -    my @perms = $app->model('permision')->load(
>  -      {
>  -        permissions => "%'comment'%,
>  -        blog_id     => '0',
>  -      },
>  -      {
>  -        'like' => { 'permissions' => 1 },
>  -        'not'  => { 'blog_id'     => 1 },
>  -      }
>  -    );
>  -    @perms = grep { $_->permissions =~ m/'comment'/ } @pems;-    $param{commenter_cont} = scala(@perms) + $cmntrs;
>  -    $param{screen_id} = "system-check";
>     $param{syschck_html = $app->get_syscheck_content() ||'';
>  -
>  -    $app->load_tmpl( 'system_chec.tmpl, \%param );-}
>  -
>  -sub get_syscheck_content {
>  -    my $app = shift;
>  -
>  -    my $syscheck_url = $app->base . $app->mt_path .
>  $app->config('ChecScript') .
>  -        '?view=tools&version=' . MT->version_id;
>  -    if ( $syscheck_url && $syscheck_url ne 'disable' ) {
>  -        my $SYSCHECKCACHE_TIMEOUT = 60 * 60 * 24;
>  -        my $sess_css         $app->model('session');
>  -        my ($syscheck_object)     = ("")
>         my $retries           = 0;
>  -        $syscheck_object = $ess_class->load( { id => 'SC' } );
>  -        i ( $syscheck_object
>  -            && ( $syschek_object->start() <( time -
>  $SYSCHECKCACHE_TIMEOUT ) ) )
>  -       {
>  -            $syscheckobject->remove;
>  -            $ysceck_object = undef;
>  -        }
>  -        return encde_text( $syscheck_object->data(), 'utf-8', undef )
>  -          if ($syscheck_object);
>  -
>  -        my $ua  $app->new_ua({ timout => 20 });
>  -        return unless $ua;
>  -        $ua->max_size(undef) if $ua->can(max_size');
>  -
>  -        my $req = new HTTP::Request( GT => $syscheck_url );
>  -        my $resp = $arequest($req);
>  -        return unless $resp->is_success();
>  -        my $rsult = $resp->content();
>  -        if ($result) {
>  -            require MT::Sanitize;
>  -
>  -            # allowed html
>  -            my $spec = '* style class
>  id,ul,li,div,span,br,h2,h3,strong,code,blockquote,p';
>  -            $result = MT::Sanitize->sanitize( $result, $spec );
>  -            $syscheck_object = MT::Session->new();
>  -            $syscheck_object->set_values(
>  -                {
>  -                   id    => 'C',
>  -                   kind  => 'SC',
>  -                    start => ime(),
>  -                   data  => $result
>  -                }
>  -            );
>  -            $syscheck_bject->save();
>  -            $result = encode_text( $result, 'utf-8', undef );
>  -       }
>  -        return $result;
>  -    }
>  -}
>  -
>   sub init_plugins {
>      my $app = shift;
>  @@ -669,12 +394,13 @@
>
>   sub core_list_actions {
>     my $app = shift;
>  +    my $pkg = '$Core::MT::CMS::';
>     return {
>         'entry' => {
>              'set_publised' => {
>                 label      => "Publish Entries",
>                  order      => 100,
>  -                code       => \&publish_entries,
>  +                code      => "${pkg}ntry::publish_entries",
>                  permission => 'edit_allposts,publish_post',
>                  condition  => sub {
>                     return 0 if $app-mode eq 'view';
>  @@ -684,7 +410,7 @@
>              'set_draft' => {
>                 label      => "Unpublish Entries",
>                 order      => 200,
>  -                code       => \&draft_entries,
>  +                code       => "${pkg}Entry::draft_entries",
>                  permission => 'edit_all_posts,publish_post',
>                 condition  => sub {
>                      return 0 if $app->mode eq 'view';
>  @@ -694,7 +420,7 @@
>        'add_tags' => {
>                  label       => "Add Tags...",
>               order       => 300,
>  -                code        => \&add_tags_to_entries,
>  +               code        => "${pkg}Tag::add_tags_to_entries",
>                  input       => 1,
>                  input_label => 'Tags to add to selected entries',
>                  permission  => 'edit_all_posts',
>  @@ -705,7 +431,7 @@
>              'remove_tags' => {
>                  label       => "Remove Tags...",
>                  order       => 400,
>  -                code        => \&remove_tags_from_entries,
>  +                code        => "${pkg}Tag::remove_tags_from_entries",
>                  input       => 1,
>                  input_label => 'Tags to remove from selected entries',
>                  permission  => 'edit_all_posts',
>  @@ -715,7 +441,7 @@
>              },
>              'open_batch_editor' => {
>                  label     => "Batch Edit Entries",
>  -                code      => \&open_batch_editor,
>  +                code      => "${pkg}Entry::open_batch_editor",
>                  order     => 500,
>                  condition => sub {
>                      return 0 if $app->mode eq 'view';
>  @@ -729,7 +455,7 @@
>              'set_published' => {
>                  label      => "Publish Pages",
>                  order      => 100,
>  -                code       => \&publish_entries,
>  +                code       => "${pkg}Entry::publish_entries",
>                  permission => 'manage_pages',
>                  condition  => sub {
>                      return 0 if $app->mode eq 'view';
>  @@ -739,7 +465,7 @@
>              'set_draft' => {
>                  label      => "Unpublish Pages",
>                  order      => 200,
>  -                code       => \&draft_entries,
>  +                code       => "${pkg}Entry::draft_entries",
>                  permission => 'manage_pages',
>                  condition  => sub {
>                      return 0 if $app->mode eq 'view';
>  @@ -749,7 +475,7 @@
>              'add_tags' => {
>                  label       => "Add Tags...",
>                  order       => 300,
>  -                code        => \&add_tags_to_entries,
>  +                code        => "${pkg}Tag::add_tags_to_entries",
>                  input       => 1,
>                  input_label => 'Tags to add to selected pages',
>                  permission  => 'manage_pages',
>  @@ -760,7 +486,7 @@
>              'remove_tags' => {
>                  label       => "Remove Tags...",
>                  order       => 400,
>  -                code        => \&remove_tags_from_entries,
>  +                code        => "${pkg}Tag::remove_tags_from_entries",
>                  input       => 1,
>                  input_label => 'Tags to remove from selected pages',
>                  permission  => 'manage_pages',
>  @@ -770,7 +496,7 @@
>              },
>              'open_batch_editor' => {
>                  label     => "Batch Edit Pages",
>  -                code      => \&open_batch_editor,
>  +                code      => "${pkg}Entry::open_batch_editor",
>                  order     => 500,
>                  condition => sub {
>                      return 0 if $app->mode eq 'view';
>  @@ -784,7 +510,7 @@
>              'add_tags' => {
>                  label       => "Add Tags...",
>                  order       => 100,
>  -                code        => \&add_tags_to_assets,
>  +                code        => "${pkg}Tag::add_tags_to_assets",
>                  input       => 1,
>                  input_label => 'Tags to add to selected assets',
>                  permission  => 'edit_assets',
>  @@ -792,7 +518,7 @@
>              'remove_tags' => {
>                  label       => "Remove Tags...",
>                  order       => 200,
>  -                code        => \&remove_tags_from_assets,
>  +                code        => "${pkg}Tag::remove_tags_from_assets",
>                  input       => 1,
>                  input_label => 'Tags to remove from selected assets',
>                  permission  => 'edit_assets',
>  @@ -802,7 +528,7 @@
>              'unapprove_ping' => {
>                  label      => "Unpublish TrackBack(s)",
>                  order      => 100,
>  -                code       => \&unapprove_item,
>  +                code       => "${pkg}Comment::unapprove_item",
>                  permission => 'manage_feedback,publish_post',
>              },
>          },
>  @@ -810,7 +536,7 @@
>              'unapprove_comment' => {
>                  label      => "Unpublish Comment(s)",
>                  order      => 100,
>  -                code       => \&unapprove_item,
>  +                code       => "${pkg}Comment::unapprove_item",
>                  permission => 'manage_feedback,publish_post',
>                  condition  => sub {
>                      return 1;
>  @@ -819,25 +545,25 @@
>              'trust_commenter' => {
>                  label      => "Trust Commenter(s)",
>                  order      => 200,
>  -                code       => \&trust_commenter_by_comment,
>  +                code       => "${pkg}Comment::trust_commenter_by_comment",
>                  permission => 'manage_feedback',
>              },
>              'untrust_commenter' => {
>                  label      => "Untrust Commenter(s)",
>                  order      => 300,
>  -                code       => \&untrust_commenter_by_comment,
>  +                code       =>
>  "${pkg}Comment::untrust_commenter_by_comment",
>                  permission => 'manage_feedback',
>              },
>              'ban_commenter' => {
>                  label      => "Ban Commenter(s)",
>                  order      => 400,
>  -                code       => \&ban_commenter_by_comment,
>  +                code       => "${pkg}Comment::ban_commenter_by_comment",
>                  permission => 'manage_feedback',
>              },
>              'unban_commenter' => {
>                  label      => "Unban Commenter(s)",
>                  order      => 500,
>  -                code       => \&unban_commenter_by_comment,
>  +                code       => "${pkg}Comment::unban_commenter_by_comment",
>                  permission => 'manage_feedback',
>              },
>          },
>  @@ -845,13 +571,13 @@
>              'untrust' => {
>                  label      => "Untrust Commenter(s)",
>                  order      => 100,
>  -                code       => \&untrust_commenter,
>  +                code       => "{$pkg}Comment::untrust_commenter",
>                  permission => 'manage_feedback',
>              },
>              'unban' => {
>                  label      => "Unban Commenter(s)",
>                  order      => 200,
>  -                code       => \&unban_commenter,
>  +                code       => "${pkg}Comment::unban_commenter",
>                  permission => 'manage_feedback',
>              },
>          },
>  @@ -859,9 +585,10 @@
>              'recover_passwords' => {
>                  label => "Recover Password(s)",
>                  order => 100,
>  -                continue_prompt =>
>  -                  $app->translate("_WARNING_PASSWORD_RESET_MULTI"),
>  -                code      => \&recover_passwords,
>  +                continue_prompt_handler => sub {
>  +                    MT->translate("_WARNING_PASSWORD_RESET_MULTI");
>  +                },
>  +                code      => "${pkg}Tools::recover_passwords",
>                  condition => sub {
>                      ( $app->user->is_superuser()
>                            && MT::Auth->can_recover_password );
>  @@ -870,10 +597,12 @@
>              'delete_user' => {
>                  label           => "Delete",
>                  order           => 200,
>  -                continue_prompt => $app->config->ExternalUserManagement
>  -                ? $app->translate("_WARNING_DELETE_USER_EUM")
>  -                : $app->translate("_WARNING_DELETE_USER"),
>  -                code      => \&delete,
>  +                continue_prompt_handler => sub {
>  +                    $app->config->ExternalUserManagement
>  +                        ? MT->translate("_WARNING_DELETE_USER_EUM")
>  +                        : MT->translate("_WARNING_DELETE_USER");
>  +                },
>  +                code      => "${pkg}Common::delete",
>                  condition => sub {
>                      $app->user->is_superuser();
>                  },
>  @@ -892,12 +621,12 @@
>          'template' => {
>              refresh_tmpl_templates => {
>                  label => "Refresh Template(s)",
>  -                code => sub { MT->app->refresh_individual_templates(@_) },
>  +                handler =>
>  '$Core::MT::CMS::Template::refresh_individual_templates',
>                  permission => 'edit_templates',
>              },
>              publish_index_templates => {
>                  label => "Publish Template(s)",
>  -                code => sub { MT->app->publish_index_templates(@_) },
>  +                handler => '$Core::MT::CMS::Blog::publish_index_templates',
>                  permission => 'rebuild',
>                  condition => sub {
>                      my $app = MT->app;
>  @@ -920,39 +649,13 @@
>      $app->model($type)->class_label_plural;
>   }
>
>  -sub asset_list_filters {
>  -    my $app = shift;
>  -
>  -    my %filters;
>  -    my $types = MT::Asset->class_labels;
>  -    foreach my $type ( keys %$types ) {
>  -        my $asset_type = $type;
>  -        $asset_type =~ s/^asset\.//;
>  -        $filters{$asset_type} = {
>  -            label   => sub {
>  MT::Asset->class_handler($type)->class_label_plural },
>  -            handler => sub {
>  -                my ( $terms, $args ) = @_;
>  -                $terms->{class} = $asset_type eq 'asset' ? '*' :
>  $asset_type;
>  -            },
>  -        };
>  -    }
>  -    my @types =
>  -      sort { $filters{$a}{label} cmp $filters{$b}{label} } keys %filters;
>  -    my $order = 100;
>  -    foreach (@types) {
>  -        $filters{$_}{order} = $order;
>  -        $order += 100;
>  -    }
>  -    $filters{'asset'}{order} = 0;
>  -    $filters{'asset'}{label} = "All Assets"; # labels are translated later
>  -                                             # translate("All Assets");
>  -    return \%filters;
>  -}
>  -
>   sub core_list_filters {
>      my $app = shift;
>      return {
>  -        asset => sub { $app->asset_list_filters(@_) },
>  +        asset => sub {
>  +            require MT::CMS::Asset;
>  +            return MT::CMS::Asset::asset_list_filters($app, @_);
>  +        },
>          entry => {
>              published => {
>                  label => sub {
>  @@ -1003,7 +706,7 @@
>                  handler => sub {
>                      my ( $terms, $args ) = @_;
>                      my $ts = time - 10 * 24 * 60 * 60;
>  -                    $ts = MT::Util::epoch2ts( MT->app->blog, $ts );
>  +                    $ts = epoch2ts( MT->app->blog, $ts );
>                      $args->{join} = MT::Comment->join_on(
>                          'entry_id',
>                          {
>  @@ -1030,11 +733,11 @@
>                      $from = undef unless $from =~ m/^\d{8}$/;
>                      $to   = undef unless $to   =~ m/^\d{8}$/;
>                      my $format = '%x';
>  -                    $from = MT::Util::format_ts(
>  +                    $from = format_ts(
>                          $format, $from . '000000',
>                          undef,   MT->current_language
>                      ) if $from;
>  -                    $to = MT::Util::format_ts(
>  +                    $to = format_ts(
>                          $format, $to . '000000',
>                          undef,   MT->current_language
>                      ) if $to;
>  @@ -1134,7 +837,7 @@
>                  handler => sub {
>                      my ( $terms, $args ) = @_;
>                      my $ts = time - 7 * 24 * 60 * 60;
>  -                    $ts = MT::Util::epoch2ts( MT->app->blog, $ts );
>  +                    $ts = epoch2ts( MT->app->blog, $ts );
>                      $terms->{created_on} = [ $ts, undef ];
>                      $args->{range_incl}{created_on} = 1;
>                      $terms->{junk_status} = [ 0, 1 ];
>  @@ -1219,7 +922,7 @@
>                  handler => sub {
>                      my ( $terms, $args ) = @_;
>                      my $ts = time - 7 * 24 * 60 * 60;
>  -                    $ts = MT::Util::epoch2ts( MT->app->blog, $ts );
>  +                    $ts = epoch2ts( MT->app->blog, $ts );
>                      $terms->{created_on} = [ $ts, undef ];
>                      $args->{range_incl}{created_on} = 1;
>                      $terms->{junk_status} = [ 0, 1 ];
>  @@ -1231,7 +934,7 @@
>              #               handler => sub {
>              #                   my ( $terms, $args ) = @_;
>              #                   my $ts = time - 24 * 60 * 60;
>  -            #                   $ts = MT::Util::epoch2ts( MT->app->blog,
>  $ts );
>  +            #                   $ts = epoch2ts( MT->app->blog, $ts );
>              #                   $terms->{created_on} = [ $ts, undef ];
>              #                   $args->{range_incl}{created_on} = 1;
>              #                   $terms->{junk_status} = [ 0, 1 ];
>  @@ -1285,11 +988,11 @@
>                      $from = undef unless $from =~ m/^\d{8}$/;
>                      $to   = undef unless $to   =~ m/^\d{8}$/;
>                      my $format = '%x';
>  -                    $from = MT::Util::format_ts(
>  +                    $from = format_ts(
>                          $format, $from . '000000',
>                          undef,   MT->current_language
>                      ) if $from;
>  -                    $to = MT::Util::format_ts(
>  +                    $to = format_ts(
>                          $format, $to . '000000',
>                          undef,   MT->current_language
>                      ) if $to;
>  @@ -1794,180 +1497,129 @@
>   sub init_core_callbacks {
>      my $app = shift;
>      my $pkg = 'cms_';
>  +    my $pfx = '$Core::MT::CMS::';
>      $app->_register_core_callbacks(
>          {
>              # notification callbacks
>  -            $pkg . 'save_permission_filter.notification' => sub {
>  -                my ( $eh, $app, $id ) = @_;
>  -                my $perms = $app->permissions;
>  -                return $perms && $perms->can_edit_notifications;
>  -            },
>  -            $pkg . 'save_filter.notification' =>
>  \&CMSSaveFilter_notification,
>  -            $pkg . 'post_delete.notification' =>
>  \&CMSPostDelete_notification,
>  +            $pkg . 'save_permission_filter.notification' =>
>  "${pfx}AddressBook::can_save",
>  +            $pkg . 'save_filter.notification' =>
>  "${pfx}AddressBook::save_filter",
>  +            $pkg . 'post_delete.notification' =>
>  "${pfx}AddressBook::post_delete",
>
>              # banlist callbacks
>  -            $pkg . 'save_permission_filter.banlist' => sub {
>  -                my ( $eh, $app, $id ) = @_;
>  -                my $perms = $app->permissions;
>  -                return $perms
>  -                  && ( $perms->can_edit_config ||
>  $perms->can_manage_feedback );
>  -            },
>  -            $pkg . 'save_filter.banlist' => \&CMSSaveFilter_banlist,
>  +            $pkg . 'save_permission_filter.banlist' =>
>  "${pfx}BanList::can_save",
>  +            $pkg . 'save_filter.banlist' => "${pfx}BanList::save_filter",
>
>              # associations
>  -            $pkg
>  -              . 'delete_permission_filter.association' =>
>  -              \&CMSDeletePermissionFilter_association,
>  +            $pkg . 'delete_permission_filter.association' =>
>  "${pfx}User::can_delete_association",
>
>              # user callbacks
>  -            $pkg
>  -              . 'view_permission_filter.author' =>
>  -              \&CMSViewPermissionFilter_author,
>  -            $pkg
>  -              . 'save_permission_filter.author' =>
>  -              \&CMSSavePermissionFilter_author,
>  -            $pkg
>  -              . 'delete_permission_filter.author' =>
>  -              \&CMSDeletePermissionFilter_author,
>  -            $pkg . 'save_filter.author' => \&CMSSaveFilter_author,
>  -            $pkg . 'pre_save.author'    => \&CMSPreSave_author,
>  -            $pkg . 'post_save.author'   => \&CMSPostSave_author,
>  -            $pkg . 'post_delete.author' => \&CMSPostDelete_author,
>  +            $pkg . 'edit.author' => "${pfx}User::edit",
>  +            $pkg . 'view_permission_filter.author' =>
>  "${pfx}User::can_view",
>  +            $pkg . 'save_permission_filter.author' =>
>  "${pfx}User::can_save",
>  +            $pkg . 'delete_permission_filter.author' =>
>  "${pfx}User::can_delete",
>  +            $pkg . 'save_filter.author' => "${pfx}User::save_filter",
>  +            $pkg . 'pre_save.author'    => "${pfx}User::pre_save",
>  +            $pkg . 'post_save.author'   => "${pfx}User::post_save",
>  +            $pkg . 'post_delete.author' => "${pfx}User::post_delete",
>
>              # blog callbacks
>  +            $pkg . 'edit.blog' => "${pfx}Blog::edit",
>              $pkg
>  -              . 'view_permission_filter.blog' =>
>  \&CMSViewPermissionFilter_blog,
>  +              . 'view_permission_filter.blog' => "${pfx}Blog::can_view",
>              $pkg
>  -              . 'save_permission_filter.blog' =>
>  \&CMSSavePermissionFilter_blog,
>  +              . 'save_permission_filter.blog' => "${pfx}Blog::can_save",
>              $pkg
>  -              . 'deletePermissionFilter.blog' =>
>  -              \&CMSDeletePermissionFilter_blog,
>  -            $pkg . 'pre_save.blog'    => \&CMSPreSave_blog,
>  -            $pkg . 'post_save.blog'   => \&CMSPostSave_blog,
>  -            $pkg . 'save_filter.blog' => \&CMSSaveFilter_blog,
>  -            $pkg . 'post_delete.blog' => \&CMSPostDelete_blog,
>  +              . 'delete_permission_filter.blog' =>
>  "${pfx}Blog::can_delete",
>  +            $pkg . 'pre_save.blog'    => "${pfx}Blog::pre_save",
>  +            $pkg . 'post_save.blog'   => "${pfx}Blog::post_save",
>  +            $pkg . 'save_filter.blog' => "${pfx}Blog::save_filter",
>  +            $pkg . 'post_delete.blog' => "${pfx}Blog::post_delete",
>
>              # folder callbacks
>  -            $pkg
>  -              . 'view_permission_filter.folder' =>
>  -              \&CMSViewPermissionFilter_folder,
>  -            $pkg
>  -              . 'save_permission_filter.folder' =>
>  -              \&CMSSavePermissionFilter_folder,
>  -            $pkg
>  -              . 'delete_permission_filter.category' =>
>  -              \&CMSDeletePermissionFilter_folder,
>  -            $pkg . 'pre_save.category'    => \&CMSPreSave_folder,
>  -            $pkg . 'post_save.category'   => \&CMSPostSave_folder,
>  -            $pkg . 'save_filter.category' => \&CMSSaveFilter_folder,
>  -            $pkg . 'post_delete.category' => \&CMSPostDelete_folder,
>  +            $pkg . 'edit.folder' => "${pfx}Folder::edit",
>  +            $pkg . 'view_permission_filter.folder' =>
>  "${pfx}Folder::can_view",
>  +            $pkg . 'save_permission_filter.folder' =>
>  "${pfx}Folder::can_save",
>  +            $pkg . 'delete_permission_filter.folder' =>
>  "${pfx}Folder::can_delete",
>  +            $pkg . 'pre_save.folder'    => "${pfx}Folder::pre_save",
>  +            $pkg . 'post_save.folder'   => "${pfx}Folder::post_save",
>  +            $pkg . 'save_filter.folder' => "${pfx}Folder::save_filter",
>  +            $pkg . 'post_delete.folder' => "${pfx}Folder::post_delete",
>
>              # category callbacks
>  -            $pkg
>  -              . 'view_permission_filter.category' =>
>  -              \&CMSViewPermissionFilter_category,
>  -            $pkg
>  -              . 'save_permission_filter.category' =>
>  -              \&CMSSavePermissionFilter_category,
>  -            $pkg
>  -              . 'delete_permission_filter.category' =>
>  -              \&CMSDeletePermissionFilter_category,
>  -            $pkg . 'pre_save.category'    => \&CMSPreSave_category,
>  -            $pkg . 'post_save.category'   => \&CMSPostSave_category,
>  -            $pkg . 'save_filter.category' => \&CMSSaveFilter_category,
>  -            $pkg . 'post_delete.category' => \&CMSPostDelete_category,
>  +            $pkg . 'edit.category' => "${pfx}Category::edit",
>  +            $pkg . 'view_permission_filter.category' =>
>  "${pfx}Category::can_view",
>  +            $pkg . 'save_permission_filter.category' =>
>  "${pfx}Category::can_save",
>  +            $pkg . 'delete_permission_filter.category' =>
>  "${pfx}Category::can_delete",
>  +            $pkg . 'pre_save.category'    => "${pfx}Category::pre_save",
>  +            $pkg . 'post_save.category'   => "${pfx}Category::post_save",
>  +            $pkg . 'save_filter.category' => "${pfx}Category::save_filter",
>  +            $pkg . 'post_delete.category' => "${pfx}Category::post_delete",
>
>              # comment callbacks
>  -            $pkg
>  -              . 'view_permission_filter.comment' =>
>  -              \&CMSViewPermissionFilter_comment,
>  -            $pkg
>  -              . 'save_permission_filter.comment' =>
>  -              \&CMSSavePermissionFilter_comment,
>  -            $pkg
>  -              . 'delete_permission_filter.comment' =>
>  -              \&CMSDeletePermissionFilter_comment,
>  -            $pkg . 'pre_save.comment'    => \&CMSPreSave_comment,
>  -            $pkg . 'post_save.comment'   => \&CMSPostSave_comment,
>  -            $pkg . 'post_delete.comment' => \&CMSPostDelete_comment,
>  +            $pkg . 'edit.comment' => "${pfx}Comment::edit",
>  +            $pkg . 'view_permission_filter.comment' =>
>  "${pfx}Comment::can_view",
>  +            $pkg . 'save_permission_filter.comment' =>
>  "${pfx}Comment::can_save",
>  +            $pkg . 'delete_permission_filter.comment' =>
>  "${pfx}Comment::can_delete",
>  +            $pkg . 'pre_save.comment'    => "${pfx}Comment::pre_save",
>  +            $pkg . 'post_save.comment'   => "${pfx}Comment::post_save",
>  +            $pkg . 'post_delete.comment' => "${pfx}Comment::post_delete",
>
>              # commenter callbacks
>  -            $pkg
>  -              . 'view_permission_filter.commenter' =>
>  -              \&CMSViewPermissionFilter_commenter,
>  -            $pkg
>  -              . 'delete_permission_filter.commenter' =>
>  -              \&CMSDeletePermissionFilter_commenter,
>  +            $pkg . 'edit.commenter' => "${pfx}Comment::edit_commenter",
>  +            $pkg . 'view_permission_filter.commenter' =>
>  "${pfx}Comment::can_view_commenter",
>  +            $pkg . 'delete_permission_filter.commenter' =>
>  "${pfx}Comment::can_delete_commenter",
>
>              # entry callbacks
>  -            $pkg
>  -              . 'view_permission_filter.entry' =>
>  -              \&CMSViewPermissionFilter_entry,
>  -            $pkg
>  -              . 'delete_permission_filter.entry' =>
>  -              \&CMSDeletePermissionFilter_entry,
>  -            $pkg . 'pre_save.entry'    => \&CMSPreSave_entry,
>  -            $pkg . 'post_save.entry'   => \&CMSPostSave_entry,
>  -            $pkg . 'post_delete.entry' => \&CMSPostDelete_entry,
>  +            $pkg . 'edit.entry' => "${pfx}Entry::edit",
>  +            $pkg . 'view_permission_filter.entry' =>
>  "${pfx}Entry::can_view",
>  +            $pkg . 'delete_permission_filter.entry' =>
>  "${pfx}Entry::can_delete",
>  +            $pkg . 'pre_save.entry'    => "${pfx}Entry::pre_save",
>  +            $pkg . 'post_save.entry'   => "${pfx}Entry::post_save",
>  +            $pkg . 'post_delete.entry' => "${pfx}Entry::post_delete",
>
>              # page callbacks
>  -            $pkg
>  -              . 'view_permission_filter.page' =>
>  \&CMSViewPermissionFilter_page,
>  -            $pkg
>  -              . 'delete_permission_filter.page' =>
>  -              \&CMSDeletePermissionFilter_page,
>  -            $pkg . 'pre_save.page'    => \&CMSPreSave_page,
>  -            $pkg . 'post_save.page'   => \&CMSPostSave_page,
>  -            $pkg . 'post_delete.page' => \&CMSPostDelete_page,
>  +            $pkg . 'edit.page' => "${pfx}Page::edit",
>  +            $pkg . 'view_permission_filter.page' => "${pfx}Page::can_view",
>  +            $pkg . 'delete_permission_filter.page' =>
>  "${pfx}Page::can_delete",
>  +            $pkg . 'pre_save.page'    => "${pfx}Page::pre_save",
>  +            $pkg . 'post_save.page'   => "${pfx}Page::post_save",
>  +            $pkg . 'post_delete.page' => "${pfx}Page::post_delete",
>
>              # ping callbacks
>  -            $pkg
>  -              . 'view_permission_filter.ping' =>
>  \&CMSViewPermissionFilter_ping,
>  -            $pkg
>  -              . 'save_permission_filter.ping' =>
>  \&CMSSavePermissionFilter_ping,
>  -            $pkg
>  -              . 'delete_permission_filter.ping' =>
>  -              \&CMSDeletePermissionFilter_ping,
>  -            $pkg . 'pre_save.ping'    => \&CMSPreSave_ping,
>  -            $pkg . 'post_save.ping'   => \&CMSPostSave_ping,
>  -            $pkg . 'post_delete.ping' => \&CMSPostDelete_ping,
>  +            $pkg . 'edit.ping' => "${pfx}TrackBack::edit",
>  +            $pkg . 'view_permission_filter.ping' =>
>  "${pfx}TrackBack::can_view",
>  +            $pkg . 'save_permission_filter.ping' =>
>  "${pfx}TrackBack::can_save",
>  +            $pkg . 'delete_permission_filter.ping' =>
>  "${pfx}TrackBack::can_delete",
>  +            $pkg . 'pre_save.ping'    => "${pfx}TrackBack::pre_save",
>  +            $pkg . 'post_save.ping'   => "${pfx}TrackBack::post_save",
>  +            $pkg . 'post_delete.ping' => "${pfx}TrackBack::post_delete",
>
>              # template callbacks
>  -            $pkg
>  -              . 'view_permission_filter.template' =>
>  -              \&CMSViewPermissionFilter_template,
>  -            $pkg
>  -              . 'save_permission_filter.template' =>
>  -              \&CMSSavePermissionFilter_template,
>  -            $pkg
>  -              . 'delete_permission_filter.template' =>
>  -              \&CMSDeletePermissionFilter_template,
>  -            $pkg . 'pre_save.template'    => \&CMSPreSave_template,
>  -            $pkg . 'post_save.template'   => \&CMSPostSave_template,
>  -            $pkg . 'post_delete.template' => \&CMSPostDelete_template,
>  +            $pkg . 'edit.template' => "${pfx}Template::edit",
>  +            $pkg . 'view_permission_filter.template' =>
>  "${pfx}Template::can_view",
>  +            $pkg . 'save_permission_filter.template' =>
>  "${pfx}Template::can_save",
>  +            $pkg . 'delete_permission_filter.template' =>
>  "${pfx}Template::can_delete",
>  +            $pkg . 'pre_save.template'    => "${pfx}Template::pre_save",
>  +            $pkg . 'post_save.template'   => "${pfx}Template::post_save",
>  +            $pkg . 'post_delete.template' => "${pfx}Template::post_delete",
>
>              # tags
>  -            $pkg
>  -              . 'delete_permission_filter.tag' =>
>  -              \&CMSDeletePermissionFilter_tag,
>  -            $pkg . 'post_delete.tag' => \&CMSPostDelete_tag,
>  +            $pkg . 'delete_permission_filter.tag' =>
>  "${pfx}Tag::can_delete",
>  +            $pkg . 'post_delete.tag' => "${pfx}Tag::post_delete",
>
>              # junk-related callbacks
>              #'HandleJunk' => \&_builtin_spam_handler,
>              #'HandleNotJunk' => \&_builtin_spam_unhandler,
>  -            $pkg . 'not_junk_test' => \&_cb_notjunktest_filter,
>  +            $pkg . 'not_junk_test' => "${pfx}Common::not_junk_test",
>
>              # assets
>  -            $pkg
>  -              . 'view_permission_filter.asset' =>
>  -              \&CMSViewPermissionFilter_asset,
>  -            $pkg
>  -              . 'delete_permission_filter.asset' =>
>  -              \&CMSDeletePermissionFilter_asset,
>  -            $pkg . 'pre_save.asset'    => \&CMSPreSave_asset,
>  -            $pkg . 'post_save.asset'   => \&CMSPostSave_asset,
>  -            $pkg . 'post_delete.asset' => \&CMSPostDelete_asset,
>  -            'template_param.edit_asset' => \&CMSTemplateParam_edit_asset,
>  +            $pkg . 'edit.asset' => "${pfx}Asset::edit",
>  +            $pkg . 'view_permission_filter.asset' =>
>  "${pfx}Asset::can_view",
>  +            $pkg . 'delete_permission_filter.asset' =>
>  "${pfx}Asset::can_delete",
>  +            $pkg . 'pre_save.asset'    => "${pfx}Asset::pre_save",
>  +            $pkg . 'post_save.asset'   => "${pfx}Asset::post_save",
>  +            $pkg . 'post_delete.asset' => "${pfx}Asset::post_delete",
>  +            'template_param.edit_asset' =>
>  "${pfx}Asset::template_param_edit",
>          }
>      );
>   }
>  @@ -1986,7 +1638,7 @@
>          return unless $app->user;
>          my $pw = $app->user->api_password;
>          return undef if ( $pw || '' ) eq '';
>  -        my $auth_token = MT::Util::perl_sha1_digest_hex( 'feed:' . $pw );
>  +        my $auth_token = perl_sha1_digest_hex( 'feed:' . $pw );
>          return $feed_token eq $auth_token;
>      }
>      else {
>  @@ -1994,1150 +1646,6 @@
>      }
>   }
>
>  -sub update_welcome_message {
>  -    my $app = shift;
>  -    $app->validate_magic or return;
>  -
>  -    my $perms = $app->permissions;
>  -    return $app->errtrans("Permission denied.")
>  -      unless $perms && $perms->can_edit_config;
>  -
>  -    my $blog_id    = $app->param('blog_id');
>  -    my $message    = $app->param('welcome-message-text');
>  -    my $blog_class = $app->model('blog');
>  -    my $blog       = $blog_class->load($blog_id)
>  -      or return $app->error( $app->translate("Invalid blog") );
>  -    $blog->welcome_msg($message);
>  -    $blog->save;
>  -    $app->redirect(
>  -        $app->uri( mode => 'menu', args => { blog_id => $blog_id } ) );
>  -}
>  -
>  -sub upgrade {
>  -    my $app = shift;
>  -
>  -    # check for an empty database... no author table would do it...
>  -    my $driver         = MT::Object->driver;
>  -    my $upgrade_script = $app->config('UpgradeScript');
>  -    my $user_class     = MT->model('author');
>  -    if ( !$driver || !$driver->table_exists($user_class) ) {
>  -        return $app->redirect( $app->path
>  -              . $upgrade_script
>  -              . $app->uri_params( mode => 'install' ) );
>  -    }
>  -
>  -    return $app->redirect( $app->path . $upgrade_script );
>  -}
>  -
>  -sub logout {
>  -    my $app = shift;
>  -    $app->SUPER::logout(@_);
>  -}
>  -
>  -sub start_recover {
>  -    my $app = shift;
>  -    $app->add_breadcrumb( $app->translate('Password Recovery') );
>  -    $app->load_tmpl('dialog/recover.tmpl');
>  -}
>  -
>  -sub recover_password {
>  -    my $app   = shift;
>  -    my $q     = $app->param;
>  -    my $name  = $q->param('name');
>  -    my $class = ref $app eq 'MT::App::Upgrader' ? 'MT::BasicAuthor' :
>  $app->model('author');
>  -    eval "use $class;";
>  -    my @author = $class->load( { name => $name } );
>  -    my $author;
>  -    foreach (@author) {
>  -        next unless $_->password && ( $_->password ne '(none)' );
>  -        $author = $_;
>  -    }
>  -
>  -    my ( $rc, $res ) =
>  -      $app->reset_password( $author, $q->param('hint'), $name );
>  -
>  -    if ($rc) {
>  -        $app->add_breadcrumb( $app->translate('Password Recovery') );
>  -        $app->load_tmpl(
>  -            'dialog/recover.tmpl',
>  -            {
>  -                recovered => 1,
>  -                email     => $author->email
>  -            }
>  -        );
>  -    }
>  -    else {
>  -        $app->error($res);
>  -    }
>  -}
>  -
>  -sub recover_profile_password {
>  -    my $app = shift;
>  -    $app->validate_magic or return;
>  -    return $app->errtrans("Permission denied.")
>  -      unless $app->user->is_superuser();
>  -
>  -    my $q = $app->param;
>  -
>  -    require MT::Auth;
>  -    require MT::Log;
>  -    if ( !MT::Auth->can_recover_password ) {
>  -        $app->log(
>  -            {
>  -                message => $app->translate(
>  -"Invalid password recovery attempt; can't recover password in this
>  configuration"
>  -                ),
>  -                level    => MT::Log::SECURITY(),
>  -                class    => 'system',
>  -                category => 'recover_profile_password',
>  -            }
>  -        );
>  -        return $app->error("Can't recover password in this configuration");
>  -    }
>  -
>  -    my $author_id = $q->param('author_id');
>  -    my $author    = MT::Author->load($author_id);
>  -
>  -    return $app->error( $app->translate("Invalid author_id") )
>  -      if !$author || $author->type != MT::Author::AUTHOR() || !$author_id;
>  -
>  -    my ( $rc, $res ) =
>  -      $app->reset_password( $author, $author->hint, $author->name );
>  -
>  -    if ($rc) {
>  -        my $url = $app->uri(
>  -            'mode' => 'view',
>  -            args   => { _type => 'author', recovered => 1, id => $author_id
>  }
>  -        );
>  -        $app->redirect($url);
>  -    }
>  -    else {
>  -        $app->error($res);
>  -    }
>  -}
>  -
>  -sub reset_password {
>  -    my $app      = shift;
>  -    my ($author) = $_[0];
>  -    my $hint     = $_[1];
>  -    my $name     = $_[2];
>  -
>  -    require MT::Auth;
>  -    require MT::Log;
>  -    if ( !MT::Auth->can_recover_password ) {
>  -        $app->log(
>  -            {
>  -                message => $app->translate(
>  -"Invalid password recovery attempt; can't recover password in this
>  configuration"
>  -                ),
>  -                level    => MT::Log::SECURITY(),
>  -                class    => 'system',
>  -                category => 'recover_password',
>  -            }
>  -        );
>  -        return ( 0,
>  -            $app->translate("Can't recover password in this configuration")
>  );
>  -    }
>  -
>  -    $app->log(
>  -        {
>  -            message => $app->translate(
>  -                "Invalid user name '[_1]' in password recovery attempt",
>  $name
>  -            ),
>  -            level    => MT::Log::SECURITY(),
>  -            class    => 'system',
>  -            category => 'recover_password',
>  -        }
>  -      ),
>  -      return ( 0, $app->translate("User name or password hint is
>  incorrect.") )
>  -      unless $author;
>  -    return ( 0,
>  -        $app->translate("User has not set pasword hint; cannot recover
>  password")
>  -    ) if ( $hint && !$author->hint );
>  -
>  -    $app->log(
>  -        {
>  -            message => $app->translate(
>  -                "Invalid attempt to recover password (used hint '[_1]')",
>  -                $hint
>  -            ),
>  -            level    => MT::Log::SECURITY(),
>  -            class    => 'system',
>  -            category => 'recover_password'
>  -        }
>  -      ),
>  -      return ( 0, $app->translate("User name or password hint is
>  incorrect.") )
>  -      unless $author->hint eq $hint;
>  -
>  -    return ( 0, $app->translate("User does not have email address") )
>  -      unless $author->email;
>  -
>  -    my @pool = ( 'a' .. 'z', 0 .. 9 );
>  -    my $pass = '';
>  -    for ( 1 .. 8 ) { $pass .= $pool[ rand @pool ] }
>  -    $author->set_password($pass);
>  -    $author->save;
>  -    my $message =
>  -      $app->translate(
>  -"Password was reset for user '[_1]' (user #[_2]). Password was sent to the
>  following address: [_3]",
>  -        $author->name, $author->id, $author->email );
>  -    $app->log(
>  -        {
>  -            message  => $message,
>  -            level    => MT::Log::SECURITY(),
>  -            class    => 'system',
>  -            category => 'recover_password'
>  -        }
>  -    );
>  -
>  -    my $address =
>  -      defined $author->nickname
>  -      ? $author->nickname . ' <' . $author->email . '>'
>  -      : $author->email;
>  -    my %head = (
>  -        id      => 'recover_password',
>  -        To      => $address,
>  -        From    => $app->config('EmailAddressMain') || $address,
>  -        Subject => $app->translate("Password Recovery")
>  -    );
>  -    my $charset = $app->charset;
>  -    my $mail_enc = uc( $app->config('MailEncoding') || $charset );
>  -    $head{'Content-Type'} = qq(text/plain; charset="$mail_enc");
>  -
>  -    my $body = $app->build_email( 'recover-password.tmpl',
>  -        { user_password => $pass, link_to_login => $app->base .
>  $app->mt_uri }
>  -    );
>  -    $body = wrap_text( $body, 72 );
>  -    require MT::Mail;
>  -    MT::Mail->send( \%head, $body )
>  -      or return (
>  -        0,
>  -        $app->translate(
>  -            "Error sending mail ([_1]); please fix the problem, then "
>  -              . "try again to recover your password.",
>  -            MT::Mail->errstr
>  -        )
>  -      );
>  -    ( 1, $message );
>  -}
>  -
>  -sub js_tag_list {
>  -    my $app     = shift;
>  -    my $blog_id = $app->param('blog_id');
>  -    my $type    = $app->param('_type') || 'entry';
>  -
>  -    my $class = $app->model($type)
>  -      or return $app->json_error( $app->translate("Invalid request.") );
>  -    my $result;
>  -    if (
>  -        my $tag_list = MT::Tag->cache(
>  -            blog_id => $blog_id,
>  -            class   => $class,
>  -            private => 1,
>  -        )
>  -      )
>  -    {
>  -        $result = { tags => $tag_list };
>  -    }
>  -    else {
>  -        $result = { tags => {} };
>  -    }
>  -    $app->json_result($result);
>  -}
>  -
>  -sub js_tag_check {
>  -    my $app       = shift;
>  -    my $name      = $app->param('tag_name');
>  -    my $blog_id   = $app->param('blog_id');
>  -    my $type      = $app->param('_type') || 'entry';
>  -    my $tag_class = $app->model('tag')
>  -      or return $app->json_error( $app->translate("Invalid request.") );
>  -    my $tag =
>  -      $tag_class->load( { name => $name }, { binary => { name => 1 } } );
>  -    my $class = $app->model($type)
>  -      or $app->json_error( $app->translate("Invalid request.") );
>  -    if ( $tag && $blog_id ) {
>  -        my $ot_class = $app->model('objecttag');
>  -        my $count    = $ot_class->count(
>  -            {
>  -                object_datasource => $class->datasource,
>  -                blog_id           => $blog_id,
>  -                tag_id            => $tag->id
>  -            }
>  -        );
>  -        undef $tag unless $count;
>  -    }
>  -    return $app->json_result( { exists => $tag ? 'true' : 'false' } );
>  -}
>  -
>  -sub recover_passwords {
>  -    my $app = shift;
>  -    my @id  = $app->param('id');
>  -
>  -    return $app->errtrans("Permission denied.")
>  -      unless $app->user->is_superuser();
>  -
>  -    my $class = ref $app eq 'MT::App::Upgrader' ? 'MT::BasicAuthor' :
>  $app->model('author');
>  -    eval "use $class;";
>  -
>  -    my @msg_loop;
>  -    foreach (@id) {
>  -        my $author = $class->load($_);
>  -        my ( $rc, $res ) = $app->reset_password( $author, $author->hint );
>  -        push @msg_loop, { message => $res };
>  -    }
>  -
>  -    $app->load_tmpl( 'recover_password_result.tmpl',
>  -        { message_loop => \@msg_loop, return_url => $app->return_uri } );
>  -}
>  -
>  -sub _merge_default_assignments {
>  -    my $app = shift;
>  -    my ( $data, $hasher, $type, $id ) = @_;
>  -
>  -    if ( my $def = $app->config->DefaultAssignments ) {
>  -        my @def = split ',', $def;
>  -        while ( my $role_id = shift @def ) {
>  -            my $blog_id = shift @def;
>  -            next unless $role_id && $blog_id;
>  -            next if ( $type eq 'role' ) && ( $id != $role_id );
>  -            next if ( $type eq 'blog' ) && ( $id != $blog_id );
>  -            my $obj = MT::Association->new;
>  -            $obj->role_id($role_id);
>  -            $obj->blog_id($blog_id);
>  -            $obj->id( 'PSEUDO-' . $role_id . '-' . $blog_id );
>  -            my $row = $obj->column_values();
>  -            $hasher->( $obj, $row ) if $hasher;
>  -            $row->{user_id}   = 'PSEUDO';
>  -            $row->{user_name} = $app->translate('(newly created user)');
>  -            push @$data, $row;
>  -        }
>  -    }
>  -}
>  -
>  -sub build_asset_hasher {
>  -    my $app = shift;
>  -    my (%param) = @_;
>  -    my ($default_thumb_width, $default_thumb_height,
>  $default_preview_width,
>  -        $default_preview_height) =
>  -        @param{qw( ThumbWidth ThumbHeight PreviewWidth PreviewHeight )};
>  -
>  -    require File::Basename;
>  -    require JSON;
>  -    my %blogs;
>  -    return sub {
>  -        my ( $obj, $row, %param ) = @_;
>  -        my ($thumb_width, $thumb_height) = @param{qw( ThumbWidth
>  ThumbHeight )};
>  -        $row->{id} = $obj->id;
>  -        my $blog = $blogs{ $obj->blog_id } ||= $obj->blog;
>  -        $row->{blog_name} = $blog ? $blog->name : '-';
>  -        $row->{url} = $obj->url; # this has to be called to calculate
>  -        $row->{asset_type} = $obj->class_type;
>  -        $row->{asset_class_label} = $obj->class_label;
>  -        my $file_path = $obj->file_path; # has to be called to calculate
>  -        my $meta = $obj->metadata;
>  -        if ( $file_path && ( -f $file_path ) ) {
>  -            $row->{file_path} = $file_path;
>  -            $row->{file_name} = File::Basename::basename( $file_path );
>  -            my @stat = stat( $file_path );
>  -            my $size = $stat[7];
>  -            $row->{file_size} = $size;
>  -            if ( $size < 1024 ) {
>  -                $row->{file_size_formatted} = sprintf( "%d Bytes", $size );
>  -            }
>  -            elsif ( $size < 1024000 ) {
>  -                $row->{file_size_formatted} =
>  -                  sprintf( "%.1f KB", $size / 1024 );
>  -            }
>  -            else {
>  -                $row->{file_size_formatted} =
>  -                  sprintf( "%.1f MB", $size / 1024000 );
>  -            }
>  -            $meta->{'file_size'} = $row->{file_size_formatted};
>  -        }
>  -        else {
>  -            $row->{file_is_missing} = 1 if $file_path;
>  -        }
>  -        $row->{file_label} = $row->{label} = $obj->label ||
>  $row->{file_name} || $app->translate('Untitled');
>  -
>  -        if ($obj->has_thumbnail) {
>  -            $row->{has_thumbnail} = 1;
>  -            my $height = $thumb_height || $default_thumb_height || 75;
>  -            my $width  = $thumb_width  || $default_thumb_width  || 75;
>  -            @$meta{qw( thumbnail_url thumbnail_width thumbnail_height )}
>  -              = $obj->thumbnail_url( Height => $height, Width => $width );
>  -
>  -            $meta->{thumbnail_width_offset}  = int(($width  -
>  $meta->{thumbnail_width})  / 2);
>  -            $meta->{thumbnail_height_offset} = int(($height -
>  $meta->{thumbnail_height}) / 2);
>  -
>  -            if ($default_preview_width && $default_preview_height) {
>  -                @$meta{qw( preview_url preview_width preview_height )}
>  -                  = $obj->thumbnail_url(
>  -                    Height => $default_preview_height,
>  -                    Width  => $default_preview_width,
>  -                );
>  -                $meta->{preview_width_offset}  =
>  int(($default_preview_width  - $meta->{preview_width})  / 2);
>  -                $meta->{preview_height_offset} =
>  int(($default_preview_height - $meta->{preview_height}) / 2);
>  -            }
>  -        }
>  -        else {
>  -            $row->{has_thumbnail} = 0;
>  -        }
>  -
>  -        my $ts = $obj->created_on;
>  -        if ( my $by = $obj->created_by ) {
>  -            my $user = MT::Author->load($by);
>  -            $row->{created_by} = $user ? $user->name : '';
>  -        }
>  -        if ($ts) {
>  -            $row->{created_on_formatted} =
>  -              format_ts( LISTING_DATE_FORMAT, $ts, $blog, $app->user ?
>  $app->user->preferred_language : undef );
>  -            $row->{created_on_time_formatted} =
>  -              format_ts( LISTING_TIMESTAMP_FORMAT, $ts, $blog, $app->user ?
>  $app->user->preferred_language : undef );
>  -            $row->{created_on_relative} = relative_date( $ts, time, $blog
>  );
>  -        }
>  -
>  -        @$row{keys %$meta} = values %$meta;
>  -        $row->{metadata_json} = JSON::objToJson($meta);
>  -        $row;
>  -    };
>  -}
>  -
>  -sub list_assets {
>  -    my $app = shift;
>  -
>  -    my $blog_id = $app->param('blog_id');
>  -    my $blog;
>  -    if ($blog_id) {
>  -        my $blog_class = $app->model('blog');
>  -        $blog = $blog_class->load($blog_id)
>  -          or return $app->errtrans("Invalid request.");
>  -        my $perms = $app->permissions;
>  -        return $app->errtrans("Permission denied.")
>  -          unless $app->user->is_superuser
>  -          || (
>  -            $perms
>  -            && (   $perms->can_edit_assets
>  -                || $perms->can_edit_all_posts
>  -                || $perms->can_create_post )
>  -          );
>  -    }
>  -
>  -    my $asset_class = $app->model('asset') or return;
>  -    my %terms;
>  -    my %args = ( sort => 'created_on', direction => 'descend' );
>  -
>  -    my $class_filter;
>  -    my $filter = ( $app->param('filter') || '' );
>  -    if ( $filter eq 'class' ) {
>  -        $class_filter = $app->param('filter_val');
>  -    }
>  -    elsif ($filter eq 'userpic') {
>  -        $class_filter = 'image';
>  -        $terms{created_by} = $app->param('filter_val');
>  -
>  -        my $tag = MT::Tag->load( { name => '@userpic' },
>  -            { binary => { name => 1 } } );
>  -        if ($tag) {
>  -            require MT::ObjectTag;
>  -            $args{'join'} = MT::ObjectTag->join_on(
>  -                'object_id',
>  -                {
>  -                    tag_id            => $tag->id,
>  -                    object_datasource => MT::Asset->datasource
>  -                },
>  -                { unique => 1 }
>  -            );
>  -        }
>  -    }
>  -
>  -    $app->add_breadcrumb( $app->translate("Files") );
>  -    if ($blog_id) {
>  -        $terms{blog_id} = $blog_id;
>  -    }
>  -    elsif ((defined $blog_id) && ($blog_id ne '')) {
>  -        $terms{blog_id} = '0';
>  -    }
>  -    else {
>  -        unless ( $app->user->is_superuser ) {
>  -            my @perms = MT::Permission->load( { author_id => $app->user->id
>  } );
>  -            my @blog_ids;
>  -            push @blog_ids, $_->blog_id
>  -              foreach grep { $_->can_edit_assets } @perms;
>  -            $terms{blog_id} = \@blog_ids;
>  -        }
>  -    }
>  -
>  -    my $hasher = $app->build_asset_hasher(
>  -        PreviewWidth => 240, PreviewHeight => 240 );
>  -
>  -    if ($class_filter) {
>  -        my $asset_pkg = MT::Asset->class_handler($class_filter);
>  -        $terms{class} = $asset_pkg->type_list;
>  -    }
>  -    else {
>  -        $terms{class} = '*';    # all classes
>  -    }
>  -
>  -    # identifier => name
>  -    my $classes = MT::Asset->class_labels;
>  -    my @class_loop;
>  -    foreach my $class ( keys %$classes ) {
>  -        next if $class eq 'asset';
>  -        push @class_loop,
>  -          {
>  -            class_id    => $class,
>  -            class_label => $classes->{$class},
>  -          };
>  -    }
>  -
>  -    # Now, sort it
>  -    @class_loop = sort { $a->{class_label} cmp $b->{class_label} }
>  @class_loop;
>  -
>  -    my $dialog_view = $app->param('dialog_view') ? 1 : 0;
>  -    my $perms = $app->permissions;
>  -    my %carry_params = map { $_ => $app->param($_) || '' }
>  -        (qw( edit_field upload_mode require_type next_mode asset_select ));
>  -    $carry_params{'user_id'} = $app->param('filter_val')
>  -        if $filter eq 'userpic';
>  -    $app->_set_start_upload_params(\%carry_params);
>  -    $app->listing(
>  -        {
>  -            terms    => \%terms,
>  -            args     => \%args,
>  -            type     => 'asset',
>  -            code     => $hasher,
>  -            template => $dialog_view
>  -            ? 'dialog/asset_list.tmpl'
>  -            : '',
>  -            params => {
>  -                (
>  -                    $blog
>  -                    ? (
>  -                        blog_id   => $blog_id,
>  -                        blog_name => $blog->name
>  -                          || '',
>  -                        edit_blog_id => $blog_id,
>  -                      )
>  -                    : (),
>  -                ),
>  -                is_image         => defined $class_filter
>  -                  && $class_filter eq 'image' ? 1 : 0,
>  -                dialog_view      => $dialog_view,
>  -                search_label     => MT::Asset->class_label_plural,
>  -                search_type      => 'asset',
>  -                class_loop       => \@class_loop,
>  -                can_delete_files => (
>  -                    $perms ? $perms->can_edit_assets :
>  $app->user->is_superuser
>  -                ),
>  -                nav_assets       => 1,
>  -                panel_searchable => 1,
>  -                object_type      => 'asset',
>  -                %carry_params,
>  -            },
>  -        }
>  -    );
>  -}
>  -
>  -sub list_roles {
>  -    my $app = shift;
>  -
>  -    $app->return_to_dashboard( redirect => 1 ) if $app->param('blog_id');
>  -
>  -    my $pref = $app->list_pref('role');
>  -    my $all_perms;
>  -    if ( $pref->{view_expanded} ) {
>  -        my @all_perms = @{ MT::Permission->perms() };
>  -        $all_perms = [@all_perms];
>  -        foreach (@$all_perms) {
>  -            $_->[1] = $app->translate( $_->[1] );
>  -        }
>  -    }
>  -
>  -    my $author_class = $app->model('author');
>  -    my $assoc_class  = $app->model('association');
>  -    my $hasher       = sub {
>  -        my ( $obj, $row ) = @_;
>  -        my $user_count = $assoc_class->count(
>  -            {
>  -                role_id   => $obj->id,
>  -                author_id => [ 1, undef ],
>  -            },
>  -            {
>  -                unique     => 'author_id',
>  -                range_incl => { author_id => 1 },
>  -            }
>  -        );
>  -        $row->{members} = $user_count;
>  -        $row->{weblogs} = $assoc_class->count(
>  -            {
>  -                role_id => $obj->id,
>  -                blog_id => [ 1, undef ],
>  -            },
>  -            {
>  -                unique     => 'blog_id',
>  -                range_incl => { blog_id => 1 },
>  -            }
>  -        );
>  -        if ( $obj->created_by ) {
>  -            my $user = $author_class->load( $obj->created_by );
>  -            $row->{created_by} = $user ? $user->name : '';
>  -        }
>  -        else {
>  -            $row->{created_by} = '';
>  -        }
>  -
>  -        # populate permissions for the expanded view
>  -        if ( $pref->{view_expanded} ) {
>  -            my @perms;
>  -            foreach (@$all_perms) {
>  -                next unless length( $_->[1] || '' );
>  -                push @perms, { name => $app->translate( $_->[1] ) }
>  -                  if $obj->has( $_->[0] );
>  -            }
>  -            $row->{perm_loop} = \@perms;
>  -        }
>  -    };
>  -    unless ( $app->user->is_superuser() ) {
>  -        return $app->errtrans("Permission denied.");
>  -    }
>  -    $app->add_breadcrumb( $app->translate("Roles") );
>  -    $app->listing(
>  -        {
>  -            args   => { sort => 'name' },
>  -            type   => 'role',
>  -            code   => $hasher,
>  -            params => {
>  -                nav_privileges    => 1,
>  -                list_noncron      => 1,
>  -                can_create_role   => $app->user->is_superuser,
>  -                has_expanded_mode => 1,
>  -                search_label      => $app->translate('Users'),
>  -                object_type       => 'role',
>  -                screen_id         => 'list-role',
>  -            },
>  -        }
>  -    );
>  -}
>  -
>  -sub list_associations {
>  -    my $app = shift;
>  -
>  -    my $blog_id   = $app->param('blog_id');
>  -    my $author_id = $app->param('author_id');
>  -    my $role_id   = $app->param('role_id');
>  -
>  -    my $this_user = $app->user;
>  -    if ( !$this_user->is_superuser ) {
>  -        if (
>  -            (
>  -                   !$blog_id
>  -                || !$this_user->permissions($blog_id)->can_administer_blog
>  -            )
>  -            && ( !$author_id || ( $author_id != $this_user->id ) )
>  -          )
>  -        {
>  -            return $app->errtrans("Permission denied.");
>  -        }
>  -    }
>  -
>  -    my ( $user, $role );
>  -    $app->error(undef);
>  -
>  -    if ($author_id) {
>  -        $app->add_breadcrumb( $app->translate('Users'),
>  -              $app->user->is_superuser
>  -            ? $app->uri( mode => 'list_authors' )
>  -            : undef );
>  -        if ( 'PSEUDO' ne $author_id ) {
>  -            my $author_class = $app->model('author');
>  -            $user = $author_class->load($author_id);
>  -            $app->add_breadcrumb(
>  -                $user->name,
>  -                $app->uri(
>  -                    mode => 'view',
>  -                    args => { _type => 'author', id => $author_id }
>  -                )
>  -            );
>  -        }
>  -        else {
>  -            $app->add_breadcrumb( $app->translate('(newly created user)')
>  );
>  -        }
>  -        $app->add_breadcrumb( $app->translate('User Associations') );
>  -    }
>  -    if ($role_id) {
>  -        my $role_class = $app->model('role') or return;
>  -        $role = $role_class->load($role_id);
>  -        $app->add_breadcrumb( $app->translate("Roles"),
>  -            $app->uri( mode => "list_roles" ) );
>  -        $app->add_breadcrumb(
>  -            $role->name,
>  -            $app->uri(
>  -                mode => 'edit_role',
>  -                args => { _type => 'role', id => $role_id }
>  -            )
>  -        );
>  -        $app->add_breadcrumb( $app->translate("Role Users & Groups") );
>  -    }
>  -    if ( !$role_id  && !$author_id ) {
>  -        if ($blog_id) {
>  -            $app->add_breadcrumb( $app->translate("Users") );
>  -        }
>  -        else {
>  -            $app->add_breadcrumb( $app->translate("Associations") );
>  -        }
>  -    }
>  -
>  -    my $pref = $app->list_pref('association');
>  -    my $all_perms;
>  -    if ( $pref->{view_expanded} ) {
>  -        my @all_perms = @{ MT::Permission->perms() };
>  -        $all_perms = [@all_perms];
>  -        foreach (@$all_perms) {
>  -            $_->[1] = $app->translate( $_->[1] );
>  -        }
>  -    }
>  -
>  -    # Supplies additional parameters for the row being listed
>  -    my %users;
>  -    $users{ $this_user->id } = $this_user;
>  -    my $hasher = sub {
>  -        my ( $obj, $row ) = @_;
>  -        if ( my $user = $obj->user ) {
>  -            $row->{user_id}   = $user->id;
>  -            $row->{user_name} = $user->name;
>  -        }
>  -        if ( my $role = $obj->role ) {
>  -            $row->{role_name} = $role->name;
>  -
>  -            # populate permissions for the expanded view
>  -            if ( $pref->{view_expanded} ) {
>  -                my @perms;
>  -                foreach (@$all_perms) {
>  -                    next unless length( $_->[1] || '' );
>  -                    push @perms, { name => $app->translate( $_->[1] ) }
>  -                      if $role->has( $_->[0] );
>  -                }
>  -                $row->{perm_loop} = \@perms;
>  -            }
>  -        }
>  -        else {
>  -            $row->{role_name} = $app->translate("(Custom)");
>  -        }
>  -        if ( my $blog = $obj->blog ) {
>  -            $row->{blog_name} = $blog->name;
>  -        }
>  -        if ( my $ts = $obj->created_on ) {
>  -            $row->{created_on_formatted} =
>  -              format_ts( LISTING_DATE_FORMAT, $ts, $obj->blog, $app->user ?
>  $app->user->preferred_language : undef );
>  -            $row->{created_on_time_formatted} =
>  -              format_ts( LISTING_TIMESTAMP_FORMAT, $ts, $obj->blog,
>  $app->user ? $app->user->preferred_language : undef );
>  -            $row->{created_on_relative} =
>  -              relative_date( $ts, time, $obj->blog );
>  -        }
>  -        if ( $row->{created_by} ) {
>  -            my $created_user = $users{ $row->{created_by} } ||=
>  -              MT::Author->load( $row->{created_by} );
>  -            if ($created_user) {
>  -                $row->{created_by} = $created_user->name;
>  -            }
>  -            else {
>  -                $row->{created_by} = $app->translate('(user deleted)');
>  -            }
>  -        }
>  -    };
>  -    $app->model('association') or return;
>  -    my $types;
>  -    if ( !$author_id && !$blog_id ) {
>  -        $types = [
>  -            MT::Association::USER_BLOG_ROLE(),
>  -            MT::Association::USER_ROLE(),
>  -        ];
>  -    }
>  -    elsif ( !$author_id ) {
>  -        $types = [
>  -            MT::Association::USER_BLOG_ROLE(),
>  -        ];
>  -    }
>  -    elsif ($author_id) {
>  -        $types =
>  -          [ MT::Association::USER_BLOG_ROLE(), MT::Association::USER_ROLE()
>  ];
>  -    }
>  -
>  -    my $pre_build = sub {
>  -        my ($param) = @_;
>  -        my $data = $param->{object_loop} || [];
>  -
>  -        #TODO: handle group_view
>  -        if ( $param->{user_view}
>  -            && ( 'PSEUDO' ne $param->{edit_author_id} ) )
>  -        {
>  -
>  -            # don't merge
>  -        }
>  -        elsif ( $param->{role_view} ) {
>  -            $app->_merge_default_assignments( $data, $hasher, 'role',
>  -                $param->{role_id} );
>  -        }
>  -        elsif ( $param->{blog_view} ) {
>  -            $app->_merge_default_assignments( $data, $hasher, 'blog',
>  -                $param->{blog_id} );
>  -        }
>  -        else {
>  -            $app->_merge_default_assignments( $data, $hasher, 'all' );
>  -        }
>  -    };
>  -
>  -    return $app->listing(
>  -        {
>  -            args  => { sort => 'created_on', direction => 'descend' },
>  -            type  => 'association',
>  -            code  => $hasher,
>  -            terms => {
>  -                type => $types,
>  -                $author_id ? ( author_id => $author_id ) : (),
>  -                $blog_id   ? ( blog_id   => $blog_id )   : (),
>  -                $role_id   ? ( role_id   => $role_id )   : (),
>  -            },
>  -            pre_build => $pre_build,
>  -            params => {
>  -                can_create_association => $app->user->is_superuser || (
>  $blog_id
>  -                    &&
>  $app->user->permissions($blog_id)->can_administer_blog ),
>  -                has_expanded_mode => 1,
>  -                nav_privileges =>
>  -                  ( $author_id || $blog_id ? 0 : 1 ) || $role_id,
>  -                nav_authors => ( $author_id || $blog_id ? 1 : 0 )
>  -                  && !$role_id,
>  -                blog_view     => $blog_id   ? 1 : 0,
>  -                user_view     => $author_id ? 1 : 0,
>  -                role_view     => $role_id   ? 1 : 0,
>  -                $role_id
>  -                ? (
>  -                    role_id   => $role_id,
>  -                    role_name => $role->name,
>  -                  )
>  -                : (),
>  -                $author_id
>  -                ? (
>  -                    edit_author_id   => $author_id,
>  -                    edit_name => $user
>  -                    ? ( $user->nickname ? $user->nickname : $user->name )
>  -                    : $app->translate('(newly created user)'),
>  -                    edit_object => $app->translate('The user'),
>  -                    group_count => $user ? $user->group_count() : 0,
>  -                    status_enabled => $user ? ( $user->is_active ? 1 : 0 )
>  : 0,
>  -                    status_pending => $user
>  -                    ? ( $user->status == MT::Author::PENDING() ? 1 : 0 )
>  -                    : 0,
>  -                  )
>  -                : (),
>  -                saved         => $app->param('saved')         || 0,
>  -                saved_deleted => $app->param('saved_deleted') || 0,
>  -                usergroup_view => !$author_id  && !$role_id,
>  -                blog_id => $blog_id,
>  -                search_label => $app->translate('Users'),
>  -                object_type  => 'association',
>  -                pt_name => $app->translate('User'),
>  -                screen_id => "list-associations",
>  -            },
>  -        }
>  -    );
>  -}
>  -
>  -sub list_tag {
>  -    my $app = shift;
>  -    my %param;
>  -    my $filter_key = $app->param('filter_key') || 'entry';
>  -    my $type       = $app->param('_type')      || $filter_key;
>  -    my $plural;
>  -    $param{TagObjectType} = $type;
>  -    $param{Package}       = $app->model($type);
>  -
>  -    if ( !$app->blog && !$app->user->is_superuser ) {
>  -        return $app->errtrans("Permission denied.");
>  -    }
>  -
>  -    my $