From 18d6fc5a49743904b59a593fdb78159e4ed5af0d Mon Sep 17 00:00:00 2001 From: Jon Stovell Date: Thu, 30 Jan 2025 17:16:12 -0700 Subject: [PATCH 01/13] Moves Theme::sslRedirect() to QueryString::sslRedirect() Signed-off-by: Jon Stovell --- Sources/QueryString.php | 30 +++++++++++++++++++++++++++++- Sources/Theme.php | 18 +----------------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/Sources/QueryString.php b/Sources/QueryString.php index 0aa5a48737..51200ca41b 100644 --- a/Sources/QueryString.php +++ b/Sources/QueryString.php @@ -339,7 +339,10 @@ public static function cleanRequest(): void $_SERVER['REQUEST_URL'] = $_SERVER['REQUEST_URI']; } - // And make sure HTTP_USER_AGENT is set. + // Should we redirect to HTTPS? + self::sslRedirect(); + + // Make sure HTTP_USER_AGENT is set. $_SERVER['HTTP_USER_AGENT'] = isset($_SERVER['HTTP_USER_AGENT']) ? Utils::htmlspecialchars(Db::$db->unescape_string($_SERVER['HTTP_USER_AGENT']), ENT_QUOTES) : ''; // Some final checking. @@ -470,6 +473,10 @@ function ($m) { return $buffer; } + /************************* + * Internal static methods + *************************/ + /** * Handles redirecting 'index.php?msg=123' links to the canonical URL. */ @@ -523,6 +530,27 @@ protected static function redirectFromMsg(): void Utils::redirectexit($redirect_url); } } + + /** + * Checks to see if we're forcing SSL, and redirects if necessary. + */ + protected static function sslRedirect(): void + { + if ( + !empty(Config::$modSettings['force_ssl']) + && empty(Config::$maintenance) + && !Sapi::httpsOn() + && SMF != 'SSI' + ) { + if (isset($_GET['sslRedirect'])) { + Lang::load('Errors'); + ErrorHandler::fatalLang('login_ssl_required', false); + } + + Utils::redirectexit(strtr($_SERVER['REQUEST_URL'], ['http://' => 'https://']) . (str_contains($_SERVER['REQUEST_URL'], '?') ? ';' : '?') . 'sslRedirect'); + } + } + } ?> \ No newline at end of file diff --git a/Sources/Theme.php b/Sources/Theme.php index 1e0d074051..fc0eddc524 100644 --- a/Sources/Theme.php +++ b/Sources/Theme.php @@ -1305,6 +1305,7 @@ public static function template_header(): void // Add security warning if security issues are detected Utils::$context['warnings'] = Security::checkSecurityFiles(); + if (Utils::$context['warnings']) { $layers[] = 'security_warning'; } @@ -1888,7 +1889,6 @@ protected function __construct(int $id = 0, int $member = -1) protected function initialize(): void { $this->requireAgreement(); - $this->sslRedirect(); $this->fixUrl(); // Create User::$me if it is missing (e.g., an error very early in the login process). @@ -2018,22 +2018,6 @@ protected function requireAgreement(): void } } - /** - * Check to see if we're forcing SSL, and redirect if necessary. - */ - protected function sslRedirect(): void - { - if (!empty(Config::$modSettings['force_ssl']) && empty(Config::$maintenance) - && !Sapi::httpsOn() && SMF != 'SSI') { - if (isset($_GET['sslRedirect'])) { - Lang::load('Errors'); - ErrorHandler::fatalLang('login_ssl_required', false); - } - - Utils::redirectexit(strtr($_SERVER['REQUEST_URL'], ['http://' => 'https://']) . (strpos($_SERVER['REQUEST_URL'], '?') > 0 ? ';' : '?') . 'sslRedirect'); - } - } - /** * If the user got here using an unexpected URL, fix it. */ From 97fcc62e2c716b8768a90b9f42bf0cf46653103a Mon Sep 17 00:00:00 2001 From: Jon Stovell Date: Thu, 30 Jan 2025 17:33:52 -0700 Subject: [PATCH 02/13] Moves Theme::requireAgreement() to Forum::requireAgreement() Signed-off-by: Jon Stovell --- Sources/Forum.php | 33 +++++++++++++++++++++++++++++++++ Sources/Theme.php | 27 --------------------------- 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/Sources/Forum.php b/Sources/Forum.php index ca078a4be0..98a69cf01a 100644 --- a/Sources/Forum.php +++ b/Sources/Forum.php @@ -620,6 +620,9 @@ protected function init(): void */ protected function main(): void { + // If the user needs to accept the agreement or privacy policy, redirect now. + $this->requireAgreement(); + // If we are in a topic and don't have permission to approve it then duck out now. if (!empty(Topic::$topic_id) && empty(Board::$info->cur_topic_approved) && !User::$me->allowedTo('approve_posts') && (User::$me->id != Board::$info->cur_topic_starter || User::$me->is_guest)) { ErrorHandler::fatalLang('not_a_topic', false); @@ -640,6 +643,36 @@ protected function main(): void Config::checkCron(); } + /** + * If necessary, redirect to the agreement or privacy policy so that we can + * force the user to accept the current version. + */ + protected function requireAgreement(): void + { + // Perhaps we've changed the agreement or privacy policy? + // Only redirect if all of the following conditions are met: + if ( + // They're not a guest. + !empty(User::$me->id) + // They're not an admin. + && empty(User::$me->is_admin) + // This isn't a called from SSI. + && SMF != 'SSI' + // This isn't an XML request. + && !isset($_REQUEST['xml']) + // They're trying to do an action that requires accepting the agreement and/or policy. + && self::$current_action?->isAgreementAction() !== true + && ( + // They haven't accepted the latest version of the agreement. + Actions\Agreement::canRequireAgreement() + // Or they haven't accepted the latest version of the privacy policy. + || Actions\Agreement::canRequirePrivacyPolicy() + ) + ) { + Utils::redirectexit('action=agreement'); + } + } + /** * Resolves the appropriate action to execute based on the current request context. * diff --git a/Sources/Theme.php b/Sources/Theme.php index fc0eddc524..3310c8336f 100644 --- a/Sources/Theme.php +++ b/Sources/Theme.php @@ -15,7 +15,6 @@ namespace SMF; -use SMF\Actions\Agreement; use SMF\Actions\Notify; use SMF\Cache\CacheApi; use SMF\Db\DatabaseApi as Db; @@ -1888,7 +1887,6 @@ protected function __construct(int $id = 0, int $member = -1) */ protected function initialize(): void { - $this->requireAgreement(); $this->fixUrl(); // Create User::$me if it is missing (e.g., an error very early in the login process). @@ -1993,31 +1991,6 @@ protected function initialize(): void Utils::$context['theme_loaded'] = true; } - /** - * If necessary, redirect to the agreement or privacy policy so that we can - * force the user to accept the current version. - */ - protected function requireAgreement(): void - { - // Perhaps we've changed the agreement or privacy policy? Only redirect if: - // 1. They're not a guest or admin - // 2. This isn't called from SSI - // 3. This isn't an XML request - // 4. They're not trying to do any of the following actions: - // 4a. View or accept the agreement and/or policy - // 4b. Login or logout - // 4c. Get a feed (RSS, ATOM, etc.) - if (!empty(User::$me->id) && empty(User::$me->is_admin) && SMF != 'SSI' && !isset($_REQUEST['xml']) && Forum::getCurrentAction()?->isAgreementAction() !== true) { - $can_accept_agreement = !empty(Config::$modSettings['requireAgreement']) && Agreement::canRequireAgreement(); - - $can_accept_privacy_policy = !empty(Config::$modSettings['requirePolicyAgreement']) && Agreement::canRequirePrivacyPolicy(); - - if ($can_accept_agreement || $can_accept_privacy_policy) { - Utils::redirectexit('action=agreement'); - } - } - } - /** * If the user got here using an unexpected URL, fix it. */ From 9662156a8381f50f5beaeabb2a5af72834a6021e Mon Sep 17 00:00:00 2001 From: Jon Stovell Date: Thu, 30 Jan 2025 18:12:21 -0700 Subject: [PATCH 03/13] Handles 'www' redirects in QueryString, not Theme Signed-off-by: Jon Stovell --- Sources/QueryString.php | 28 ++++++++++++++++++++++++++++ Sources/Theme.php | 23 ++++++----------------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/Sources/QueryString.php b/Sources/QueryString.php index 51200ca41b..97cb251896 100644 --- a/Sources/QueryString.php +++ b/Sources/QueryString.php @@ -342,6 +342,9 @@ public static function cleanRequest(): void // Should we redirect to HTTPS? self::sslRedirect(); + // Should we redirect because of an incorrectly added/removed 'www.'? + self::wwwRedirect(); + // Make sure HTTP_USER_AGENT is set. $_SERVER['HTTP_USER_AGENT'] = isset($_SERVER['HTTP_USER_AGENT']) ? Utils::htmlspecialchars(Db::$db->unescape_string($_SERVER['HTTP_USER_AGENT']), ENT_QUOTES) : ''; @@ -551,6 +554,31 @@ protected static function sslRedirect(): void } } + /** + * Checks if $_SERVER['REQUEST_URL'] is incorrect due to an added/removed + * 'www.', and redirects if necessary. + */ + protected static function wwwRedirect(): void + { + if (SMF == 'SSI') { + return; + } + + $requested_host = Url::create($_SERVER['REQUEST_URL'])->host; + $canonical_host = Url::create(Config::$boardurl)->host; + + if ($requested_host === $canonical_host) { + return; + } + + if ( + $canonical_host === 'www.' . $requested_host + || 'www.' . $canonical_host === $requested_host + ) { + Utils::redirectexit(strtr($_SERVER['REQUEST_URL'], [$requested_host => $canonical_host]), false, true); + } + } + } ?> \ No newline at end of file diff --git a/Sources/Theme.php b/Sources/Theme.php index 3310c8336f..f7203f6ca1 100644 --- a/Sources/Theme.php +++ b/Sources/Theme.php @@ -2022,28 +2022,17 @@ protected function fixUrl(): void } } - // Hmm... check #2 - is it just different by a www? Send them to the correct place!! - if (empty($do_fix) && strtr($detected_url, ['://' => '://www.']) == Config::$boardurl && (empty($_GET) || count($_GET) == 1) && SMF != 'SSI') { - // Okay, this seems weird, but we don't want an endless loop - this will make $_GET not empty ;). - if (empty($_GET)) { - Utils::redirectexit('wwwRedirect'); - } else { - $k = key($_GET); - $v = current($_GET); - - if ($k != 'wwwRedirect') { - Utils::redirectexit('wwwRedirect;' . $k . '=' . $v); - } - } - } - - // #3 is just a check for SSL... + // #2 is just a check for SSL... if (strtr($detected_url, ['https://' => 'http://']) == Config::$boardurl) { $do_fix = true; } - // Okay, #4 - perhaps it's an IP address? We're gonna want to use that one, then. (assuming it's the IP or something...) + // Okay, #3 - perhaps it's an IP address? We're gonna want to use that one, then. (assuming it's the IP or something...) if (!empty($do_fix) || preg_match('~^http[s]?://(?:[\d\.:]+|\[[\d:]+\](?::\d+)?)(?:$|/)~', $detected_url) == 1) { + $do_fix = true; + } + + if (!empty($do_fix)) { // Caching is good ;). $oldurl = Config::$boardurl; From 1d67685ee9d8aea32d3969f51e7af47d3292761d Mon Sep 17 00:00:00 2001 From: Jon Stovell Date: Thu, 30 Jan 2025 21:21:12 -0700 Subject: [PATCH 04/13] Moves most of the logic in Theme::fixUrl() to Forum::fixUrl() Signed-off-by: Jon Stovell --- Sources/QueryString.php | 62 +++++++++++++++++++++++++++++++ Sources/Theme.php | 82 +++++++++-------------------------------- 2 files changed, 79 insertions(+), 65 deletions(-) diff --git a/Sources/QueryString.php b/Sources/QueryString.php index 97cb251896..7ac505e9d3 100644 --- a/Sources/QueryString.php +++ b/Sources/QueryString.php @@ -345,6 +345,9 @@ public static function cleanRequest(): void // Should we redirect because of an incorrectly added/removed 'www.'? self::wwwRedirect(); + // If the user got here using an unexpected but valid URL, fix it. + self::fixUrl(); + // Make sure HTTP_USER_AGENT is set. $_SERVER['HTTP_USER_AGENT'] = isset($_SERVER['HTTP_USER_AGENT']) ? Utils::htmlspecialchars(Db::$db->unescape_string($_SERVER['HTTP_USER_AGENT']), ENT_QUOTES) : ''; @@ -579,6 +582,65 @@ protected static function wwwRedirect(): void } } + /** + * If the user got here using an unexpected but valid URL, fix it. + */ + protected static function fixUrl(): void + { + if (SMF == 'SSI') { + return; + } + + if (str_starts_with($_SERVER['REQUEST_URL'], Config::$boardurl)) { + return; + } + + $requested_url = Url::create($_SERVER['REQUEST_URL']); + $canonical_url = Url::create(Config::$boardurl); + + // Is the requested URL a known alias of the canonical forum URL? + if (!empty(Config::$modSettings['forum_alias_urls'])) { + $aliases = explode(',', Config::$modSettings['forum_alias_urls']); + + foreach ($aliases as $alias) { + $alias = Sapi::httpsOn() ? strtr($alias, 'http://', 'https://') : strtr($alias, 'https://', 'http://'); + + if (str_starts_with($_SERVER['REQUEST_URL'], $alias)) { + $new_url = $alias; + } + } + } + + // Is the requested URL using a raw IP address instead of a domain name? + if (!isset($new_url) && IP::create($requested_url->host)->isValid()) { + $new_url = strtr(Config::$boardurl, [$canonical_url->host, $requested_url->host]); + } + + if ( + // If the scheme is incorrect, adjust it. + $requested_url->scheme !== $canonical_url->scheme + // But don't downgrade a canonical HTTPS scheme to HTTP. + && $canonical_url->scheme !== 'https' + ) { + $new_url = strtr($new_url ?? Config::$boardurl, [$canonical_url->scheme . '://', $requested_url->scheme . '://']); + } + + // Change our internal settings to use the requested URL. + if (isset($new_url)) { + // The theme will need to know about this change. + Utils::$context['canonical_boardurl'] = Config::$boardurl; + + // Fix Config::$boardurl and Config::$scripturl. + Config::$boardurl = $new_url; + Config::$scripturl = strtr(Config::$scripturl, [Utils::$context['canonical_boardurl'] => Config::$boardurl]); + $_SERVER['REQUEST_URL'] = strtr($_SERVER['REQUEST_URL'], [Utils::$context['canonical_boardurl'] => Config::$boardurl]); + + // And just a few mod settings :). + Config::$modSettings['smileys_url'] = strtr(Config::$modSettings['smileys_url'], [Utils::$context['canonical_boardurl'] => Config::$boardurl]); + Config::$modSettings['avatar_url'] = strtr(Config::$modSettings['avatar_url'], [Utils::$context['canonical_boardurl'] => Config::$boardurl]); + Config::$modSettings['custom_avatar_url'] = strtr(Config::$modSettings['custom_avatar_url'], [Utils::$context['canonical_boardurl'] => Config::$boardurl]); + } + } } ?> \ No newline at end of file diff --git a/Sources/Theme.php b/Sources/Theme.php index f7203f6ca1..d96caec83d 100644 --- a/Sources/Theme.php +++ b/Sources/Theme.php @@ -1996,76 +1996,28 @@ protected function initialize(): void */ protected function fixUrl(): void { - // Check to see if they're accessing it from the wrong place. - if (isset($_SERVER['HTTP_HOST']) || isset($_SERVER['SERVER_NAME'])) { - $detected_url = Sapi::httpsOn() ? 'https://' : 'http://'; - - $detected_url .= empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] . (empty($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == '80' ? '' : ':' . $_SERVER['SERVER_PORT']) : $_SERVER['HTTP_HOST']; - - $temp = preg_replace('~/' . basename(Config::$scripturl) . '(/.+)?$~', '', strtr(dirname($_SERVER['PHP_SELF']), '\\', '/')); - - if ($temp != '/') { - $detected_url .= $temp; - } + if (!isset(Utils::$context['canonical_boardurl'])) { + return; } - if (isset($detected_url) && $detected_url != Config::$boardurl) { - // Try #1 - check if it's in a list of alias addresses. - if (!empty(Config::$modSettings['forum_alias_urls'])) { - $aliases = explode(',', Config::$modSettings['forum_alias_urls']); - - foreach ($aliases as $alias) { - // Rip off all the boring parts, spaces, etc. - if ($detected_url == trim($alias) || strtr($detected_url, ['http://' => '', 'https://' => '']) == trim($alias)) { - $do_fix = true; - } - } - } - - // #2 is just a check for SSL... - if (strtr($detected_url, ['https://' => 'http://']) == Config::$boardurl) { - $do_fix = true; - } + // Fix the theme urls... + $this->settings['theme_url'] = strtr($this->settings['theme_url'], [Utils::$context['canonical_boardurl'] => Config::$boardurl]); + $this->settings['default_theme_url'] = strtr($this->settings['default_theme_url'], [Utils::$context['canonical_boardurl'] => Config::$boardurl]); + $this->settings['actual_theme_url'] = strtr($this->settings['actual_theme_url'], [Utils::$context['canonical_boardurl'] => Config::$boardurl]); + $this->settings['images_url'] = strtr($this->settings['images_url'], [Utils::$context['canonical_boardurl'] => Config::$boardurl]); + $this->settings['default_images_url'] = strtr($this->settings['default_images_url'], [Utils::$context['canonical_boardurl'] => Config::$boardurl]); + $this->settings['actual_images_url'] = strtr($this->settings['actual_images_url'], [Utils::$context['canonical_boardurl'] => Config::$boardurl]); - // Okay, #3 - perhaps it's an IP address? We're gonna want to use that one, then. (assuming it's the IP or something...) - if (!empty($do_fix) || preg_match('~^http[s]?://(?:[\d\.:]+|\[[\d:]+\](?::\d+)?)(?:$|/)~', $detected_url) == 1) { - $do_fix = true; + // Clean up after Board::load(). + if (isset(Board::$info->moderators)) { + foreach (Board::$info->moderators as $k => $dummy) { + Board::$info->moderators[$k]['href'] = strtr($dummy['href'], [Utils::$context['canonical_boardurl'] => Config::$boardurl]); + Board::$info->moderators[$k]['link'] = strtr($dummy['link'], ['"' . Utils::$context['canonical_boardurl'] => '"' . Config::$boardurl]); } + } - if (!empty($do_fix)) { - // Caching is good ;). - $oldurl = Config::$boardurl; - - // Fix Config::$boardurl and Config::$scripturl. - Config::$boardurl = $detected_url; - Config::$scripturl = strtr(Config::$scripturl, [$oldurl => Config::$boardurl]); - $_SERVER['REQUEST_URL'] = strtr($_SERVER['REQUEST_URL'], [$oldurl => Config::$boardurl]); - - // Fix the theme urls... - $this->settings['theme_url'] = strtr($this->settings['theme_url'], [$oldurl => Config::$boardurl]); - $this->settings['default_theme_url'] = strtr($this->settings['default_theme_url'], [$oldurl => Config::$boardurl]); - $this->settings['actual_theme_url'] = strtr($this->settings['actual_theme_url'], [$oldurl => Config::$boardurl]); - $this->settings['images_url'] = strtr($this->settings['images_url'], [$oldurl => Config::$boardurl]); - $this->settings['default_images_url'] = strtr($this->settings['default_images_url'], [$oldurl => Config::$boardurl]); - $this->settings['actual_images_url'] = strtr($this->settings['actual_images_url'], [$oldurl => Config::$boardurl]); - - // And just a few mod settings :). - Config::$modSettings['smileys_url'] = strtr(Config::$modSettings['smileys_url'], [$oldurl => Config::$boardurl]); - Config::$modSettings['avatar_url'] = strtr(Config::$modSettings['avatar_url'], [$oldurl => Config::$boardurl]); - Config::$modSettings['custom_avatar_url'] = strtr(Config::$modSettings['custom_avatar_url'], [$oldurl => Config::$boardurl]); - - // Clean up after Board::load(). - if (isset(Board::$info->moderators)) { - foreach (Board::$info->moderators as $k => $dummy) { - Board::$info->moderators[$k]['href'] = strtr($dummy['href'], [$oldurl => Config::$boardurl]); - Board::$info->moderators[$k]['link'] = strtr($dummy['link'], ['"' . $oldurl => '"' . Config::$boardurl]); - } - } - - foreach (Utils::$context['linktree'] as $k => $dummy) { - Utils::$context['linktree'][$k]['url'] = strtr($dummy['url'], [$oldurl => Config::$boardurl]); - } - } + foreach (Utils::$context['linktree'] as $k => $dummy) { + Utils::$context['linktree'][$k]['url'] = strtr($dummy['url'], [Utils::$context['canonical_boardurl'] => Config::$boardurl]); } } From 80c91534667bf5d1db9a9abe076228be26455ba3 Mon Sep 17 00:00:00 2001 From: Jon Stovell Date: Sun, 2 Feb 2025 16:08:38 -0700 Subject: [PATCH 05/13] Correctly calls ErrorHandler::fatalLang for unknown actions Signed-off-by: Jon Stovell --- Sources/Forum.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Forum.php b/Sources/Forum.php index 98a69cf01a..ca2bbb60db 100644 --- a/Sources/Forum.php +++ b/Sources/Forum.php @@ -723,9 +723,9 @@ protected function findAction(?string $action): string|callable|false if (($fallback_action = Utils::getCallable($fallback_action)) !== false) { return $fallback_action; } - - ErrorHandler::fatalLang('not_found', false, [], 404); } + + ErrorHandler::fatalLang('not_found', false, [], 404); } // Otherwise, it was set - so let's go to that action. From 8100fcdeec611bae31b5610fdedcc0fbd0deba76 Mon Sep 17 00:00:00 2001 From: Jon Stovell Date: Tue, 4 Feb 2025 14:37:37 -0700 Subject: [PATCH 06/13] Fixes Forum::fixUrl() Ironic, isn't it? Signed-off-by: Jon Stovell --- Sources/QueryString.php | 48 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/Sources/QueryString.php b/Sources/QueryString.php index 7ac505e9d3..72e380c5a7 100644 --- a/Sources/QueryString.php +++ b/Sources/QueryString.php @@ -591,12 +591,46 @@ protected static function fixUrl(): void return; } + $canonical_url = Url::create(Config::$boardurl); + + // Check to see if they're accessing it from the wrong place. + if (isset($_SERVER['HTTP_HOST']) || isset($_SERVER['SERVER_NAME'])) { + $requested_url = Sapi::httpsOn() ? 'https://' : 'http://'; + + if (!empty($_SERVER['HTTP_HOST'])) { + $requested_url .= $_SERVER['HTTP_HOST']; + } else { + $requested_url .= $_SERVER['SERVER_NAME']; + + if (!empty($_SERVER['SERVER_PORT']) && (int) $_SERVER['SERVER_PORT'] === 80) { + $requested_url .= ':' . $_SERVER['SERVER_PORT']; + } + } + + $_SERVER['REQUEST_URL'] = preg_replace( + '/^' . + preg_quote( + $canonical_url->scheme . + '://' . + $canonical_url->host . + ( + !empty($canonical_url->port) && $canonical_url->port !== 80 + ? ':' . $canonical_url->port + : '' + ), + '/', + ) . + '/u', + $requested_url, + $_SERVER['REQUEST_URL'], + ); + } + if (str_starts_with($_SERVER['REQUEST_URL'], Config::$boardurl)) { return; } $requested_url = Url::create($_SERVER['REQUEST_URL']); - $canonical_url = Url::create(Config::$boardurl); // Is the requested URL a known alias of the canonical forum URL? if (!empty(Config::$modSettings['forum_alias_urls'])) { @@ -611,9 +645,15 @@ protected static function fixUrl(): void } } - // Is the requested URL using a raw IP address instead of a domain name? - if (!isset($new_url) && IP::create($requested_url->host)->isValid()) { - $new_url = strtr(Config::$boardurl, [$canonical_url->host, $requested_url->host]); + // Is the requested URL using localhost or an IP address instead of a domain name? + if ( + !isset($new_url) + && ( + $requested_url->host === 'localhost' + || IP::create($requested_url->host)->isValid() + ) + ) { + $new_url = strtr(Config::$boardurl, [$canonical_url->host => $requested_url->host]); } if ( From e092da57d48a806f56396d0a281bd6617b079f0d Mon Sep 17 00:00:00 2001 From: Jon Stovell Date: Sun, 9 Feb 2025 00:11:41 -0700 Subject: [PATCH 07/13] Fixes MIME type for XML output Signed-off-by: Jon Stovell --- Sources/Theme.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Theme.php b/Sources/Theme.php index e350b94311..2a9dfa37ff 100644 --- a/Sources/Theme.php +++ b/Sources/Theme.php @@ -1290,7 +1290,7 @@ public static function template_header(): void } } - $content_type = Forum::getCurrentAction()?->getOutputType()->getMimeType() ?? 'text/' . (isset($_REQUEST['xml']) ? 'xml' : 'html'); + $content_type = Forum::getCurrentAction()?->getOutputType()->getMimeType() ?? (isset($_REQUEST['xml']) ? 'application/xml' : 'text/html'); header('Content-Type: ' . $content_type . '; charset=' . (empty(Utils::$context['character_set']) ? 'ISO-8859-1' : Utils::$context['character_set'])); From f70e1d1fa07f0f7392467318bc5728a4527cc267 Mon Sep 17 00:00:00 2001 From: Jon Stovell Date: Sun, 9 Feb 2025 02:11:54 -0700 Subject: [PATCH 08/13] Moves as much code as possible out of action constructors Signed-off-by: Jon Stovell --- Sources/Actions/Activate.php | 6 +- Sources/Actions/Admin/ACP.php | 6 +- Sources/Actions/Admin/Attachments.php | 6 +- Sources/Actions/Admin/Bans.php | 6 +- Sources/Actions/Admin/Boards.php | 48 +++--- Sources/Actions/Admin/Calendar.php | 50 +++--- Sources/Actions/Admin/ErrorLog.php | 22 +-- Sources/Actions/Admin/Features.php | 20 ++- Sources/Actions/Admin/Languages.php | 24 +-- Sources/Actions/Admin/Mail.php | 32 ++-- Sources/Actions/Admin/Maintenance.php | 48 +++--- Sources/Actions/Admin/Membergroups.php | 22 +-- Sources/Actions/Admin/Members.php | 150 +++++++++--------- Sources/Actions/Admin/Mods.php | 32 ++-- Sources/Actions/Admin/News.php | 54 +++---- Sources/Actions/Admin/Permissions.php | 54 +++---- Sources/Actions/Admin/Posts.php | 54 +++---- Sources/Actions/Admin/Registration.php | 56 +++---- Sources/Actions/Admin/RepairBoards.php | 38 ++--- Sources/Actions/Admin/Reports.php | 74 ++++----- Sources/Actions/Admin/Search.php | 46 +++--- Sources/Actions/Admin/SearchEngines.php | 32 ++-- Sources/Actions/Admin/Server.php | 18 +-- Sources/Actions/Admin/Smileys.php | 106 ++++++------- Sources/Actions/Admin/Subscriptions.php | 46 +++--- Sources/Actions/Admin/Tasks.php | 52 +++--- Sources/Actions/Admin/Themes.php | 65 ++++---- Sources/Actions/Announce.php | 26 +-- Sources/Actions/BoardIndex.php | 70 ++++---- Sources/Actions/Calendar.php | 16 +- Sources/Actions/CoppaForm.php | 26 +-- Sources/Actions/Credits.php | 16 +- Sources/Actions/Display.php | 70 ++++---- Sources/Actions/Feed.php | 12 +- Sources/Actions/Help.php | 7 +- Sources/Actions/MessageIndex.php | 40 ++--- Sources/Actions/Moderation/Home.php | 18 +-- Sources/Actions/Moderation/Logs.php | 10 +- Sources/Actions/Moderation/Main.php | 36 ++--- Sources/Actions/Moderation/Posts.php | 6 +- .../Actions/Moderation/ReportedContent.php | 56 +++---- Sources/Actions/Moderation/ShowNotice.php | 28 ++-- Sources/Actions/Moderation/Warnings.php | 6 - Sources/Actions/Moderation/WatchedUsers.php | 44 ++--- Sources/Actions/PersonalMessage.php | 78 ++++----- Sources/Actions/Profile/AlertsPopup.php | 16 +- Sources/Actions/Profile/Export.php | 6 +- Sources/Actions/Profile/Main.php | 77 +++++---- Sources/Actions/Profile/Popup.php | 34 ++-- Sources/Actions/Profile/ShowPermissions.php | 8 +- Sources/Actions/Profile/Summary.php | 18 +-- Sources/Actions/QuoteFast.php | 22 +-- Sources/Actions/Recent.php | 56 +++---- Sources/Actions/Reminder.php | 12 +- Sources/Actions/ReportToMod.php | 6 +- Sources/Actions/Search2.php | 30 ++-- Sources/Actions/Unread.php | 6 +- Sources/Actions/UnreadReplies.php | 6 +- Sources/Actions/Who.php | 18 +-- Sources/Actions/XmlHttp.php | 4 +- 60 files changed, 958 insertions(+), 1093 deletions(-) diff --git a/Sources/Actions/Activate.php b/Sources/Actions/Activate.php index 67819d30c8..082fded068 100644 --- a/Sources/Actions/Activate.php +++ b/Sources/Actions/Activate.php @@ -96,6 +96,9 @@ public function isRestrictedGuestAccessAllowed(): bool */ public function execute(): void { + Lang::load('Login'); + Theme::loadTemplate('Login'); + if (!isset($this->member)) { if (empty($_REQUEST['u']) && empty($_POST['user'])) { $this->showResendRequest(); @@ -239,9 +242,6 @@ protected function __construct() Utils::redirectexit('action=profile'); } - Lang::load('Login'); - Theme::loadTemplate('Login'); - // We can't activate anyone without knowing whom to activate. if (empty($_REQUEST['u']) && empty($_POST['user'])) { return; diff --git a/Sources/Actions/Admin/ACP.php b/Sources/Actions/Admin/ACP.php index 893d99a8f0..0e855a8b58 100644 --- a/Sources/Actions/Admin/ACP.php +++ b/Sources/Actions/Admin/ACP.php @@ -741,6 +741,8 @@ public function getOutputType(): OutputTypeInterface */ public function execute(): void { + $this->init(); + // Make sure the administrator has a valid session... User::$me->validateSession(); @@ -1859,9 +1861,9 @@ public static function adminLogin(string $type = 'admin'): void ******************/ /** - * Constructor. Protected to force instantiation via self::load(). + * Does some initial setup. */ - protected function __construct() + protected function init() { // Load the language and templates.... Lang::load('Admin'); diff --git a/Sources/Actions/Admin/Attachments.php b/Sources/Actions/Admin/Attachments.php index 4fb4233033..2b22569808 100644 --- a/Sources/Actions/Admin/Attachments.php +++ b/Sources/Actions/Admin/Attachments.php @@ -91,6 +91,8 @@ class Attachments implements ActionInterface */ public function execute(): void { + $this->init(); + $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); if (!empty($call)) { @@ -2568,9 +2570,9 @@ public static function attachDirStatus(string $dir, int $expected_files): array ******************/ /** - * Constructor. Protected to force instantiation via self::load(). + * Does some initial setup. */ - protected function __construct() + protected function init() { // You have to be able to moderate the forum to do this. User::$me->isAllowedTo('manage_attachments'); diff --git a/Sources/Actions/Admin/Bans.php b/Sources/Actions/Admin/Bans.php index fb803687f0..c26a3c1605 100644 --- a/Sources/Actions/Admin/Bans.php +++ b/Sources/Actions/Admin/Bans.php @@ -85,6 +85,8 @@ class Bans implements ActionInterface */ public function execute(): void { + $this->init(); + User::$me->isAllowedTo('manage_bans'); $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); @@ -1468,9 +1470,9 @@ public static function list_getNumBanLogEntries(): int ******************/ /** - * Constructor. Protected to force instantiation via self::load(). + * Does some initial setup. */ - protected function __construct() + protected function init() { Theme::loadTemplate('ManageBans'); diff --git a/Sources/Actions/Admin/Boards.php b/Sources/Actions/Admin/Boards.php index 92e5352d8a..db7082139c 100644 --- a/Sources/Actions/Admin/Boards.php +++ b/Sources/Actions/Admin/Boards.php @@ -93,6 +93,30 @@ class Boards implements ActionInterface */ public function execute(): void { + // Special handling for modifycat. + if (($_REQUEST['action'] ?? '') === 'modifycat') { + self::modifyCat(); + } + + // Everything's gonna need this. + Lang::load('ManageBoards'); + + // Create the tabs for the template. + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['boards_and_cats'], + 'help' => 'manage_boards', + 'description' => Lang::$txt['boards_and_cats_desc'], + 'tabs' => [ + 'main' => [ + ], + 'newcat' => [ + ], + 'settings' => [ + 'description' => Lang::$txt['mboards_settings_desc'], + ], + ], + ]; + // Have you got the proper permissions? User::$me->isAllowedTo(self::$subactions[$this->subaction][1]); @@ -930,30 +954,6 @@ public static function getConfigVars(): array */ protected function __construct() { - // Special handling for modifycat. - if (($_REQUEST['action'] ?? '') === 'modifycat') { - self::modifyCat(); - } - - // Everything's gonna need this. - Lang::load('ManageBoards'); - - // Create the tabs for the template. - Menu::$loaded['admin']->tab_data = [ - 'title' => Lang::$txt['boards_and_cats'], - 'help' => 'manage_boards', - 'description' => Lang::$txt['boards_and_cats_desc'], - 'tabs' => [ - 'main' => [ - ], - 'newcat' => [ - ], - 'settings' => [ - 'description' => Lang::$txt['mboards_settings_desc'], - ], - ], - ]; - IntegrationHook::call('integrate_manage_boards', [&self::$subactions]); // Default to sub action 'main' or 'settings' depending on permissions. diff --git a/Sources/Actions/Admin/Calendar.php b/Sources/Actions/Admin/Calendar.php index e295cfad1a..c813cc679c 100644 --- a/Sources/Actions/Admin/Calendar.php +++ b/Sources/Actions/Admin/Calendar.php @@ -86,6 +86,30 @@ class Calendar implements ActionInterface */ public function execute(): void { + // Everything's gonna need this. + Lang::load('Calendar+ManageCalendar'); + + // Set up the two tabs here... + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['manage_calendar'], + 'help' => 'calendar', + 'description' => Lang::$txt['calendar_settings_desc'], + ]; + + if (!empty(Config::$modSettings['cal_enabled'])) { + 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'], + ], + ]; + } + User::$me->isAllowedTo('admin_forum'); $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); @@ -634,35 +658,11 @@ 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']); + unset(self::$subactions['holidays'], self::$subactions['editholiday'], self::$subactions['import']); $this->subaction = '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'], - ]; - - if (!empty(Config::$modSettings['cal_enabled'])) { - 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']])) { diff --git a/Sources/Actions/Admin/ErrorLog.php b/Sources/Actions/Admin/ErrorLog.php index 7ccbaeb006..dfdf0c3898 100644 --- a/Sources/Actions/Admin/ErrorLog.php +++ b/Sources/Actions/Admin/ErrorLog.php @@ -107,6 +107,14 @@ class ErrorLog implements ActionInterface */ public function execute(): void { + // Templates, etc... + Lang::load('ManageMaintenance'); + Theme::loadTemplate('Errors'); + + foreach ($this->filters as &$filter) { + $filter['txt'] = Lang::$txt[$filter['txt']]; + } + // Check for the administrative permission to do this. User::$me->isAllowedTo('admin_forum'); @@ -488,20 +496,6 @@ public function viewBacktrace(): void * Internal methods ******************/ - /** - * Constructor. Protected to force instantiation via self::load(). - */ - protected function __construct() - { - // Templates, etc... - Lang::load('ManageMaintenance'); - Theme::loadTemplate('Errors'); - - foreach ($this->filters as &$filter) { - $filter['txt'] = Lang::$txt[$filter['txt']]; - } - } - /** * Delete all or some of the errors in the error log. * diff --git a/Sources/Actions/Admin/Features.php b/Sources/Actions/Admin/Features.php index 6d5bd02671..71156c1d41 100644 --- a/Sources/Actions/Admin/Features.php +++ b/Sources/Actions/Admin/Features.php @@ -88,6 +88,8 @@ class Features implements ActionInterface */ public function execute(): void { + $this->init(); + // You need to be an admin to edit settings! User::$me->isAllowedTo('admin_forum'); @@ -1882,6 +1884,18 @@ public static function list_getProfileFieldSize(): int * Constructor. Protected to force instantiation via self::load(). */ protected function __construct() + { + IntegrationHook::call('integrate_modify_features', [&self::$subactions]); + + if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { + $this->subaction = $_REQUEST['sa']; + } + } + + /** + * Does some initial setup. + */ + protected function init() { Lang::load('Help'); Lang::load('ManageSettings'); @@ -1917,12 +1931,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/Languages.php b/Sources/Actions/Admin/Languages.php index b879d2715f..08764f6aa4 100644 --- a/Sources/Actions/Admin/Languages.php +++ b/Sources/Actions/Admin/Languages.php @@ -81,6 +81,18 @@ class Languages implements ActionInterface */ public function execute(): void { + Theme::loadTemplate('ManageLanguages'); + Lang::load('ManageSettings'); + + Utils::$context['page_title'] = Lang::$txt['edit_languages']; + Utils::$context['sub_template'] = 'show_settings'; + + // Load up all the tabs... + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['language_configuration'], + 'description' => Lang::$txt['language_description'], + ]; + $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); if (!empty($call)) { @@ -1614,18 +1626,6 @@ public static function list_getLanguages(): array */ protected function __construct() { - Theme::loadTemplate('ManageLanguages'); - Lang::load('ManageSettings'); - - Utils::$context['page_title'] = Lang::$txt['edit_languages']; - Utils::$context['sub_template'] = 'show_settings'; - - // Load up all the tabs... - Menu::$loaded['admin']->tab_data = [ - 'title' => Lang::$txt['language_configuration'], - 'description' => Lang::$txt['language_description'], - ]; - IntegrationHook::call('integrate_manage_languages', [&self::$subactions]); // By default we're managing languages. diff --git a/Sources/Actions/Admin/Mail.php b/Sources/Actions/Admin/Mail.php index 2beb625e53..b2559e2aea 100644 --- a/Sources/Actions/Admin/Mail.php +++ b/Sources/Actions/Admin/Mail.php @@ -88,6 +88,22 @@ class Mail implements ActionInterface */ public function execute(): void { + // You need to be an admin to edit settings! + User::$me->isAllowedTo('admin_forum'); + + Lang::load('Help'); + Lang::load('ManageMail'); + + Utils::$context['page_title'] = Lang::$txt['mailqueue_title']; + Utils::$context['sub_template'] = 'show_settings'; + + // Load up all the tabs... + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['mailqueue_title'], + 'help' => '', + 'description' => Lang::$txt['mailqueue_desc'], + ]; + $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); if (!empty($call)) { @@ -535,15 +551,6 @@ public static function timeSince(int $time_diff): string */ protected function __construct() { - // You need to be an admin to edit settings! - User::$me->isAllowedTo('admin_forum'); - - Lang::load('Help'); - Lang::load('ManageMail'); - - 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']])) { @@ -551,13 +558,6 @@ protected function __construct() } Utils::$context['sub_action'] = $this->subaction; - - // Load up all the tabs... - Menu::$loaded['admin']->tab_data = [ - 'title' => Lang::$txt['mailqueue_title'], - 'help' => '', - 'description' => Lang::$txt['mailqueue_desc'], - ]; } /** diff --git a/Sources/Actions/Admin/Maintenance.php b/Sources/Actions/Admin/Maintenance.php index e8db19d2c2..44e00cd529 100644 --- a/Sources/Actions/Admin/Maintenance.php +++ b/Sources/Actions/Admin/Maintenance.php @@ -135,6 +135,30 @@ class Maintenance implements ActionInterface */ public function execute(): void { + // You absolutely must be an admin by here! + User::$me->isAllowedTo('admin_forum'); + + // Need something to talk about? + Lang::load('ManageMaintenance'); + Theme::loadTemplate('ManageMaintenance'); + + // This uses admin tabs - as it should! + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['maintain_title'], + 'description' => Lang::$txt['maintain_info'], + 'tabs' => [ + 'routine' => [], + 'database' => [], + 'members' => [], + 'topics' => [], + ], + ]; + + // 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'] : ''; + $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)) { @@ -2286,25 +2310,6 @@ public static function reattributePosts(int $memID, ?string $email = null, ?stri */ protected function __construct() { - // You absolutely must be an admin by here! - User::$me->isAllowedTo('admin_forum'); - - // Need something to talk about? - Lang::load('ManageMaintenance'); - Theme::loadTemplate('ManageMaintenance'); - - // This uses admin tabs - as it should! - Menu::$loaded['admin']->tab_data = [ - 'title' => Lang::$txt['maintain_title'], - 'description' => Lang::$txt['maintain_info'], - 'tabs' => [ - 'routine' => [], - 'database' => [], - 'members' => [], - 'topics' => [], - ], - ]; - IntegrationHook::call('integrate_manage_maintenance', [&self::$subactions]); if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { @@ -2315,11 +2320,6 @@ protected function __construct() if (isset($_REQUEST['activity'], self::$subactions[$this->subaction]['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'] : ''; } /** diff --git a/Sources/Actions/Admin/Membergroups.php b/Sources/Actions/Admin/Membergroups.php index 81e4a97cab..dff3c0bdff 100644 --- a/Sources/Actions/Admin/Membergroups.php +++ b/Sources/Actions/Admin/Membergroups.php @@ -83,6 +83,17 @@ class Membergroups implements ActionInterface */ public function execute(): void { + // Language and template stuff, the usual. + Lang::load('ManageMembers'); + Theme::loadTemplate('ManageMembergroups'); + + // Setup the admin tabs. + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['membergroups_title'], + 'help' => 'membergroups', + 'description' => Lang::$txt['membergroups_description'], + ]; + // Do the permission check, you might not be allowed here. User::$me->isAllowedTo(self::$subactions[$this->subaction][1]); @@ -969,17 +980,6 @@ public static function getConfigVars(): array */ protected function __construct() { - // Language and template stuff, the usual. - Lang::load('ManageMembers'); - Theme::loadTemplate('ManageMembergroups'); - - // Setup the admin tabs. - Menu::$loaded['admin']->tab_data = [ - 'title' => Lang::$txt['membergroups_title'], - 'help' => 'membergroups', - 'description' => Lang::$txt['membergroups_description'], - ]; - IntegrationHook::call('integrate_manage_membergroups', [&self::$subactions]); if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { diff --git a/Sources/Actions/Admin/Members.php b/Sources/Actions/Admin/Members.php index 95da0e6f40..db074ebf46 100644 --- a/Sources/Actions/Admin/Members.php +++ b/Sources/Actions/Admin/Members.php @@ -138,6 +138,81 @@ class Members implements ActionInterface */ public function execute(): void { + // Load the essentials. + Lang::load('ManageMembers'); + Theme::loadTemplate('ManageMembers'); + + // Fetch our activation counts. + $this->getActivationCounts(); + + // For the page header... do we show activation? + $this->show_activate = (!empty(Config::$modSettings['registration_method']) && Config::$modSettings['registration_method'] == 1) || !empty($this->awaiting_activation); + + // What about approval? + $this->show_approve = (!empty(Config::$modSettings['registration_method']) && Config::$modSettings['registration_method'] == 2) || !empty($this->awaiting_approval) || !empty(Config::$modSettings['approveAccountDeletion']); + + // Setup the admin tabs. + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['admin_members'], + 'help' => 'view_members', + 'description' => Lang::$txt['admin_members_list'], + 'tabs' => [], + ]; + + Utils::$context['tabs'] = [ + 'viewmembers' => [ + 'label' => Lang::$txt['view_all_members'], + 'description' => Lang::$txt['admin_members_list'], + 'url' => Config::$scripturl . '?action=admin;area=viewmembers;sa=all', + 'selected_actions' => ['all'], + ], + 'search' => [ + 'label' => Lang::$txt['mlist_search'], + 'description' => Lang::$txt['admin_members_list'], + 'url' => Config::$scripturl . '?action=admin;area=viewmembers;sa=search', + 'selected_actions' => ['search', 'query'], + ], + ]; + Utils::$context['last_tab'] = 'search'; + + // Do we have approvals + if ($this->show_approve) { + Utils::$context['tabs']['approve'] = [ + 'label' => Lang::getTxt('admin_browse_awaiting_approval', [$this->awaiting_approval]), + 'description' => Lang::$txt['admin_browse_approve_desc'], + 'url' => Config::$scripturl . '?action=admin;area=viewmembers;sa=browse;type=approve', + ]; + Utils::$context['last_tab'] = 'approve'; + } + + // Do we have activations to show? + if ($this->show_activate) { + Utils::$context['tabs']['activate'] = [ + 'label' => Lang::getTxt('admin_browse_awaiting_activate', [$this->awaiting_activation]), + 'description' => Lang::$txt['admin_browse_activate_desc'], + 'url' => Config::$scripturl . '?action=admin;area=viewmembers;sa=browse;type=activate', + ]; + Utils::$context['last_tab'] = 'activate'; + } + + // 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; + $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)) { @@ -1342,63 +1417,6 @@ public static function list_getNumMembers(string $where, array $where_params = [ */ protected function __construct() { - // Load the essentials. - Lang::load('ManageMembers'); - Theme::loadTemplate('ManageMembers'); - - // Fetch our activation counts. - $this->getActivationCounts(); - - // For the page header... do we show activation? - $this->show_activate = (!empty(Config::$modSettings['registration_method']) && Config::$modSettings['registration_method'] == 1) || !empty($this->awaiting_activation); - - // What about approval? - $this->show_approve = (!empty(Config::$modSettings['registration_method']) && Config::$modSettings['registration_method'] == 2) || !empty($this->awaiting_approval) || !empty(Config::$modSettings['approveAccountDeletion']); - - // Setup the admin tabs. - Menu::$loaded['admin']->tab_data = [ - 'title' => Lang::$txt['admin_members'], - 'help' => 'view_members', - 'description' => Lang::$txt['admin_members_list'], - 'tabs' => [], - ]; - - Utils::$context['tabs'] = [ - 'viewmembers' => [ - 'label' => Lang::$txt['view_all_members'], - 'description' => Lang::$txt['admin_members_list'], - 'url' => Config::$scripturl . '?action=admin;area=viewmembers;sa=all', - 'selected_actions' => ['all'], - ], - 'search' => [ - 'label' => Lang::$txt['mlist_search'], - 'description' => Lang::$txt['admin_members_list'], - 'url' => Config::$scripturl . '?action=admin;area=viewmembers;sa=search', - 'selected_actions' => ['search', 'query'], - ], - ]; - Utils::$context['last_tab'] = 'search'; - - // Do we have approvals - if ($this->show_approve) { - Utils::$context['tabs']['approve'] = [ - 'label' => Lang::getTxt('admin_browse_awaiting_approval', [$this->awaiting_approval]), - 'description' => Lang::$txt['admin_browse_approve_desc'], - 'url' => Config::$scripturl . '?action=admin;area=viewmembers;sa=browse;type=approve', - ]; - Utils::$context['last_tab'] = 'approve'; - } - - // Do we have activations to show? - if ($this->show_activate) { - Utils::$context['tabs']['activate'] = [ - 'label' => Lang::getTxt('admin_browse_awaiting_activate', [$this->awaiting_activation]), - 'description' => Lang::$txt['admin_browse_activate_desc'], - 'url' => Config::$scripturl . '?action=admin;area=viewmembers;sa=browse;type=activate', - ]; - 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]); @@ -1408,24 +1426,6 @@ protected function __construct() // 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..27f70b785b 100644 --- a/Sources/Actions/Admin/Mods.php +++ b/Sources/Actions/Admin/Mods.php @@ -68,6 +68,22 @@ class Mods implements ActionInterface */ public function execute(): void { + Lang::load('Help'); + Lang::load('ManageSettings'); + + Utils::$context['page_title'] = Lang::$txt['admin_modifications']; + + // Load up all the tabs... + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['admin_modifications'], + 'help' => 'modsettings', + 'description' => Lang::$txt['modification_settings_desc'], + 'tabs' => [ + 'general' => [ + ], + ], + ]; + // You need to be an admin to edit settings! User::$me->isAllowedTo('admin_forum'); @@ -154,22 +170,6 @@ public static function getConfigVars(): array */ protected function __construct() { - Lang::load('Help'); - Lang::load('ManageSettings'); - - Utils::$context['page_title'] = Lang::$txt['admin_modifications']; - - // Load up all the tabs... - Menu::$loaded['admin']->tab_data = [ - 'title' => Lang::$txt['admin_modifications'], - 'help' => 'modsettings', - 'description' => Lang::$txt['modification_settings_desc'], - 'tabs' => [ - 'general' => [ - ], - ], - ]; - // Make it easier for mods to add new areas. IntegrationHook::call('integrate_modify_modifications', [&self::$subactions]); diff --git a/Sources/Actions/Admin/News.php b/Sources/Actions/Admin/News.php index 65dd43d7ea..ac69eb791d 100644 --- a/Sources/Actions/Admin/News.php +++ b/Sources/Actions/Admin/News.php @@ -235,6 +235,33 @@ function addNewsItem () */ public function execute(): void { + Theme::loadTemplate('ManageNews'); + + // Create the tabs for the template. + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['news_title'], + 'help' => 'edit_news', + 'description' => Lang::$txt['admin_news_desc'], + 'tabs' => [ + 'editnews' => [ + ], + 'mailingmembers' => [ + 'description' => Lang::$txt['news_mailing_desc'], + ], + 'settings' => [ + 'description' => Lang::$txt['news_settings_desc'], + ], + ], + ]; + + // 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(); + // Have you got the proper permissions? User::$me->isAllowedTo(self::$subactions[$this->subaction][1]); @@ -1221,37 +1248,10 @@ public static function prepareMailingForPreview(): void */ protected function __construct() { - Theme::loadTemplate('ManageNews'); - - // Create the tabs for the template. - Menu::$loaded['admin']->tab_data = [ - 'title' => Lang::$txt['news_title'], - 'help' => 'edit_news', - 'description' => Lang::$txt['admin_news_desc'], - 'tabs' => [ - 'editnews' => [ - ], - 'mailingmembers' => [ - 'description' => Lang::$txt['news_mailing_desc'], - ], - 'settings' => [ - 'description' => Lang::$txt['news_settings_desc'], - ], - ], - ]; - 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..747d14b6a9 100644 --- a/Sources/Actions/Admin/Permissions.php +++ b/Sources/Actions/Admin/Permissions.php @@ -937,6 +937,33 @@ class Permissions implements ActionInterface */ public function execute(): void { + Lang::load('ManagePermissions+ManageMembers'); + Theme::loadTemplate('ManagePermissions'); + + // Create the tabs for the template. + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['permissions_title'], + 'help' => 'permissions', + 'description' => '', + 'tabs' => [ + 'index' => [ + 'description' => Lang::$txt['permissions_groups'], + ], + 'board' => [ + 'description' => Lang::$txt['permission_by_board_desc'], + ], + 'profiles' => [ + 'description' => Lang::$txt['permissions_profiles_desc'], + ], + 'postmod' => [ + 'description' => Lang::$txt['permissions_post_moderation_desc'], + ], + 'settings' => [ + 'description' => Lang::$txt['permission_settings_desc'], + ], + ], + ]; + 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]); @@ -2375,33 +2402,6 @@ public static function buildHidden(): void */ protected function __construct() { - Lang::load('ManagePermissions+ManageMembers'); - Theme::loadTemplate('ManagePermissions'); - - // Create the tabs for the template. - Menu::$loaded['admin']->tab_data = [ - 'title' => Lang::$txt['permissions_title'], - 'help' => 'permissions', - 'description' => '', - 'tabs' => [ - 'index' => [ - 'description' => Lang::$txt['permissions_groups'], - ], - 'board' => [ - 'description' => Lang::$txt['permission_by_board_desc'], - ], - 'profiles' => [ - 'description' => Lang::$txt['permissions_profiles_desc'], - ], - 'postmod' => [ - 'description' => Lang::$txt['permissions_post_moderation_desc'], - ], - 'settings' => [ - 'description' => Lang::$txt['permission_settings_desc'], - ], - ], - ]; - IntegrationHook::call('integrate_manage_permissions', [&self::$subactions]); if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { diff --git a/Sources/Actions/Admin/Posts.php b/Sources/Actions/Admin/Posts.php index 4e3c8986c0..8cf4027eee 100644 --- a/Sources/Actions/Admin/Posts.php +++ b/Sources/Actions/Admin/Posts.php @@ -73,6 +73,33 @@ class Posts implements ActionInterface */ public function execute(): void { + // Make sure you can be here. + User::$me->isAllowedTo('admin_forum'); + Lang::load('Drafts'); + + Utils::$context['page_title'] = Lang::$txt['manageposts_title']; + + // Tabs for browsing the different post functions. + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['manageposts_title'], + 'help' => 'posts_and_topics', + 'description' => Lang::$txt['manageposts_description'], + 'tabs' => [ + 'posts' => [ + 'description' => Lang::$txt['manageposts_settings_description'], + ], + 'censor' => [ + 'description' => Lang::$txt['admin_censored_desc'], + ], + 'topics' => [ + 'description' => Lang::$txt['manageposts_topic_settings_description'], + ], + 'drafts' => [ + 'description' => Lang::$txt['managedrafts_settings_description'], + ], + ], + ]; + $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); if (!empty($call)) { @@ -442,33 +469,6 @@ public static function draftConfigVars(): array */ protected function __construct() { - // Make sure you can be here. - User::$me->isAllowedTo('admin_forum'); - Lang::load('Drafts'); - - Utils::$context['page_title'] = Lang::$txt['manageposts_title']; - - // Tabs for browsing the different post functions. - Menu::$loaded['admin']->tab_data = [ - 'title' => Lang::$txt['manageposts_title'], - 'help' => 'posts_and_topics', - 'description' => Lang::$txt['manageposts_description'], - 'tabs' => [ - 'posts' => [ - 'description' => Lang::$txt['manageposts_settings_description'], - ], - 'censor' => [ - 'description' => Lang::$txt['admin_censored_desc'], - ], - 'topics' => [ - 'description' => Lang::$txt['manageposts_topic_settings_description'], - ], - 'drafts' => [ - 'description' => Lang::$txt['managedrafts_settings_description'], - ], - ], - ]; - IntegrationHook::call('integrate_manage_posts', [&self::$subactions]); if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { diff --git a/Sources/Actions/Admin/Registration.php b/Sources/Actions/Admin/Registration.php index a9e4f6804c..8de36a0384 100644 --- a/Sources/Actions/Admin/Registration.php +++ b/Sources/Actions/Admin/Registration.php @@ -82,6 +82,34 @@ class Registration implements ActionInterface */ public function execute(): void { + // Loading, always loading. + Lang::load('Login'); + Theme::loadTemplate('Register'); + + // Next create the tabs for the template. + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['registration_center'], + 'help' => 'registrations', + 'description' => Lang::$txt['admin_settings_desc'], + 'tabs' => [ + 'register' => [ + 'description' => Lang::$txt['admin_register_desc'], + ], + 'agreement' => [ + 'description' => Lang::$txt['registration_agreement_desc'], + ], + 'policy' => [ + 'description' => Lang::$txt['privacy_policy_desc'], + ], + 'reservednames' => [ + 'description' => Lang::$txt['admin_reserved_desc'], + ], + 'settings' => [ + 'description' => Lang::$txt['admin_settings_desc'], + ], + ], + ]; + // Must have sufficient permissions. User::$me->isAllowedTo(self::$subactions[$this->subaction][1]); @@ -509,34 +537,6 @@ public static function getConfigVars(): array */ protected function __construct() { - // Loading, always loading. - Lang::load('Login'); - Theme::loadTemplate('Register'); - - // Next create the tabs for the template. - Menu::$loaded['admin']->tab_data = [ - 'title' => Lang::$txt['registration_center'], - 'help' => 'registrations', - 'description' => Lang::$txt['admin_settings_desc'], - 'tabs' => [ - 'register' => [ - 'description' => Lang::$txt['admin_register_desc'], - ], - 'agreement' => [ - 'description' => Lang::$txt['registration_agreement_desc'], - ], - 'policy' => [ - 'description' => Lang::$txt['privacy_policy_desc'], - ], - 'reservednames' => [ - 'description' => Lang::$txt['admin_reserved_desc'], - ], - 'settings' => [ - 'description' => Lang::$txt['admin_settings_desc'], - ], - ], - ]; - IntegrationHook::call('integrate_manage_registrations', [&self::$subactions]); if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { diff --git a/Sources/Actions/Admin/RepairBoards.php b/Sources/Actions/Admin/RepairBoards.php index 3561acff6d..e1bff06319 100644 --- a/Sources/Actions/Admin/RepairBoards.php +++ b/Sources/Actions/Admin/RepairBoards.php @@ -781,6 +781,22 @@ class RepairBoards implements ActionInterface */ public function execute(): void { + // Print out the top of the webpage. + Utils::$context['page_title'] = Lang::$txt['admin_repair']; + Utils::$context['sub_template'] = 'repair_boards'; + Menu::$loaded['admin']['current_subsection'] = 'general'; + + // Load the language file. + Lang::load('ManageMaintenance'); + + // Make sure the tabs stay nice. + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['maintain_title'], + 'help' => '', + 'description' => Lang::$txt['maintain_info'], + 'tabs' => [], + ]; + User::$me->isAllowedTo('admin_forum'); // Try to secure more memory. @@ -851,28 +867,6 @@ public function execute(): void * Internal methods ******************/ - /** - * Constructor. Protected to force instantiation via self::load(). - */ - protected function __construct() - { - // Print out the top of the webpage. - Utils::$context['page_title'] = Lang::$txt['admin_repair']; - Utils::$context['sub_template'] = 'repair_boards'; - Menu::$loaded['admin']['current_subsection'] = 'general'; - - // Load the language file. - Lang::load('ManageMaintenance'); - - // Make sure the tabs stay nice. - Menu::$loaded['admin']->tab_data = [ - 'title' => Lang::$txt['maintain_title'], - 'help' => '', - 'description' => Lang::$txt['maintain_info'], - 'tabs' => [], - ]; - } - /** * Checks for errors in steps, until 5 seconds have passed. * diff --git a/Sources/Actions/Admin/Reports.php b/Sources/Actions/Admin/Reports.php index 3ea6371001..d5d9a97b8e 100644 --- a/Sources/Actions/Admin/Reports.php +++ b/Sources/Actions/Admin/Reports.php @@ -149,6 +149,43 @@ class Reports implements ActionInterface */ public function execute(): void { + // Only admins, only EVER admins! + User::$me->isAllowedTo('admin_forum'); + + // Let's get our things running... + Theme::loadTemplate('Reports'); + Lang::load('Reports'); + Theme::loadJavaScriptFile('reports.js', ['defer' => true, 'minimize' => true], 'smf_reports'); + + Utils::$context['page_title'] = Lang::$txt['generate_reports']; + + // Load up all the tabs... + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['generate_reports'], + 'help' => '', + 'description' => Lang::$txt['generate_reports_desc'], + ]; + + $is_first = 0; + + foreach (self::$subactions as $k => $func) { + if (!is_string($func)) { + continue; + } + + $this->report_types[$k] = [ + 'id' => $k, + 'title' => Lang::$txt['gr_type_' . $k] ?? $k, + 'description' => Lang::$txt['gr_type_desc_' . $k] ?? null, + 'function' => $func, + 'is_first' => $is_first++ == 0, + ]; + } + + Utils::$context['report_types'] = &$this->report_types; + Utils::$context['sub_template'] = &$this->sub_template; + Utils::$context['tables'] = &$this->tables; + if (empty($this->subaction)) { $this->sub_template = 'report_type'; @@ -641,48 +678,11 @@ public static function call(): void */ protected function __construct() { - // Only admins, only EVER admins! - User::$me->isAllowedTo('admin_forum'); - - // Let's get our things running... - Theme::loadTemplate('Reports'); - Lang::load('Reports'); - Theme::loadJavaScriptFile('reports.js', ['defer' => true, 'minimize' => true], 'smf_reports'); - - Utils::$context['page_title'] = Lang::$txt['generate_reports']; - // For backward compatibility... Utils::$context['report_types'] = &self::$subactions; IntegrationHook::call('integrate_report_types', [&self::$subactions]); - // Load up all the tabs... - Menu::$loaded['admin']->tab_data = [ - 'title' => Lang::$txt['generate_reports'], - 'help' => '', - 'description' => Lang::$txt['generate_reports_desc'], - ]; - - $is_first = 0; - - foreach (self::$subactions as $k => $func) { - if (!is_string($func)) { - continue; - } - - $this->report_types[$k] = [ - 'id' => $k, - 'title' => Lang::$txt['gr_type_' . $k] ?? $k, - 'description' => Lang::$txt['gr_type_desc_' . $k] ?? null, - 'function' => $func, - 'is_first' => $is_first++ == 0, - ]; - } - - 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..d811b907cb 100644 --- a/Sources/Actions/Admin/Search.php +++ b/Sources/Actions/Admin/Search.php @@ -74,6 +74,29 @@ class Search implements ActionInterface */ public function execute(): void { + User::$me->isAllowedTo('admin_forum'); + + Lang::load('Search'); + Theme::loadTemplate('ManageSearch'); + + // Create the tabs for the template. + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['manage_search'], + 'help' => 'search', + 'description' => Lang::$txt['search_settings_desc'], + 'tabs' => [ + 'weights' => [ + 'description' => Lang::$txt['search_weights_desc'], + ], + 'method' => [ + 'description' => Lang::$txt['search_method_desc'], + ], + 'settings' => [ + 'description' => Lang::$txt['search_settings_desc'], + ], + ], + ]; + $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); if (!empty($call)) { @@ -359,29 +382,6 @@ public static function getConfigVars(): array */ protected function __construct() { - User::$me->isAllowedTo('admin_forum'); - - Lang::load('Search'); - Theme::loadTemplate('ManageSearch'); - - // Create the tabs for the template. - Menu::$loaded['admin']->tab_data = [ - 'title' => Lang::$txt['manage_search'], - 'help' => 'search', - 'description' => Lang::$txt['search_settings_desc'], - 'tabs' => [ - 'weights' => [ - 'description' => Lang::$txt['search_weights_desc'], - ], - 'method' => [ - 'description' => Lang::$txt['search_method_desc'], - ], - 'settings' => [ - 'description' => Lang::$txt['search_settings_desc'], - ], - ], - ]; - // Load any apis. Utils::$context['search_apis'] = SearchApi::detect(); diff --git a/Sources/Actions/Admin/SearchEngines.php b/Sources/Actions/Admin/SearchEngines.php index 614020064b..2cf6bbc4de 100644 --- a/Sources/Actions/Admin/SearchEngines.php +++ b/Sources/Actions/Admin/SearchEngines.php @@ -92,6 +92,22 @@ class SearchEngines implements ActionInterface */ public function execute(): void { + User::$me->isAllowedTo('admin_forum'); + + Lang::load('Search'); + Theme::loadTemplate('ManageSearch'); + + 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'], + ]; + } + $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); if (!empty($call)) { @@ -1105,27 +1121,11 @@ public static function recacheSpiderNames(): void */ protected function __construct() { - User::$me->isAllowedTo('admin_forum'); - - 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']])) { diff --git a/Sources/Actions/Admin/Server.php b/Sources/Actions/Admin/Server.php index d7c6c8de73..fdd352cf47 100644 --- a/Sources/Actions/Admin/Server.php +++ b/Sources/Actions/Admin/Server.php @@ -185,6 +185,15 @@ class Server implements ActionInterface */ public function execute(): void { + Lang::load('ManageSettings'); + + // Load up all the tabs... + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['admin_server_settings'], + 'help' => 'serversettings', + 'description' => Lang::$txt['admin_basic_settings'], + ]; + // This is just to keep the database password more secure. User::$me->isAllowedTo('admin_forum'); @@ -1224,15 +1233,6 @@ public static function checkSettingsFileWriteSafe(): bool */ protected function __construct() { - Lang::load('ManageSettings'); - - // Load up all the tabs... - Menu::$loaded['admin']->tab_data = [ - 'title' => Lang::$txt['admin_server_settings'], - 'help' => 'serversettings', - 'description' => Lang::$txt['admin_basic_settings'], - ]; - IntegrationHook::call('integrate_server_settings', [&self::$subactions]); if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { diff --git a/Sources/Actions/Admin/Smileys.php b/Sources/Actions/Admin/Smileys.php index 5af713792b..0b004d859c 100644 --- a/Sources/Actions/Admin/Smileys.php +++ b/Sources/Actions/Admin/Smileys.php @@ -166,6 +166,59 @@ class Smileys implements ActionInterface */ public function execute(): void { + 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'], + 'help' => 'smileys', + 'description' => Lang::$txt['smiley_settings_explain'], + 'tabs' => [ + 'editsets' => [ + 'description' => Lang::$txt['smiley_editsets_explain'], + ], + 'addsmiley' => [ + 'description' => Lang::$txt['smiley_addsmiley_explain'], + ], + 'editsmileys' => [ + 'description' => Lang::$txt['smiley_editsmileys_explain'], + ], + 'setorder' => [ + 'description' => Lang::$txt['smiley_setorder_explain'], + ], + 'editicons' => [ + 'description' => Lang::$txt['icons_edit_icons_explain'], + ], + 'settings' => [ + 'description' => Lang::$txt['smiley_settings_explain'], + ], + ], + ]; + + // Some settings may not be enabled, disallow these from the tabs as appropriate. + if (empty(Config::$modSettings['messageIcons_enable'])) { + Menu::$loaded['admin']->tab_data['tabs']['editicons']['disabled'] = true; + } + + if (empty(Config::$modSettings['smiley_enable'])) { + Menu::$loaded['admin']->tab_data['tabs']['addsmiley']['disabled'] = true; + Menu::$loaded['admin']->tab_data['tabs']['editsmileys']['disabled'] = true; + Menu::$loaded['admin']->tab_data['tabs']['setorder']['disabled'] = true; + } + + Utils::$context['sub_action'] = &$this->subaction; + + Utils::$context['page_title'] = Lang::$txt['smileys_manage']; + Utils::$context['sub_template'] = $this->subaction; + + self::findSmileysDir(); + self::getKnownSmileySets(); + + Utils::$context['smiley_sets'] = &self::$smiley_sets; + $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); if (!empty($call)) { @@ -2273,11 +2326,6 @@ 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'); - // 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']); @@ -2287,59 +2335,11 @@ protected function __construct() unset(self::$subactions['editicon'], self::$subactions['editicons']); } - // Load up all the tabs... - Menu::$loaded['admin']->tab_data = [ - 'title' => Lang::$txt['smileys_manage'], - 'help' => 'smileys', - 'description' => Lang::$txt['smiley_settings_explain'], - 'tabs' => [ - 'editsets' => [ - 'description' => Lang::$txt['smiley_editsets_explain'], - ], - 'addsmiley' => [ - 'description' => Lang::$txt['smiley_addsmiley_explain'], - ], - 'editsmileys' => [ - 'description' => Lang::$txt['smiley_editsmileys_explain'], - ], - 'setorder' => [ - 'description' => Lang::$txt['smiley_setorder_explain'], - ], - 'editicons' => [ - 'description' => Lang::$txt['icons_edit_icons_explain'], - ], - 'settings' => [ - 'description' => Lang::$txt['smiley_settings_explain'], - ], - ], - ]; - - // Some settings may not be enabled, disallow these from the tabs as appropriate. - if (empty(Config::$modSettings['messageIcons_enable'])) { - Menu::$loaded['admin']->tab_data['tabs']['editicons']['disabled'] = true; - } - - if (empty(Config::$modSettings['smiley_enable'])) { - Menu::$loaded['admin']->tab_data['tabs']['addsmiley']['disabled'] = true; - Menu::$loaded['admin']->tab_data['tabs']['editsmileys']['disabled'] = true; - 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(); - - Utils::$context['smiley_sets'] = &self::$smiley_sets; } /** diff --git a/Sources/Actions/Admin/Subscriptions.php b/Sources/Actions/Admin/Subscriptions.php index 5793b924b1..ad93cd12db 100644 --- a/Sources/Actions/Admin/Subscriptions.php +++ b/Sources/Actions/Admin/Subscriptions.php @@ -95,6 +95,30 @@ class Subscriptions implements ActionInterface */ public function execute(): void { + // Load the required language and template. + Lang::load('ManagePaid'); + Theme::loadTemplate('ManagePaid'); + + Utils::$context['page_title'] = Lang::$txt['paid_subscriptions']; + + // Tabs for browsing the different subscription functions. + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['paid_subscriptions'], + 'help' => '', + 'description' => Lang::$txt['paid_subscriptions_desc'], + ]; + + if (!empty(Config::$modSettings['paid_enabled']) && !empty(Config::$modSettings['paid_currency_symbol'])) { + Menu::$loaded['admin']->tab_data['tabs'] = [ + 'view' => [ + 'description' => Lang::$txt['paid_subs_view_desc'], + ], + 'settings' => [ + 'description' => Lang::$txt['paid_subs_settings_desc'], + ], + ]; + } + // Make sure you can do this. User::$me->isAllowedTo(self::$subactions[$this->subaction][1]); @@ -2237,32 +2261,10 @@ public static function list_getSubscribedUsers(int $start, int $items_per_page, */ protected function __construct() { - // Load the required language and template. - Lang::load('ManagePaid'); - Theme::loadTemplate('ManagePaid'); - - Utils::$context['page_title'] = Lang::$txt['paid_subscriptions']; - - // Tabs for browsing the different subscription functions. - Menu::$loaded['admin']->tab_data = [ - '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'] = [ - 'view' => [ - 'description' => Lang::$txt['paid_subs_view_desc'], - ], - 'settings' => [ - 'description' => Lang::$txt['paid_subs_settings_desc'], - ], - ]; } IntegrationHook::call('integrate_manage_subscriptions', [&self::$subactions]); diff --git a/Sources/Actions/Admin/Tasks.php b/Sources/Actions/Admin/Tasks.php index 37d45cace2..919311cbf3 100644 --- a/Sources/Actions/Admin/Tasks.php +++ b/Sources/Actions/Admin/Tasks.php @@ -78,6 +78,32 @@ class Tasks implements ActionInterface */ public function execute(): void { + User::$me->isAllowedTo('admin_forum'); + + Lang::load('ManageScheduledTasks'); + Theme::loadTemplate('ManageScheduledTasks'); + + // Tab data might already be set if this was called from Logs::execute(). + if (empty(Menu::$loaded['admin']->tab_data)) { + // Now for the lovely tabs. That we all love. + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['scheduled_tasks_title'], + 'help' => '', + 'description' => Lang::$txt['maintain_info'], + 'tabs' => [ + 'tasks' => [ + 'description' => Lang::$txt['maintain_tasks_desc'], + ], + 'tasklog' => [ + 'description' => Lang::$txt['scheduled_log_desc'], + ], + 'settings' => [ + 'description' => Lang::$txt['scheduled_tasks_settings_desc'], + ], + ], + ]; + } + $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); if (!empty($call)) { @@ -646,32 +672,6 @@ public static function list_getNumTaskLogEntries(): int */ protected function __construct() { - User::$me->isAllowedTo('admin_forum'); - - Lang::load('ManageScheduledTasks'); - Theme::loadTemplate('ManageScheduledTasks'); - - // Tab data might already be set if this was called from Logs::execute(). - if (empty(Menu::$loaded['admin']->tab_data)) { - // Now for the lovely tabs. That we all love. - Menu::$loaded['admin']->tab_data = [ - 'title' => Lang::$txt['scheduled_tasks_title'], - 'help' => '', - 'description' => Lang::$txt['maintain_info'], - 'tabs' => [ - 'tasks' => [ - 'description' => Lang::$txt['maintain_tasks_desc'], - ], - 'tasklog' => [ - 'description' => Lang::$txt['scheduled_log_desc'], - ], - 'settings' => [ - 'description' => Lang::$txt['scheduled_tasks_settings_desc'], - ], - ], - ]; - } - IntegrationHook::call('integrate_manage_scheduled_tasks', [&self::$subactions]); if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { diff --git a/Sources/Actions/Admin/Themes.php b/Sources/Actions/Admin/Themes.php index acd13fe00a..c6ca2fd68d 100644 --- a/Sources/Actions/Admin/Themes.php +++ b/Sources/Actions/Admin/Themes.php @@ -100,6 +100,38 @@ class Themes implements ActionInterface */ public function execute(): void { + User::$me->isAllowedTo('admin_forum'); + + // Load the important language files... + Lang::load('Admin'); + Lang::load('Themes'); + Lang::load('ThemeStrings'); + Lang::load('Drafts'); + + // Default the page title to Theme Administration by default. + Utils::$context['page_title'] = Lang::$txt['themeadmin_title']; + + if (!empty(Utils::$context['admin_menu_name'])) { + Menu::$loaded['admin']->tab_data = [ + 'title' => Lang::$txt['themeadmin_title'], + 'description' => Lang::$txt['themeadmin_description'], + 'tabs' => [ + 'admin' => [ + 'description' => Lang::$txt['themeadmin_admin_desc'], + ], + 'list' => [ + 'description' => Lang::$txt['themeadmin_list_desc'], + ], + 'reset' => [ + 'description' => Lang::$txt['themeadmin_reset_desc'], + ], + 'edit' => [ + 'description' => Lang::$txt['themeadmin_edit_desc'], + ], + ], + ]; + } + // Whatever they decide to do, clean the minify cache. Theme::deleteAllMinified(); @@ -1337,39 +1369,6 @@ protected function __construct() Utils::redirectexit('action=admin;area=theme;' . (isset($_GET['sa']) ? ';sa=' . $_GET['sa'] : '') . (isset($_GET['u']) ? ';u=' . $_GET['u'] : '')); } - User::$me->isAllowedTo('admin_forum'); - - // Load the important language files... - Lang::load('Admin'); - Lang::load('Themes'); - Lang::load('ThemeStrings'); - Lang::load('Drafts'); - - // Default the page title to Theme Administration by default. - Utils::$context['page_title'] = Lang::$txt['themeadmin_title']; - - if (!empty(Utils::$context['admin_menu_name'])) { - Menu::$loaded['admin']->tab_data = [ - 'title' => Lang::$txt['themeadmin_title'], - 'description' => Lang::$txt['themeadmin_description'], - 'tabs' => [ - 'admin' => [ - 'description' => Lang::$txt['themeadmin_admin_desc'], - ], - 'list' => [ - 'description' => Lang::$txt['themeadmin_list_desc'], - ], - 'reset' => [ - 'description' => Lang::$txt['themeadmin_reset_desc'], - ], - 'edit' => [ - 'description' => Lang::$txt['themeadmin_edit_desc'], - ], - ], - ]; - } - - // CRUD self::$subactions as needed. IntegrationHook::call('integrate_manage_themes', [&self::$subactions]); if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { diff --git a/Sources/Actions/Announce.php b/Sources/Actions/Announce.php index 92f83b5df5..bca8add1f8 100644 --- a/Sources/Actions/Announce.php +++ b/Sources/Actions/Announce.php @@ -78,6 +78,19 @@ class Announce implements ActionInterface, Routable */ public function execute(): void { + User::$me->isAllowedTo('announce_topic'); + + User::$me->validateSession(); + + if (empty(Topic::$topic_id)) { + ErrorHandler::fatalLang('topic_gone', false); + } + + Lang::load('Post'); + Theme::loadTemplate('Post'); + + Utils::$context['page_title'] = Lang::$txt['announce_topic']; + $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); if (!empty($call)) { @@ -285,19 +298,6 @@ public function send(): void */ protected function __construct() { - User::$me->isAllowedTo('announce_topic'); - - User::$me->validateSession(); - - if (empty(Topic::$topic_id)) { - ErrorHandler::fatalLang('topic_gone', false); - } - - Lang::load('Post'); - 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/BoardIndex.php b/Sources/Actions/BoardIndex.php index 38e7b96902..13c8a86652 100644 --- a/Sources/Actions/BoardIndex.php +++ b/Sources/Actions/BoardIndex.php @@ -56,6 +56,35 @@ class BoardIndex implements ActionInterface, Routable */ public function execute(): void { + Lang::load('Calendar'); + + Theme::loadTemplate('BoardIndex'); + Utils::$context['template_layers'][] = 'boardindex_outer'; + + Utils::$context['page_title'] = Lang::getTxt('forum_index', ['forum_name' => Utils::$context['forum_name']]); + + // Set a canonical URL for this page. + Utils::$context['canonical_url'] = Config::$scripturl; + + // Do not let search engines index anything if there is a random thing in $_GET. + if (!empty($_GET)) { + Utils::$context['robot_no_index'] = true; + } + + // Replace the collapse and expand default alts. + Theme::addJavaScriptVar('smf_expandAlt', Lang::$txt['show_category'], true); + Theme::addJavaScriptVar('smf_collapseAlt', Lang::$txt['hide_category'], true); + + if (!empty(Theme::$current->settings['show_newsfader'])) { + Theme::loadJavaScriptFile('slippry.min.js', [], 'smf_jquery_slippry'); + Theme::loadCSSFile('slider.min.css', [], 'smf_jquery_slider'); + } + + // Set a few minor things. + Utils::$context['show_stats'] = User::$me->allowedTo('view_stats') && !empty(Config::$modSettings['trackStats']); + Utils::$context['show_buddies'] = !empty(User::$me->buddies); + Utils::$context['show_who'] = User::$me->allowedTo('who_view') && !empty(Config::$modSettings['who_enabled']); + // Retrieve the categories and boards. $boardIndexOptions = [ 'include_categories' => true, @@ -612,47 +641,6 @@ public static function buildRoute(array $params): array return ['route' => $route, 'params' => $params]; } - /****************** - * Internal methods - ******************/ - - /** - * Prepares to show the board index. - * - * Protected to force instantiation via self::load(). - */ - protected function __construct() - { - Lang::load('Calendar'); - - Theme::loadTemplate('BoardIndex'); - Utils::$context['template_layers'][] = 'boardindex_outer'; - - Utils::$context['page_title'] = Lang::getTxt('forum_index', ['forum_name' => Utils::$context['forum_name']]); - - // Set a canonical URL for this page. - Utils::$context['canonical_url'] = Config::$scripturl; - - // Do not let search engines index anything if there is a random thing in $_GET. - if (!empty($_GET)) { - Utils::$context['robot_no_index'] = true; - } - - // Replace the collapse and expand default alts. - Theme::addJavaScriptVar('smf_expandAlt', Lang::$txt['show_category'], true); - Theme::addJavaScriptVar('smf_collapseAlt', Lang::$txt['hide_category'], true); - - if (!empty(Theme::$current->settings['show_newsfader'])) { - Theme::loadJavaScriptFile('slippry.min.js', [], 'smf_jquery_slippry'); - Theme::loadCSSFile('slider.min.css', [], 'smf_jquery_slider'); - } - - // Set a few minor things. - Utils::$context['show_stats'] = User::$me->allowedTo('view_stats') && !empty(Config::$modSettings['trackStats']); - Utils::$context['show_buddies'] = !empty(User::$me->buddies); - Utils::$context['show_who'] = User::$me->allowedTo('who_view') && !empty(Config::$modSettings['who_enabled']); - } - /************************* * Internal static methods *************************/ diff --git a/Sources/Actions/Calendar.php b/Sources/Actions/Calendar.php index 7bf5b2c9bf..02e2800399 100644 --- a/Sources/Actions/Calendar.php +++ b/Sources/Actions/Calendar.php @@ -85,6 +85,14 @@ class Calendar implements ActionInterface, Routable */ public function execute(): void { + Lang::load('Calendar'); + + // Some global template resources. + Utils::$context['calendar_resources'] = [ + 'min_year' => Config::$modSettings['cal_minyear'], + 'max_year' => Config::$modSettings['cal_maxyear'], + ]; + $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); if (!empty($call)) { @@ -1730,8 +1738,6 @@ public static function parseRoute(array $route, array $params = []): array */ protected function __construct() { - Lang::load('Calendar'); - if ($_GET['action'] === 'clock') { $this->subaction = 'clock'; } elseif (!empty($_GET['sa']) && isset(self::$subactions[$_GET['sa']])) { @@ -1762,12 +1768,6 @@ protected function __construct() else { User::$me->isAllowedTo('calendar_view'); } - - // Some global template resources. - Utils::$context['calendar_resources'] = [ - 'min_year' => Config::$modSettings['cal_minyear'], - 'max_year' => Config::$modSettings['cal_maxyear'], - ]; } /** diff --git a/Sources/Actions/CoppaForm.php b/Sources/Actions/CoppaForm.php index 7037d504c7..a64652689d 100644 --- a/Sources/Actions/CoppaForm.php +++ b/Sources/Actions/CoppaForm.php @@ -48,6 +48,14 @@ public function isRestrictedGuestAccessAllowed(): bool */ public function execute(): void { + Lang::load('Login'); + Theme::loadTemplate('Register'); + + // No User ID?? + if (!isset($_GET['member'])) { + ErrorHandler::fatalLang('no_access', false); + } + // Get the user details... $request = Db::$db->query( '', @@ -185,24 +193,6 @@ public static function parseRoute(array $route, array $params = []): array return $params; } - - /****************** - * Internal methods - ******************/ - - /** - * Constructor. Protected to force instantiation via self::load(). - */ - protected function __construct() - { - Lang::load('Login'); - Theme::loadTemplate('Register'); - - // No User ID?? - if (!isset($_GET['member'])) { - ErrorHandler::fatalLang('no_access', false); - } - } } ?> \ No newline at end of file diff --git a/Sources/Actions/Credits.php b/Sources/Actions/Credits.php index b09579b900..d07be87505 100644 --- a/Sources/Actions/Credits.php +++ b/Sources/Actions/Credits.php @@ -58,6 +58,9 @@ class Credits implements ActionInterface, Routable */ public function execute(): void { + // Don't blink. Don't even blink. Blink and you're dead. + Lang::load('Who'); + // Discourage robots from indexing this page. Utils::$context['robot_no_index'] = true; @@ -420,19 +423,6 @@ public static function call(bool $in_admin = false): void self::$obj->in_admin = $in_admin; self::$obj->execute(); } - - /****************** - * Internal methods - ******************/ - - /** - * Constructor. Protected to force instantiation via self::load(). - */ - protected function __construct() - { - // Don't blink. Don't even blink. Blink and you're dead. - Lang::load('Who'); - } } ?> \ No newline at end of file diff --git a/Sources/Actions/Display.php b/Sources/Actions/Display.php index 1c990567b2..d4ee1b0162 100644 --- a/Sources/Actions/Display.php +++ b/Sources/Actions/Display.php @@ -109,6 +109,10 @@ class Display implements ActionInterface, Routable /** * Does the heavy lifting to show the posts in this topic. * + * - Handles any redirects we might need to do. + * - Loads topic info. + * - Loads permissions. + * - Prepares stuff for the templates. * - Sets up anti-spam verification and old topic warnings. * - Gets the list of users viewing the topic. * - Loads events and polls attached to the topic. @@ -119,6 +123,33 @@ class Display implements ActionInterface, Routable */ public function execute(): void { + // What are you gonna display if this is empty?! + if (empty(Topic::$topic_id)) { + ErrorHandler::fatalLang('no_board', false); + } + + $this->checkPrevNextRedirect(); + $this->preventPrefetch(); + + // Load the topic info. + Topic::load(); + + $this->incrementNumViews(); + $this->checkMovedMergedRedirect(); + + $this->setStart(); + $this->setPaginationAndLinks(); + $this->setRobotNoIndex(); + + $this->setModerators(); + $this->setUnapprovedPostsMessage(); + + // Now set all the wonderful, wonderful permissions... like moderation ones... + foreach (Topic::$info->doPermissions() as $perm => $val) { + Utils::$context[$perm] = &Topic::$info->permissions[$perm]; + } + + $this->setupTemplate(); $this->setupVerification(); $this->setOldTopicWarning(); $this->getWhoViewing(); @@ -297,45 +328,6 @@ public static function buildRoute(array $params): array * Internal methods ******************/ - /** - * Constructor. Protected to force instantiation via load(). - * - * - Handles any redirects we might need to do. - * - Loads topic info. - * - Loads permissions. - * - Prepares most of the stuff for the templates. - */ - protected function __construct() - { - // What are you gonna display if this is empty?! - if (empty(Topic::$topic_id)) { - ErrorHandler::fatalLang('no_board', false); - } - - $this->checkPrevNextRedirect(); - $this->preventPrefetch(); - - // Load the topic info. - Topic::load(); - - $this->incrementNumViews(); - $this->checkMovedMergedRedirect(); - - $this->setStart(); - $this->setPaginationAndLinks(); - $this->setRobotNoIndex(); - - $this->setModerators(); - $this->setUnapprovedPostsMessage(); - - // Now set all the wonderful, wonderful permissions... like moderation ones... - foreach (Topic::$info->doPermissions() as $perm => $val) { - Utils::$context[$perm] = &Topic::$info->permissions[$perm]; - } - - $this->setupTemplate(); - } - /** * Redirect to the previous or next topic, if requested in the URL params. */ diff --git a/Sources/Actions/Feed.php b/Sources/Actions/Feed.php index 92e7e5c761..78edf90125 100644 --- a/Sources/Actions/Feed.php +++ b/Sources/Actions/Feed.php @@ -287,7 +287,13 @@ public function __construct(?string $subaction = null, ?int $member = null) // Bail out if feeds are disabled. $this->checkEnabled(); + } + /** + * Fetches the data based on the sub-action, builds the XML, and emits it. + */ + public function execute(): void + { // The feed metadata and query are a bit more complicated... Lang::load('Stats'); @@ -451,13 +457,7 @@ public function __construct(?string $subaction = null, ?int $member = null) foreach ($this->metadata as $key => $value) { $this->metadata[$key] = strip_tags($value); } - } - /** - * Fetches the data based on the sub-action, builds the XML, and emits it. - */ - public function execute(): void - { $this->getData(); $this->xml = self::build($this->format, $this->data, $this->metadata, $this->subaction); $this->emit(); diff --git a/Sources/Actions/Help.php b/Sources/Actions/Help.php index 2491b36010..7117e17e60 100644 --- a/Sources/Actions/Help.php +++ b/Sources/Actions/Help.php @@ -73,6 +73,9 @@ public function isRestrictedGuestAccessAllowed(): bool */ public function execute(): void { + Theme::loadTemplate('Help'); + Lang::load('Manual'); + $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); if (!empty($call)) { @@ -132,10 +135,6 @@ public function index() */ protected function __construct() { - 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']])) { diff --git a/Sources/Actions/MessageIndex.php b/Sources/Actions/MessageIndex.php index 1cf7cc3a75..56175e86bf 100644 --- a/Sources/Actions/MessageIndex.php +++ b/Sources/Actions/MessageIndex.php @@ -142,6 +142,22 @@ class MessageIndex implements ActionInterface, Routable */ public function execute(): void { + if (empty(Board::$info->id)) { + ErrorHandler::fatalLang('no_board', false); + } + + $this->checkRedirect(); + $this->preventPrefetch(); + + $this->setSortMethod(); + $this->setPaginationAndLinks(); + + $this->setModerators(); + $this->setUnapprovedPostsMessage(); + + $this->setupTemplate(); + $this->setRobotNoIndex(); + $this->buildTopicList(); $this->buildChildBoardIndex(); @@ -462,30 +478,6 @@ public static function buildRoute(array $params): array * Internal methods ******************/ - /** - * Prepares to show the message index. - * - * Protected to force instantiation via self::load(). - */ - protected function __construct() - { - if (empty(Board::$info->id)) { - ErrorHandler::fatalLang('no_board', false); - } - - $this->checkRedirect(); - $this->preventPrefetch(); - - $this->setSortMethod(); - $this->setPaginationAndLinks(); - - $this->setModerators(); - $this->setUnapprovedPostsMessage(); - - $this->setupTemplate(); - $this->setRobotNoIndex(); - } - /** * Redirects to the target URL for this board, if applicable. */ diff --git a/Sources/Actions/Moderation/Home.php b/Sources/Actions/Moderation/Home.php index 16efd2870a..0032737b3f 100644 --- a/Sources/Actions/Moderation/Home.php +++ b/Sources/Actions/Moderation/Home.php @@ -84,6 +84,12 @@ class Home implements ActionInterface */ public function execute(): void { + Theme::loadTemplate('ModerationCenter'); + Theme::loadJavaScriptFile('admin.js', ['minimize' => true], 'smf_admin'); + + Utils::$context['page_title'] = Lang::$txt['moderation_center']; + Utils::$context['sub_template'] = 'moderation_center'; + // Normally this will already have been done, but just in case... Main::checkAccessPermissions(); @@ -132,18 +138,6 @@ public function execute(): void * Internal methods ******************/ - /** - * Constructor. Protected to force instantiation via self::load(). - */ - protected function __construct() - { - Theme::loadTemplate('ModerationCenter'); - Theme::loadJavaScriptFile('admin.js', ['minimize' => true], 'smf_admin'); - - Utils::$context['page_title'] = Lang::$txt['moderation_center']; - Utils::$context['sub_template'] = 'moderation_center'; - } - /** * Show an area for the moderator to type into. */ diff --git a/Sources/Actions/Moderation/Logs.php b/Sources/Actions/Moderation/Logs.php index 7d46a6316c..82d920eb4c 100644 --- a/Sources/Actions/Moderation/Logs.php +++ b/Sources/Actions/Moderation/Logs.php @@ -221,6 +221,11 @@ class Logs implements ActionInterface */ public function execute(): void { + Lang::load('Admin+Modlog'); + + // If we're coming from a search, set those variables. + $this->setupSearch(); + $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); if (!empty($call)) { @@ -654,15 +659,10 @@ protected function __construct() $this->can_delete = User::$me->allowedTo('admin_forum'); - Lang::load('Admin+Modlog'); - // Setup the direction stuff... if (!empty($_REQUEST['sort']) && isset(self::$sort_types[$_REQUEST['sort']])) { $this->sort = $_REQUEST['sort']; } - - // If we're coming from a search, set those variables. - $this->setupSearch(); } /** diff --git a/Sources/Actions/Moderation/Main.php b/Sources/Actions/Moderation/Main.php index 80aa7507cc..df1b070740 100644 --- a/Sources/Actions/Moderation/Main.php +++ b/Sources/Actions/Moderation/Main.php @@ -224,6 +224,20 @@ class Main implements ActionInterface, Routable */ public function execute(): void { + // Don't run this twice... and don't conflict with the admin bar. + if (!isset(Utils::$context['admin_area'])) { + self::checkAccessPermissions(); + + // Load the language, and the template. + Lang::load('ModerationCenter'); + Theme::loadTemplate(false, 'admin'); + + Utils::$context['admin_preferences'] = !empty(Theme::$current->options['admin_preferences']) ? Utils::jsonDecode(Theme::$current->options['admin_preferences'], true) : []; + Utils::$context['robot_no_index'] = true; + + $this->setModerationAreas(); + } + $this->createMenu(); if (isset(Menu::$loaded['moderate']->include_data['file'])) { @@ -407,28 +421,6 @@ public static function parseRoute(array $route, array $params = []): array * Internal methods ******************/ - /** - * Constructor. Protected to force instantiation via self::load(). - */ - protected function __construct() - { - // Don't run this twice... and don't conflict with the admin bar. - if (isset(Utils::$context['admin_area'])) { - return; - } - - self::checkAccessPermissions(); - - // Load the language, and the template. - Lang::load('ModerationCenter'); - Theme::loadTemplate(false, 'admin'); - - Utils::$context['admin_preferences'] = !empty(Theme::$current->options['admin_preferences']) ? Utils::jsonDecode(Theme::$current->options['admin_preferences'], true) : []; - Utils::$context['robot_no_index'] = true; - - $this->setModerationAreas(); - } - /** * Sets any dynamic values in $this->moderation_areas. */ diff --git a/Sources/Actions/Moderation/Posts.php b/Sources/Actions/Moderation/Posts.php index 2e02a9d9b8..bb4c024136 100644 --- a/Sources/Actions/Moderation/Posts.php +++ b/Sources/Actions/Moderation/Posts.php @@ -83,6 +83,9 @@ class Posts implements ActionInterface */ public function execute(): void { + Lang::load('ModerationCenter'); + Theme::loadTemplate('ModerationCenter'); + $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); if (!empty($call)) { @@ -860,9 +863,6 @@ public static function list_getNumUnapprovedAttachments(string $approve_query): */ protected function __construct() { - Lang::load('ModerationCenter'); - Theme::loadTemplate('ModerationCenter'); - IntegrationHook::call('integrate_post_moderation', [&self::$subactions]); if (!empty($_REQUEST['sa']) && isset(self::$subactions[$_REQUEST['sa']])) { diff --git a/Sources/Actions/Moderation/ReportedContent.php b/Sources/Actions/Moderation/ReportedContent.php index deb8303113..380cc7dd75 100644 --- a/Sources/Actions/Moderation/ReportedContent.php +++ b/Sources/Actions/Moderation/ReportedContent.php @@ -119,6 +119,34 @@ class ReportedContent implements ActionInterface */ public function execute(): void { + if (!in_array($this->type, self::$types)) { + ErrorHandler::fatalLang('no_access', false); + } + + Utils::$context['report_type'] = $this->type; + + Lang::load('ModerationCenter'); + Theme::loadTemplate('ReportedContent'); + + // Do we need to show a confirmation message? + Utils::$context['report_post_action'] = !empty($_SESSION['rc_confirmation']) ? $_SESSION['rc_confirmation'] : []; + unset($_SESSION['rc_confirmation']); + + // Set up the comforting bits... + Utils::$context['page_title'] = Lang::$txt['mc_reported_' . $this->type]; + + // Put the open and closed options into tabs, because we can... + Menu::$loaded['moderate']->tab_data = [ + 'title' => Lang::$txt['mc_reported_' . $this->type], + 'help' => '', + 'description' => Lang::$txt['mc_reported_' . $this->type . '_desc'], + ]; + + // This comes under the umbrella of moderating posts. + if ($this->type == 'members' || User::$me->mod_cache['bq'] == '0=1') { + User::$me->isAllowedTo('moderate_forum'); + } + $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); if (!empty($call)) { @@ -625,34 +653,6 @@ protected function __construct() // area=reported{type} $this->type = substr($_GET['area'], 8); - if (!in_array($this->type, self::$types)) { - ErrorHandler::fatalLang('no_access', false); - } - - Utils::$context['report_type'] = $this->type; - - Lang::load('ModerationCenter'); - Theme::loadTemplate('ReportedContent'); - - // Do we need to show a confirmation message? - Utils::$context['report_post_action'] = !empty($_SESSION['rc_confirmation']) ? $_SESSION['rc_confirmation'] : []; - unset($_SESSION['rc_confirmation']); - - // Set up the comforting bits... - Utils::$context['page_title'] = Lang::$txt['mc_reported_' . $this->type]; - - // Put the open and closed options into tabs, because we can... - Menu::$loaded['moderate']->tab_data = [ - 'title' => Lang::$txt['mc_reported_' . $this->type], - 'help' => '', - 'description' => Lang::$txt['mc_reported_' . $this->type . '_desc'], - ]; - - // This comes under the umbrella of moderating posts. - if ($this->type == 'members' || User::$me->mod_cache['bq'] == '0=1') { - User::$me->isAllowedTo('moderate_forum'); - } - // Go ahead and add your own sub-actions. IntegrationHook::call('integrate_reported_' . $this->type, [&self::$subactions]); diff --git a/Sources/Actions/Moderation/ShowNotice.php b/Sources/Actions/Moderation/ShowNotice.php index 5d6605f82c..4b7ffdd3df 100644 --- a/Sources/Actions/Moderation/ShowNotice.php +++ b/Sources/Actions/Moderation/ShowNotice.php @@ -41,6 +41,15 @@ class ShowNotice implements ActionInterface */ public function execute(): void { + // Before we get too excited, is the current user allowed to see this? + User::$me->isAllowedTo(['issue_warning', 'view_warning_any']); + + Utils::$context['page_title'] = Lang::$txt['show_notice']; + Utils::$context['sub_template'] = 'show_notice'; + Utils::$context['template_layers'] = []; + + Theme::loadTemplate('ModerationCenter'); + $id_notice = (int) $_GET['nid']; $request = Db::$db->query( @@ -64,25 +73,6 @@ public function execute(): void input_types: Parser::INPUT_BBC | Parser::INPUT_MARKDOWN, ); } - - /****************** - * Internal methods - ******************/ - - /** - * Constructor. Protected to force instantiation via self::load(). - */ - protected function __construct() - { - // Before we get too excited, is the current user allowed to see this? - User::$me->isAllowedTo(['issue_warning', 'view_warning_any']); - - Utils::$context['page_title'] = Lang::$txt['show_notice']; - Utils::$context['sub_template'] = 'show_notice'; - Utils::$context['template_layers'] = []; - - Theme::loadTemplate('ModerationCenter'); - } } ?> \ No newline at end of file diff --git a/Sources/Actions/Moderation/Warnings.php b/Sources/Actions/Moderation/Warnings.php index de5a2ff183..f5896f23af 100644 --- a/Sources/Actions/Moderation/Warnings.php +++ b/Sources/Actions/Moderation/Warnings.php @@ -749,12 +749,6 @@ protected function __construct() } } } - - /************************* - * Internal static methods - *************************/ - - // code... } ?> \ No newline at end of file diff --git a/Sources/Actions/Moderation/WatchedUsers.php b/Sources/Actions/Moderation/WatchedUsers.php index f21039f51c..1ce423cc39 100644 --- a/Sources/Actions/Moderation/WatchedUsers.php +++ b/Sources/Actions/Moderation/WatchedUsers.php @@ -45,6 +45,23 @@ class WatchedUsers implements ActionInterface */ public function execute(): void { + // Some important context! + Utils::$context['page_title'] = Lang::$txt['mc_watched_users_title']; + Utils::$context['view_posts'] = isset($_GET['sa']) && $_GET['sa'] == 'post'; + Utils::$context['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0; + + Theme::loadTemplate('ModerationCenter'); + + // Get some key settings! + Config::$modSettings['warning_watch'] = empty(Config::$modSettings['warning_watch']) ? 1 : Config::$modSettings['warning_watch']; + + // Put some pretty tabs on cause we're gonna be doing hot stuff here... + Menu::$loaded['moderate']->tab_data = [ + 'title' => Lang::$txt['mc_watched_users_title'], + 'help' => '', + 'description' => Lang::$txt['mc_watched_users_desc'], + ]; + // First off - are we deleting? if (!empty($_REQUEST['delete'])) { User::$me->checkSession(!is_array($_REQUEST['delete']) ? 'get' : 'post'); @@ -448,33 +465,6 @@ public static function list_getWatchedUserPosts(int $start, int $items_per_page, return $member_posts; } - - /****************** - * Internal methods - ******************/ - - /** - * Constructor. Protected to force instantiation via self::load(). - */ - protected function __construct() - { - // Some important context! - Utils::$context['page_title'] = Lang::$txt['mc_watched_users_title']; - Utils::$context['view_posts'] = isset($_GET['sa']) && $_GET['sa'] == 'post'; - Utils::$context['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0; - - Theme::loadTemplate('ModerationCenter'); - - // Get some key settings! - Config::$modSettings['warning_watch'] = empty(Config::$modSettings['warning_watch']) ? 1 : Config::$modSettings['warning_watch']; - - // Put some pretty tabs on cause we're gonna be doing hot stuff here... - Menu::$loaded['moderate']->tab_data = [ - 'title' => Lang::$txt['mc_watched_users_title'], - 'help' => '', - 'description' => Lang::$txt['mc_watched_users_desc'], - ]; - } } ?> \ No newline at end of file diff --git a/Sources/Actions/PersonalMessage.php b/Sources/Actions/PersonalMessage.php index 7f92b939a5..9529cc2574 100644 --- a/Sources/Actions/PersonalMessage.php +++ b/Sources/Actions/PersonalMessage.php @@ -291,6 +291,45 @@ public function isAgreementAction(): bool */ public function execute(): void { + Lang::load('PersonalMessage+Drafts'); + Theme::loadTemplate(isset($_REQUEST['xml']) ? 'Xml' : 'PersonalMessage'); + + $this->buildLimitBar(); + + Label::load(); + + // Some stuff for the labels... + $this->current_label_id = isset($_REQUEST['l']) && isset(Label::$loaded[$_REQUEST['l']]) ? (int) $_REQUEST['l'] : -1; + $this->current_label = Label::$loaded[$this->current_label_id]['name']; + + // This is convenient. Do you know how annoying it is to do this every time?! + $this->current_label_redirect = 'action=pm;f=' . $this->folder . (isset($_GET['start']) ? ';start=' . $_GET['start'] : '') . (isset($_REQUEST['l']) ? ';l=' . $_REQUEST['l'] : ''); + + // A previous message was sent successfully? Show a small indication. + if (isset($_GET['done']) && ($_GET['done'] == 'sent')) { + Utils::$context['pm_sent'] = true; + } + + // Some context stuff for the templates. + Utils::$context['display_mode'] = &$this->mode; + Utils::$context['folder'] = &$this->folder; + Utils::$context['currently_using_labels'] = !empty(Label::$loaded); + Utils::$context['current_label_id'] = &$this->current_label_id; + Utils::$context['current_label'] = &$this->current_label; + Utils::$context['can_issue_warning'] = User::$me->allowedTo('issue_warning') && Config::$modSettings['warning_settings'][0] == 1; + Utils::$context['can_moderate_forum'] = User::$me->allowedTo('moderate_forum'); + + // Are PM drafts enabled? + Utils::$context['drafts_type'] = 'pm'; + Utils::$context['drafts_save'] = !empty(Config::$modSettings['drafts_pm_enabled']) && User::$me->allowedTo('pm_draft'); + Utils::$context['drafts_autosave'] = !empty(Utils::$context['drafts_save']) && !empty(Config::$modSettings['drafts_autosave_enabled']) && !empty(Theme::$current->options['drafts_autosave_enabled']); + + // Build the linktree for all the actions... + Utils::$context['linktree'][] = [ + 'url' => Config::$scripturl . '?action=pm', + 'name' => Lang::$txt['personal_messages'], + ]; + // No guests! User::$me->kickIfGuest(); @@ -803,9 +842,6 @@ public static function parseRoute(array $route, array $params = []): array */ protected function __construct() { - Lang::load('PersonalMessage+Drafts'); - Theme::loadTemplate(isset($_REQUEST['xml']) ? 'Xml' : 'PersonalMessage'); - if (!isset($_REQUEST['sa']) && ($_REQUEST['f'] ?? '') === 'drafts') { $_REQUEST['sa'] = 'showpmdrafts'; unset($_REQUEST['f']); @@ -819,44 +855,8 @@ protected function __construct() $this->folder = 'sent'; } - $this->buildLimitBar(); - - Label::load(); - - // Some stuff for the labels... - $this->current_label_id = isset($_REQUEST['l']) && isset(Label::$loaded[$_REQUEST['l']]) ? (int) $_REQUEST['l'] : -1; - $this->current_label = Label::$loaded[$this->current_label_id]['name']; - - // This is convenient. Do you know how annoying it is to do this every time?! - $this->current_label_redirect = 'action=pm;f=' . $this->folder . (isset($_GET['start']) ? ';start=' . $_GET['start'] : '') . (isset($_REQUEST['l']) ? ';l=' . $_REQUEST['l'] : ''); - // Preferences... $this->mode = User::$me->pm_prefs & 3; - - // A previous message was sent successfully? Show a small indication. - if (isset($_GET['done']) && ($_GET['done'] == 'sent')) { - Utils::$context['pm_sent'] = true; - } - - // Some context stuff for the templates. - Utils::$context['display_mode'] = &$this->mode; - Utils::$context['folder'] = &$this->folder; - Utils::$context['currently_using_labels'] = !empty(Label::$loaded); - Utils::$context['current_label_id'] = &$this->current_label_id; - Utils::$context['current_label'] = &$this->current_label; - Utils::$context['can_issue_warning'] = User::$me->allowedTo('issue_warning') && Config::$modSettings['warning_settings'][0] == 1; - Utils::$context['can_moderate_forum'] = User::$me->allowedTo('moderate_forum'); - - // Are PM drafts enabled? - Utils::$context['drafts_type'] = 'pm'; - Utils::$context['drafts_save'] = !empty(Config::$modSettings['drafts_pm_enabled']) && User::$me->allowedTo('pm_draft'); - Utils::$context['drafts_autosave'] = !empty(Utils::$context['drafts_save']) && !empty(Config::$modSettings['drafts_autosave_enabled']) && !empty(Theme::$current->options['drafts_autosave_enabled']); - - // Build the linktree for all the actions... - Utils::$context['linktree'][] = [ - 'url' => Config::$scripturl . '?action=pm', - 'name' => Lang::$txt['personal_messages'], - ]; } /** diff --git a/Sources/Actions/Profile/AlertsPopup.php b/Sources/Actions/Profile/AlertsPopup.php index b9590324eb..d3be9c1898 100644 --- a/Sources/Actions/Profile/AlertsPopup.php +++ b/Sources/Actions/Profile/AlertsPopup.php @@ -39,6 +39,9 @@ class AlertsPopup implements ActionInterface */ public function execute(): void { + // Load the Alerts language file. + Lang::load('Alerts'); + // We do not want to output debug information here. Config::$db_show_debug = false; @@ -57,19 +60,6 @@ public function execute(): void Utils::$context['unread_alerts'] = Alert::fetch(User::$me->id, false, !empty($counter) ? User::$me->alerts - $counter : $limit, 0, !isset($_REQUEST['counter'])); } } - - /****************** - * Internal methods - ******************/ - - /** - * Constructor. Protected to force instantiation via self::load(). - */ - protected function __construct() - { - // Load the Alerts language file. - Lang::load('Alerts'); - } } ?> \ No newline at end of file diff --git a/Sources/Actions/Profile/Export.php b/Sources/Actions/Profile/Export.php index 021dca89a8..c0e027a14d 100644 --- a/Sources/Actions/Profile/Export.php +++ b/Sources/Actions/Profile/Export.php @@ -113,6 +113,8 @@ class Export implements ActionInterface */ public function execute(): void { + $this->init(); + if (empty(Config::$modSettings['export_dir']) || !is_dir(Config::$modSettings['export_dir']) || !Utils::makeWritable(Config::$modSettings['export_dir'])) { self::createDir(); } @@ -441,9 +443,9 @@ function (&$value, $key) { ******************/ /** - * Constructor. Protected to force instantiation via self::load(). + * Sets up some stuff we need. */ - protected function __construct() + protected function init() { if (!isset(Utils::$context['token_check'])) { Utils::$context['token_check'] = 'profile-ex' . Utils::$context['id_member']; diff --git a/Sources/Actions/Profile/Main.php b/Sources/Actions/Profile/Main.php index 6a95afcbc3..9b73f9ae72 100644 --- a/Sources/Actions/Profile/Main.php +++ b/Sources/Actions/Profile/Main.php @@ -572,11 +572,49 @@ public function isAgreementAction(): bool */ public function execute(): void { + // Don't reload this as we may have processed error strings. + if (empty(Profile::$member->save_errors)) { + Lang::load('Profile+Drafts'); + } + + Theme::loadTemplate('Profile'); + + // No profile can be found. + if (!isset(Profile::$member->id) || Profile::$member->id == 0) { + ErrorHandler::fatalLang('no_access', false); + } + + // Group management isn't actually a permission. But we need it to be for this, so we need a phantom permission. + // And we care about what the current user can do, not what the user whose profile it is. + if (User::$me->mod_cache['gq'] != '0=1') { + User::$me->permissions[] = 'approve_group_requests'; + } + + // If paid subscriptions are enabled, make sure we actually have at least one subscription available... + Utils::$context['subs_available'] = false; + + if (!empty(Config::$modSettings['paid_enabled'])) { + $get_active_subs = Db::$db->query( + '', + 'SELECT COUNT(*) + FROM {db_prefix}subscriptions + WHERE active = {int:active}', + [ + 'active' => 1, + ], + ); + list($num_subs) = Db::$db->fetch_row($get_active_subs); + Db::$db->free_result($get_active_subs); + + Utils::$context['subs_available'] = !empty($num_subs); + } + // Is there an updated message to show? if (isset($_GET['updated'])) { Utils::$context['profile_updated'] = Lang::$txt['profile_updated_own']; } + $this->setProfileAreas(); $menu = $this->createMenu(); $this->securityChecks(); @@ -808,47 +846,8 @@ public static function parseRoute(array $route, array $params = []): array */ protected function __construct() { - // Don't reload this as we may have processed error strings. - if (empty(Profile::$member->save_errors)) { - Lang::load('Profile+Drafts'); - } - - Theme::loadTemplate('Profile'); - // Load the data of the member whose profile we are viewing. Profile::load(); - - // No profile can be found. - if (!isset(Profile::$member->id) || Profile::$member->id == 0) { - ErrorHandler::fatalLang('no_access', false); - } - - // Group management isn't actually a permission. But we need it to be for this, so we need a phantom permission. - // And we care about what the current user can do, not what the user whose profile it is. - if (User::$me->mod_cache['gq'] != '0=1') { - User::$me->permissions[] = 'approve_group_requests'; - } - - // If paid subscriptions are enabled, make sure we actually have at least one subscription available... - Utils::$context['subs_available'] = false; - - if (!empty(Config::$modSettings['paid_enabled'])) { - $get_active_subs = Db::$db->query( - '', - 'SELECT COUNT(*) - FROM {db_prefix}subscriptions - WHERE active = {int:active}', - [ - 'active' => 1, - ], - ); - list($num_subs) = Db::$db->fetch_row($get_active_subs); - Db::$db->free_result($get_active_subs); - - Utils::$context['subs_available'] = !empty($num_subs); - } - - $this->setProfileAreas(); } /** diff --git a/Sources/Actions/Profile/Popup.php b/Sources/Actions/Profile/Popup.php index 15598befc4..737b80cdcc 100644 --- a/Sources/Actions/Profile/Popup.php +++ b/Sources/Actions/Profile/Popup.php @@ -109,6 +109,18 @@ class Popup implements ActionInterface */ public function execute(): void { + // Finalize various string values. + array_walk_recursive( + $this->profile_items, + function (&$value, $key) { + if ($key === 'title') { + $value = Lang::$txt[$value] ?? $value; + } + + $value = strtr($value, ['{scripturl}' => Config::$scripturl]); + }, + ); + // We do not want to output debug information here. Config::$db_show_debug = false; @@ -126,28 +138,6 @@ public function execute(): void } } } - - /****************** - * Internal methods - ******************/ - - /** - * Constructor. Protected to force instantiation via self::load(). - */ - protected function __construct() - { - // Finalize various string values. - array_walk_recursive( - $this->profile_items, - function (&$value, $key) { - if ($key === 'title') { - $value = Lang::$txt[$value] ?? $value; - } - - $value = strtr($value, ['{scripturl}' => Config::$scripturl]); - }, - ); - } } ?> \ No newline at end of file diff --git a/Sources/Actions/Profile/ShowPermissions.php b/Sources/Actions/Profile/ShowPermissions.php index 427ca7064b..deb173f7d8 100644 --- a/Sources/Actions/Profile/ShowPermissions.php +++ b/Sources/Actions/Profile/ShowPermissions.php @@ -44,6 +44,10 @@ class ShowPermissions implements ActionInterface */ public function execute(): void { + Lang::load('ManagePermissions'); + Lang::load('Admin'); + Theme::loadTemplate('ManageMembers'); + // Verify if the user has sufficient permissions. User::$me->isAllowedTo('manage_permissions'); @@ -248,10 +252,6 @@ public function execute(): void */ protected function __construct() { - Lang::load('ManagePermissions'); - Lang::load('Admin'); - Theme::loadTemplate('ManageMembers'); - if (!isset(Profile::$member)) { Profile::load(); } diff --git a/Sources/Actions/Profile/Summary.php b/Sources/Actions/Profile/Summary.php index df1477d6ea..65b7ac8e38 100644 --- a/Sources/Actions/Profile/Summary.php +++ b/Sources/Actions/Profile/Summary.php @@ -53,8 +53,14 @@ public function execute(): void 'icon_class' => 'main_icons profile_hd', ]; + // Expand the warning settings. + list(Config::$modSettings['warning_enable'], Config::$modSettings['user_limit']) = explode(',', Config::$modSettings['warning_settings']); + // Set up the stuff and load the user. Utils::$context += [ + 'disabled_fields' => isset(Config::$modSettings['disabled_profile_fields']) ? array_flip(explode(',', Config::$modSettings['disabled_profile_fields'])) : [], + 'signature_enabled' => substr(Config::$modSettings['signature_settings'], 0, 1) == 1, + 'can_see_ip' => User::$me->allowedTo('moderate_forum'), 'page_title' => Lang::getTxt('profile_of_username', Profile::$member->formatted), 'can_send_pm' => User::$me->allowedTo('pm_send'), 'can_have_buddy' => User::$me->allowedTo('profile_extra_own') && !empty(Config::$modSettings['enable_buddylist']), @@ -243,18 +249,6 @@ protected function __construct() if (!isset(Profile::$member)) { Profile::load(); } - - // Are there things we don't show? - Utils::$context['disabled_fields'] = isset(Config::$modSettings['disabled_profile_fields']) ? array_flip(explode(',', Config::$modSettings['disabled_profile_fields'])) : []; - - // Is the signature even enabled on this forum? - Utils::$context['signature_enabled'] = substr(Config::$modSettings['signature_settings'], 0, 1) == 1; - - // Expand the warning settings. - list(Config::$modSettings['warning_enable'], Config::$modSettings['user_limit']) = explode(',', Config::$modSettings['warning_settings']); - - // Can the viewer see this member's IP address? - Utils::$context['can_see_ip'] = User::$me->allowedTo('moderate_forum'); } } diff --git a/Sources/Actions/QuoteFast.php b/Sources/Actions/QuoteFast.php index cbcc8be4b9..95d3be4e1f 100644 --- a/Sources/Actions/QuoteFast.php +++ b/Sources/Actions/QuoteFast.php @@ -57,6 +57,12 @@ public function getOutputType(): OutputTypeInterface */ public function execute(): void { + Lang::load('Post'); + + if (!isset($_REQUEST['xml'])) { + Theme::loadTemplate('Post'); + } + $query_customizations = [ 'selects' => [ 'COALESCE(mem.real_name, m.poster_name) AS poster_name', @@ -168,22 +174,6 @@ public function execute(): void IntegrationHook::call('integrate_quotefast', [$row]); } - - /****************** - * Internal methods - ******************/ - - /** - * Constructor. Protected to force instantiation via self::load(). - */ - protected function __construct() - { - Lang::load('Post'); - - if (!isset($_REQUEST['xml'])) { - Theme::loadTemplate('Post'); - } - } } ?> \ No newline at end of file diff --git a/Sources/Actions/Recent.php b/Sources/Actions/Recent.php index 0cfb3a7630..6672caf428 100644 --- a/Sources/Actions/Recent.php +++ b/Sources/Actions/Recent.php @@ -114,6 +114,34 @@ class Recent implements ActionInterface, Routable */ public function execute(): void { + Utils::$context['posts'] = []; + + Theme::loadTemplate('Recent'); + Utils::$context['page_title'] = Lang::$txt['recent_posts']; + Utils::$context['sub_template'] = 'recent'; + + Utils::$context['is_redirect'] = false; + + // Limit the start value to 90 or less. + Utils::$context['start'] = min(self::PER_PAGE * (self::PAGES - 1), (int) ($_REQUEST['start'] ?? 0)); + // Also make it an even multiple of our posts per page value. + Utils::$context['start'] -= Utils::$context['start'] % self::PER_PAGE; + + // Convert $_REQUEST['boards'] to an array of integers. + if (!empty($_REQUEST['boards'])) { + $_REQUEST['boards'] = array_map('intval', explode(',', $_REQUEST['boards'])); + } + + // Board requests takes precedence over category requests. + if (!empty($_REQUEST['boards']) || !empty(Board::$info->id)) { + unset($_REQUEST['c']); + } + + // Convert $_REQUEST['c'] to an array of integers. + if (!empty($_REQUEST['c'])) { + $_REQUEST['c'] = array_map('intval', explode(',', $_REQUEST['c'])); + } + $this->getBoards(); $this->getCatName(); @@ -210,34 +238,6 @@ public static function getLastPost(): array protected function __construct() { $this->action_url = Config::$scripturl . '?action=recent'; - - Utils::$context['posts'] = []; - - Theme::loadTemplate('Recent'); - Utils::$context['page_title'] = Lang::$txt['recent_posts']; - Utils::$context['sub_template'] = 'recent'; - - Utils::$context['is_redirect'] = false; - - // Limit the start value to 90 or less. - Utils::$context['start'] = min(self::PER_PAGE * (self::PAGES - 1), (int) ($_REQUEST['start'] ?? 0)); - // Also make it an even multiple of our posts per page value. - Utils::$context['start'] -= Utils::$context['start'] % self::PER_PAGE; - - // Convert $_REQUEST['boards'] to an array of integers. - if (!empty($_REQUEST['boards'])) { - $_REQUEST['boards'] = array_map('intval', explode(',', $_REQUEST['boards'])); - } - - // Board requests takes precedence over category requests. - if (!empty($_REQUEST['boards']) || !empty(Board::$info->id)) { - unset($_REQUEST['c']); - } - - // Convert $_REQUEST['c'] to an array of integers. - if (!empty($_REQUEST['c'])) { - $_REQUEST['c'] = array_map('intval', explode(',', $_REQUEST['c'])); - } } /** diff --git a/Sources/Actions/Reminder.php b/Sources/Actions/Reminder.php index 1b932917ba..9d8cdacc44 100644 --- a/Sources/Actions/Reminder.php +++ b/Sources/Actions/Reminder.php @@ -92,6 +92,12 @@ public function isRestrictedGuestAccessAllowed(): bool */ public function execute(): void { + Lang::load('Profile'); + Theme::loadTemplate('Reminder'); + + Utils::$context['page_title'] = Lang::$txt['authentication_reminder']; + Utils::$context['robot_no_index'] = true; + $call = method_exists($this, self::$subactions[$this->subaction]) ? [$this, self::$subactions[$this->subaction]] : Utils::getCallable(self::$subactions[$this->subaction]); if (!empty($call)) { @@ -403,12 +409,6 @@ public function secretAnswer2(): void */ protected function __construct() { - 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..688e648879 100644 --- a/Sources/Actions/ReportToMod.php +++ b/Sources/Actions/ReportToMod.php @@ -112,6 +112,9 @@ class ReportToMod implements ActionInterface, Routable */ public function execute(): void { + Utils::$context['robot_no_index'] = true; + Utils::$context['comment_body'] = ''; + // No guests! User::$me->kickIfGuest(); @@ -306,9 +309,6 @@ public function submit(): void */ protected function __construct() { - 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)); } diff --git a/Sources/Actions/Search2.php b/Sources/Actions/Search2.php index e5c3fd5a92..438a259d2f 100644 --- a/Sources/Actions/Search2.php +++ b/Sources/Actions/Search2.php @@ -84,6 +84,18 @@ public function execute(): void // Are you allowed? User::$me->isAllowedTo('search_posts'); + // Maximum length of the string. + Utils::$context['search_string_limit'] = SearchApi::MAX_LENGTH; + + // Number of pages hard maximum - normally not set at all. + Config::$modSettings['search_max_results'] = empty(Config::$modSettings['search_max_results']) ? 200 * Config::$modSettings['search_results_per_page'] : (int) Config::$modSettings['search_max_results']; + + $_REQUEST['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] - ((int) $_REQUEST['start'] % Config::$modSettings['search_results_per_page']) : 0; + + Lang::load('Search'); + + Utils::$context['robot_no_index'] = true; + // Load up the search API we are going to use. SearchApi::load(); SearchApi::$loadedApi->initializeSearch(); @@ -238,24 +250,6 @@ public static function parseRoute(array $route, array $params = []): array * Internal methods ******************/ - /** - * Constructor. Protected to force instantiation via self::load(). - */ - protected function __construct() - { - // Maximum length of the string. - Utils::$context['search_string_limit'] = SearchApi::MAX_LENGTH; - - // Number of pages hard maximum - normally not set at all. - Config::$modSettings['search_max_results'] = empty(Config::$modSettings['search_max_results']) ? 200 * Config::$modSettings['search_results_per_page'] : (int) Config::$modSettings['search_max_results']; - - $_REQUEST['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] - ((int) $_REQUEST['start'] % Config::$modSettings['search_results_per_page']) : 0; - - Lang::load('Search'); - - Utils::$context['robot_no_index'] = true; - } - /** * If coming from the quick search box and trying to search on members, * redirect to the right place for that. diff --git a/Sources/Actions/Unread.php b/Sources/Actions/Unread.php index 4d07942319..c3e2dc581b 100644 --- a/Sources/Actions/Unread.php +++ b/Sources/Actions/Unread.php @@ -215,6 +215,8 @@ class Unread implements ActionInterface, Routable */ public function execute(): void { + $this->init(); + $this->getBoards(); $this->setSortMethod(); $this->getCatName(); @@ -244,9 +246,9 @@ public function execute(): void ******************/ /** - * Constructor. Protected to force instantiation via self::load(). + * Does some initial stuff. */ - protected function __construct() + protected function init() { // Guests can't have unread things, we don't know anything about them. User::$me->kickIfGuest(); diff --git a/Sources/Actions/UnreadReplies.php b/Sources/Actions/UnreadReplies.php index ed6c227a24..c17f455b65 100644 --- a/Sources/Actions/UnreadReplies.php +++ b/Sources/Actions/UnreadReplies.php @@ -59,14 +59,14 @@ class UnreadReplies extends Unread ******************/ /** - * Constructor. Protected to force instantiation via self::load(). + * Does some initial stuff. */ - protected function __construct() + protected function init() { // 'all' is never applicable for this action. unset($_GET['all']); - parent::__construct(); + parent::init(); Utils::$context['page_title'] = Lang::$txt['unread_replies']; $this->linktree_name = Lang::$txt['unread_replies']; diff --git a/Sources/Actions/Who.php b/Sources/Actions/Who.php index f10164b2cb..ba9547100c 100644 --- a/Sources/Actions/Who.php +++ b/Sources/Actions/Who.php @@ -109,6 +109,10 @@ class Who implements ActionInterface, Routable */ public function execute(): void { + // Load the 'Who' template. + Theme::loadTemplate('Who'); + Lang::load('Who'); + // Permissions, permissions, permissions. User::$me->isAllowedTo('who_view'); @@ -636,20 +640,6 @@ public static function determineActions(mixed $urls, string|bool $preferred_pref return $data; } - - /****************** - * Internal methods - ******************/ - - /** - * Constructor. Protected to force instantiation via self::load(). - */ - protected function __construct() - { - // Load the 'Who' template. - Theme::loadTemplate('Who'); - Lang::load('Who'); - } } ?> \ No newline at end of file diff --git a/Sources/Actions/XmlHttp.php b/Sources/Actions/XmlHttp.php index 6798b988e1..40fe104ac4 100644 --- a/Sources/Actions/XmlHttp.php +++ b/Sources/Actions/XmlHttp.php @@ -101,6 +101,8 @@ public function isAgreementAction(): bool */ public function execute(): void { + Theme::loadTemplate('Xml'); + if (!isset($this->subaction)) { ErrorHandler::fatalLang('no_access', false); } @@ -419,8 +421,6 @@ public function warning_preview(): void */ protected function __construct() { - Theme::loadTemplate('Xml'); - // Easy adding of sub actions. IntegrationHook::call('integrate_XMLhttpMain_subActions', [&self::$subactions]); From aaf26581af17c06ab032de172d71cfc2a8de693c Mon Sep 17 00:00:00 2001 From: Jon Stovell Date: Sun, 9 Feb 2025 02:14:40 -0700 Subject: [PATCH 09/13] Fixes logic order for loading actions vs initializing the theme Signed-off-by: Jon Stovell --- Sources/Forum.php | 93 ++++++++++--------- Sources/Theme.php | 221 +++++++++++++++++++++++----------------------- 2 files changed, 164 insertions(+), 150 deletions(-) diff --git a/Sources/Forum.php b/Sources/Forum.php index ca2bbb60db..7f1bbe1e4b 100644 --- a/Sources/Forum.php +++ b/Sources/Forum.php @@ -446,6 +446,7 @@ public function __construct() public function execute(): void { $this->init(); + $requested_action = $_REQUEST['action'] ?? null; $current_action = $this->findAction($requested_action); @@ -457,33 +458,7 @@ public function execute(): void IntegrationHook::call('integrate_init_action', [$action]); } - $this->main(); - - // Is the forum in maintenance mode? (doesn't apply to administrators.) - if ( - !empty(Config::$maintenance) - && !User::$me->allowedTo('admin_forum') - && self::$current_action?->canShowInMaintenanceMode() === false - ) { - // Don't even try it, sonny. - self::inMaintenance(); - } - - // If guest access is off, a guest can only do one of a few actions. - if ( - empty(Config::$modSettings['allow_guestAccess']) - && User::$me->is_guest - && ( - self::$current_action?->isRestrictedGuestAccessAllowed() === false - || ( - !isset($_REQUEST['action']) - || !in_array($_REQUEST['action'] ?? '', self::$guest_access_actions) - && self::$current_action?->isRestrictedGuestAccessAllowed() === null - ) - ) - ) { - User::$me->kickIfGuest(null, false); - } + $this->preflight(); if (isset($action)) { $action->execute(); @@ -599,13 +574,13 @@ protected function init(): void // Load the current user's permissions. User::$me->loadPermissions(); - // Attachments don't require the entire theme to be loaded. - if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'dlattach' && empty(Config::$maintenance)) { - BrowserDetector::call(); - } + // Analyze the user agent string. + BrowserDetector::call(); + // Load the current theme. (note that ?theme=1 will also work, may be used for guest theming.) - else { - Theme::load(); + // Attachments don't require the entire theme to be loaded. + if (($_REQUEST['action'] ?? '') !== 'dlattach' || empty(Config::$maintenance)) { + Theme::load(0, false); } // Check if the user should be disallowed access. @@ -613,23 +588,36 @@ protected function init(): void } /** - * The main forum loader. - * - * This method handles the main forum logic, such as checking permissions, - * logging user activity, and tracking forum statistics. + * Runs various checks that are required before calling the action. */ - protected function main(): void + protected function preflight(): void { + Theme::$current->initialize(); + // If the user needs to accept the agreement or privacy policy, redirect now. $this->requireAgreement(); // If we are in a topic and don't have permission to approve it then duck out now. - if (!empty(Topic::$topic_id) && empty(Board::$info->cur_topic_approved) && !User::$me->allowedTo('approve_posts') && (User::$me->id != Board::$info->cur_topic_starter || User::$me->is_guest)) { + if ( + !empty(Topic::$topic_id) + && empty(Board::$info->cur_topic_approved) + && !User::$me->allowedTo('approve_posts') + && ( + User::$me->id != Board::$info->cur_topic_starter + || User::$me->is_guest + ) + ) { ErrorHandler::fatalLang('not_a_topic', false); } // Don't log if this is an attachment, avatar, toggle of editor buttons, theme option, XML feed, popup, etc. - if (self::$current_action?->canBeLogged() === true || (self::$current_action === null && !QueryString::isFilteredRequest(self::$unlogged_actions, 'action'))) { + if ( + self::$current_action?->canBeLogged() === true + || ( + self::$current_action === null + && !QueryString::isFilteredRequest(self::$unlogged_actions, 'action') + ) + ) { // Log this user as online. User::$me->logOnline(); @@ -641,6 +629,31 @@ protected function main(): void // Make sure that our scheduled tasks have been running as intended. Config::checkCron(); + + // Is the forum in maintenance mode? (doesn't apply to administrators.) + if ( + !empty(Config::$maintenance) + && !User::$me->allowedTo('admin_forum') + && self::$current_action?->canShowInMaintenanceMode() === false + ) { + // Don't even try it, sonny. + self::inMaintenance(); + } + + // If guest access is off, a guest can only do one of a few actions. + if ( + empty(Config::$modSettings['allow_guestAccess']) + && User::$me->is_guest + && ( + self::$current_action?->isRestrictedGuestAccessAllowed() !== true + && ( + !isset($_REQUEST['action']) + || !in_array($_REQUEST['action'], self::$guest_access_actions) + ) + ) + ) { + User::$me->kickIfGuest(null, false); + } } /** diff --git a/Sources/Theme.php b/Sources/Theme.php index 2a9dfa37ff..c8d13bc4ad 100644 --- a/Sources/Theme.php +++ b/Sources/Theme.php @@ -140,6 +140,117 @@ class Theme 'name', ]; + /**************** + * Public methods + ****************/ + + /** + * Sets a bunch of Utils::$context variables, loads templates and language + * files, and does other stuff that is required to use the theme for output. + */ + public function initialize(): void + { + $this->fixUrl(); + + // Create User::$me if it is missing (e.g., an error very early in the login process). + if (!isset(User::$me)) { + User::load(); + } + + $this->fixSmileySet(); + + // Some basic information... + if (!isset(Utils::$context['html_headers'])) { + Utils::$context['html_headers'] = ''; + } + + if (!isset(Utils::$context['javascript_files'])) { + Utils::$context['javascript_files'] = []; + } + + if (!isset(Utils::$context['css_files'])) { + Utils::$context['css_files'] = []; + } + + if (!isset(Utils::$context['css_header'])) { + Utils::$context['css_header'] = []; + } + + if (!isset(Utils::$context['javascript_inline'])) { + Utils::$context['javascript_inline'] = ['standard' => [], 'defer' => []]; + } + + if (!isset(Utils::$context['javascript_vars'])) { + Utils::$context['javascript_vars'] = []; + } + + Utils::$context['login_url'] = Config::$scripturl . '?action=login2'; + Utils::$context['menu_separator'] = !empty($this->settings['use_image_buttons']) ? ' ' : ' | '; + Utils::$context['session_var'] = $_SESSION['session_var']; + Utils::$context['session_id'] = $_SESSION['session_value']; + Utils::$context['forum_name'] = Config::$mbname; + Utils::$context['forum_name_html_safe'] = Utils::htmlspecialchars(Utils::$context['forum_name']); + Utils::$context['header_logo_url_html_safe'] = empty($this->settings['header_logo_url']) ? '' : Utils::htmlspecialchars($this->settings['header_logo_url']); + Utils::$context['current_action'] = isset($_REQUEST['action']) ? Utils::htmlspecialchars($_REQUEST['action']) : null; + Utils::$context['current_subaction'] = $_REQUEST['sa'] ?? null; + Utils::$context['can_register'] = empty(Config::$modSettings['registration_method']) || Config::$modSettings['registration_method'] != 3; + + if (isset(Config::$modSettings['load_average'])) { + Utils::$context['load_average'] = Config::$modSettings['load_average']; + } + + $this->loadTemplatesAndLangFiles(); + + // Allow overriding the forum's default time/number formats. + if (empty(User::$profiles[User::$me->id]['time_format']) && !empty(Lang::$txt['time_format'])) { + User::$me->time_format = Lang::$txt['time_format']; + } + + // Set the character set from the template. + Utils::$context['character_set'] = empty(Config::$modSettings['global_character_set']) ? Lang::$txt['lang_character_set'] : Config::$modSettings['global_character_set']; + Utils::$context['right_to_left'] = !empty(Lang::$txt['lang_rtl']); + + // Guests may still need a name. + if (User::$me->is_guest && empty(User::$me->name)) { + User::$me->name = Lang::$txt['guest_title']; + } + + // Any theme-related strings that need to be loaded? + Lang::load('ThemeStrings', '', false); + + // Make a special URL for the language. + $this->settings['lang_images_url'] = $this->settings['images_url'] . '/' . (!empty(Lang::$txt['image_lang']) ? Lang::$txt['image_lang'] : User::$me->language); + + $this->loadCss(); + + $this->loadVariant(); + + Utils::$context['tabindex'] = 1; + + $this->loadJavaScript(); + + $this->setupLinktree(); + + // Any files to include at this point? + if (!empty(Config::$modSettings['integrate_theme_include'])) { + $theme_includes = explode(',', Config::$modSettings['integrate_theme_include']); + + foreach ($theme_includes as $include) { + $include = strtr(trim($include), ['$boarddir' => Config::$boarddir, '$sourcedir' => Config::$sourcedir, '$themedir' => $this->settings['theme_dir']]); + + if (file_exists($include)) { + require_once $include; + } + } + } + + // Call load theme integration functions. + IntegrationHook::call('integrate_load_theme'); + + // We are ready to go. + Utils::$context['theme_loaded'] = true; + } + /*********************** * Public static methods ***********************/ @@ -1881,116 +1992,6 @@ protected function __construct(int $id = 0, int $member = -1) } } - /** - * Sets a bunch of Utils::$context variables, loads templates and language - * files, and does other stuff that is required to use the theme for output. - */ - protected function initialize(): void - { - $this->fixUrl(); - - // Create User::$me if it is missing (e.g., an error very early in the login process). - if (!isset(User::$me)) { - User::load(); - } - - $this->fixSmileySet(); - - // Some basic information... - if (!isset(Utils::$context['html_headers'])) { - Utils::$context['html_headers'] = ''; - } - - if (!isset(Utils::$context['javascript_files'])) { - Utils::$context['javascript_files'] = []; - } - - if (!isset(Utils::$context['css_files'])) { - Utils::$context['css_files'] = []; - } - - if (!isset(Utils::$context['css_header'])) { - Utils::$context['css_header'] = []; - } - - if (!isset(Utils::$context['javascript_inline'])) { - Utils::$context['javascript_inline'] = ['standard' => [], 'defer' => []]; - } - - if (!isset(Utils::$context['javascript_vars'])) { - Utils::$context['javascript_vars'] = []; - } - - Utils::$context['login_url'] = Config::$scripturl . '?action=login2'; - Utils::$context['menu_separator'] = !empty($this->settings['use_image_buttons']) ? ' ' : ' | '; - Utils::$context['session_var'] = $_SESSION['session_var']; - Utils::$context['session_id'] = $_SESSION['session_value']; - Utils::$context['forum_name'] = Config::$mbname; - Utils::$context['forum_name_html_safe'] = Utils::htmlspecialchars(Utils::$context['forum_name']); - Utils::$context['header_logo_url_html_safe'] = empty($this->settings['header_logo_url']) ? '' : Utils::htmlspecialchars($this->settings['header_logo_url']); - Utils::$context['current_action'] = isset($_REQUEST['action']) ? Utils::htmlspecialchars($_REQUEST['action']) : null; - Utils::$context['current_subaction'] = $_REQUEST['sa'] ?? null; - Utils::$context['can_register'] = empty(Config::$modSettings['registration_method']) || Config::$modSettings['registration_method'] != 3; - - if (isset(Config::$modSettings['load_average'])) { - Utils::$context['load_average'] = Config::$modSettings['load_average']; - } - - // Detect the browser. This is separated out because it's also used in attachment downloads - BrowserDetector::call(); - - $this->loadTemplatesAndLangFiles(); - - // Allow overriding the forum's default time/number formats. - if (empty(User::$profiles[User::$me->id]['time_format']) && !empty(Lang::$txt['time_format'])) { - User::$me->time_format = Lang::$txt['time_format']; - } - - // Set the character set from the template. - Utils::$context['character_set'] = empty(Config::$modSettings['global_character_set']) ? Lang::$txt['lang_character_set'] : Config::$modSettings['global_character_set']; - Utils::$context['right_to_left'] = !empty(Lang::$txt['lang_rtl']); - - // Guests may still need a name. - if (User::$me->is_guest && empty(User::$me->name)) { - User::$me->name = Lang::$txt['guest_title']; - } - - // Any theme-related strings that need to be loaded? - Lang::load('ThemeStrings', '', false); - - // Make a special URL for the language. - $this->settings['lang_images_url'] = $this->settings['images_url'] . '/' . (!empty(Lang::$txt['image_lang']) ? Lang::$txt['image_lang'] : User::$me->language); - - $this->loadCss(); - - $this->loadVariant(); - - Utils::$context['tabindex'] = 1; - - $this->loadJavaScript(); - - $this->setupLinktree(); - - // Any files to include at this point? - if (!empty(Config::$modSettings['integrate_theme_include'])) { - $theme_includes = explode(',', Config::$modSettings['integrate_theme_include']); - - foreach ($theme_includes as $include) { - $include = strtr(trim($include), ['$boarddir' => Config::$boarddir, '$sourcedir' => Config::$sourcedir, '$themedir' => $this->settings['theme_dir']]); - - if (file_exists($include)) { - require_once $include; - } - } - } - - // Call load theme integration functions. - IntegrationHook::call('integrate_load_theme'); - - // We are ready to go. - Utils::$context['theme_loaded'] = true; - } - /** * If the user got here using an unexpected URL, fix it. */ From 4281a8e79408ebcffa4a82cf1e766713eda5289d Mon Sep 17 00:00:00 2001 From: Jon Stovell Date: Sun, 9 Feb 2025 02:15:43 -0700 Subject: [PATCH 10/13] Checks correct var in Post2::getOutputType() & Post2::isSimpleAction() Signed-off-by: Jon Stovell --- Sources/Actions/Post2.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Actions/Post2.php b/Sources/Actions/Post2.php index 2a1750235f..a115ff1d47 100644 --- a/Sources/Actions/Post2.php +++ b/Sources/Actions/Post2.php @@ -107,12 +107,12 @@ class Post2 extends Post public function isSimpleAction(): bool { - return isset($_REQUEST['preview']); + return isset($_REQUEST['xml']); } public function getOutputType(): OutputTypeInterface { - return isset($_REQUEST['preview']) ? new OutputTypes\Xml() : new OutputTypes\Html(); + return isset($_REQUEST['xml']) ? new OutputTypes\Xml() : new OutputTypes\Html(); } /** From 61a83f88124ca603aa8690cd5c7ee65660d93a4c Mon Sep 17 00:00:00 2001 From: Jon Stovell Date: Mon, 10 Feb 2025 12:43:34 -0700 Subject: [PATCH 11/13] Checks correct var in PersonalMessage::getOutputType() & PersonalMessage::isSimpleAction() Signed-off-by: Jon Stovell --- Sources/Actions/PersonalMessage.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Actions/PersonalMessage.php b/Sources/Actions/PersonalMessage.php index 9529cc2574..703ccef8e4 100644 --- a/Sources/Actions/PersonalMessage.php +++ b/Sources/Actions/PersonalMessage.php @@ -273,12 +273,12 @@ public function canBeLogged(): bool public function isSimpleAction(): bool { - return isset($_GET['sa']) && $_GET['sa'] == 'popup' || isset($_REQUEST['preview']); + return isset($_GET['sa']) && $_GET['sa'] == 'popup' || isset($_REQUEST['xml']); } public function getOutputType(): OutputTypeInterface { - return isset($_REQUEST['preview']) ? new OutputTypes\Xml() : new OutputTypes\Html(); + return isset($_REQUEST['xml']) ? new OutputTypes\Xml() : new OutputTypes\Html(); } public function isAgreementAction(): bool From 53e3c200b6af47db79f68c5c7ed4b0f77b2b7dce Mon Sep 17 00:00:00 2001 From: Jon Stovell Date: Mon, 10 Feb 2025 14:04:25 -0700 Subject: [PATCH 12/13] Newsletter previews do not use XML output Signed-off-by: Jon Stovell --- Sources/Actions/Admin/ACP.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Sources/Actions/Admin/ACP.php b/Sources/Actions/Admin/ACP.php index 0e855a8b58..ab5f3f8f7d 100644 --- a/Sources/Actions/Admin/ACP.php +++ b/Sources/Actions/Admin/ACP.php @@ -28,8 +28,6 @@ use SMF\Lang; use SMF\Mail; use SMF\Menu; -use SMF\OutputTypeInterface; -use SMF\OutputTypes; use SMF\Parser; use SMF\Routable; use SMF\SecurityToken; @@ -722,16 +720,6 @@ class ACP implements ActionInterface, Routable * Public methods ****************/ - public function isSimpleAction(): bool - { - return isset($_REQUEST['preview']); - } - - public function getOutputType(): OutputTypeInterface - { - return isset($_REQUEST['preview']) ? new OutputTypes\Xml() : new OutputTypes\Html(); - } - /** * The main admin handling function. * From fb8e4d2c960966e8035f922183cd8c28291cee6c Mon Sep 17 00:00:00 2001 From: Jon Stovell Date: Mon, 10 Feb 2025 14:37:16 -0700 Subject: [PATCH 13/13] Correctly handles forum_alias_urls values without schemes Signed-off-by: Jon Stovell --- Sources/QueryString.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Sources/QueryString.php b/Sources/QueryString.php index a9683a4bb0..3ca0f08ce4 100644 --- a/Sources/QueryString.php +++ b/Sources/QueryString.php @@ -873,7 +873,13 @@ protected static function fixUrl(): void $aliases = explode(',', Config::$modSettings['forum_alias_urls']); foreach ($aliases as $alias) { - $alias = Sapi::httpsOn() ? strtr($alias, 'http://', 'https://') : strtr($alias, 'https://', 'http://'); + $alias = trim($alias); + + if (!preg_match('~^[A-Za-z][0-9A-Za-z+\-.]*://~', $alias)) { + $alias = (Sapi::httpsOn() ? 'https://' : 'http://') . ltrim($alias, ':/'); + } + + $alias = Sapi::httpsOn() ? strtr($alias, ['http://' => 'https://']) : strtr($alias, ['https://' => 'http://']); if (str_starts_with($_SERVER['REQUEST_URL'], $alias)) { $new_url = $alias;