diff --git a/Sources/Actions/Activate.php b/Sources/Actions/Activate.php index 67819d30c8..194d2e6b2d 100644 --- a/Sources/Actions/Activate.php +++ b/Sources/Actions/Activate.php @@ -25,6 +25,8 @@ use SMF\Lang; use SMF\Logging; use SMF\Mail; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Routable; use SMF\Security; use SMF\Theme; @@ -34,35 +36,11 @@ /** * Activates a user's account. */ -class Activate implements ActionInterface, Routable +class Activate implements ActionInterface, ProvidesSubActionInterface, Routable { use ActionRouter; use ActionTrait; - - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The sub-action to call. - */ - public string $subaction = ''; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'activate' => 'activate', - 'resend' => 'resend', - ]; + use ProvidesSubActionTrait; /********************* * Internal properties @@ -106,17 +84,15 @@ public function execute(): void return; } - if (empty($this->subaction)) { + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); + + if (empty($this->sub_action)) { $this->showResendRequest(); return; } - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -234,6 +210,9 @@ public static function parseRoute(array $route, array $params = []): array */ protected function __construct() { + $this->addSubAction('activate', [$this, 'activate']); + $this->addSubAction('resend', [$this, 'resend']); + // Logged in users should not bother to activate their accounts if (!empty(User::$me->id)) { Utils::redirectexit('action=profile'); @@ -259,10 +238,6 @@ protected function __construct() if (!empty($_REQUEST['code'])) { $_REQUEST['sa'] = 'activate'; } - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } } /** diff --git a/Sources/Actions/Admin/Attachments.php b/Sources/Actions/Admin/Attachments.php index 4fb4233033..c5530eb87e 100644 --- a/Sources/Actions/Admin/Attachments.php +++ b/Sources/Actions/Admin/Attachments.php @@ -29,6 +29,8 @@ use SMF\ItemList; use SMF\Lang; use SMF\Menu; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Sapi; use SMF\SecurityToken; use SMF\Theme; @@ -41,47 +43,12 @@ /** * Maintains and manages attachments and avatars. */ -class Attachments implements ActionInterface +class Attachments implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'browse'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'attachments' => 'attachmentSettings', - 'avatars' => 'avatarSettings', - 'browse' => 'browse', - 'maintenance' => 'maintain', - 'remove' => 'remove', - 'byage' => 'removeByAge', - 'bysize' => 'removeBySize', - 'removeall' => 'removeAll', - 'repair' => 'repair', - 'attachpaths' => 'paths', - 'transfer' => 'transfer', - ]; - /**************** * Public methods ****************/ @@ -91,11 +58,9 @@ class Attachments implements ActionInterface */ public function execute(): void { - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); + IntegrationHook::call('integrate_manage_attachments', [&$this->sub_actions]); - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -2572,6 +2537,19 @@ public static function attachDirStatus(string $dir, int $expected_files): array */ protected function __construct() { + $this->setDefaultSubAction('browse'); + $this->addSubAction('attachments', [$this, 'attachmentSettings']); + $this->addSubAction('avatars', [$this, 'avatarSettings']); + $this->addSubAction('browse', [$this, 'browse']); + $this->addSubAction('maintenance', [$this, 'maintain']); + $this->addSubAction('remove', [$this, 'remove']); + $this->addSubAction('byage', [$this, 'removeByAge']); + $this->addSubAction('bysize', [$this, 'removeBySize']); + $this->addSubAction('removeall', [$this, 'removeAll']); + $this->addSubAction('repair', [$this, 'repair']); + $this->addSubAction('attachpaths', [$this, 'paths']); + $this->addSubAction('transfer', [$this, 'transfer']); + // You have to be able to moderate the forum to do this. User::$me->isAllowedTo('manage_attachments'); @@ -2585,14 +2563,6 @@ protected function __construct() 'description' => Lang::$txt['attachments_desc'], ]; - IntegrationHook::call('integrate_manage_attachments', [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[strtolower($_REQUEST['sa'])])) { - $this->subaction = strtolower($_REQUEST['sa']); - } - - Utils::$context['sub_action'] = &$this->subaction; - // Default page title is good. Utils::$context['page_title'] = Lang::$txt['attachments_avatars']; } diff --git a/Sources/Actions/Admin/Bans.php b/Sources/Actions/Admin/Bans.php index fb803687f0..d20ef46a3e 100644 --- a/Sources/Actions/Admin/Bans.php +++ b/Sources/Actions/Admin/Bans.php @@ -31,6 +31,8 @@ use SMF\Lang; use SMF\Logging; use SMF\Menu; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\SecurityToken; use SMF\Theme; use SMF\Time; @@ -40,42 +42,12 @@ /** * This class contains all the methods used for the ban center. */ -class Bans implements ActionInterface +class Bans implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'list'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'list' => 'list', - 'edit' => 'edit', - 'add' => 'edit', - 'browse' => 'browseTriggers', - 'edittrigger' => 'editTrigger', - 'log' => 'log', - ]; - /**************** * Public methods ****************/ @@ -87,11 +59,19 @@ public function execute(): void { User::$me->isAllowedTo('manage_bans'); - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); + IntegrationHook::call('integrate_manage_bans', [&$this->sub_actions]); + + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); - if (!empty($call)) { - call_user_func($call); + // Mark the appropriate menu entry as selected + if (array_key_exists($this->sub_action, Menu::$loaded['admin']->tab_data['tabs'])) { + Menu::$loaded['admin']->tab_data['tabs'][$this->sub_action]['is_selected'] = true; } + + Utils::$context['page_title'] = Lang::$txt['ban_title']; + Utils::$context['sub_action'] = $this->sub_action; + + $this->callSubAction(); } /** @@ -1472,6 +1452,13 @@ public static function list_getNumBanLogEntries(): int */ protected function __construct() { + $this->addSubAction('list', [$this, 'list']); + $this->addSubAction('edit', [$this, 'edit']); + $this->addSubAction('add', [$this, 'edit']); + $this->addSubAction('browse', [$this, 'browseTriggers']); + $this->addSubAction('edittrigger', [$this, 'editTrigger']); + $this->addSubAction('log', [$this, 'log']); + Theme::loadTemplate('ManageBans'); // Tab data might already be set if this was called from Logs::execute(). @@ -1502,20 +1489,6 @@ protected function __construct() ], ]; } - - IntegrationHook::call('integrate_manage_bans', [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } - - // Mark the appropriate menu entry as selected - if (array_key_exists($this->subaction, Menu::$loaded['admin']->tab_data['tabs'])) { - Menu::$loaded['admin']->tab_data['tabs'][$this->subaction]['is_selected'] = true; - } - - Utils::$context['page_title'] = Lang::$txt['ban_title']; - Utils::$context['sub_action'] = $this->subaction; } /** diff --git a/Sources/Actions/Admin/Boards.php b/Sources/Actions/Admin/Boards.php index 92e5352d8a..4374878b99 100644 --- a/Sources/Actions/Admin/Boards.php +++ b/Sources/Actions/Admin/Boards.php @@ -28,6 +28,8 @@ use SMF\Lang; use SMF\Menu; use SMF\Parser; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\SecurityToken; use SMF\Theme; use SMF\Url; @@ -37,53 +39,12 @@ /** * Manages and maintains the boards and categories of the forum. */ -class Boards implements ActionInterface +class Boards implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'main'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - * - * Format: 'sub-action' => array('function', 'permission') - */ - public static array $subactions = [ - 'main' => ['main', 'manage_boards'], - 'board' => ['editBoard', 'manage_boards'], - 'board2' => ['editBoard2', 'manage_boards'], - 'cat' => ['editCategory', 'manage_boards'], - 'cat2' => ['editCategory2', 'manage_boards'], - 'move' => ['main', 'manage_boards'], - 'newcat' => ['editCategory', 'manage_boards'], - 'newboard' => ['editBoard', 'manage_boards'], - 'settings' => ['settings', 'admin_forum'], - ]; - - /********************* - * Internal properties - *********************/ - - // code... - /**************** * Public methods ****************/ @@ -93,14 +54,7 @@ class Boards implements ActionInterface */ public function execute(): void { - // Have you got the proper permissions? - User::$me->isAllowedTo(self::$subactions[$this->subaction][1]); - - $call = method_exists($this, self::$subactions[$this->subaction][0]) ? [$this, self::$subactions[$this->subaction][0]] : Utils::getCallable(self::$subactions[$this->subaction][0]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -930,7 +884,24 @@ public static function getConfigVars(): array */ protected function __construct() { + if (User::$me->allowedTo('manage_boards')) { + $this->addSubAction('main', [$this, 'main']); + $this->addSubAction('board', [$this, 'editBoard']); + $this->addSubAction('board2', [$this, 'editBoard2']); + $this->addSubAction('cat', [$this, 'editCategory']); + $this->addSubAction('cat2', [$this, 'editCategory2']); + $this->addSubAction('move', [$this, 'main']); + $this->addSubAction('newcat', [$this, 'editCategory']); + $this->addSubAction('newboard', [$this, 'editBoard']); + } + + if (User::$me->allowedTo('admin_forum')) { + $this->addSubAction('settings', [$this, 'settings']); + } + // Special handling for modifycat. + $this->addSubAction('modifycat', [self::class, 'modifyCat']); + if (($_REQUEST['action'] ?? '') === 'modifycat') { self::modifyCat(); } @@ -954,10 +925,14 @@ protected function __construct() ], ]; - IntegrationHook::call('integrate_manage_boards', [&self::$subactions]); + $sub_actions = []; + IntegrationHook::call('integrate_manage_boards', [&$sub_actions]); - // Default to sub action 'main' or 'settings' depending on permissions. - $this->subaction = isset($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : (User::$me->allowedTo('manage_boards') ? 'main' : 'settings'); + foreach ($sub_actions as $sa => [$func, $perm]) { + if (User::$me->allowedTo($perm)) { + $this->addSubAction($sa, method_exists($this, $func) ? [$this, $func] : $func); + } + } } } diff --git a/Sources/Actions/Admin/Calendar.php b/Sources/Actions/Admin/Calendar.php index e295cfad1a..3f7b9082e3 100644 --- a/Sources/Actions/Admin/Calendar.php +++ b/Sources/Actions/Admin/Calendar.php @@ -29,6 +29,8 @@ use SMF\ItemList; use SMF\Lang; use SMF\Menu; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\SecurityToken; use SMF\TaskRunner; use SMF\Theme; @@ -43,40 +45,12 @@ /** * This class allows you to manage the calendar. */ -class Calendar implements ActionInterface +class Calendar implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'holidays'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'holidays' => 'holidays', - 'editholiday' => 'edit', - 'import' => 'import', - 'settings' => 'settings', - ]; - /**************** * Public methods ****************/ @@ -86,13 +60,11 @@ class Calendar implements ActionInterface */ public function execute(): void { - User::$me->isAllowedTo('admin_forum'); + IntegrationHook::call('integrate_manage_calendar', [&$this->sub_actions]); - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); + User::$me->isAllowedTo('admin_forum'); - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -634,40 +606,38 @@ public static function getConfigVars(): array */ protected function __construct() { - // Everything's gonna need this. Lang::load('Calendar+ManageCalendar'); - if (empty(Config::$modSettings['cal_enabled'])) { - unset(self::$subactions['holidays'], self::$subactions['editholiday']); - $this->subaction = 'settings'; + if (!empty(Config::$modSettings['cal_enabled'])) { + $this->addSubAction('holidays', [$this, 'holidays']); + $this->addSubAction('editholiday', [$this, 'edit']); } + $this->addSubAction('import', [$this, 'import']); + $this->addSubAction('settings', [$this, 'settings']); + // Set up the two tabs here... Menu::$loaded['admin']->tab_data = [ 'title' => Lang::$txt['manage_calendar'], 'help' => 'calendar', 'description' => Lang::$txt['calendar_settings_desc'], + 'tabs' => [ + 'import' => [ + 'description' => Lang::$txt['calendar_import_desc'], + ], + ], ]; if (!empty(Config::$modSettings['cal_enabled'])) { - Menu::$loaded['admin']->tab_data['tabs'] = [ + Menu::$loaded['admin']->tab_data['tabs'] += [ 'holidays' => [ 'description' => Lang::$txt['manage_holidays_desc'], ], - 'import' => [ - 'description' => Lang::$txt['calendar_import_desc'], - ], 'settings' => [ 'description' => Lang::$txt['calendar_settings_desc'], ], ]; } - - IntegrationHook::call('integrate_manage_calendar', [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } } } diff --git a/Sources/Actions/Admin/Features.php b/Sources/Actions/Admin/Features.php index 6d5bd02671..01c8b73c7d 100644 --- a/Sources/Actions/Admin/Features.php +++ b/Sources/Actions/Admin/Features.php @@ -30,6 +30,8 @@ use SMF\Parser; use SMF\Parsers\MarkdownParser; use SMF\Profile; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Sapi; use SMF\SecurityToken; use SMF\Theme; @@ -40,45 +42,12 @@ /** * Class to manage various core features. */ -class Features implements ActionInterface +class Features implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'basic'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'basic' => 'basic', - 'bbc' => 'bbc', - 'layout' => 'layout', - 'sig' => 'signature', - 'profile' => 'profile', - 'profileedit' => 'profileEdit', - 'likes' => 'likes', - 'mentions' => 'mentions', - 'alerts' => 'alerts', - ]; - /**************** * Public methods ****************/ @@ -88,17 +57,17 @@ class Features implements ActionInterface */ public function execute(): void { + IntegrationHook::call('integrate_modify_features', [&$this->sub_actions]); + + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); + // You need to be an admin to edit settings! User::$me->isAllowedTo('admin_forum'); Utils::$context['sub_template'] = 'show_settings'; - Utils::$context['sub_action'] = $this->subaction; + Utils::$context['sub_action'] = $this->getSubAction(); - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction(); } /** @@ -1883,6 +1852,16 @@ public static function list_getProfileFieldSize(): int */ protected function __construct() { + $this->addSubAction('basic', [$this, 'basic']); + $this->addSubAction('bbc', [$this, 'bbc']); + $this->addSubAction('layout', [$this, 'layout']); + $this->addSubAction('sig', [$this, 'signature']); + $this->addSubAction('profile', [$this, 'profile']); + $this->addSubAction('profileedit', [$this, 'profileEdit']); + $this->addSubAction('likes', [$this, 'likes']); + $this->addSubAction('mentions', [$this, 'mentions']); + $this->addSubAction('alerts', [$this, 'alerts']); + Lang::load('Help'); Lang::load('ManageSettings'); @@ -1917,12 +1896,6 @@ protected function __construct() ], ], ]; - - IntegrationHook::call('integrate_modify_features', [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } } /** diff --git a/Sources/Actions/Admin/Find.php b/Sources/Actions/Admin/Find.php index 5e617781ab..1f1fe78de0 100644 --- a/Sources/Actions/Admin/Find.php +++ b/Sources/Actions/Admin/Find.php @@ -23,6 +23,8 @@ use SMF\Lang; use SMF\Menu; use SMF\PackageManager\XmlArray; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Sapi; use SMF\User; use SMF\Utils; @@ -31,22 +33,15 @@ /** * Provides the search functionality inside the admin control panel. */ -class Find implements ActionInterface +class Find implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; + use ProvidesSubActionTrait; /******************* * Public properties *******************/ - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'internal'; - /** * @var array * @@ -129,21 +124,6 @@ class Find implements ActionInterface */ public array $include_files = []; - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'internal' => 'internal', - 'online' => 'online', - 'member' => 'member', - ]; - /**************** * Public methods ****************/ @@ -153,17 +133,19 @@ class Find implements ActionInterface */ public function execute(): void { + $this->findRequestedSubAction($_REQUEST['search_type'] ?? null); + User::$me->isAllowedTo('admin_forum'); - Utils::$context['search_type'] = $this->subaction; + Utils::$context['search_type'] = $this->sub_action; Utils::$context['search_term'] = isset($_REQUEST['search_term']) ? Utils::htmlspecialchars($_REQUEST['search_term'], ENT_QUOTES) : ''; Utils::$context['sub_template'] = 'admin_search_results'; Utils::$context['page_title'] = Lang::$txt['admin_search_results']; // Keep track of what the admin wants. - if (empty(Utils::$context['admin_preferences']['sb']) || Utils::$context['admin_preferences']['sb'] != $this->subaction) { - Utils::$context['admin_preferences']['sb'] = $this->subaction; + if (empty(Utils::$context['admin_preferences']['sb']) || Utils::$context['admin_preferences']['sb'] != $this->sub_action) { + Utils::$context['admin_preferences']['sb'] = $this->sub_action; // Update the preferences. ACP::updateAdminPreferences(); @@ -172,11 +154,7 @@ public function execute(): void if (trim(Utils::$context['search_term']) == '') { Utils::$context['search_results'] = []; } else { - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction(); } } @@ -365,7 +343,9 @@ public function online(): void */ protected function __construct() { - $this->subaction = !isset($_REQUEST['search_type']) || !isset(self::$subactions[$_REQUEST['search_type']]) ? 'internal' : $_REQUEST['search_type']; + $this->addSubAction('internal', [$this, 'internal']); + $this->addSubAction('online', [$this, 'online']); + $this->addSubAction('member', [$this, 'member']); } } diff --git a/Sources/Actions/Admin/Languages.php b/Sources/Actions/Admin/Languages.php index b879d2715f..4943896f88 100644 --- a/Sources/Actions/Admin/Languages.php +++ b/Sources/Actions/Admin/Languages.php @@ -28,6 +28,8 @@ use SMF\Menu; use SMF\PackageManager\SubsPackage; use SMF\PackageManager\XmlArray; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\SecurityToken; use SMF\Theme; use SMF\User; @@ -37,41 +39,12 @@ /** * This class handles the administration of languages tasks. */ -class Languages implements ActionInterface +class Languages implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'edit'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'edit' => 'editLanguages', - 'add' => 'add', - 'settings' => 'settings', - 'downloadlang' => 'download', - 'editlang' => 'editEntries', - ]; - /**************** * Public methods ****************/ @@ -81,11 +54,13 @@ class Languages implements ActionInterface */ public function execute(): void { - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); - if (!empty($call)) { - call_user_func($call); - } + IntegrationHook::call('integrate_manage_languages', [&$this->sub_actions]); + + Utils::$context['sub_action'] = $this->sub_action; + + $this->callSubAction(); } /** @@ -1614,6 +1589,12 @@ public static function list_getLanguages(): array */ protected function __construct() { + $this->addSubAction('edit', [$this, 'editLanguages']); + $this->addSubAction('add', [$this, 'add']); + $this->addSubAction('settings', [$this, 'settings']); + $this->addSubAction('downloadlang', [$this, 'download']); + $this->addSubAction('editlang', [$this, 'editEntries']); + Theme::loadTemplate('ManageLanguages'); Lang::load('ManageSettings'); @@ -1625,15 +1606,6 @@ protected function __construct() 'title' => Lang::$txt['language_configuration'], 'description' => Lang::$txt['language_description'], ]; - - IntegrationHook::call('integrate_manage_languages', [&self::$subactions]); - - // By default we're managing languages. - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } - - Utils::$context['sub_action'] = $this->subaction; } /** diff --git a/Sources/Actions/Admin/Logs.php b/Sources/Actions/Admin/Logs.php index 2d2fb18bdf..9b5f17efbb 100644 --- a/Sources/Actions/Admin/Logs.php +++ b/Sources/Actions/Admin/Logs.php @@ -23,6 +23,8 @@ use SMF\IntegrationHook; use SMF\Lang; use SMF\Menu; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Theme; use SMF\User; use SMF\Utils; @@ -30,74 +32,16 @@ /** * Dispatcher to show various kinds of logs. */ -class Logs implements ActionInterface +class Logs implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'errorlog'; - /************************** * Public static properties **************************/ - /** - * @var array - * - * These are the logs they can load. - * - * Format: 'sa' => array('file', 'function', 'disabled' => 'setting_to_check') - */ - public static array $subactions = [ - 'errorlog' => [ - '', - 'errorlog', - // At runtime, will be set to empty(Config::$modSettings['enableErrorLogging']) - 'disabled' => 'enableErrorLogging', - ], - 'adminlog' => [ - '', - 'adminlog', - // At runtime, will be set to empty(Config::$modSettings['adminlog_enabled']) - 'disabled' => 'adminlog_enabled', - ], - 'modlog' => [ - '', - 'modlog', - // At runtime, will be set to empty(Config::$modSettings['modlog_enabled']) - 'disabled' => 'modlog_enabled', - ], - 'banlog' => [ - '', - 'banlog', - ], - 'spiderlog' => [ - '', - 'spiderlog', - // At runtime, will be set to empty(Config::$modSettings['spider_mode']) - 'disabled' => 'spider_mode', - ], - 'tasklog' => [ - '', - 'tasklog', - ], - 'settings' => [ - '', - 'settings', - ], - ]; - /** * @var array * @@ -156,15 +100,12 @@ public function execute(): void ], ]; - if (!empty(self::$subactions[$this->subaction][0])) { - require_once Config::$sourcedir . '/' . self::$subactions[$this->subaction][0]; - } + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); - $call = method_exists($this, self::$subactions[$this->subaction][1]) ? [$this, self::$subactions[$this->subaction][1]] : Utils::getCallable(self::$subactions[$this->subaction][1]); + // @todo Is this context variable necessary? + Utils::$context['sub_action'] = $this->sub_action; - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction(); } /** @@ -368,20 +309,40 @@ public static function getConfigVars(): array */ protected function __construct() { - foreach (self::$subactions as &$subaction) { - if (isset($subaction['disabled'])) { - $subaction['disabled'] = empty(Config::$modSettings[$subaction['disabled']]); - } + if (!empty(Config::$modSettings['enableErrorLogging'])) { + $this->addSubAction('errorlog', [$this, 'errorlog']); + } + + if (!empty(Config::$modSettings['adminlog_enabled'])) { + $this->addSubAction('adminlog', [$this, 'adminlog']); + } + + if (!empty(Config::$modSettings['modlog_enabled'])) { + $this->addSubAction('modlog', [$this, 'modlog']); } - IntegrationHook::call('integrate_manage_logs', [&self::$subactions]); + $this->addSubAction('banlog', [$this, 'banlog']); + + if (!empty(Config::$modSettings['spider_mode'])) { + $this->addSubAction('spiderlog', [$this, 'spiderlog']); + } + + $this->addSubAction('tasklog', [$this, 'tasklog']); + $this->addSubAction('settings', [$this, 'settings']); + + $sub_actions = []; + IntegrationHook::call('integrate_manage_logs', [&$sub_actions]); + + foreach ($sub_actions as $sa => $arr) { + if (!isset($arr['disabled']) || ($arr['disabled'] === false || !empty(Config::$modSettings[$arr['disabled']]))) { + $this->addSubAction($sa, method_exists($this, $arr[1]) ? [$this, $arr[1]] : $arr[1]); + } + } // By default, error log should be shown in descending order. if (!isset($_REQUEST['sa'])) { $_REQUEST['desc'] = true; } - - $this->subaction = isset($_REQUEST['sa'], self::$subactions[$_REQUEST['sa']]) && empty(self::$subactions[$_REQUEST['sa']]['disabled']) ? $_REQUEST['sa'] : 'errorlog'; } } diff --git a/Sources/Actions/Admin/Mail.php b/Sources/Actions/Admin/Mail.php index a3840e1bec..88be8ca072 100644 --- a/Sources/Actions/Admin/Mail.php +++ b/Sources/Actions/Admin/Mail.php @@ -24,6 +24,8 @@ use SMF\ItemList; use SMF\Lang; use SMF\Menu; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Sapi; use SMF\Theme; use SMF\User; @@ -32,40 +34,12 @@ /** * Handles mail configuration, as well as reviewing the mail queue. */ -class Mail implements ActionInterface +class Mail implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'browse'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'browse' => 'browse', - 'clear' => 'clear', - 'settings' => 'settings', - 'test' => 'test', - ]; - /********************* * Internal properties *********************/ @@ -87,11 +61,11 @@ class Mail implements ActionInterface */ public function execute(): void { - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); + IntegrationHook::call('integrate_manage_mail', [&$this->sub_actions]); - if (!empty($call)) { - call_user_func($call); - } + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); + + $this->callSubAction(); } /** @@ -520,6 +494,11 @@ public static function timeSince(int $time_diff): string */ protected function __construct() { + $this->addSubAction('browse', [$this, 'browse']); + $this->addSubAction('clear', [$this, 'clear']); + $this->addSubAction('settings', [$this, 'settings']); + $this->addSubAction('test', [$this, 'test']); + // You need to be an admin to edit settings! User::$me->isAllowedTo('admin_forum'); @@ -529,13 +508,7 @@ protected function __construct() Utils::$context['page_title'] = Lang::$txt['mailqueue_title']; Utils::$context['sub_template'] = 'show_settings'; - IntegrationHook::call('integrate_manage_mail', [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } - - Utils::$context['sub_action'] = $this->subaction; + Utils::$context['sub_action'] = $this->sub_action; // Load up all the tabs... Menu::$loaded['admin']->tab_data = [ diff --git a/Sources/Actions/Admin/Maintenance.php b/Sources/Actions/Admin/Maintenance.php index e8db19d2c2..5d900eba7e 100644 --- a/Sources/Actions/Admin/Maintenance.php +++ b/Sources/Actions/Admin/Maintenance.php @@ -31,6 +31,8 @@ use SMF\Lang; use SMF\Logging; use SMF\Menu; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Sapi; use SMF\SecurityToken; use SMF\TaskRunner; @@ -42,24 +44,16 @@ /** * Forum maintenance. Important stuff. */ -class Maintenance implements ActionInterface +class Maintenance implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; /******************* * Public properties *******************/ - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'routine'; - /** * @var string * @@ -135,15 +129,11 @@ class Maintenance implements ActionInterface */ public function execute(): void { - $call = method_exists($this, self::$subactions[$this->subaction]['function']) ? [$this, self::$subactions[$this->subaction]['function']] : Utils::getCallable(self::$subactions[$this->subaction]['function']); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); // Any special activity? if (!empty($this->activity)) { - $call = method_exists($this, self::$subactions[$this->subaction]['activities'][$this->activity]) ? [$this, self::$subactions[$this->subaction]['activities'][$this->activity]] : Utils::getCallable(self::$subactions[$this->subaction]['activities'][$this->activity]); + $call = method_exists($this, self::$subactions[$this->sub_action]['activities'][$this->activity]) ? [$this, self::$subactions[$this->sub_action]['activities'][$this->activity]] : Utils::getCallable(self::$subactions[$this->sub_action]['activities'][$this->activity]); if (!empty($call)) { call_user_func($call); @@ -2307,19 +2297,19 @@ protected function __construct() IntegrationHook::call('integrate_manage_maintenance', [&self::$subactions]); - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; + foreach (self::$subactions as $sa => $arr) { + $this->addSubAction($sa, method_exists($this, $arr['function']) ? [$this, $arr['function']] : $arr['function']); } // Doing something special? - if (isset($_REQUEST['activity'], self::$subactions[$this->subaction]['activities'][$_REQUEST['activity']])) { + if (isset($_REQUEST['activity'], self::$subactions[$this->sub_action]['activities'][$_REQUEST['activity']])) { $this->activity = $_REQUEST['activity']; } // Set a few things. Utils::$context['page_title'] = Lang::$txt['maintain_title']; - Utils::$context['sub_action'] = $this->subaction; - Utils::$context['sub_template'] = !empty(self::$subactions[$this->subaction]['template']) ? self::$subactions[$this->subaction]['template'] : ''; + Utils::$context['sub_action'] = $this->sub_action; + Utils::$context['sub_template'] = self::$subactions[$this->sub_action]['template'] ?? ''; } /** diff --git a/Sources/Actions/Admin/Membergroups.php b/Sources/Actions/Admin/Membergroups.php index 81e4a97cab..057ed2e1a6 100644 --- a/Sources/Actions/Admin/Membergroups.php +++ b/Sources/Actions/Admin/Membergroups.php @@ -27,6 +27,8 @@ use SMF\Lang; use SMF\Logging; use SMF\Menu; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\SecurityToken; use SMF\Theme; use SMF\User; @@ -35,45 +37,12 @@ /** * This class is concerned with anything in the Manage Membergroups admin screen. */ -class Membergroups implements ActionInterface +class Membergroups implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'index'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - * - * Format: 'sa' => array('method', 'required_permission') - */ - public static array $subactions = [ - 'index' => ['index', 'manage_membergroups'], - 'add' => ['add', 'manage_membergroups'], - 'edit' => ['edit', 'manage_membergroups'], - 'settings' => ['settings', 'admin_forum'], - - // This subaction is handled by the Groups action. - 'members' => ['SMF\\Actions\\Groups::call', 'manage_membergroups'], - ]; - /**************** * Public methods ****************/ @@ -83,14 +52,7 @@ class Membergroups implements ActionInterface */ public function execute(): void { - // Do the permission check, you might not be allowed here. - User::$me->isAllowedTo(self::$subactions[$this->subaction][1]); - - $call = method_exists($this, self::$subactions[$this->subaction][0]) ? [$this, self::$subactions[$this->subaction][0]] : Utils::getCallable(self::$subactions[$this->subaction][0]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -969,6 +931,32 @@ public static function getConfigVars(): array */ protected function __construct() { + if (User::$me->allowedTo('manage_membergroups')) { + $this->addSubAction('index', [$this, 'index']); + $this->addSubAction('add', [$this, 'add']); + $this->addSubAction('edit', [$this, 'edit']); + } + + if (User::$me->allowedTo('admin_forum')) { + $this->addSubAction('settings', [$this, 'settings']); + } + + if (User::$me->allowedTo('manage_membergroups')) { + // This subaction is handled by the Groups action. + $this->addSubAction('members', [$this, 'SMF\\Actions\\Groups::call']); + } + + $sub_actions = []; + IntegrationHook::call('integrate_manage_membergroups', [&$sub_actions]); + + foreach ($sub_actions as $sa => [$func, $perm]) { + if (User::$me->allowedTo($perm)) { + $this->addSubAction($sa, method_exists($this, $func) ? [$this, $func] : $func); + } + } + + User::$me->isAllowedTo('manage_membergroups'); + // Language and template stuff, the usual. Lang::load('ManageMembers'); Theme::loadTemplate('ManageMembergroups'); @@ -979,13 +967,12 @@ protected function __construct() 'help' => 'membergroups', 'description' => Lang::$txt['membergroups_description'], ]; - IntegrationHook::call('integrate_manage_membergroups', [&self::$subactions]); - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } elseif (!User::$me->allowedTo('manage_membergroups')) { - $this->subaction = 'settings'; + foreach (self::$subactions as $sa => [$func, $perm]) { + if (User::$me->allowedTo($perm)) { + $this->addSubAction($sa, [$this, $func]); + } } } } diff --git a/Sources/Actions/Admin/Members.php b/Sources/Actions/Admin/Members.php index 95da0e6f40..9124fe25e6 100644 --- a/Sources/Actions/Admin/Members.php +++ b/Sources/Actions/Admin/Members.php @@ -28,6 +28,8 @@ use SMF\Logging; use SMF\Mail; use SMF\Menu; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Theme; use SMF\Time; use SMF\User; @@ -36,24 +38,12 @@ /** * Shows a list of members or a selection of members. */ -class Members implements ActionInterface +class Members implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'all'; - /** * @var bool * @@ -110,25 +100,6 @@ class Members implements ActionInterface */ public int $current_filter = -1; - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - * - * Format: 'sa' => array('method', 'required_permission') - */ - public static array $subactions = [ - 'all' => ['view', 'moderate_forum'], - 'approve' => ['approve', 'moderate_forum'], - 'browse' => ['browse', 'moderate_forum'], - 'search' => ['search', 'moderate_forum'], - 'query' => ['view', 'moderate_forum'], - ]; - /**************** * Public methods ****************/ @@ -138,11 +109,22 @@ class Members implements ActionInterface */ public function execute(): void { - $call = method_exists($this, self::$subactions[$this->subaction][0]) ? [$this, self::$subactions[$this->subaction][0]] : Utils::getCallable(self::$subactions[$this->subaction][0]); + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); - if (!empty($call)) { - call_user_func($call); + // Call our hook now, letting customizations add to the subActions and/or modify Utils::$context as needed. + IntegrationHook::call('integrate_manage_members', [&$this->sub_actions]); + + // Find the active tab. + if (isset(Utils::$context['tabs'][$this->sub_action])) { + Utils::$context['tabs'][$this->sub_action]['is_selected'] = true; + } elseif (isset($this->sub_action)) { + foreach (Utils::$context['tabs'] as $id_tab => $tab_data) { + if (!empty($tab_data['selected_actions']) && in_array($this->sub_action, $tab_data['selected_actions'])) { + Utils::$context['tabs'][$id_tab]['is_selected'] = true; + } + } } + $this->callSubAction(); } /** @@ -173,7 +155,7 @@ public function view(): void } // Check input after a member search has been submitted. - if ($this->subaction == 'query') { + if ($this->sub_action == 'query') { // Retrieving the membergroups and postgroups. $this->membergroups = []; $this->postgroups = []; @@ -255,7 +237,7 @@ public function view(): void $search_params = []; - if ($this->subaction == 'query' && !empty($_REQUEST['params']) && empty($_POST['types'])) { + if ($this->sub_action == 'query' && !empty($_REQUEST['params']) && empty($_POST['types'])) { $search_params = Utils::jsonDecode(base64_decode($_REQUEST['params']), true); } elseif (!empty($_POST)) { $search_params['types'] = $_POST['types']; @@ -430,7 +412,7 @@ public function view(): void } // Construct the additional URL part with the query info in it. - $params_url = $this->subaction == 'query' ? ';sa=query;params=' . $search_url_params : ''; + $params_url = $this->sub_action == 'query' ? ';sa=query;params=' . $search_url_params : ''; // Get the title and sub template ready.. Utils::$context['page_title'] = Lang::$txt['admin_members']; @@ -1342,6 +1324,14 @@ public static function list_getNumMembers(string $where, array $where_params = [ */ protected function __construct() { + User::$me->isAllowedTo('moderate_forum'); + + $this->addSubAction('all', [$this, 'view']); + $this->addSubAction('approve', [$this, 'approve']); + $this->addSubAction('browse', [$this, 'browse']); + $this->addSubAction('search', [$this, 'search']); + $this->addSubAction('query', [$this, 'view']); + // Load the essentials. Lang::load('ManageMembers'); Theme::loadTemplate('ManageMembers'); @@ -1399,30 +1389,6 @@ protected function __construct() Utils::$context['last_tab'] = 'activate'; } - // Call our hook now, letting customizations add to the subActions and/or modify Utils::$context as needed. - IntegrationHook::call('integrate_manage_members', [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } - - // We know the sub action, now we know what you're allowed to do. - User::$me->isAllowedTo(self::$subactions[$this->subaction][1]); - - // Set the last tab. - Utils::$context['tabs'][Utils::$context['last_tab']]['is_last'] = true; - - // Find the active tab. - if (isset(Utils::$context['tabs'][$this->subaction])) { - Utils::$context['tabs'][$this->subaction]['is_selected'] = true; - } elseif (isset($this->subaction)) { - foreach (Utils::$context['tabs'] as $id_tab => $tab_data) { - if (!empty($tab_data['selected_actions']) && in_array($this->subaction, $tab_data['selected_actions'])) { - Utils::$context['tabs'][$id_tab]['is_selected'] = true; - } - } - } - Utils::$context['membergroups'] = &$this->membergroups; Utils::$context['postgroups'] = &$this->postgroups; Utils::$context['current_filter'] = &$this->current_filter; diff --git a/Sources/Actions/Admin/Mods.php b/Sources/Actions/Admin/Mods.php index 808795f7a5..4722c8dfaa 100644 --- a/Sources/Actions/Admin/Mods.php +++ b/Sources/Actions/Admin/Mods.php @@ -22,43 +22,20 @@ use SMF\IntegrationHook; use SMF\Lang; use SMF\Menu; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\User; use SMF\Utils; /** * This my friend, is for all the mod authors out there. */ -class Mods implements ActionInterface +class Mods implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'general'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'general' => 'general', - ]; - /**************** * Public methods ****************/ @@ -68,17 +45,18 @@ class Mods implements ActionInterface */ public function execute(): void { + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); + // You need to be an admin to edit settings! User::$me->isAllowedTo('admin_forum'); - Utils::$context['sub_template'] = 'show_settings'; - Utils::$context['sub_action'] = $this->subaction; + // Make it easier for mods to add new areas. + IntegrationHook::call('integrate_modify_modifications', [&$this->sub_actions]); - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); + Utils::$context['sub_template'] = 'show_settings'; + Utils::$context['sub_action'] = $this->sub_action; - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction(); } /** @@ -154,6 +132,8 @@ public static function getConfigVars(): array */ protected function __construct() { + $this->addSubAction('general', [$this, 'general']); + Lang::load('Help'); Lang::load('ManageSettings'); @@ -169,13 +149,6 @@ protected function __construct() ], ], ]; - - // Make it easier for mods to add new areas. - IntegrationHook::call('integrate_modify_modifications', [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } } } diff --git a/Sources/Actions/Admin/News.php b/Sources/Actions/Admin/News.php index 65dd43d7ea..9ff7af4d98 100644 --- a/Sources/Actions/Admin/News.php +++ b/Sources/Actions/Admin/News.php @@ -32,6 +32,8 @@ use SMF\Msg; use SMF\Parser; use SMF\PersonalMessage\PM; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\SecurityToken; use SMF\Theme; use SMF\Time; @@ -41,24 +43,12 @@ /** * This class manages... the news. :P */ -class News implements ActionInterface +class News implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'editnews'; - /** * @var array * @@ -207,25 +197,6 @@ function addNewsItem () }', ]; - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - * - * Format: 'sub-action' => array('function', 'permission') - */ - public static array $subactions = [ - 'editnews' => ['edit', 'edit_news'], - 'mailingmembers' => ['selectMembers', 'send_mail'], - 'mailingcompose' => ['compose', 'send_mail'], - 'mailingsend' => ['send', 'send_mail'], - 'settings' => ['settings', 'admin_forum'], - ]; - /**************** * Public methods ****************/ @@ -235,10 +206,14 @@ function addNewsItem () */ public function execute(): void { - // Have you got the proper permissions? - User::$me->isAllowedTo(self::$subactions[$this->subaction][1]); + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); + + // Force the right area... + if (str_starts_with($this->sub_action, 'mailing')) { + Menu::$loaded['admin']['current_subsection'] = 'mailingmembers'; + } - call_user_func([$this, self::$subactions[$this->subaction][0]]); + $this->callSubAction(); } /** @@ -1221,6 +1196,29 @@ public static function prepareMailingForPreview(): void */ protected function __construct() { + if (User::$me->allowedTo('edit_news')) { + $this->addSubAction('editnews', [$this, 'edit']); + } + + if (User::$me->allowedTo('send_mail')) { + $this->addSubAction('mailingmembers', [$this, 'selectMembers']); + $this->addSubAction('mailingcompose', [$this, 'compose']); + $this->addSubAction('mailingsend', [$this, 'send']); + } + + if (User::$me->allowedTo('admin_forum')) { + $this->addSubAction('settings', [$this, 'settings']); + } + + $sub_actions = []; + IntegrationHook::call('integrate_manage_news', [&$sub_actions]); + + foreach ($sub_actions as $sa => [$func, $perm]) { + if (User::$me->allowedTo($perm)) { + $this->addSubAction($sa, [$this, $func]); + } + } + Theme::loadTemplate('ManageNews'); // Create the tabs for the template. @@ -1240,16 +1238,6 @@ protected function __construct() ], ]; - IntegrationHook::call('integrate_manage_news', [&self::$subactions]); - - // Default to sub action 'main' or 'settings' depending on permissions. - $this->subaction = isset($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : (User::$me->allowedTo('edit_news') ? 'editnews' : (User::$me->allowedTo('send_mail') ? 'mailingmembers' : 'settings')); - - // Force the right area... - if (str_starts_with($this->subaction, 'mailing')) { - Menu::$loaded['admin']['current_subsection'] = 'mailingmembers'; - } - // Insert dynamic values into the list options. $this->setListOptions(); } diff --git a/Sources/Actions/Admin/Permissions.php b/Sources/Actions/Admin/Permissions.php index 30981e2fde..44184a608e 100644 --- a/Sources/Actions/Admin/Permissions.php +++ b/Sources/Actions/Admin/Permissions.php @@ -28,6 +28,8 @@ use SMF\IntegrationHook; use SMF\Lang; use SMF\Menu; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\SecurityToken; use SMF\Theme; use SMF\User; @@ -36,10 +38,10 @@ /** * Permissions handles all possible permission stuff. */ -class Permissions implements ActionInterface +class Permissions implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; /***************** @@ -67,14 +69,6 @@ class Permissions implements ActionInterface * Public properties *******************/ - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'index'; - /** * @var array * @@ -94,24 +88,6 @@ class Permissions implements ActionInterface * Public static properties **************************/ - /** - * @var array - * - * Available sub-actions. - * - * Format: 'sub-action' => array('method_to_call', 'permission_needed') - */ - public static array $subactions = [ - 'index' => ['index', 'manage_permissions'], - 'board' => ['board', 'manage_permissions'], - 'modify' => ['modify', 'manage_permissions'], - 'modify2' => ['modify2', 'manage_permissions'], - 'quick' => ['quick', 'manage_permissions'], - 'postmod' => ['postmod', 'manage_permissions'], - 'profiles' => ['profiles', 'manage_permissions'], - 'settings' => ['settings', 'admin_forum'], - ]; - /** * @var array * @@ -937,13 +913,8 @@ class Permissions implements ActionInterface */ public function execute(): void { - User::$me->isAllowedTo(self::$subactions[$this->subaction][1]); - - $call = method_exists($this, self::$subactions[$this->subaction][0]) ? [$this, self::$subactions[$this->subaction][0]] : Utils::getCallable(self::$subactions[$this->subaction][0]); - - if (!empty($call)) { - call_user_func($call); - } + User::$me->isAllowedTo('manage_permissions'); + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -2365,7 +2336,6 @@ public static function buildHidden(): void Utils::$context['hidden_permissions'] = self::$hidden; } - /****************** * Internal methods ******************/ @@ -2375,6 +2345,29 @@ public static function buildHidden(): void */ protected function __construct() { + User::$me->isAllowedTo('manage_permissions'); + + $this->addSubAction('index', [$this, 'index']); + $this->addSubAction('board', [$this, 'board']); + $this->addSubAction('modify', [$this, 'modify']); + $this->addSubAction('modify2', [$this, 'modify2']); + $this->addSubAction('quick', [$this, 'quick']); + $this->addSubAction('postmod', [$this, 'postmod']); + $this->addSubAction('profiles', [$this, 'profiles']); + + if (User::$me->allowedTo('admin_forum')) { + $this->addSubAction('settings', [$this, 'settings']); + } + + $sub_actions = []; + IntegrationHook::call('integrate_manage_permissions', [&$sub_actions]); + + foreach ($sub_actions as $sa => [$func, $perm]) { + if (User::$me->allowedTo($perm)) { + $this->addSubAction($sa, [$this, $func]); + } + } + Lang::load('ManagePermissions+ManageMembers'); Theme::loadTemplate('ManagePermissions'); @@ -2401,12 +2394,6 @@ protected function __construct() ], ], ]; - - IntegrationHook::call('integrate_manage_permissions', [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } } /** diff --git a/Sources/Actions/Admin/Posts.php b/Sources/Actions/Admin/Posts.php index 4e3c8986c0..f0b6f93f9c 100644 --- a/Sources/Actions/Admin/Posts.php +++ b/Sources/Actions/Admin/Posts.php @@ -24,6 +24,8 @@ use SMF\Lang; use SMF\Menu; use SMF\Msg; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\SecurityToken; use SMF\TaskRunner; use SMF\User; @@ -32,37 +34,10 @@ /** * This class contains all the administration settings for topics and posts. */ -class Posts implements ActionInterface +class Posts implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'posts'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'posts' => 'posts', - 'censor' => 'censor', - 'topics' => 'topics', - 'drafts' => 'drafts', - ]; + use ProvidesSubActionTrait; /**************** * Public methods @@ -73,11 +48,8 @@ class Posts implements ActionInterface */ public function execute(): void { - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + IntegrationHook::call('integrate_manage_posts', [&$this->sub_actions]); + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -442,6 +414,11 @@ public static function draftConfigVars(): array */ protected function __construct() { + $this->addSubAction('posts', [$this, 'posts']); + $this->addSubAction('censor', [$this, 'censor']); + $this->addSubAction('topics', [$this, 'topics']); + $this->addSubAction('drafts', [$this, 'drafts']); + // Make sure you can be here. User::$me->isAllowedTo('admin_forum'); Lang::load('Drafts'); @@ -468,12 +445,6 @@ protected function __construct() ], ], ]; - - IntegrationHook::call('integrate_manage_posts', [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } } } diff --git a/Sources/Actions/Admin/Registration.php b/Sources/Actions/Admin/Registration.php index a9e4f6804c..1f23a10e56 100644 --- a/Sources/Actions/Admin/Registration.php +++ b/Sources/Actions/Admin/Registration.php @@ -27,6 +27,8 @@ use SMF\Logging; use SMF\Menu; use SMF\Profile; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\SecurityToken; use SMF\Theme; use SMF\Time; @@ -37,41 +39,11 @@ * This class helps the administrator setting registration settings and policy * as well as allow the administrator to register new members themselves. */ -class Registration implements ActionInterface +class Registration implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'register'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - * - * Format: 'sa' => array('method', 'required_permission') - */ - public static array $subactions = [ - 'register' => ['register', 'moderate_forum'], - 'agreement' => ['agreement', 'admin_forum'], - 'policy' => ['privacyPolicy', 'admin_forum'], - 'reservednames' => ['reservedNames', 'admin_forum'], - 'settings' => ['settings', 'admin_forum'], - ]; /**************** * Public methods @@ -82,14 +54,12 @@ class Registration implements ActionInterface */ public function execute(): void { - // Must have sufficient permissions. - User::$me->isAllowedTo(self::$subactions[$this->subaction][1]); + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); - $call = method_exists($this, self::$subactions[$this->subaction][0]) ? [$this, self::$subactions[$this->subaction][0]] : Utils::getCallable(self::$subactions[$this->subaction][0]); + // @todo Is this context variable necessary? + Utils::$context['sub_action'] = $this->sub_action; - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction(); } /** @@ -509,6 +479,26 @@ public static function getConfigVars(): array */ protected function __construct() { + User::$me->isAllowedTo('moderate_forum'); + + $this->addSubAction('register', [$this, 'register']); + + if (User::$me->allowedTo('admin_forum')) { + $this->addSubAction('agreement', [$this, 'agreement']); + $this->addSubAction('policy', [$this, 'privacyPolicy']); + $this->addSubAction('reservednames', [$this, 'reservedNames']); + $this->addSubAction('settings', [$this, 'settings']); + } + + $sub_actions = []; + IntegrationHook::call('integrate_manage_registrations', [&$sub_actions]); + + foreach ($sub_actions as $sa => [$func, $perm]) { + if (User::$me->allowedTo($perm)) { + $this->addSubAction($sa, [$this, $func]); + } + } + // Loading, always loading. Lang::load('Login'); Theme::loadTemplate('Register'); @@ -536,17 +526,6 @@ protected function __construct() ], ], ]; - - IntegrationHook::call('integrate_manage_registrations', [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } elseif (!User::$me->allowedTo('moderate_forum')) { - $this->subaction = 'settings'; - } - - // @todo Is this context variable necessary? - Utils::$context['sub_action'] = $this->subaction; } } diff --git a/Sources/Actions/Admin/Reports.php b/Sources/Actions/Admin/Reports.php index 3ea6371001..3c1f041399 100644 --- a/Sources/Actions/Admin/Reports.php +++ b/Sources/Actions/Admin/Reports.php @@ -27,6 +27,8 @@ use SMF\IntegrationHook; use SMF\Lang; use SMF\Menu; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Theme; use SMF\Time; use SMF\User; @@ -36,24 +38,16 @@ * This class is exclusively for generating reports to help assist forum * administrators keep track of their forum configuration and state. */ -class Reports implements ActionInterface +class Reports implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; /******************* * Public properties *******************/ - /** - * @var string - * - * The requested sub-action (i.e. the report type). - * This should be set by the constructor. - */ - public string $subaction = ''; - /** * @var string * @@ -82,24 +76,6 @@ class Reports implements ActionInterface ], ]; - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'boards' => 'boards', - 'board_access' => 'boardAccess', - 'board_perms' => 'boardPerms', - 'member_groups' => 'memberGroups', - 'group_perms' => 'groupPerms', - 'staff' => 'staff', - ]; - /********************* * Internal properties *********************/ @@ -149,15 +125,15 @@ class Reports implements ActionInterface */ public function execute(): void { - if (empty($this->subaction)) { - $this->sub_template = 'report_type'; + $this->callSubAction(); + if ($this->sub_action === 'index') { return; } // Specific template? Use that instead of main! if (isset($_REQUEST['st'], $this->reportTemplates[$_REQUEST['st']])) { - $this->sub_template = $_REQUEST['st']; + Utils::$context['sub_template'] = $_REQUEST['st']; // Are we disabling the other layers - print friendly for example? if ($this->reportTemplates[$_REQUEST['st']]['layers'] !== null) { @@ -166,26 +142,29 @@ public function execute(): void } // Make the page title more descriptive. - Utils::$context['page_title'] .= ' - ' . (Lang::$txt['gr_type_' . $this->subaction] ?? $this->subaction); + Utils::$context['page_title'] .= ' - ' . (Lang::$txt['gr_type_' . $this->sub_action] ?? $this->sub_action); // Build the reports button array. Utils::$context['report_buttons'] = [ 'generate_reports' => ['text' => 'generate_reports', 'image' => 'print.png', 'url' => Config::$scripturl . '?action=admin;area=reports', 'active' => true], - 'print' => ['text' => 'print', 'image' => 'print.png', 'url' => Config::$scripturl . '?action=admin;area=reports;rt=' . $this->subaction . ';st=print', 'custom' => 'target="_blank"'], + 'print' => ['text' => 'print', 'image' => 'print.png', 'url' => Config::$scripturl . '?action=admin;area=reports;rt=' . $this->sub_action . ';st=print', 'custom' => 'target="_blank"'], ]; // Allow mods to add additional buttons here. IntegrationHook::call('integrate_report_buttons'); - // Now generate the data. - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } - // Finish the tables before exiting - this is to help the templates a little more. $this->finishTables(); + Utils::$context['tables'] = $this->tables; + Utils::$context['sub_template'] = $this->sub_template; + } + + /** + * Choose a report. + */ + public function index(): void + { + Utils::$context['sub_template'] = 'report_type'; } /** @@ -606,32 +585,6 @@ public function staff(): void } } - /*********************** - * Public static methods - ***********************/ - - /** - * Static wrapper for constructor. - * - * @return self An instance of this class. - */ - public static function load(): static - { - if (!isset(self::$obj)) { - self::$obj = new self(); - } - - return self::$obj; - } - - /** - * Convenience method to load() and execute() an instance of this class. - */ - public static function call(): void - { - self::load()->execute(); - } - /****************** * Internal methods ******************/ @@ -641,6 +594,13 @@ public static function call(): void */ protected function __construct() { + $this->addSubAction('index', [$this, 'index']); + $this->addSubAction('boards', [$this, 'boards']); + $this->addSubAction('board_perms', [$this, 'boardPerms']); + $this->addSubAction('member_groups', [$this, 'memberGroups']); + $this->addSubAction('group_perms', [$this, 'groupPerms']); + $this->addSubAction('staff', [$this, 'staff']); + // Only admins, only EVER admins! User::$me->isAllowedTo('admin_forum'); @@ -652,9 +612,9 @@ protected function __construct() Utils::$context['page_title'] = Lang::$txt['generate_reports']; // For backward compatibility... - Utils::$context['report_types'] = &self::$subactions; + Utils::$context['report_types'] = &$this->sub_actions; - IntegrationHook::call('integrate_report_types', [&self::$subactions]); + IntegrationHook::call('integrate_report_types', [&$this->sub_actions]); // Load up all the tabs... Menu::$loaded['admin']->tab_data = [ @@ -665,8 +625,8 @@ protected function __construct() $is_first = 0; - foreach (self::$subactions as $k => $func) { - if (!is_string($func)) { + foreach ($this->sub_actions as $k => $func) { + if ($k === 'index') { continue; } @@ -674,18 +634,12 @@ protected function __construct() 'id' => $k, 'title' => Lang::$txt['gr_type_' . $k] ?? $k, 'description' => Lang::$txt['gr_type_desc_' . $k] ?? null, - 'function' => $func, 'is_first' => $is_first++ == 0, ]; } + $this->findRequestedSubAction($_REQUEST['rt'] ?? null); Utils::$context['report_types'] = &$this->report_types; - Utils::$context['sub_template'] = &$this->sub_template; - Utils::$context['tables'] = &$this->tables; - - if (!empty($_REQUEST['rt']) && isset(self::$subactions[$_REQUEST['rt']])) { - $this->subaction = $_REQUEST['rt']; - } } /** diff --git a/Sources/Actions/Admin/Search.php b/Sources/Actions/Admin/Search.php index a682de225f..c3f8df58e6 100644 --- a/Sources/Actions/Admin/Search.php +++ b/Sources/Actions/Admin/Search.php @@ -50,21 +50,6 @@ class Search implements ActionInterface */ public string $subaction = 'weights'; - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'settings' => 'settings', - 'weights' => 'weights', - 'method' => 'method', - ]; - /**************** * Public methods ****************/ @@ -74,11 +59,12 @@ class Search implements ActionInterface */ public function execute(): void { - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); - if (!empty($call)) { - call_user_func($call); - } + // @todo Is this context variable necessary? + Utils::$context['sub_action'] = $this->sub_action; + + $this->callSubAction(); } /** @@ -361,6 +347,10 @@ protected function __construct() { User::$me->isAllowedTo('admin_forum'); + $this->addSubAction('settings', [$this, 'settings']); + $this->addSubAction('weights', [$this, 'weights']); + $this->addSubAction('method', [$this, 'method']); + Lang::load('Search'); Theme::loadTemplate('ManageSearch'); @@ -391,19 +381,14 @@ protected function __construct() if (isset($class_vars['admin_subactions'])) { foreach ($class_vars['admin_subactions'] as $type => $subaction) { - self::$subactions[$subaction['sa']] = $subaction['func']; + $this->addSubAction($subaction['sa'], $subaction['func']); } } } } - IntegrationHook::call('integrate_manage_search', [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } - - Utils::$context['sub_action'] = $this->subaction; + $sub_actions = []; + IntegrationHook::call('integrate_manage_search', [&$sub_actions]); } } diff --git a/Sources/Actions/Admin/SearchEngines.php b/Sources/Actions/Admin/SearchEngines.php index 614020064b..ff976c6f68 100644 --- a/Sources/Actions/Admin/SearchEngines.php +++ b/Sources/Actions/Admin/SearchEngines.php @@ -27,6 +27,8 @@ use SMF\ItemList; use SMF\Lang; use SMF\Menu; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\SecurityToken; use SMF\Theme; use SMF\Time; @@ -37,41 +39,12 @@ /** * Manages the settings related to search engines. */ -class SearchEngines implements ActionInterface +class SearchEngines implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'stats'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'stats' => 'stats', - 'logs' => 'logs', - 'spiders' => 'view', - 'settings' => 'settings', - 'editspiders' => 'edit', - ]; - /**************************** * Internal static properties ****************************/ @@ -92,11 +65,33 @@ class SearchEngines implements ActionInterface */ public function execute(): void { - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); + if (!empty(Config::$modSettings['spider_mode'])) { + $this->addSubAction('stats', [$this, 'stats']); + $this->addSubAction('logs', [$this, 'logs']); + $this->addSubAction('spiders', [$this, 'view']); + $this->addSubAction('editspiders', [$this, 'edit']); + } + + $this->addSubAction('settings', [$this, 'settings']); - if (!empty($call)) { - call_user_func($call); + Utils::$context['page_title'] = Lang::$txt['search_engines']; + + // Tab data might already be set if this was called from Logs::execute(). + if (empty(Menu::$loaded['admin']->tab_data)) { + // Some more tab data. + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['search_engines'], + 'description' => Lang::$txt['search_engines_description'], + ]; } + + IntegrationHook::call('integrate_manage_search_engines', [&$this->sub_actions]); + + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); + + Utils::$context['sub_action'] = &$this->sub_action; + + $this->callSubAction(); } /** @@ -1109,30 +1104,6 @@ protected function __construct() Lang::load('Search'); Theme::loadTemplate('ManageSearch'); - - if (empty(Config::$modSettings['spider_mode'])) { - self::$subactions = array_intersect_key(self::$subactions, ['settings' => true]); - $this->subaction = 'settings'; - } - - Utils::$context['page_title'] = Lang::$txt['search_engines']; - - // Tab data might already be set if this was called from Logs::execute(). - if (empty(Menu::$loaded['admin']->tab_data)) { - // Some more tab data. - Menu::$loaded['admin']->tab_data = [ - 'title' => Lang::$txt['search_engines'], - 'description' => Lang::$txt['search_engines_description'], - ]; - } - - IntegrationHook::call('integrate_manage_search_engines', [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } - - Utils::$context['sub_action'] = &$this->subaction; } /** diff --git a/Sources/Actions/Admin/Server.php b/Sources/Actions/Admin/Server.php index d7c6c8de73..0d03891714 100644 --- a/Sources/Actions/Admin/Server.php +++ b/Sources/Actions/Admin/Server.php @@ -26,6 +26,8 @@ use SMF\IntegrationHook; use SMF\Lang; use SMF\Menu; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Sapi; use SMF\SecurityToken; use SMF\Theme; @@ -82,10 +84,10 @@ * - PLUS you can override label and help parameters by forcing their keys in the array, for example: * array('text', 'invalidlabel', 3, 'label' => 'Actual Label') */ -class Server implements ActionInterface +class Server implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; /***************** @@ -106,38 +108,6 @@ class Server implements ActionInterface 'loadavg_forum' => 40.0, ]; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'general'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'general' => 'general', - 'database' => 'database', - 'cookie' => 'cookie', - 'security' => 'security', - 'cache' => 'cache', - 'export' => 'export', - 'loads' => 'loadBalancing', - 'phpinfo' => 'phpinfo', - ]; - /** * @var bool * @@ -185,12 +155,16 @@ class Server implements ActionInterface */ public function execute(): void { + IntegrationHook::call('integrate_server_settings', [&$this->sub_actions]); + // This is just to keep the database password more secure. User::$me->isAllowedTo('admin_forum'); Utils::$context['page_title'] = Lang::$txt['admin_server_settings']; Utils::$context['sub_template'] = 'show_settings'; + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); + // Warn the user if there's any relevant information regarding Settings.php. self::checkSettingsFileWriteSafe(); @@ -203,14 +177,10 @@ public function execute(): void } Utils::$context['settings_not_writable'] = self::$settings_not_writable; - Utils::$context['sub_action'] = $this->subaction; + Utils::$context['sub_action'] = $this->sub_action; // Call the right method for this sub-action. - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction(); } /** @@ -1224,6 +1194,15 @@ public static function checkSettingsFileWriteSafe(): bool */ protected function __construct() { + $this->addSubAction('general', [$this, 'general']); + $this->addSubAction('database', [$this, 'database']); + $this->addSubAction('cookie', [$this, 'cookie']); + $this->addSubAction('security', [$this, 'security']); + $this->addSubAction('cache', [$this, 'cache']); + $this->addSubAction('export', [$this, 'export']); + $this->addSubAction('loads', [$this, 'loadBalancing']); + $this->addSubAction('phpinfo', [$this, 'phpinfo']); + Lang::load('ManageSettings'); // Load up all the tabs... @@ -1232,12 +1211,6 @@ protected function __construct() 'help' => 'serversettings', 'description' => Lang::$txt['admin_basic_settings'], ]; - - IntegrationHook::call('integrate_server_settings', [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } } /** diff --git a/Sources/Actions/Admin/Smileys.php b/Sources/Actions/Admin/Smileys.php index 09b23bca60..f063bdff98 100644 --- a/Sources/Actions/Admin/Smileys.php +++ b/Sources/Actions/Admin/Smileys.php @@ -33,6 +33,8 @@ use SMF\Msg; use SMF\PackageManager\SubsPackage; use SMF\Parser; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\SecurityToken; use SMF\Theme; use SMF\User; @@ -42,47 +44,12 @@ /** * This class takes care of all administration of smileys. */ -class Smileys implements ActionInterface +class Smileys implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'editsets'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'editsets' => 'editSets', - 'import' => 'editSets', - 'modifyset' => 'editSets', - 'addsmiley' => 'add', - 'editsmileys' => 'edit', - 'modifysmiley' => 'edit', - 'setorder' => 'setOrder', - 'install' => 'install', - 'editicon' => 'editIcon', - 'editicons' => 'editIcon', - 'settings' => 'settings', - ]; - /** * @var array * @@ -151,12 +118,6 @@ class Smileys implements ActionInterface */ public static bool $smileys_dir_found; - /********************* - * Internal properties - *********************/ - - // code... - /**************** * Public methods ****************/ @@ -166,11 +127,16 @@ class Smileys implements ActionInterface */ public function execute(): void { - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); + IntegrationHook::call('integrate_manage_smileys', [&$this->sub_actions]); - if (!empty($call)) { - call_user_func($call); - } + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); + + Utils::$context['sub_action'] = &$this->sub_action; + + Utils::$context['page_title'] = Lang::$txt['smileys_manage']; + Utils::$context['sub_template'] = $this->sub_action; + + $this->callSubAction(); } /** @@ -217,7 +183,7 @@ public function editSets(): void } // Add a new smiley set. elseif (!empty($_POST['add'])) { - $this->subaction = 'modifyset'; + $this->sub_action = 'modifyset'; } // Create or modify a smiley set. elseif (isset($_POST['set'])) { @@ -299,7 +265,7 @@ public function editSets(): void } // Importing any smileys from an existing set? - if ($this->subaction == 'import') { + if ($this->sub_action == 'import') { User::$me->checkSession('get'); SecurityToken::validate('admin-mss', 'request'); @@ -309,12 +275,12 @@ public function editSets(): void } // Force the process to continue. - $this->subaction = 'modifyset'; + $this->sub_action = 'modifyset'; Utils::$context['sub_template'] = 'modifyset'; } // If we're modifying or adding a smiley set, some context info needs to be set. - if ($this->subaction == 'modifyset') { + if ($this->sub_action == 'modifyset') { $_GET['set'] = !isset($_GET['set']) ? -1 : $_GET['set']; if ($_GET['set'] == -1 || !isset(self::$smiley_sets[$_GET['set']])) { @@ -1004,7 +970,7 @@ public function edit(): void } // Prepare overview of all (custom) smileys. - if ($this->subaction == 'editsmileys') { + if ($this->sub_action == 'editsmileys') { // Determine the language specific sort order of smiley locations. $smiley_locations = [ Lang::$txt['smileys_location_form'], @@ -1243,7 +1209,7 @@ function changeSet(newSet) addInlineJavaScript("\n\t" . 'changeSet("' . Config::$modSettings['smiley_sets_default'] . '");', true); } // Modifying smileys. - elseif ($this->subaction == 'modifysmiley') { + elseif ($this->sub_action == 'modifysmiley') { Utils::$context['selected_set'] = Config::$modSettings['smiley_sets_default']; $request = Db::$db->query( @@ -1811,7 +1777,7 @@ public function editIcon(): void ); } // Editing/Adding an icon? - elseif ($this->subaction == 'editicon' && isset($_GET['icon'])) { + elseif ($this->sub_action == 'editicon' && isset($_GET['icon'])) { $_GET['icon'] = (int) $_GET['icon']; foreach (['icon_filename', 'icon_description'] as $key) { @@ -2004,7 +1970,7 @@ public function editIcon(): void new ItemList($listOptions); // If we're adding/editing an icon we'll need a list of boards - if ($this->subaction == 'editicon' || isset($_POST['add'])) { + if ($this->sub_action == 'editicon' || isset($_POST['add'])) { // Force the sub_template just in case. Utils::$context['sub_template'] = 'editicon'; @@ -2273,20 +2239,31 @@ public static function list_getMessageIcons(int $start, int $items_per_page, str */ protected function __construct() { - User::$me->isAllowedTo('manage_smileys'); - - Lang::load('ManageSmileys'); - Theme::loadTemplate('ManageSmileys'); + $this->addSubAction('editsets', [$this, 'editSets']); + $this->addSubAction('import', [$this, 'editSets']); + $this->addSubAction('modifyset', [$this, 'editSets']); - // If customized smileys is disabled don't show the setting page - if (empty(Config::$modSettings['smiley_enable'])) { - unset(self::$subactions['addsmiley'], self::$subactions['editsmileys'], self::$subactions['setorder'], self::$subactions['modifysmiley']); + if (!empty(Config::$modSettings['smiley_enable'])) { + $this->addSubAction('addsmiley', [$this, 'add']); + $this->addSubAction('editsmileys', [$this, 'edit']); + $this->addSubAction('modifysmiley', [$this, 'edit']); + $this->addSubAction('setorder', [$this, 'setOrder']); } - if (empty(Config::$modSettings['messageIcons_enable'])) { - unset(self::$subactions['editicon'], self::$subactions['editicons']); + $this->addSubAction('install', [$this, 'install']); + + if (!empty(Config::$modSettings['messageIcons_enable'])) { + $this->addSubAction('editicon', [$this, 'editIcon']); + $this->addSubAction('editicons', [$this, 'editIcon']); } + $this->addSubAction('settings', [$this, 'settings']); + + User::$me->isAllowedTo('manage_smileys'); + + Lang::load('ManageSmileys'); + Theme::loadTemplate('ManageSmileys'); + // Load up all the tabs... Menu::$loaded['admin']->tab_data = [ 'title' => Lang::$txt['smileys_manage'], @@ -2325,17 +2302,6 @@ protected function __construct() Menu::$loaded['admin']->tab_data['tabs']['setorder']['disabled'] = true; } - IntegrationHook::call('integrate_manage_smileys', [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } - - Utils::$context['sub_action'] = &$this->subaction; - - Utils::$context['page_title'] = Lang::$txt['smileys_manage']; - Utils::$context['sub_template'] = $this->subaction; - self::findSmileysDir(); self::getKnownSmileySets(); diff --git a/Sources/Actions/Admin/Subscriptions.php b/Sources/Actions/Admin/Subscriptions.php index 5793b924b1..68bb7f2b37 100644 --- a/Sources/Actions/Admin/Subscriptions.php +++ b/Sources/Actions/Admin/Subscriptions.php @@ -25,6 +25,8 @@ use SMF\ItemList; use SMF\Lang; use SMF\Menu; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\SecurityToken; use SMF\TaskRunner; use SMF\Theme; @@ -36,43 +38,12 @@ * Contains all the administration functions for paid subscriptions. * (and some more than that :P) */ -class Subscriptions implements ActionInterface +class Subscriptions implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'view'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - * - * Format: 'sa' => array('method', 'required_permission') - */ - public static array $subactions = [ - 'view' => ['view', 'admin_forum'], - 'viewsub' => ['viewUsers', 'admin_forum'], - 'modify' => ['modify', 'admin_forum'], - 'modifyuser' => ['modifyUser', 'admin_forum'], - 'settings' => ['settings', 'admin_forum'], - ]; - /** * @var array * @@ -80,12 +51,6 @@ class Subscriptions implements ActionInterface */ public static array $all = []; - /********************* - * Internal properties - *********************/ - - // code... - /**************** * Public methods ****************/ @@ -95,14 +60,9 @@ class Subscriptions implements ActionInterface */ public function execute(): void { - // Make sure you can do this. - User::$me->isAllowedTo(self::$subactions[$this->subaction][1]); - - $call = method_exists($this, self::$subactions[$this->subaction][0]) ? [$this, self::$subactions[$this->subaction][0]] : Utils::getCallable(self::$subactions[$this->subaction][0]); + IntegrationHook::call('integrate_manage_subscriptions', [&$this->sub_actions]); - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -2237,6 +2197,17 @@ public static function list_getSubscribedUsers(int $start, int $items_per_page, */ protected function __construct() { + if (!empty(Config::$modSettings['paid_enabled']) && !empty(Config::$modSettings['paid_currency_symbol'])) { + $this->addSubAction('view', [$this, 'view']); + $this->addSubAction('viewsub', [$this, 'viewUsers']); + $this->addSubAction('modify', [$this, 'modify']); + $this->addSubAction('modifyuser', [$this, 'modifyUser']); + } + + $this->addSubAction('settings', [$this, 'settings']); + + User::$me->isAllowedTo('admin_forum'); + // Load the required language and template. Lang::load('ManagePaid'); Theme::loadTemplate('ManagePaid'); @@ -2248,28 +2219,15 @@ protected function __construct() 'title' => Lang::$txt['paid_subscriptions'], 'help' => '', 'description' => Lang::$txt['paid_subscriptions_desc'], - ]; - - // If not enabled or not fully configured yet, only show the settings. - if (empty(Config::$modSettings['paid_enabled']) || empty(Config::$modSettings['paid_currency_symbol'])) { - self::$subactions = array_intersect_key(self::$subactions, ['settings' => true]); - $this->subaction = 'settings'; - } else { - Menu::$loaded['admin']->tab_data['tabs'] = [ + 'tabs' => [ 'view' => [ 'description' => Lang::$txt['paid_subs_view_desc'], ], 'settings' => [ 'description' => Lang::$txt['paid_subs_settings_desc'], ], - ]; - } - - IntegrationHook::call('integrate_manage_subscriptions', [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } + ], + ]; } } diff --git a/Sources/Actions/Admin/Tasks.php b/Sources/Actions/Admin/Tasks.php index 37d45cace2..2b15c4f640 100644 --- a/Sources/Actions/Admin/Tasks.php +++ b/Sources/Actions/Admin/Tasks.php @@ -25,6 +25,8 @@ use SMF\ItemList; use SMF\Lang; use SMF\Menu; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\SecurityToken; use SMF\TaskRunner; use SMF\Theme; @@ -35,40 +37,12 @@ /** * This class concerns itself with scheduled tasks management. */ -class Tasks implements ActionInterface +class Tasks implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'tasks'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'tasks' => 'tasks', - 'taskedit' => 'edit', - 'tasklog' => 'log', - 'settings' => 'settings', - ]; - /**************** * Public methods ****************/ @@ -78,11 +52,9 @@ class Tasks implements ActionInterface */ public function execute(): void { - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); + IntegrationHook::call('integrate_manage_scheduled_tasks', [&$this->sub_actions]); - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -646,6 +618,11 @@ public static function list_getNumTaskLogEntries(): int */ protected function __construct() { + $this->addSubAction('tasks', [$this, 'tasks']); + $this->addSubAction('taskedit', [$this, 'edit']); + $this->addSubAction('tasklog', [$this, 'log']); + $this->addSubAction('settings', [$this, 'settings']); + User::$me->isAllowedTo('admin_forum'); Lang::load('ManageScheduledTasks'); @@ -671,12 +648,6 @@ protected function __construct() ], ]; } - - IntegrationHook::call('integrate_manage_scheduled_tasks', [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } } } diff --git a/Sources/Actions/Admin/Themes.php b/Sources/Actions/Admin/Themes.php index acd13fe00a..513abd2222 100644 --- a/Sources/Actions/Admin/Themes.php +++ b/Sources/Actions/Admin/Themes.php @@ -26,6 +26,8 @@ use SMF\Lang; use SMF\Menu; use SMF\PackageManager\{SubsPackage, XmlArray}; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Sapi; use SMF\SecurityToken; use SMF\Theme; @@ -52,45 +54,12 @@ * - tar and gzip the directory - and you're done! * - please include any special license in a license.txt file. */ -class Themes implements ActionInterface +class Themes implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'admin'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'admin' => 'admin', - 'list' => 'list', - 'reset' => 'setOptions', - 'options' => 'setOptions', - 'remove' => 'remove', - 'enable' => 'enable', - 'install' => 'install', - 'edit' => 'edit', - 'copy' => 'copy', - ]; - /**************** * Public methods ****************/ @@ -103,15 +72,9 @@ public function execute(): void // Whatever they decide to do, clean the minify cache. Theme::deleteAllMinified(); - if (isset(self::$subactions[$this->subaction])) { - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - } else { - $call = Utils::getCallable($this->subaction); - } + IntegrationHook::call('integrate_manage_themes', [&$this->sub_actions]); - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -1328,6 +1291,16 @@ public function copy(): void */ protected function __construct() { + $this->addSubAction('admin', [$this, 'admin']); + $this->addSubAction('list', [$this, 'list']); + $this->addSubAction('reset', [$this, 'setOptions']); + $this->addSubAction('options', [$this, 'setOptions']); + $this->addSubAction('remove', [$this, 'remove']); + $this->addSubAction('enable', [$this, 'enable']); + $this->addSubAction('install', [$this, 'install']); + $this->addSubAction('edit', [$this, 'edit']); + $this->addSubAction('copy', [$this, 'copy']); + // PickTheme() has been migrated to SMF\Actions\ThemeChooser::call() if (isset($_GET['sa']) && $_GET['sa'] === 'pick') { Utils::redirectexit('action=themechooser' . (isset($_GET['u']) ? ';u=' . $_GET['u'] : '')); @@ -1368,13 +1341,6 @@ protected function __construct() ], ]; } - - // CRUD self::$subactions as needed. - IntegrationHook::call('integrate_manage_themes', [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } } /** diff --git a/Sources/Actions/Announce.php b/Sources/Actions/Announce.php index 92f83b5df5..669775dca2 100644 --- a/Sources/Actions/Announce.php +++ b/Sources/Actions/Announce.php @@ -28,6 +28,8 @@ use SMF\Logging; use SMF\Mail; use SMF\Parser; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Routable; use SMF\Theme; use SMF\Topic; @@ -37,38 +39,13 @@ /** * This class handles sending announcements about topics. */ -class Announce implements ActionInterface, Routable +class Announce implements ActionInterface, ProvidesSubActionInterface, Routable { use ActionRouter; use ActionTrait; + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'selectgroup'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'selectgroup' => 'select', - 'send' => 'send', - ]; - /**************** * Public methods ****************/ @@ -78,11 +55,7 @@ class Announce implements ActionInterface, Routable */ public function execute(): void { - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -285,6 +258,9 @@ public function send(): void */ protected function __construct() { + $this->addSubAction('selectgroup', [$this, 'select']); + $this->addSubAction('send', [$this, 'send']); + User::$me->isAllowedTo('announce_topic'); User::$me->validateSession(); @@ -297,10 +273,6 @@ protected function __construct() Theme::loadTemplate('Post'); Utils::$context['page_title'] = Lang::$txt['announce_topic']; - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } } } diff --git a/Sources/Actions/BackwardCompatibility.php b/Sources/Actions/BackwardCompatibility.php index 6ad330ab5b..bb09402c82 100644 --- a/Sources/Actions/BackwardCompatibility.php +++ b/Sources/Actions/BackwardCompatibility.php @@ -23,7 +23,7 @@ trait BackwardCompatibility /** * Called by Subs-Compat.php BackwardCompatibility wrapper functions to provide subaction - * execution for existing mods + * execution for existing mods. Any new code should not depend on it. * * @param null|string $sa * @param bool $return_config @@ -35,20 +35,17 @@ public static function subActionProvider(?string $sa = null, bool $return_config return self::getConfigVars(); } - self::load(); + $obj = self::load(); if (is_string($sa)) { - // make sure it's a supported subaction - if (array_key_exists($sa, self::$subactions)) { - self::$obj->subaction = $sa; - } + $obj->setDefaultAction($sa); } if (is_string($activity)) { - self::$obj->activity = $activity; + $obj->activity = $activity; } - self::$obj->execute(); + $obj->execute(); } } diff --git a/Sources/Actions/Calendar.php b/Sources/Actions/Calendar.php index 7bf5b2c9bf..acf812c7cb 100644 --- a/Sources/Actions/Calendar.php +++ b/Sources/Actions/Calendar.php @@ -30,6 +30,8 @@ use SMF\ErrorHandler; use SMF\IntegrationHook; use SMF\Lang; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Routable; use SMF\Theme; use SMF\Time; @@ -43,39 +45,12 @@ * This class has only one real task, showing the calendar. * Original module by Aaron O'Neil - aaron@mud-master.com */ -class Calendar implements ActionInterface, Routable +class Calendar implements ActionInterface, ProvidesSubActionInterface, Routable { use ActionTrait; + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'show'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions of this action. - */ - public static array $subactions = [ - 'show' => 'show', - 'ical' => 'export', - 'post' => 'post', - 'clock' => 'clock', - ]; - /**************** * Public methods ****************/ @@ -85,11 +60,7 @@ class Calendar implements ActionInterface, Routable */ public function execute(): void { - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction(); } /** @@ -1732,20 +1703,23 @@ protected function __construct() { Lang::load('Calendar'); + $this->addSubAction('show', [$this, 'show']); + $this->addSubAction('ical', [$this, 'export']); + $this->addSubAction('post', [$this, 'post']); + $this->addSubAction('clock', [$this, 'clock']); + if ($_GET['action'] === 'clock') { - $this->subaction = 'clock'; - } elseif (!empty($_GET['sa']) && isset(self::$subactions[$_GET['sa']])) { - $this->subaction = $_GET['sa']; - } + $this->setDefaultSubAction('clock'); - if ($this->subaction === 'clock') { return; } + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); + // Special case for handling calendar subscriptions. if ( User::$me->is_guest - && $this->subaction === 'ical' + && $this->sub_action === 'ical' && isset($_REQUEST['u'], $_REQUEST['token']) ) { $user = current(User::load((int) $_REQUEST['u'])); diff --git a/Sources/Actions/Credits.php b/Sources/Actions/Credits.php index b09579b900..e77a554d77 100644 --- a/Sources/Actions/Credits.php +++ b/Sources/Actions/Credits.php @@ -416,9 +416,9 @@ public function execute(): void */ public static function call(bool $in_admin = false): void { - self::load(); - self::$obj->in_admin = $in_admin; - self::$obj->execute(); + $obj = self::load(); + $obj->in_admin = $in_admin; + $obj->execute(); } /****************** diff --git a/Sources/Actions/DisplayAdminFile.php b/Sources/Actions/DisplayAdminFile.php index 1677aacdef..4e14f70143 100644 --- a/Sources/Actions/DisplayAdminFile.php +++ b/Sources/Actions/DisplayAdminFile.php @@ -47,8 +47,6 @@ public function canBeLogged(): bool */ public function execute(): void { - Sapi::setMemoryLimit('32M'); - if (empty($_REQUEST['filename']) || !is_string($_REQUEST['filename'])) { ErrorHandler::fatalLang('no_access', false); } diff --git a/Sources/Actions/Feed.php b/Sources/Actions/Feed.php index 92e7e5c761..3f65638d92 100644 --- a/Sources/Actions/Feed.php +++ b/Sources/Actions/Feed.php @@ -30,6 +30,8 @@ use SMF\IP; use SMF\Lang; use SMF\Parser; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Routable; use SMF\Sapi; use SMF\Theme; @@ -63,10 +65,11 @@ * * Uses Stats, Profile, Post, and PersonalMessage language files. */ -class Feed implements ActionInterface, Routable +class Feed implements ActionInterface, ProvidesSubActionInterface, Routable { use ActionRouter; use ActionTrait; + use ProvidesSubActionTrait; /***************** * Class constants @@ -122,13 +125,6 @@ class Feed implements ActionInterface, Routable * Public properties *******************/ - /** - * @var string - * - * The requested sub-action. - */ - public string $subaction = 'recent'; - /** * @var string * @@ -210,24 +206,6 @@ class Feed implements ActionInterface, Routable 'footer' => '', ]; - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * List all the different types of data they can pull. - */ - public static array $subactions = [ - 'recent' => 'getXmlRecent', - 'news' => 'getXmlNews', - 'members' => 'getXmlMembers', - 'profile' => 'getXmlProfile', - 'posts' => 'getXmlPosts', - 'personal_messages' => 'getXmlPMs', - ]; - /********************* * Internal properties *********************/ @@ -277,7 +255,7 @@ public function __construct(?string $subaction = null, ?int $member = null) $this->host = Url::create(Config::$scripturl)->host; // Easy adding of sub actions - IntegrationHook::call('integrate_xmlfeeds', [&self::$subactions]); + IntegrationHook::call('integrate_xmlfeeds', [&$this->sub_actions]); // These things are simple to set. $this->setSubaction($subaction); @@ -458,13 +436,22 @@ public function __construct(?string $subaction = null, ?int $member = null) */ public function execute(): void { + $this->addSubAction('recent', [$this, 'getXmlRecent']); + $this->addSubAction('news', [$this, 'getXmlNews']); + $this->addSubAction('members', [$this, 'getXmlMembers']); + $this->addSubAction('profile', [$this, 'getXmlProfile']); + $this->addSubAction('posts', [$this, 'getXmlPosts']); + $this->addSubAction('personal_messages', [$this, 'getXmlPMs']); + + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); + $this->getData(); - $this->xml = self::build($this->format, $this->data, $this->metadata, $this->subaction); + $this->xml = self::build($this->format, $this->data, $this->metadata, $this->getSubAction()); $this->emit(); } /** - * Retrieve the correct type of data based on $this->subaction. + * Retrieve the correct type of data based on $this->sub_action. * The array will be structured to match $this->format. * * @return array An array of arrays of feed items. Each array has keys corresponding to the appropriate tags for the specified format. @@ -472,7 +459,7 @@ public function execute(): void public function getData(): array { // We only want some information, not all of it. - $cachekey = [$this->subaction, $this->limit, $this->ascending, $this->start_after]; + $cachekey = [$this->sub_action, $this->limit, $this->ascending, $this->start_after]; if (!empty($this->member)) { $cachekey[] = $this->member; @@ -493,10 +480,10 @@ public function getData(): array if (empty($this->data)) { // Should we call one of this class's own methods, or something added by a mod? - if (is_callable([$this, self::$subactions[$this->subaction]])) { - $call = [$this, self::$subactions[$this->subaction]]; + if (is_callable([$this, $this->sub_actions[$this->sub_action]])) { + $call = [$this, $this->sub_actions[$this->sub_action]]; } else { - $call = Utils::getCallable(self::$subactions[$this->subaction]); + $call = Utils::getCallable($this->sub_actions[$this->sub_action]); } $this->data = !empty($call) ? call_user_func($call, $this->format) : []; @@ -522,9 +509,9 @@ public function emit(): void { // Descriptive filenames = good $filename[] = $this->metadata['title']; - $filename[] = $this->subaction; + $filename[] = $this->sub_action; - if (in_array($this->subaction, ['profile', 'posts', 'personal_messages'])) { + if (in_array($this->sub_action, ['profile', 'posts', 'personal_messages'])) { $filename[] = 'u=' . $this->member; } @@ -3055,12 +3042,12 @@ public static function parseRoute(array $route, array $params = []): array */ protected function setSubaction(?string $subaction): void { - if (isset($subaction, self::$subactions[$subaction])) { - $this->subaction = $subaction; - } elseif (isset($_GET['sa'], self::$subactions[$_GET['sa']])) { - $this->subaction = $_GET['sa']; + if (isset($subaction, $this->sub_actions[$subaction])) { + $this->sub_action = $subaction; + } elseif (isset($_GET['sa'], $this->sub_actions[$_GET['sa']])) { + $this->sub_action = $_GET['sa']; } else { - $this->subaction = array_key_first(self::$subactions); + $this->sub_action = array_key_first($this->sub_actions); } } @@ -3129,7 +3116,7 @@ protected function setlimit(): void protected function checkEnabled(): void { // Users can always export their own profile data. - if (in_array($this->subaction, ['profile', 'posts', 'personal_messages']) && $this->member == User::$me->id && !User::$me->is_guest) { + if (in_array($this->sub_action, ['profile', 'posts', 'personal_messages']) && $this->member == User::$me->id && !User::$me->is_guest) { return; } diff --git a/Sources/Actions/Groups.php b/Sources/Actions/Groups.php index 771991dbe3..0cc2271005 100644 --- a/Sources/Actions/Groups.php +++ b/Sources/Actions/Groups.php @@ -25,6 +25,8 @@ use SMF\Lang; use SMF\PageIndex; use SMF\Parser; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Routable; use SMF\SecurityToken; use SMF\Slug; @@ -35,38 +37,13 @@ /** * Shows group info. */ -class Groups implements ActionInterface, Routable +class Groups implements ActionInterface, ProvidesSubActionInterface, Routable { use ActionRouter; use ActionTrait; + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'index'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'index' => 'index', - 'members' => 'members', - ]; - /********************* * Internal properties *********************/ @@ -92,21 +69,18 @@ public function execute(): void { User::$me->isAllowedTo('view_mlist'); + IntegrationHook::call('integrate_manage_groups', [&$this->sub_actions]); + + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); + // Get the template stuff up and running. Lang::load('ManageMembers'); Lang::load('ModerationCenter'); Theme::loadTemplate('ManageMembergroups'); - Utils::$context['linktree'][] = [ - 'url' => Config::$scripturl . $this->action_url, - 'name' => Lang::$txt['groups'], - ]; + // If needed, set the mod center menu. - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - if (!empty($call)) { - call_user_func($call); - } } /** @@ -497,11 +471,9 @@ public static function parseRoute(array $route, array $params = []): array */ protected function __construct() { - IntegrationHook::call('integrate_manage_groups', [&self::$subactions]); - - if (!empty($_GET['sa']) && isset(self::$subactions[$_GET['sa']])) { - $this->subaction = $_GET['sa']; - } + $this->addSubAction('index', [$this, 'index']); + $this->addSubAction('members', [$this, 'members']); + $this->addSubAction('requests', [$this, 'requests']); } } diff --git a/Sources/Actions/Help.php b/Sources/Actions/Help.php index 2491b36010..1904380a38 100644 --- a/Sources/Actions/Help.php +++ b/Sources/Actions/Help.php @@ -21,6 +21,8 @@ use SMF\Config; use SMF\IntegrationHook; use SMF\Lang; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Routable; use SMF\Theme; use SMF\Utils; @@ -28,37 +30,13 @@ /** * This class has the important job of taking care of help messages and the help center. */ -class Help implements ActionInterface, Routable +class Help implements ActionInterface, ProvidesSubActionInterface, Routable { use ActionRouter; use ActionTrait; + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'index'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'index' => 'index', - ]; - /**************** * Public methods ****************/ @@ -73,11 +51,9 @@ public function isRestrictedGuestAccessAllowed(): bool */ public function execute(): void { - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); + IntegrationHook::call('integrate_manage_help', [&$this->sub_actions]); - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -132,15 +108,10 @@ public function index() */ protected function __construct() { + $this->addSubAction('index', [$this, 'index']); + Theme::loadTemplate('Help'); Lang::load('Manual'); - - // CRUD $subactions as needed. - IntegrationHook::call('integrate_manage_help', [&self::$subactions]); - - if (!empty($_GET['sa']) && isset(self::$subactions[$_GET['sa']])) { - $this->subaction = $_GET['sa']; - } } } diff --git a/Sources/Actions/Like.php b/Sources/Actions/Like.php index 02de3150cb..e5b1fd02d3 100644 --- a/Sources/Actions/Like.php +++ b/Sources/Actions/Like.php @@ -26,6 +26,8 @@ use SMF\Lang; use SMF\OutputTypeInterface; use SMF\OutputTypes; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Routable; use SMF\Theme; use SMF\Time; @@ -35,44 +37,11 @@ /** * Handles liking posts and displaying the list of who liked a post. */ -class Like implements ActionInterface, Routable +class Like implements ActionInterface, ProvidesSubActionInterface, Routable { use ActionRouter; use ActionTrait; - - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'like'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - * - * @todo Do delete, insert, and count really need to be sub-actions? They - * are never used as sub-actions in practice. Instead, they are only ever - * called internally by the like() method. Moreover, the control flow - * regarding hooks, etc., assumes that they are only called by like(). - */ - public static array $subactions = [ - 'like' => 'like', - 'view' => 'view', - 'delete' => 'delete', - 'insert' => 'insert', - 'count' => 'count', - ]; + use ProvidesSubActionTrait; /********************* * Internal properties @@ -232,20 +201,18 @@ public function execute(): void // So at this point, whatever type of like the user supplied and the // item of content in question, we know it exists. // Now we need to figure out what we're doing with that. - if (isset(self::$subactions[$this->subaction])) { + if (isset($_REQUEST['sa'], $this->sub_actions[$_REQUEST['sa']])) { + $this->findRequestedSubAction($_REQUEST['sa']); + // Guest can only view likes. - if ($this->subaction != 'view') { + if ($this->sub_action != 'view') { User::$me->kickIfGuest(); } User::$me->checkSession('get'); // Call the appropriate method. - if (method_exists($this, self::$subactions[$this->subaction])) { - call_user_func([$this, self::$subactions[$this->subaction]]); - } else { - call_user_func(self::$subactions[$this->subaction]); - } + $this->callSubAction($_REQUEST['sa']); } // Send the response. @@ -276,14 +243,22 @@ public function get(string $property = ''): mixed */ protected function __construct() { + /* + * @todo Do delete, insert, and count really need to be sub-actions? They + * are never used as sub-actions in practice. Instead, they are only ever + * called internally by the like() method. Moreover, the control flow + * regarding hooks, etc., assumes that they are only called by like(). + */ + $this->addSubAction('like', [$this, 'like']); + $this->addSubAction('view', [$this, 'view']); + $this->addSubAction('delete', [$this, 'delete']); + $this->addSubAction('insert', [$this, 'insert']); + $this->addSubAction('count', [$this, 'count']); + if (!empty($_REQUEST['sa']) && $_REQUEST['sa'] === '_count') { $_REQUEST['sa'] = 'count'; } - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } - $this->type = $_GET['ltype'] ?? ''; $this->content = (int) ($_GET['like'] ?? 0); $this->js = isset($_GET['js']); @@ -379,7 +354,7 @@ protected function check(): void * * See also issueLike() for further notes. */ - $can_like = IntegrationHook::call('integrate_valid_likes', [$this->type, $this->content, $this->subaction, $this->js, $this->extra]); + $can_like = IntegrationHook::call('integrate_valid_likes', [$this->type, $this->content, $this->sub_action, $this->js, $this->extra]); $found = false; @@ -414,7 +389,7 @@ protected function check(): void // Is the user able to like this? // Viewing a list of likes doesn't require this permission. - if ($this->subaction != 'view' && isset($this->valid_likes['can_like']) && is_string($this->valid_likes['can_like'])) { + if ($this->sub_action != 'view' && isset($this->valid_likes['can_like']) && is_string($this->valid_likes['can_like'])) { $this->error = $this->valid_likes['can_like']; return; @@ -440,7 +415,7 @@ protected function delete(): void ); // Are we calling this directly? If so, set the data for the response. - if ($this->subaction == __FUNCTION__) { + if ($this->sub_action == __FUNCTION__) { $this->data = __FUNCTION__; } @@ -535,7 +510,7 @@ protected function insert(): void } // Are we calling this directly? If so, set the data for the response. - if ($this->subaction == __FUNCTION__) { + if ($this->sub_action == __FUNCTION__) { $this->data = __FUNCTION__; } } @@ -561,7 +536,7 @@ protected function count(): void $this->num_likes = (int) $likes; - if ($this->subaction == __FUNCTION__) { + if ($this->sub_action == __FUNCTION__) { $this->data = $this->num_likes; } } @@ -745,7 +720,7 @@ protected function respond(): void if ($this->error) { // If this is a generic error, set it up good. if ($this->error == 'cannot_') { - $this->error = $this->subaction == 'view' ? 'cannot_view_likes' : 'cannot_like_content'; + $this->error = $this->sub_action == 'view' ? 'cannot_view_likes' : 'cannot_like_content'; } // Is this request coming from an AJAX call? @@ -772,14 +747,14 @@ protected function respond(): void // These fine gentlemen all share the same template. $generic = ['delete', 'insert', 'count']; - if (in_array($this->subaction, $generic)) { + if (in_array($this->sub_action, $generic)) { Utils::$context['sub_template'] = 'generic'; Utils::$context['data'] = Lang::$txt['like_' . $this->data] ?? $this->data; } // Directly pass the current called sub-action and the data // generated by its associated Method. else { - Utils::$context['sub_template'] = $this->subaction; + Utils::$context['sub_template'] = $this->sub_action; Utils::$context['data'] = $this->data; } } @@ -796,7 +771,7 @@ protected function sendJsonReponse(): void // If there is an error, send it. if ($this->error) { if ($this->error == 'cannot_') { - $this->error = $this->subaction == 'view' ? 'cannot_view_likes' : 'cannot_like_content'; + $this->error = $this->sub_action == 'view' ? 'cannot_view_likes' : 'cannot_like_content'; } $print['error'] = $this->error; diff --git a/Sources/Actions/Login2.php b/Sources/Actions/Login2.php index ad333eaae0..9df1ac0075 100644 --- a/Sources/Actions/Login2.php +++ b/Sources/Actions/Login2.php @@ -24,6 +24,8 @@ use SMF\ErrorHandler; use SMF\IntegrationHook; use SMF\Lang; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Routable; use SMF\Sapi; use SMF\Security; @@ -35,37 +37,11 @@ /** * Validates the submitted credentials and logs the user in if they pass. */ -class Login2 implements ActionInterface, Routable +class Login2 implements ActionInterface, ProvidesSubActionInterface, Routable { use ActionRouter; use ActionTrait; - - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'main'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'main' => 'main', - 'salt' => 'updateSalt', - 'check' => 'checkCookie', - ]; + use ProvidesSubActionTrait; /**************** * Public methods @@ -113,11 +89,7 @@ public function execute(): void self::checkAjax(); - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -454,9 +426,9 @@ public static function validatePasswordFlood(int $id_member, string $member_name */ protected function __construct() { - if (!empty($_GET['sa']) && isset(self::$subactions[$_GET['sa']])) { - $this->subaction = $_GET['sa']; - } + $this->addSubAction('main', [$this, 'main']); + $this->addSubAction('salt', [$this, 'updateSalt']); + $this->addSubAction('check', [$this, 'checkCookie']); } /** diff --git a/Sources/Actions/MarkRead.php b/Sources/Actions/MarkRead.php index c3c89c8b23..8089998f55 100644 --- a/Sources/Actions/MarkRead.php +++ b/Sources/Actions/MarkRead.php @@ -21,6 +21,8 @@ use SMF\Board; use SMF\Config; use SMF\Db\DatabaseApi as Db; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Routable; use SMF\Topic; use SMF\User; @@ -29,38 +31,11 @@ /** * Mark boards and topics as read (or unread in some cases). */ -class MarkRead implements ActionInterface, Routable +class MarkRead implements ActionInterface, ProvidesSubActionInterface, Routable { use ActionSuffixRouter; use ActionTrait; - - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'board'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'all' => 'all', - 'unreadreplies' => 'replies', - 'topic' => 'topic', - 'board' => 'board', - ]; + use ProvidesSubActionTrait; /**************** * Public methods @@ -71,11 +46,7 @@ class MarkRead implements ActionInterface, Routable */ public function execute(): void { - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -484,14 +455,15 @@ public static function parseRoute(array $route, array $params = []): array */ protected function __construct() { + $this->addSubAction('board', [$this, 'board']); + $this->addSubAction('unreadreplies', [$this, 'replies']); + $this->addSubAction('topic', [$this, 'topic']); + $this->addSubAction('all', [$this, 'all']); + // No Guests allowed! User::$me->kickIfGuest(); User::$me->checkSession('get'); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } } } diff --git a/Sources/Actions/Memberlist.php b/Sources/Actions/Memberlist.php index 43766813fe..2ad9d12347 100644 --- a/Sources/Actions/Memberlist.php +++ b/Sources/Actions/Memberlist.php @@ -25,6 +25,8 @@ use SMF\Lang; use SMF\PageIndex; use SMF\Parser; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Routable; use SMF\Theme; use SMF\Time; @@ -35,24 +37,17 @@ * This class contains the methods for displaying and searching in the * members list. */ -class Memberlist implements ActionInterface, Routable +class Memberlist implements ActionInterface, ProvidesSubActionInterface, Routable { use ActionRouter; use ActionTrait; + use ProvidesSubActionTrait; use BackwardCompatibility; /******************* * Public properties *******************/ - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'all'; - /** * @var array * @@ -80,20 +75,6 @@ class Memberlist implements ActionInterface, Routable */ public int $cache_step_size = 500; - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'all' => 'all', - 'search' => 'search', - ]; - /**************** * Public methods ****************/ @@ -110,6 +91,11 @@ class Memberlist implements ActionInterface, Routable */ public function execute(): void { + // Allow mods to add sub-actions and sort_links. + IntegrationHook::call('integrate_memberlist_subactions', [&$this->sub_actions, $this->sort_links]); + + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); + // Make sure they can view the memberlist. User::$me->isAllowedTo('view_mlist'); @@ -118,10 +104,10 @@ public function execute(): void foreach ($this->sort_links as $sa => &$sort_link) { $sort_link['label'] = Lang::$txt[$sort_link['label']] ?? ($sort_link['label'] ?? ($sort_link['action'] ?? $sa)); - $sort_link['selected'] = $this->subaction === ($sort_link['action'] ?? $sa); + $sort_link['selected'] = $this->sub_action === ($sort_link['action'] ?? $sa); } - Utils::$context['listing_by'] = &$this->subaction; + Utils::$context['listing_by'] = &$this->sub_action; Utils::$context['sort_links'] = &$this->sort_links; Utils::$context['num_members'] = Config::$modSettings['totalMembers']; @@ -214,11 +200,7 @@ public function execute(): void // Allow mods to add additional buttons here IntegrationHook::call('integrate_memberlist_buttons'); - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction(); } /** @@ -865,12 +847,8 @@ public static function parseRoute(array $route, array $params = []): array */ protected function __construct() { - // Allow mods to add sub-actions and sort_links. - IntegrationHook::call('integrate_memberlist_subactions', [&self::$subactions, $this->sort_links]); - - if (!empty($_GET['sa']) && isset(self::$subactions[$_GET['sa']])) { - $this->subaction = $_GET['sa']; - } + $this->addSubAction('all', [$this, 'all']); + $this->addSubAction('search', [$this, 'search']); } } diff --git a/Sources/Actions/Moderation/Groups.php b/Sources/Actions/Moderation/Groups.php index 7dda3a1663..b9fc69af61 100644 --- a/Sources/Actions/Moderation/Groups.php +++ b/Sources/Actions/Moderation/Groups.php @@ -38,33 +38,6 @@ class Groups extends ViewGroups use ActionRouter; use ActionTrait; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'index'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'index' => 'index', - 'members' => 'members', - 'requests' => 'requests', - ]; - /********************* * Internal properties *********************/ @@ -96,11 +69,7 @@ public function execute(): void Lang::load('ModerationCenter'); Theme::loadTemplate('ManageMembergroups'); - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -647,11 +616,11 @@ public static function parseRoute(array $route, array $params = []): array */ protected function __construct() { - IntegrationHook::call('integrate_manage_groups', [&self::$subactions]); + $this->addSubAction('index', [$this, 'index']); + $this->addSubAction('members', [$this, 'members']); + $this->addSubAction('requests', [$this, 'requests']); - if (!empty($_GET['sa']) && isset(self::$subactions[$_GET['sa']])) { - $this->subaction = $_GET['sa']; - } + IntegrationHook::call('integrate_manage_groups', [&$this->sub_actions); } } diff --git a/Sources/Actions/Moderation/Logs.php b/Sources/Actions/Moderation/Logs.php index 7d46a6316c..442498c54a 100644 --- a/Sources/Actions/Moderation/Logs.php +++ b/Sources/Actions/Moderation/Logs.php @@ -25,6 +25,8 @@ use SMF\Lang; use SMF\Logging; use SMF\Menu; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\SecurityToken; use SMF\Time; use SMF\User; @@ -34,9 +36,10 @@ * The moderation and administration logs are this class's only job. * It views them, and that's about all it does. */ -class Logs implements ActionInterface +class Logs implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; + use ProvidesSubActionTrait; /******************* * Public properties @@ -50,13 +53,6 @@ class Logs implements ActionInterface */ public string $action = 'moderate'; - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'modlog'; /** * @var int @@ -79,16 +75,6 @@ class Logs implements ActionInterface 'admin', ]; - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'modlog' => 'modlog', - 'adminlog' => 'adminlog', - ]; - /** * @var array * @@ -221,11 +207,11 @@ class Logs implements ActionInterface */ public function execute(): void { - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); - if (!empty($call)) { - call_user_func($call); - } + $this->log_type = $this->sub_action == 'adminlog' ? 3 : 1; + + $this->callSubAction(); } /** @@ -635,16 +621,13 @@ public static function list_getModLogEntries(int $start, int $items_per_page, st */ protected function __construct() { + $this->addSubAction('modlog', [$this, 'modlog']); + $this->addSubAction('adminlog', [$this, 'adminlog']); + if (!empty($_REQUEST['action']) && in_array($_REQUEST['action'], self::$actions)) { $this->action = $_REQUEST['action']; } - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } - - $this->log_type = $this->subaction == 'adminlog' ? 3 : 1; - // These change dependant on whether we are viewing the moderation or admin log. if ($this->action == 'admin') { $this->url_start = '?action=admin;area=logs;sa=' . ($this->log_type == 3 ? 'adminlog' : 'modlog') . ';type=' . $this->log_type; @@ -745,7 +728,7 @@ protected function deleteAll(): void ], ); - $log_type = isset($this->subaction) && $this->subaction == 'adminlog' ? 'admin' : 'moderate'; + $log_type = isset($this->sub_action) && $this->sub_action == 'adminlog' ? 'admin' : 'moderate'; Logging::logAction('clearlog_' . $log_type, [], $log_type); } diff --git a/Sources/Actions/Moderation/Posts.php b/Sources/Actions/Moderation/Posts.php index 2e02a9d9b8..ac93476dbe 100644 --- a/Sources/Actions/Moderation/Posts.php +++ b/Sources/Actions/Moderation/Posts.php @@ -30,6 +30,8 @@ use SMF\Msg; use SMF\PageIndex; use SMF\Parser; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\SecurityToken; use SMF\Theme; use SMF\Time; @@ -40,40 +42,12 @@ /** * Handles things related to post moderation. */ -class Posts implements ActionInterface +class Posts implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'replies'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'replies' => 'posts', - 'topics' => 'posts', - 'attachments' => 'attachments', - 'approve' => 'approve', - ]; - /**************** * Public methods ****************/ @@ -83,11 +57,7 @@ class Posts implements ActionInterface */ public function execute(): void { - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -860,14 +830,15 @@ public static function list_getNumUnapprovedAttachments(string $approve_query): */ protected function __construct() { + $this->addSubAction('replies', [$this, 'posts']); + $this->addSubAction('topics', [$this, 'posts']); + $this->addSubAction('attachments', [$this, 'attachments']); + $this->addSubAction('approve', [$this, 'approve']); + Lang::load('ModerationCenter'); Theme::loadTemplate('ModerationCenter'); - IntegrationHook::call('integrate_post_moderation', [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } + IntegrationHook::call('integrate_post_moderation', [&$this->sub_actions]); } /** diff --git a/Sources/Actions/Moderation/ReportedContent.php b/Sources/Actions/Moderation/ReportedContent.php index 35ba773935..9b07d2532f 100644 --- a/Sources/Actions/Moderation/ReportedContent.php +++ b/Sources/Actions/Moderation/ReportedContent.php @@ -30,6 +30,8 @@ use SMF\Menu; use SMF\PageIndex; use SMF\Parser; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\SecurityToken; use SMF\Theme; use SMF\Time; @@ -39,24 +41,12 @@ /** * Handles reported members and posts, as well as moderation comments. */ -class ReportedContent implements ActionInterface +class ReportedContent implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'show'; - /** * @var string * @@ -68,20 +58,6 @@ class ReportedContent implements ActionInterface * Public static properties **************************/ - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'show' => 'show', - 'closed' => 'showClosed', - 'details' => 'details', - 'handle' => 'setState', - 'handlecomment' => 'comment', - 'editcomment' => 'modifyComment', - ]; - /** * @var array * @@ -119,11 +95,7 @@ class ReportedContent implements ActionInterface */ public function execute(): void { - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -621,6 +593,13 @@ public static function recountOpenReports(string $type): int */ protected function __construct() { + $this->addSubAction('show', [$this, 'show']); + $this->addSubAction('closed', [$this, 'showClosed']); + $this->addSubAction('details', [$this, 'details']); + $this->addSubAction('handle', [$this, 'setState']); + $this->addSubAction('handlecomment', [$this, 'comment']); + $this->addSubAction('editcomment', [$this, 'modifyComment']); + // First order of business - what are these reports about? // area=reported{type} $this->type = substr($_GET['area'], 8); @@ -654,11 +633,7 @@ protected function __construct() } // Go ahead and add your own sub-actions. - IntegrationHook::call('integrate_reported_' . $this->type, [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } + IntegrationHook::call('integrate_reported_' . $this->type, [&$this->sub_actions]); } /** diff --git a/Sources/Actions/Moderation/Warnings.php b/Sources/Actions/Moderation/Warnings.php index de5a2ff183..3b38015d16 100644 --- a/Sources/Actions/Moderation/Warnings.php +++ b/Sources/Actions/Moderation/Warnings.php @@ -26,6 +26,8 @@ use SMF\Logging; use SMF\Menu; use SMF\Msg; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\SecurityToken; use SMF\Theme; use SMF\Time; @@ -35,39 +37,12 @@ /** * Allows the moderator to view stuff related to warnings. */ -class Warnings implements ActionInterface +class Warnings implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'log'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'log' => ['log', ['view_warning_any', 'moderate_forum']], - 'templates' => ['templates', 'issue_warning'], - 'templateedit' => ['templateEdit', 'issue_warning'], - ]; - /**************** * Public methods ****************/ @@ -85,11 +60,7 @@ public function execute(): void 'description' => Lang::$txt['mc_warnings_description'], ]; - $call = method_exists($this, self::$subactions[$this->subaction][0]) ? [$this, self::$subactions[$this->subaction][0]] : Utils::getCallable(self::$subactions[$this->subaction][0]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -722,39 +693,24 @@ public static function list_getWarningTemplates(int $start, int $items_per_page, */ protected function __construct() { - IntegrationHook::call('integrate_warning_log_actions', [&self::$subactions]); + User::$me->isAllowedTo('issue_warning'); - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; + if (User::$me->allowedTo(['view_warning_any', 'moderate_forum'])) { + $this->addSubAction('log', [$this, 'log']); } - // If the user can't do the specified sub-action, choose the first one they can. - if (!User::$me->allowedTo(self::$subactions[$this->subaction][1])) { - $this->subaction = ''; + $this->addSubAction('templates', [$this, 'templates']); + $this->addSubAction('templateedit', [$this, 'templateEdit']); - foreach (self::$subactions as $sa => $sa_info) { - if ($sa === $this->subaction) { - continue; - } + $sub_actions = []; + IntegrationHook::call('integrate_warning_log_actions', [&$sub_actions]); - if (User::$me->allowedTo(self::$subactions[$sa][1])) { - $this->subaction = $sa; - break; - } - } - - // This shouldn't happen, but just in case... - if (empty($this->subaction)) { - Utils::redirectexit('action=moderate;area=index'); + foreach ($sub_actions as $sa => [$func, $perm]) { + if (User::$me->allowedTo($perm)) { + $this->addSubAction($sa, [$this, $func]); } } } - - /************************* - * Internal static methods - *************************/ - - // code... } ?> \ No newline at end of file diff --git a/Sources/Actions/PersonalMessage.php b/Sources/Actions/PersonalMessage.php index 7f92b939a5..ee960192fa 100644 --- a/Sources/Actions/PersonalMessage.php +++ b/Sources/Actions/PersonalMessage.php @@ -40,6 +40,8 @@ Search, }; use SMF\Profile; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Routable; use SMF\Theme; use SMF\User; @@ -50,10 +52,11 @@ * messages. It allows viewing, sending, deleting, and marking personal * messages. */ -class PersonalMessage implements ActionInterface, Routable +class PersonalMessage implements ActionInterface, ProvidesSubActionInterface, Routable { use ActionRouter; use ActionTrait; + use ProvidesSubActionTrait; use BackwardCompatibility; /***************** @@ -162,13 +165,6 @@ class PersonalMessage implements ActionInterface, Routable ], ]; - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'show'; /** * @var string @@ -229,28 +225,6 @@ class PersonalMessage implements ActionInterface, Routable * Public static properties **************************/ - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'show' => 'show', - 'popup' => 'popup', - 'showpmdrafts' => 'drafts', - 'send' => 'send', - 'send2' => 'send2', - 'search' => 'search', - 'search2' => 'search2', - 'pmactions' => 'applyActions', - 'removeall2' => 'removeAll', - 'prune' => 'prune', - 'report' => 'report', - 'manlabels' => 'labels', - 'manrules' => 'rules', - 'settings' => 'settings', - ]; - /********************* * Internal properties *********************/ @@ -303,21 +277,19 @@ public function execute(): void Received::setNotNew(); } + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); + // No menu in AJAX requests or the popup. - if (!isset($_REQUEST['xml']) && $this->subaction !== 'popup') { - if ($this->subaction === 'show') { + if (!isset($_REQUEST['xml']) && $this->sub_action !== 'popup') { + if ($this->sub_action === 'show') { $this->createMenu($this->current_label_id == -1 ? $this->folder : 'label' . $this->current_label_id); } else { - $this->createMenu($this->subaction); + $this->createMenu($this->sub_action); } } // Now let's get on with the main job... - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction(); } /** @@ -803,6 +775,21 @@ public static function parseRoute(array $route, array $params = []): array */ protected function __construct() { + $this->addSubAction('show', [$this, 'show']); + $this->addSubAction('popup', [$this, 'popup']); + $this->addSubAction('showpmdrafts', [$this, 'drafts']); + $this->addSubAction('send', [$this, 'send']); + $this->addSubAction('send2', [$this, 'send2']); + $this->addSubAction('search', [$this, 'search']); + $this->addSubAction('search2', [$this, 'search2']); + $this->addSubAction('pmactions', [$this, 'applyActions']); + $this->addSubAction('removeall2', [$this, 'removeAll']); + $this->addSubAction('prune', [$this, 'prune']); + $this->addSubAction('report', [$this, 'report']); + $this->addSubAction('manlabels', [$this, 'labels']); + $this->addSubAction('manrules', [$this, 'rules']); + $this->addSubAction('settings', [$this, 'settings']); + Lang::load('PersonalMessage+Drafts'); Theme::loadTemplate(isset($_REQUEST['xml']) ? 'Xml' : 'PersonalMessage'); @@ -811,10 +798,6 @@ protected function __construct() unset($_REQUEST['f']); } - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } - if (isset($_REQUEST['f']) && $_REQUEST['f'] === 'sent') { $this->folder = 'sent'; } diff --git a/Sources/Actions/Post.php b/Sources/Actions/Post.php index d4f2860fda..02952c0e19 100644 --- a/Sources/Actions/Post.php +++ b/Sources/Actions/Post.php @@ -32,6 +32,8 @@ use SMF\Msg; use SMF\Parser; use SMF\Poll; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Routable; use SMF\Security; use SMF\Theme; @@ -45,10 +47,11 @@ /** * This class handles posting and modifying replies and new topics. */ -class Post implements ActionInterface, Routable +class Post implements ActionInterface, ProvidesSubActionInterface, Routable { use ActionSuffixRouter; use ActionTrait; + use ProvidesSubActionTrait; use BackwardCompatibility; /***************** @@ -71,8 +74,6 @@ class Post implements ActionInterface, Routable * * The sub-action to call. */ - public string $subaction = 'show'; - /** * @var array * @@ -140,15 +141,6 @@ class Post implements ActionInterface, Routable * Public static properties **************************/ - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'show' => 'show', - ]; - /********************* * Internal properties *********************/ @@ -205,13 +197,9 @@ public function execute(): void } // Allow mods to add new sub-actions. - IntegrationHook::call('integrate_post_subactions', [&self::$subactions]); - - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); + IntegrationHook::call('integrate_post_subactions', [&$this->sub_actions]); - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -473,6 +461,8 @@ public static function parseRoute(array $route, array $params = []): array */ protected function __construct() { + $this->addSubAction('show', [$this, 'show']); + // Add references to some properties to Utils::$context. Utils::$context['becomes_approved'] = &$this->becomes_approved; @@ -821,7 +811,7 @@ protected function initiateEvent(): void // If the user doesn't have permission to edit the post in this topic, redirect them. if ((empty(Topic::$info->id_member_started) || Topic::$info->id_member_started != User::$me->id || !User::$me->allowedTo('modify_own')) && !User::$me->allowedTo('modify_any')) { $calendar_action = Calendar::load(); - $calendar_action->subaction = 'post'; + $calendar_action->setDefaultAction('post'); $calendar_action->execute(); return; @@ -1395,7 +1385,7 @@ protected function showAttachments(): void $_SESSION['temp_attachments'] = []; } // Hmm, coming in fresh and there are files in session. - elseif ($this->subaction != 'submit' || !empty($_POST['from_qr'])) { + elseif ($this->sub_action != 'submit' || !empty($_POST['from_qr'])) { // Let's be nice and see if they belong here first. if ((empty($_REQUEST['msg']) && empty($_SESSION['temp_attachments']['post']['msg']) && $_SESSION['temp_attachments']['post']['board'] == (!empty(Board::$info->id) ? Board::$info->id : 0)) || (!empty($_REQUEST['msg']) && $_SESSION['temp_attachments']['post']['msg'] == $_REQUEST['msg'])) { // See if any files still exist before showing the warning message and the files attached. diff --git a/Sources/Actions/Post2.php b/Sources/Actions/Post2.php index 2a1750235f..f8f573c6fa 100644 --- a/Sources/Actions/Post2.php +++ b/Sources/Actions/Post2.php @@ -51,13 +51,6 @@ class Post2 extends Post * Public properties *******************/ - /** - * @var string - * - * The sub-action to call. - */ - public string $subaction = 'submit'; - /** * @var bool * @@ -68,20 +61,6 @@ class Post2 extends Post */ public bool $authorIsGuest = true; - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'submit' => 'submit', - 'show' => 'show', - ]; - /********************* * Internal properties *********************/ @@ -128,13 +107,9 @@ public function execute(): void } // Allow mods to add new sub-actions. - IntegrationHook::call('integrate_post2_subactions', [&self::$subactions]); + IntegrationHook::call('integrate_post2_subactions', [&$this->sub_actions]); - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -726,6 +701,9 @@ public function submit(): void */ protected function __construct() { + $this->addSubAction('submit', [$this, 'submit']); + $this->addSubAction('show', [$this, 'show']); + parent::__construct(); } diff --git a/Sources/Actions/Profile/BackwardCompatibility.php b/Sources/Actions/Profile/BackwardCompatibility.php index 7fb3f02f71..a7f2ce5fec 100644 --- a/Sources/Actions/Profile/BackwardCompatibility.php +++ b/Sources/Actions/Profile/BackwardCompatibility.php @@ -23,6 +23,8 @@ trait BackwardCompatibility use BackCompat; /** + * Helps provide backwards compatbility for profile + * actions. Any new code should not depend on it. * * Backwards compatibility function for handling profile-related subactions * @@ -48,7 +50,7 @@ public static function subActionProvider( } if ($loadSelfFirst) { - self::load(); + $obj = self::load(); if ($loadProfile) { Profile::load($memID); @@ -57,7 +59,7 @@ public static function subActionProvider( if ($loadProfile) { Profile::load($memID); } - self::load(); + $obj = self::load(); } if ($updateRequest) { @@ -65,10 +67,10 @@ public static function subActionProvider( } if (isset($sa)) { - self::$obj->subaction = $sa; + $obj->setDefaultAction($sa); } - self::$obj->execute(); + $obj->execute(); } } diff --git a/Sources/Actions/Profile/BuddyIgnoreLists.php b/Sources/Actions/Profile/BuddyIgnoreLists.php index 6a98728b2b..458c867bcd 100644 --- a/Sources/Actions/Profile/BuddyIgnoreLists.php +++ b/Sources/Actions/Profile/BuddyIgnoreLists.php @@ -25,6 +25,8 @@ use SMF\Menu; use SMF\Parser; use SMF\Profile; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Theme; use SMF\User; use SMF\Utils; @@ -32,38 +34,12 @@ /** * Show all the users buddies, as well as an add/delete interface. */ -class BuddyIgnoreLists implements ActionInterface +class BuddyIgnoreLists implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'buddies'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'buddies' => 'buddies', - 'ignore' => 'ignore', - ]; - /** * @var array * @@ -83,6 +59,8 @@ class BuddyIgnoreLists implements ActionInterface */ public function execute(): void { + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); + // Do a quick check to ensure people aren't getting here illegally! if (!User::$me->is_owner || empty(Config::$modSettings['enable_buddylist'])) { ErrorHandler::fatalLang('no_access', false); @@ -91,8 +69,8 @@ public function execute(): void // Can we email the user directly? Utils::$context['can_moderate_forum'] = User::$me->allowedTo('moderate_forum'); - Utils::$context['list_area'] = $this->subaction; - Utils::$context['sub_template'] = self::$subtemplates[$this->subaction]; + Utils::$context['list_area'] = $this->sub_action; + Utils::$context['sub_template'] = self::$subtemplates[$this->sub_action]; // Create the tabs for the template. Menu::$loaded['profile']->tab_data = [ @@ -107,11 +85,7 @@ public function execute(): void Theme::loadJavaScriptFile('suggest.js', ['defer' => false, 'minimize' => true], 'smf_suggest'); - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction(); } /** @@ -478,13 +452,12 @@ public function ignore(): void */ protected function __construct() { + $this->addSubAction('buddies', [$this, 'buddies']); + $this->addSubAction('ignore', [$this, 'ignore']); + if (!isset(Profile::$member)) { Profile::load(); } - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } } } diff --git a/Sources/Actions/Profile/Notification.php b/Sources/Actions/Profile/Notification.php index 6d2b240f11..d17d9b7a37 100644 --- a/Sources/Actions/Profile/Notification.php +++ b/Sources/Actions/Profile/Notification.php @@ -27,6 +27,8 @@ use SMF\Lang; use SMF\Menu; use SMF\Profile; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\SecurityToken; use SMF\Theme; use SMF\Time; @@ -36,24 +38,16 @@ /** * Handles preferences related to notifications. */ -class Notification implements ActionInterface +class Notification implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; /******************* * Public properties *******************/ - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'alerts'; - /** * @var array * @@ -277,18 +271,6 @@ class Notification implements ActionInterface * Public static properties **************************/ - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'alerts' => 'configuration', - 'markread' => 'markRead', - 'topics' => 'topics', - 'boards' => 'boards', - ]; - /** * @var array * @@ -310,10 +292,12 @@ class Notification implements ActionInterface */ public function execute(): void { + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); + // Going to want this for consistency. Theme::loadCSSFile('admin.css', [], 'smf_admin'); - Utils::$context['sub_template'] = self::$subtemplates[$this->subaction]; + Utils::$context['sub_template'] = self::$subtemplates[$this->sub_action]; if (isset(Menu::$loaded['profile'])) { Menu::$loaded['profile']->tab_data = [ @@ -323,11 +307,7 @@ public function execute(): void ]; } - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction(); } /** @@ -985,13 +965,14 @@ public static function list_getBoardNotifications(int $start, int $items_per_pag */ protected function __construct() { + $this->addSubAction('alerts', [$this, 'configuration']); + $this->addSubAction('markread', [$this, 'markRead']); + $this->addSubAction('topics', [$this, 'topics']); + $this->addSubAction('boards', [$this, 'boards']); + if (!isset(Profile::$member)) { Profile::load(); } - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } } /** diff --git a/Sources/Actions/Profile/ShowPosts.php b/Sources/Actions/Profile/ShowPosts.php index 8721962f52..a946e09290 100644 --- a/Sources/Actions/Profile/ShowPosts.php +++ b/Sources/Actions/Profile/ShowPosts.php @@ -31,6 +31,8 @@ use SMF\PageIndex; use SMF\Parser; use SMF\Profile; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Theme; use SMF\Time; use SMF\User; @@ -39,40 +41,12 @@ /** * Rename here and in the exportStatic call at the end of the file. */ -class ShowPosts implements ActionInterface +class ShowPosts implements ActionInterface, ProvidesSubActionInterface { use ActionTrait; - + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'messages'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'messages' => 'messages', - 'topics' => 'topics', - 'unwatchedtopics' => 'unwatched', - 'attach' => 'attachments', - ]; - /**************** * Public methods ****************/ @@ -114,11 +88,7 @@ public function execute(): void ErrorHandler::fatalLang('loadavg_show_posts_disabled', false); } - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -579,13 +549,14 @@ public static function list_getNumAttachments(array $boards_allowed): int */ protected function __construct() { + $this->addSubAction('messages', [$this, 'messages']); + $this->addSubAction('topics', [$this, 'topics']); + $this->addSubAction('unwatchedtopics', [$this, 'unwatched']); + $this->addSubAction('attach', [$this, 'attachments']); + if (!isset(Profile::$member)) { Profile::load(); } - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } } /** diff --git a/Sources/Actions/QuickModeration.php b/Sources/Actions/QuickModeration.php index 2d803e3a96..44eb56e85f 100644 --- a/Sources/Actions/QuickModeration.php +++ b/Sources/Actions/QuickModeration.php @@ -37,12 +37,6 @@ class QuickModeration implements ActionInterface, Routable use ActionRouter; use ActionTrait; - /******************* - * Public properties - *******************/ - - // code... - /************************** * Public static properties **************************/ diff --git a/Sources/Actions/Register.php b/Sources/Actions/Register.php index 2f98734ec1..88b1e61aef 100644 --- a/Sources/Actions/Register.php +++ b/Sources/Actions/Register.php @@ -25,6 +25,8 @@ use SMF\OutputTypes; use SMF\Parser; use SMF\Profile; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Routable; use SMF\SecurityToken; use SMF\Theme; @@ -35,22 +37,16 @@ /** * Shows the registration form. */ -class Register implements ActionInterface, Routable +class Register implements ActionInterface, ProvidesSubActionInterface, Routable { use ActionRouter; use ActionTrait; + use ProvidesSubActionTrait; /******************* * Public properties *******************/ - /** - * @var string - * - * The sub-action to call. - */ - public string $subaction = 'show'; - /** * @var array * @@ -58,20 +54,6 @@ class Register implements ActionInterface, Routable */ public array $errors = []; - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'show' => 'show', - 'usernamecheck' => 'checkUsername', - ]; - /**************** * Public methods ****************/ @@ -96,11 +78,7 @@ public function getOutputType(): OutputTypeInterface */ public function execute(): void { - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -367,9 +345,8 @@ public function checkUsername(): void */ protected function __construct() { - if (!empty($_GET['sa']) && isset(self::$subactions[$_GET['sa']])) { - $this->subaction = $_GET['sa']; - } + $this->addSubAction('show', [$this, 'show']); + $this->addSubAction('usernamecheck', [$this, 'checkUsername']); } } diff --git a/Sources/Actions/Reminder.php b/Sources/Actions/Reminder.php index 1b932917ba..790204e72e 100644 --- a/Sources/Actions/Reminder.php +++ b/Sources/Actions/Reminder.php @@ -23,6 +23,8 @@ use SMF\IntegrationHook; use SMF\Lang; use SMF\Mail; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Routable; use SMF\Security; use SMF\SecurityToken; @@ -33,22 +35,11 @@ /** * Handle sending out reminders, and checking the secret answer and question. */ -class Reminder implements ActionInterface, Routable +class Reminder implements ActionInterface, ProvidesSubActionInterface, Routable { use ActionRouter; use ActionTrait; - - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'main'; + use ProvidesSubActionTrait; /********************* * Internal properties @@ -61,23 +52,6 @@ class Reminder implements ActionInterface, Routable */ public User $member; - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'main' => 'main', - 'picktype' => 'pickType', - 'secret2' => 'secretAnswer2', - 'setpassword' => 'setPassword', - 'setpassword2' => 'setPassword2', - ]; - /**************** * Public methods ****************/ @@ -92,11 +66,7 @@ public function isRestrictedGuestAccessAllowed(): bool */ public function execute(): void { - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -403,15 +373,17 @@ public function secretAnswer2(): void */ protected function __construct() { + $this->addSubAction('main', [$this, 'main']); + $this->addSubAction('picktype', [$this, 'pickType']); + $this->addSubAction('secret2', [$this, 'secretAnswer2']); + $this->addSubAction('setpassword', [$this, 'setPassword']); + $this->addSubAction('setpassword2', [$this, 'setPassword2']); + Lang::load('Profile'); Theme::loadTemplate('Reminder'); Utils::$context['page_title'] = Lang::$txt['authentication_reminder']; Utils::$context['robot_no_index'] = true; - - if (!empty($_GET['sa']) && isset(self::$subactions[$_GET['sa']])) { - $this->subaction = $_GET['sa']; - } } /** diff --git a/Sources/Actions/ReportToMod.php b/Sources/Actions/ReportToMod.php index f0960e5329..d552eb1ebf 100644 --- a/Sources/Actions/ReportToMod.php +++ b/Sources/Actions/ReportToMod.php @@ -23,6 +23,8 @@ use SMF\ErrorHandler; use SMF\Lang; use SMF\Msg; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Routable; use SMF\Security; use SMF\Theme; @@ -33,45 +35,14 @@ /** * Deals with reporting posts or profiles to mods and admins. */ -class ReportToMod implements ActionInterface, Routable +class ReportToMod implements ActionInterface, ProvidesSubActionInterface, Routable { use ActionRouter; use ActionTrait; + use ProvidesSubActionTrait; use BackwardCompatibility; - /***************** - * Class constants - *****************/ - - // code... - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'show'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'show' => 'show', - 'submit' => 'submit', - ]; - - /********************* * Internal properties *********************/ @@ -123,11 +94,11 @@ public function execute(): void User::$me->isAllowedTo('report_user'); } - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); + if (isset($_POST['comment'])) { + $this->comment = trim(Utils::normalizeSpaces(Utils::sanitizeChars(Utils::normalize($_POST['comment']), 0), true, true)); } + + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -306,19 +277,18 @@ public function submit(): void */ protected function __construct() { + $this->addSubAction('show', [$this, 'show']); + $this->addSubAction('submit', [$this, 'submit']); + Utils::$context['robot_no_index'] = true; Utils::$context['comment_body'] = ''; - if (isset($_POST['comment'])) { - $this->comment = trim(Utils::normalizeSpaces(Utils::sanitizeChars(Utils::normalize($_POST['comment']), 0), true, true)); - } - $this->previewing = isset($_POST['preview']) && $this->comment !== ''; $this->submitting = isset($_POST['save']); $this->can_submit = isset($_POST[Utils::$context['session_var']]); if ($this->submitting && $this->can_submit && !$this->previewing) { - $this->subaction = 'submit'; + $this->setDefaultSubAction('submit'); } } diff --git a/Sources/Actions/TopicMerge.php b/Sources/Actions/TopicMerge.php index 9e44cabcf8..742f1a43da 100644 --- a/Sources/Actions/TopicMerge.php +++ b/Sources/Actions/TopicMerge.php @@ -30,6 +30,8 @@ use SMF\Mail; use SMF\Msg; use SMF\PageIndex; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Routable; use SMF\Search\SearchApi; use SMF\Theme; @@ -41,23 +43,17 @@ /** * Handles merging of topics. */ +class TopicMerge implements ActionInterface, ProvidesSubActionInterface class TopicMerge implements ActionInterface, Routable { use ActionTrait; + use ProvidesSubActionTrait; use BackwardCompatibility; /******************* * Public properties *******************/ - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'index'; - /** * @var array * @@ -65,22 +61,6 @@ class TopicMerge implements ActionInterface, Routable */ public array $topics = []; - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'index' => 'index', - 'done' => 'done', - 'merge' => 'merge', - 'options' => 'options', - ]; - /********************* * Internal properties *********************/ @@ -186,11 +166,7 @@ public function execute(): void // Load the template.... Theme::loadTemplate('MoveTopic'); - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -1027,10 +1003,10 @@ public function done(): void */ public static function initiate(array $topics = []): void { - self::load(); - self::$obj->subaction = 'options'; - self::$obj->topics = array_map('intval', $topics); - self::$obj->execute(); + $obj = self::load(); +// $obj->setDefaultSubAction('options'); + $obj->topics = array_map('intval', $topics); + $obj->execute(); } /** @@ -1073,15 +1049,16 @@ public static function parseRoute(array $route, array $params = []): array */ protected function __construct() { + $this->addSubAction('index', [$this, 'index']); + $this->addSubAction('done', [$this, 'done']); + $this->addSubAction('merge', [$this, 'merge']); + $this->addSubAction('options', [$this, 'options']); + // The 'merge' sub-action used to be called 'execute'. if (!empty($_GET['sa']) && $_GET['sa'] === 'execute') { $_GET['sa'] = 'merge'; $_REQUEST['sa'] = 'merge'; } - - if (!empty($_GET['sa']) && isset(self::$subactions[$_GET['sa']])) { - $this->subaction = $_GET['sa']; - } } /** diff --git a/Sources/Actions/TopicSplit.php b/Sources/Actions/TopicSplit.php index bc782c6eab..d21439453a 100644 --- a/Sources/Actions/TopicSplit.php +++ b/Sources/Actions/TopicSplit.php @@ -34,6 +34,8 @@ use SMF\OutputTypes; use SMF\PageIndex; use SMF\Parser; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Routable; use SMF\Search\SearchApi; use SMF\Theme; @@ -45,41 +47,15 @@ /** * Handles splitting of topics. */ +class TopicSplit implements ActionInterface, ProvidesSubActionInterface class TopicSplit implements ActionInterface, Routable { use ActionSuffixRouter; use ActionTrait; + use ProvidesSubActionTrait; use BackwardCompatibility; /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction = 'index'; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'index' => 'index', - 'split' => 'split', - 'selectTopics' => 'select', - 'splitSelection' => 'splitSelection', - ]; - - /********************* * Internal properties *********************/ @@ -120,11 +96,7 @@ public function execute(): void // Load up the "dependencies" - the template and getMsgMemberID(). Theme::loadTemplate(!isset($_REQUEST['xml']) ? 'Xml' : 'SplitTopics'); - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -991,15 +963,16 @@ public static function splitTopic(int $split1_ID_TOPIC, array $splitMessages, st */ protected function __construct() { + $this->addSubAction('index', [$this, 'index']); + $this->addSubAction('split', [$this, 'split']); + $this->addSubAction('selectTopics', [$this, 'select']); + $this->addSubAction('splitSelection', [$this, 'splitSelection']); + // The 'split' sub-action used to be called 'execute'. if (!empty($_GET['sa']) && $_GET['sa'] === 'execute') { $_GET['sa'] = 'split'; $_REQUEST['sa'] = 'split'; } - - if (!empty($_GET['sa']) && isset(self::$subactions[$_GET['sa']])) { - $this->subaction = $_GET['sa']; - } } } diff --git a/Sources/Actions/XmlHttp.php b/Sources/Actions/XmlHttp.php index 6798b988e1..9953a4fd39 100644 --- a/Sources/Actions/XmlHttp.php +++ b/Sources/Actions/XmlHttp.php @@ -31,6 +31,8 @@ use SMF\OutputTypes; use SMF\Parser; use SMF\Profile; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Routable; use SMF\Theme; use SMF\User; @@ -39,39 +41,14 @@ /** * Handles XML-based interaction (mainly XMLhttp) */ +class XmlHttp implements ActionInterface, ProvidesSubActionInterface class XmlHttp implements ActionInterface, Routable { use ActionRouter; use ActionTrait; + use ProvidesSubActionTrait; use BackwardCompatibility; - /******************* - * Public properties - *******************/ - - /** - * @var string - * - * The requested sub-action. - * This should be set by the constructor. - */ - public string $subaction; - - /************************** - * Public static properties - **************************/ - - /** - * @var array - * - * Available sub-actions. - */ - public static array $subactions = [ - 'jumpto' => 'jumpTo', - 'messageicons' => 'messageIcons', - 'previews' => 'previews', - ]; - /**************** * Public methods ****************/ @@ -101,15 +78,14 @@ public function isAgreementAction(): bool */ public function execute(): void { - if (!isset($this->subaction)) { + // Easy adding of sub actions. + IntegrationHook::call('integrate_XMLhttpMain_subActions', [&$this->sub_actions]); + + if (!isset($this->sub_action)) { ErrorHandler::fatalLang('no_access', false); } - $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); - - if (!empty($call)) { - call_user_func($call); - } + $this->callSubAction($_REQUEST['sa'] ?? null); } /** @@ -419,14 +395,11 @@ public function warning_preview(): void */ protected function __construct() { - Theme::loadTemplate('Xml'); + $this->addSubAction('jumpto', [$this, 'jumpTo']); + $this->addSubAction('messageicons', [$this, 'messageIcons']); + $this->addSubAction('previews', [$this, 'previews']); - // Easy adding of sub actions. - IntegrationHook::call('integrate_XMLhttpMain_subActions', [&self::$subactions]); - - if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { - $this->subaction = $_REQUEST['sa']; - } + Theme::loadTemplate('Xml'); } } diff --git a/Sources/PackageManager/PackageManager.php b/Sources/PackageManager/PackageManager.php index 9ff05a4ae7..b81fc778af 100644 --- a/Sources/PackageManager/PackageManager.php +++ b/Sources/PackageManager/PackageManager.php @@ -13,6 +13,8 @@ namespace SMF\PackageManager; +use SMF\ActionInterface; +use SMF\ActionTrait; use SMF\Cache\CacheApi; use SMF\Config; use SMF\Db\DatabaseApi as Db; @@ -24,6 +26,8 @@ use SMF\Menu; use SMF\Msg; use SMF\Parser; +use SMF\ProvidesSubActionInterface; +use SMF\ProvidesSubActionTrait; use SMF\Sapi; use SMF\Security; use SMF\Theme; @@ -35,40 +39,10 @@ /** * This is the main package manager. */ -class PackageManager +class PackageManager implements ActionInterface, ProvidesSubActionInterface { - /******************* - * Public properties - *******************/ - - /** - * @var array - * - * Delegation makes the world... that is, the package manager go 'round. - */ - public $subactions = [ - // Sub-actions for working with package files. - 'browse' => 'browse', - 'remove' => 'remove', - 'list' => 'list', - 'ftptest' => 'ftpTest', - 'install' => 'installTest', - 'install2' => 'install', - 'uninstall' => 'installTest', - 'uninstall2' => 'install', - 'options' => 'options', - 'perms' => 'permissions', - 'examine' => 'examineFile', - 'showoperations' => 'showOperations', - - // Sub-actions for working with package servers. - 'upload' => 'upload', - 'download' => 'download', - 'servers' => 'servers', - 'serveradd' => 'serverAdd', - 'serverremove' => 'serverRemove', - 'serverbrowse' => 'serverBrowse', - ]; + use ActionTrait; + use ProvidesSubActionTrait; /********************** * Protected properties @@ -89,39 +63,6 @@ class PackageManager 'browse' => 'serverbrowse', ]; - /** - * An instance of this class. - */ - protected static $obj; - - /*********************** - * Public static methods - ***********************/ - - /** - * Instantiates this class, but never more than once. - * - * @todo Add a reference to Utils::$context['instances'] as well? - * - * @return self An instance of this class. - */ - public static function load(): object - { - if (!isset(self::$obj)) { - self::$obj = new self(); - } - - return self::$obj; - } - - /** - * Convenience method to load() and execute() an instance of this class. - */ - public static function call(): void - { - self::load()->execute(); - } - /**************** * Public methods ****************/ @@ -135,14 +76,10 @@ public function execute(): void Lang::load('Packages'); Theme::loadTemplate('Packages', 'admin'); - Utils::$context['page_title'] = Lang::$txt['package']; + $this->findRequestedSubAction($_REQUEST['sa'] ?? null); - // Work out exactly who it is we are calling. - if (isset($_REQUEST['sa'], $this->subactions[$_REQUEST['sa']])) { - Utils::$context['sub_action'] = $_REQUEST['sa']; - } else { - Utils::$context['sub_action'] = 'browse'; - } + Utils::$context['page_title'] = Lang::$txt['package']; + Utils::$context['sub_action'] = $this->sub_action; // Set up some tabs... Menu::$loaded['admin']->tab_data = [ @@ -164,25 +101,16 @@ public function execute(): void ], ]; - if (Utils::$context['sub_action'] == 'browse') { + if ($this->sub_action == 'browse') { Theme::loadJavaScriptFile('suggest.js', ['defer' => false, 'minimize' => true], 'smf_suggest'); } // We need to force the "Download" tab as selected. - if (in_array(Utils::$context['sub_action'], $this->packageget_subactions)) { + if (in_array($this->sub_action, $this->packageget_subactions)) { Utils::$context['menu_data_' . Utils::$context['admin_menu_id']]['current_subsection'] = 'packageget'; } - // Call the function we're handing control to. - if (method_exists($this, $this->subactions[Utils::$context['sub_action']])) { - call_user_func([$this, $this->subactions[Utils::$context['sub_action']]]); - } else { - $call = Utils::getCallable($this->subactions[Utils::$context['sub_action']]); - - if (!empty($call)) { - call_user_func($call); - } - } + $this->callSubAction(); } /** @@ -3406,6 +3334,28 @@ public function list_getPackages(int $start, int $items_per_page, string $sort, */ protected function __construct() { + // Sub-actions for working with package files. + $this->addSubAction('browse', [$this, 'browse']); + $this->addSubAction('remove', [$this, 'remove']); + $this->addSubAction('list', [$this, 'list']); + $this->addSubAction('ftptest', [$this, 'ftpTest']); + $this->addSubAction('install', [$this, 'installTest']); + $this->addSubAction('install2', [$this, 'install']); + $this->addSubAction('uninstall', [$this, 'installTest']); + $this->addSubAction('uninstall2', [$this, 'install']); + $this->addSubAction('options', [$this, 'options']); + $this->addSubAction('perms', [$this, 'permissions']); + $this->addSubAction('examine', [$this, 'examineFile']); + $this->addSubAction('showoperations', [$this, 'showOperations']); + + // Sub-actions for working with package servers. + $this->addSubAction('upload', [$this, 'upload']); + $this->addSubAction('download', [$this, 'download']); + $this->addSubAction('servers', [$this, 'servers']); + $this->addSubAction('serveradd', [$this, 'serverAdd']); + $this->addSubAction('serverremove', [$this, 'serverRemove']); + $this->addSubAction('serverbrowse', [$this, 'serverBrowse']); + User::$me->isAllowedTo('admin_forum'); // Backward compatibility with old URLs. @@ -3418,23 +3368,23 @@ protected function __construct() $_REQUEST['sa'] = 'server' . $_REQUEST['sa']; } - if (!isset($this->subactions[$_REQUEST['sa']])) { + if (!isset($this->sub_actions[$_REQUEST['sa']])) { $_REQUEST['sa'] = 'servers'; } // Backward compatibility for deprecated integrate_package_get hook. - $temp = array_map(function ($sa) {return $this->subactions[$sa];}, $this->packageget_subactions); + $temp = array_map(fn ($sa) => $this->sub_actions[$sa], $this->packageget_subactions); IntegrationHook::call('integrate_package_get', [&$temp]); foreach ($temp as $sa => $func) { - $this->subactions[$this->packageget_subactions[$sa] ?? $sa] = $func; + $this->addSubAction($this->packageget_subactions[$sa] ?? $sa, [$this, $func]); } } elseif (isset($_REQUEST['pgdownload'])) { $_REQUEST['sa'] = 'download'; } // Give mods access to the sub-actions. - IntegrationHook::call('integrate_manage_packages', [&$this->subactions]); + IntegrationHook::call('integrate_manage_packages', [&$this->sub_actions]); } /** diff --git a/Sources/ProvidesSubActionInterface.php b/Sources/ProvidesSubActionInterface.php new file mode 100644 index 0000000000..b0352cfdeb --- /dev/null +++ b/Sources/ProvidesSubActionInterface.php @@ -0,0 +1,87 @@ + \ No newline at end of file diff --git a/Sources/ProvidesSubActionTrait.php b/Sources/ProvidesSubActionTrait.php new file mode 100644 index 0000000000..19d15d1d63 --- /dev/null +++ b/Sources/ProvidesSubActionTrait.php @@ -0,0 +1,129 @@ +sub_action = $sa; + } + + /** + * Add a sub-action. + * + * A sub-action is another term for a route where a URL parameter, usually "sa", has + * its value mapped to some function. + * + * @param string $sa + * @param callable $cb + */ + public function addSubAction(string $sa, callable $cb): void + { + $this->sub_actions[$sa] = $cb; + } + + /** + * Determines whether the provided sub-action exists. + * + * @param string $sa + * + * @return bool + */ + public function hasSubAction(string $sa): bool + { + return isset($this->sub_actions[$sa]); + } + + /** + * @return string|null + */ + public function getSubAction(): ?string + { + return $this->sub_action; + } + + /****************** + * Internal methods + ******************/ + + /** + * Finds a sub-action that is associated with the given keyword. + * + * If no sub-actions match or nothing was provided, the internal variable is set to + * either the previously specified default or the first registered sub-action. + * + * @param string|null $sa + */ + protected function findRequestedSubAction(?string $sa = null): void + { + if (isset($this->sub_actions[$sa])) { + $this->sub_action = $sa; + } else { + $this->sub_action = $this->sub_action ?? array_key_first($this->sub_actions); + } + } + + /** + * Executes the sub-action. + * + * @uses findRequestedSubAction() if $sa is null. + * + * @param string|null $sa + * + * @return mixed + */ + protected function callSubAction(?string $sa = null): mixed + { + $this->findRequestedSubAction($sa); + + return call_user_func($this->sub_actions[$this->sub_action]); + } +} + +?> \ No newline at end of file diff --git a/composer.json b/composer.json index 0b2bb25ea3..6a615ece0e 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ }, "scripts": { "lint": "php-cs-fixer --quiet check --config .php-cs-fixer.dist.php --path-mode=intersection $(git diff --name-only \"*.php\") || php-cs-fixer check --diff --config .php-cs-fixer.dist.php --path-mode=intersection $(git diff --name-only \"*.php\")", - "lint-fix": "php-cs-fixer fix -v --config .php-cs-fixer.dist.php --path-mode=intersection $(git diff --name-only \"*.php\")" + "lint-fix": "php-cs-fixer fix -v" }, "config": { "platform": {