From abe9c982f9145bb65c3cf22d2aa4e6e65d3711dd Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Thu, 16 Mar 2023 16:06:57 +0100 Subject: [PATCH] Release 5.3.27 --- com.woltlab.wcf/package.xml | 66 +-- .../install/files/lib/system/WCF.class.php | 392 +++++++++--------- 2 files changed, 199 insertions(+), 259 deletions(-) diff --git a/com.woltlab.wcf/package.xml b/com.woltlab.wcf/package.xml index 54236a59b5..7907b38c07 100644 --- a/com.woltlab.wcf/package.xml +++ b/com.woltlab.wcf/package.xml @@ -5,8 +5,8 @@ Free CMS and web-framework, designed for awesome websites and communities. Freies CMS und Web-Framework, das eindrucksvolle Websites und Communities ermöglicht. 1 - 5.3.26 - 2023-01-19 + 5.3.27 + 2023-03-16 @@ -50,67 +50,7 @@ acp/post_install.php - - - files_pre.tar - - - acp/update_com.woltlab.wcf_5.3_preUpdate.php - - - acp/update_com.woltlab.wcf_5.3.php - - - - - - - - - - - - - - - acp/update_com.woltlab.wcf_5.3_emailConfirmation.php - - - acp/update_com.woltlab.wcf_5.3_sitemaps.php - - - - - - - - - - acp/update_com.woltlab.wcf_5.3_invalidateMailForm.php - - acp/update_com.woltlab.wcf_5.3_fixActivationMethod.php - - acp/update_com.woltlab.wcf_5.3_orphanedComments.php - - acp/update_com.woltlab.wcf_5.3_style.php - - defaultStyle.tar - - - update_5.3.sql - - acp/update_com.woltlab.wcf_5.3_packageServer.php - - - - acptemplates_update.tar + files_update.tar - templates_update.tar - - diff --git a/wcfsetup/install/files/lib/system/WCF.class.php b/wcfsetup/install/files/lib/system/WCF.class.php index 7f1db7bea8..441bdf5304 100644 --- a/wcfsetup/install/files/lib/system/WCF.class.php +++ b/wcfsetup/install/files/lib/system/WCF.class.php @@ -51,7 +51,7 @@ if (!@ini_get('date.timezone')) { } // define current woltlab suite version -define('WCF_VERSION', '5.3.26'); +define('WCF_VERSION', '5.3.27'); // define current API version // @deprecated 5.2 @@ -69,7 +69,7 @@ if (!defined('NO_IMPORTS')) { /** * WCF is the central class for the WoltLab Suite Core. * It holds the database connection, access to template and language engine. - * + * * @author Marcel Werk * @copyright 2001-2019 WoltLab GmbH * @license GNU Lesser General Public License @@ -88,89 +88,89 @@ class WCF { * @deprecated 5.2 */ private static $supportedLegacyApiVersions = [2017, 2018]; - + /** * list of currently loaded applications * @var Application[] */ protected static $applications = []; - + /** * list of currently loaded application objects * @var IApplication[] */ protected static $applicationObjects = []; - + /** * list of autoload directories * @var array */ protected static $autoloadDirectories = []; - + /** * list of unique instances of each core object * @var SingletonFactory[] */ protected static $coreObject = []; - + /** * list of cached core objects * @var string[] */ protected static $coreObjectCache = []; - + /** * database object * @var MySQLDatabase */ protected static $dbObj; - + /** * language object * @var \wcf\data\language\Language */ protected static $languageObj; - + /** * overrides disabled debug mode * @var boolean */ protected static $overrideDebugMode = false; - + /** * session object * @var SessionHandler */ protected static $sessionObj; - + /** * template object * @var TemplateEngine */ protected static $tplObj; - + /** * true if Zend Opcache is loaded and enabled * @var boolean */ protected static $zendOpcacheEnabled; - + /** * force logout during destructor call * @var boolean */ protected static $forceLogout = false; - + /** * Calls all init functions of the WCF class. */ public function __construct() { // add autoload directory self::$autoloadDirectories['wcf'] = WCF_DIR . 'lib/'; - + // define tmp directory if (!defined('TMP_DIR')) define('TMP_DIR', FileUtil::getTempFolder()); - + // start initialization $this->initDB(); $this->loadOptions(); @@ -181,13 +181,13 @@ class WCF { $this->initCoreObjects(); $this->initApplications(); $this->initBlacklist(); - + EventHandler::getInstance()->fireAction($this, 'initialized'); } - + /** * Flushes the output, closes the session, performs background tasks and more. - * + * * You *must* not create output in here under normal circumstances, as it might get eaten * when gzip is enabled. */ @@ -195,17 +195,17 @@ class WCF { try { // database has to be initialized if (!is_object(self::$dbObj)) return; - + $debug = self::debugModeIsEnabled(true); if (!$debug) { // flush output if (ob_get_level()) ob_end_flush(); flush(); - + // close connection if using FPM if (function_exists('fastcgi_finish_request')) fastcgi_finish_request(); } - + // update session if (is_object(self::getSession())) { if (self::$forceLogout) { @@ -216,7 +216,7 @@ class WCF { self::getSession()->update(); } } - + // execute shutdown actions of storage handlers RegistryHandler::getInstance()->shutdown(); UserStorageHandler::getInstance()->shutdown(); @@ -225,55 +225,55 @@ class WCF { die("
WCF::destruct() Unhandled exception: ".$exception->getMessage()."\n\n".$exception->getTraceAsString());
 		}
 	}
-	
+
 	/**
 	 * Returns the database object.
-	 * 
+	 *
 	 * @return	\wcf\system\database\Database
 	 */
 	public static final function getDB() {
 		return self::$dbObj;
 	}
-	
+
 	/**
 	 * Returns the session object.
-	 * 
+	 *
 	 * @return	SessionHandler
 	 */
 	public static final function getSession() {
 		return self::$sessionObj;
 	}
-	
+
 	/**
 	 * Returns the user object.
-	 * 
+	 *
 	 * @return	\wcf\data\user\User
 	 */
 	public static final function getUser() {
 		return self::getSession()->getUser();
 	}
-	
+
 	/**
 	 * Returns the language object.
-	 * 
+	 *
 	 * @return	\wcf\data\language\Language
 	 */
 	public static final function getLanguage() {
 		return self::$languageObj;
 	}
-	
+
 	/**
 	 * Returns the template object.
-	 * 
+	 *
 	 * @return	TemplateEngine
 	 */
 	public static final function getTPL() {
 		return self::$tplObj;
 	}
-	
+
 	/**
 	 * Calls the show method on the given exception.
-	 * 
+	 *
 	 * @param	\Throwable	$e
 	 */
 	public static final function handleException($e) {
@@ -282,14 +282,14 @@ class WCF {
 			$e->show();
 			exit;
 		}
-		
+
 		if (ob_get_level()) {
 			// discard any output generated before the exception occurred, prevents exception
 			// being hidden inside HTML elements and therefore not visible in browser output
-			// 
+			//
 			// ob_get_level() can return values > 1, if the PHP setting `output_buffering` is on
 			while (ob_get_level()) ob_end_clean();
-			
+
 			// Some webservers are broken and will apply gzip encoding at all cost, but they fail
 			// to set a proper `Content-Encoding` HTTP header and mess things up even more.
 			// Especially the `identity` value appears to be unrecognized by some of them, hence
@@ -303,22 +303,22 @@ class WCF {
 					else {
 						@header('Content-Encoding: gzip');
 					}
-					
+
 					ob_start(function($output) {
 						$size = strlen($output);
 						$crc = crc32($output);
-						
+
 						$newOutput = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff";
 						$newOutput .= substr(gzcompress($output, 1), 2, -4);
 						$newOutput .= pack('V', $crc);
 						$newOutput .= pack('V', $size);
-						
+
 						return $newOutput;
 					});
 				}
 			}
 		}
-		
+
 		@header('HTTP/1.1 503 Service Unavailable');
 		try {
 			\wcf\functions\exception\printThrowable($e);
@@ -332,10 +332,10 @@ class WCF {
 			exit;
 		}
 	}
-	
+
 	/**
 	 * Turns PHP errors into an ErrorException.
-	 * 
+	 *
 	 * @param	integer		$severity
 	 * @param	string		$message
 	 * @param	string		$file
@@ -345,10 +345,10 @@ class WCF {
 	public static final function handleError($severity, $message, $file, $line) {
 		// this is necessary for the shut-up operator
 		if (!(error_reporting() & $severity)) return;
-		
+
 		throw new ErrorException($message, 0, $severity, $file, $line);
 	}
-	
+
 	/**
 	 * Loads the database configuration and creates a new connection to the database.
 	 */
@@ -358,11 +358,11 @@ class WCF {
 		$dbPort = 0;
 		$defaultDriverOptions = [];
 		require(WCF_DIR.'config.inc.php');
-		
+
 		// create database connection
 		self::$dbObj = new MySQLDatabase($dbHost, $dbUser, $dbPassword, $dbName, $dbPort, false, false, $defaultDriverOptions);
 	}
-	
+
 	/**
 	 * Loads the options file, automatically created if not exists.
 	 */
@@ -370,20 +370,20 @@ class WCF {
 		// The attachment module is always enabled since 5.2.
 		// https://github.com/WoltLab/WCF/issues/2531
 		define('MODULE_ATTACHMENT', 1);
-		
+
 		// Users cannot react to their own content since 5.2.
 		// https://github.com/WoltLab/WCF/issues/2975
 		define('LIKE_ALLOW_FOR_OWN_CONTENT', 0);
 		define('LIKE_ENABLE_DISLIKE', 0);
-		
+
 		// Thumbnails for attachments are already enabled since 5.3.
 		// https://github.com/WoltLab/WCF/pull/3444
 		define('ATTACHMENT_ENABLE_THUMBNAILS', 1);
-		
+
 		// User markings are always applied in sidebars since 5.3.
 		// https://github.com/WoltLab/WCF/issues/3330
 		define('MESSAGE_SIDEBAR_ENABLE_USER_ONLINE_MARKING', 1);
-		
+
 		// Password strength configuration is deprecated since 5.3.
 		define('REGISTER_ENABLE_PASSWORD_SECURITY_CHECK', 0);
 		define('REGISTER_PASSWORD_MIN_LENGTH', 0);
@@ -395,49 +395,49 @@ class WCF {
 		// rel=nofollow is always applied to external link since 5.3
 		// https://github.com/WoltLab/WCF/issues/3339
 		define('EXTERNAL_LINK_REL_NOFOLLOW', 1);
-		
+
 		$filename = WCF_DIR.'options.inc.php';
-		
+
 		// create options file if doesn't exist
 		if (!file_exists($filename) || filemtime($filename) <= 1) {
 			OptionEditor::rebuild();
 		}
 		require($filename);
-		
+
 		// check if option file is complete and writable
 		if (PACKAGE_ID) {
 			if (!is_writable($filename)) {
 				FileUtil::makeWritable($filename);
-				
+
 				if (!is_writable($filename)) {
 					throw new SystemException("The option file '" . $filename . "' is not writable.");
 				}
 			}
-			
+
 			// check if a previous write operation was incomplete and force rebuilding
 			if (!defined('WCF_OPTION_INC_PHP_SUCCESS')) {
 				OptionEditor::rebuild();
-				
+
 				require($filename);
 			}
-			
+
 			if (ENABLE_DEBUG_MODE) {
 				self::$dbObj->enableDebugMode();
 			}
 		}
 	}
-	
+
 	/**
 	 * Starts the session system.
 	 */
 	protected function initSession() {
 		$factory = new SessionFactory();
 		$factory->load();
-		
+
 		self::$sessionObj = SessionHandler::getInstance();
 		self::$sessionObj->setHasValidCookie($factory->hasValidCookie());
 	}
-	
+
 	/**
 	 * Initialises the language engine.
 	 */
@@ -445,16 +445,16 @@ class WCF {
 		if (isset($_GET['l']) && !self::getUser()->userID) {
 			self::getSession()->setLanguageID(intval($_GET['l']));
 		}
-		
+
 		// set mb settings
 		mb_internal_encoding('UTF-8');
 		if (function_exists('mb_regex_encoding')) mb_regex_encoding('UTF-8');
 		mb_language('uni');
-		
+
 		// get language
 		self::$languageObj = LanguageFactory::getInstance()->getUserLanguage(self::getSession()->getLanguageID());
 	}
-	
+
 	/**
 	 * Initialises the template engine.
 	 */
@@ -462,10 +462,10 @@ class WCF {
 		self::$tplObj = TemplateEngine::getInstance();
 		self::getTPL()->setLanguageID(self::getLanguage()->languageID);
 		$this->assignDefaultTemplateVariables();
-		
+
 		$this->initStyle();
 	}
-	
+
 	/**
 	 * Initializes the user's style.
 	 */
@@ -473,17 +473,17 @@ class WCF {
 		if (isset($_REQUEST['styleID'])) {
 			self::getSession()->setStyleID(intval($_REQUEST['styleID']));
 		}
-		
+
 		$styleHandler = StyleHandler::getInstance();
 		$styleHandler->changeStyle(self::getSession()->getStyleID());
 	}
-	
+
 	/**
 	 * Executes the blacklist.
 	 */
 	protected function initBlacklist() {
 		$isAjax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest');
-		
+
 		if (defined('BLACKLIST_IP_ADDRESSES') && BLACKLIST_IP_ADDRESSES != '') {
 			if (!StringUtil::executeWordFilter(UserUtil::convertIPv6To4(self::getSession()->ipAddress), BLACKLIST_IP_ADDRESSES)) {
 				if ($isAjax) {
@@ -522,7 +522,7 @@ class WCF {
 				}
 			}
 		}
-		
+
 		// handle banned users
 		if (self::getUser()->userID && self::getUser()->banned && !self::getUser()->hasOwnerAccess()) {
 			if ($isAjax) {
@@ -530,7 +530,7 @@ class WCF {
 			}
 			else {
 				self::$forceLogout = true;
-				
+
 				// remove cookies
 				if (isset($_COOKIE[COOKIE_PREFIX.'userID'])) {
 					HeaderUtil::setCookie('userID', 0);
@@ -538,36 +538,36 @@ class WCF {
 				if (isset($_COOKIE[COOKIE_PREFIX.'password'])) {
 					HeaderUtil::setCookie('password', '');
 				}
-				
+
 				throw new NamedUserException(self::getLanguage()->getDynamicVariable('wcf.user.error.isBanned'));
 			}
 		}
 	}
-	
+
 	/**
 	 * Initializes applications.
 	 */
 	protected function initApplications() {
 		// step 1) load all applications
 		$loadedApplications = [];
-		
+
 		// register WCF as application
 		self::$applications['wcf'] = ApplicationHandler::getInstance()->getApplicationByID(1);
-		
+
 		if (!class_exists(WCFACP::class, false)) {
 			static::getTPL()->assign('baseHref', self::$applications['wcf']->getPageURL());
 		}
-		
+
 		// start main application
 		$application = ApplicationHandler::getInstance()->getActiveApplication();
 		if ($application->packageID != 1) {
 			$loadedApplications[] = $this->loadApplication($application);
-			
+
 			// register primary application
 			$abbreviation = ApplicationHandler::getInstance()->getAbbreviation($application->packageID);
 			self::$applications[$abbreviation] = $application;
 		}
-		
+
 		// start dependent applications
 		$applications = ApplicationHandler::getInstance()->getDependentApplications();
 		foreach ($applications as $application) {
@@ -579,25 +579,25 @@ class WCF {
 				// ignore apps flagged for uninstallation
 				continue;
 			}
-			
+
 			$loadedApplications[] = $this->loadApplication($application, true);
 		}
-		
+
 		// step 2) run each application
 		if (!class_exists('wcf\system\WCFACP', false)) {
 			/** @var IApplication $application */
 			foreach ($loadedApplications as $application) {
 				$application->__run();
 			}
-			
+
 			// refresh the session 1 minute before it expires
 			self::getTPL()->assign('__sessionKeepAlive', SESSION_TIMEOUT - 60);
 		}
 	}
-	
+
 	/**
 	 * Loads an application.
-	 * 
+	 *
 	 * @param	Application		$application
 	 * @param	boolean			$isDependentApplication
 	 * @return	IApplication
@@ -608,7 +608,7 @@ class WCF {
 		// package cache might be outdated
 		if ($package === null) {
 			$package = new Package($application->packageID);
-			
+
 			// package cache is outdated, discard cache
 			if ($package->packageID) {
 				PackageEditor::resetCache();
@@ -618,13 +618,13 @@ class WCF {
 				throw new SystemException("application identified by package id '".$application->packageID."' is unknown");
 			}
 		}
-		
+
 		$abbreviation = ApplicationHandler::getInstance()->getAbbreviation($application->packageID);
 		$packageDir = FileUtil::getRealPath(WCF_DIR.$package->packageDir);
 		self::$autoloadDirectories[$abbreviation] = $packageDir . 'lib/';
-		
+
 		$className = $abbreviation.'\system\\'.strtoupper($abbreviation).'Core';
-		
+
 		// class was not found, possibly the app was moved, but `packageDir` has not been adjusted
 		if (!class_exists($className)) {
 			// check if both the Core and the app are on the same domain
@@ -635,7 +635,7 @@ class WCF {
 				if ($relativePath !== './') {
 					$packageDir = FileUtil::getRealPath(WCF_DIR.$relativePath);
 					self::$autoloadDirectories[$abbreviation] = $packageDir . 'lib/';
-					
+
 					if (class_exists($className)) {
 						// the class can now be found, update the `packageDir` value
 						(new PackageEditor($package))->update(['packageDir' => $relativePath]);
@@ -643,28 +643,28 @@ class WCF {
 				}
 			}
 		}
-		
+
 		if (class_exists($className) && is_subclass_of($className, IApplication::class)) {
 			// include config file
 			$configPath = $packageDir . PackageInstallationDispatcher::CONFIG_FILE;
 			if (!file_exists($configPath)) {
 				Package::writeConfigFile($package->packageID);
 			}
-			
+
 			if (file_exists($configPath)) {
 				require_once($configPath);
 			}
 			else {
 				throw new SystemException('Unable to load configuration for '.$package->package);
 			}
-			
+
 			// register template path if not within ACP
 			if (!class_exists('wcf\system\WCFACP', false)) {
 				// add template path and abbreviation
 				static::getTPL()->addApplication($abbreviation, $packageDir . 'templates/');
 			}
 			EmailTemplateEngine::getInstance()->addApplication($abbreviation, $packageDir . 'templates/');
-			
+
 			// init application and assign it as template variable
 			self::$applicationObjects[$application->packageID] = call_user_func([$className, 'getInstance']);
 			static::getTPL()->assign('__'.$abbreviation, self::$applicationObjects[$application->packageID]);
@@ -674,7 +674,7 @@ class WCF {
 			unset(self::$autoloadDirectories[$abbreviation]);
 			throw new SystemException("Unable to run '".$package->package."', '".$className."' is missing or does not implement '".IApplication::class."'.");
 		}
-		
+
 		// register template path in ACP
 		if (class_exists('wcf\system\WCFACP', false)) {
 			static::getTPL()->addApplication($abbreviation, $packageDir . 'acp/templates/');
@@ -683,16 +683,16 @@ class WCF {
 			// assign base tag
 			static::getTPL()->assign('baseHref', $application->getPageURL());
 		}
-		
+
 		// register application
 		self::$applications[$abbreviation] = $application;
-		
+
 		return self::$applicationObjects[$application->packageID];
 	}
-	
+
 	/**
 	 * Returns the corresponding application object. Does not support the 'wcf' pseudo application.
-	 * 
+	 *
 	 * @param	Application	$application
 	 * @return	IApplication
 	 */
@@ -700,36 +700,36 @@ class WCF {
 		if (isset(self::$applicationObjects[$application->packageID])) {
 			return self::$applicationObjects[$application->packageID];
 		}
-		
+
 		return null;
 	}
-	
+
 	/**
 	 * Returns the invoked application.
-	 * 
+	 *
 	 * @return      Application
 	 * @since	3.1
 	 */
 	public static function getActiveApplication() {
 		return ApplicationHandler::getInstance()->getActiveApplication();
 	}
-	
+
 	/**
 	 * Loads an application on runtime, do not use this outside the package installation.
-	 * 
+	 *
 	 * @param	integer		$packageID
 	 */
 	public static function loadRuntimeApplication($packageID) {
 		$package = new Package($packageID);
 		$application = new Application($packageID);
-		
+
 		$abbreviation = Package::getAbbreviation($package->package);
 		$packageDir = FileUtil::getRealPath(WCF_DIR.$package->packageDir);
 		self::$autoloadDirectories[$abbreviation] = $packageDir . 'lib/';
 		self::$applications[$abbreviation] = $application;
 		self::getTPL()->addApplication($abbreviation, $packageDir . 'acp/templates/');
 	}
-	
+
 	/**
 	 * Initializes core object cache.
 	 */
@@ -738,26 +738,26 @@ class WCF {
 		if (PACKAGE_ID == 0) {
 			return;
 		}
-		
+
 		self::$coreObjectCache = CoreObjectCacheBuilder::getInstance()->getData();
 	}
-	
+
 	/**
 	 * Assigns some default variables to the template engine.
 	 */
 	protected function assignDefaultTemplateVariables() {
 		$wcf = $this;
-		
+
 		if (ENABLE_ENTERPRISE_MODE) {
 			$wcf = new TemplateScriptingCore($wcf);
 		}
-		
+
 		self::getTPL()->registerPrefilter(['event', 'hascontent', 'lang', 'jslang']);
 		self::getTPL()->assign([
 			'__wcf' => $wcf,
 			'__wcfVersion' => LAST_UPDATE_TIME // @deprecated 2.1, use LAST_UPDATE_TIME directly
 		]);
-		
+
 		$isAjax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest');
 		// Execute background queue in this request, if it was requested and AJAX isn't used.
 		if (!$isAjax) {
@@ -765,20 +765,20 @@ class WCF {
 				self::getTPL()->assign([
 					'forceBackgroundQueuePerform' => true
 				]);
-				
+
 				self::getSession()->unregister('forceBackgroundQueuePerform');
 			}
 		}
-		
+
 		EmailTemplateEngine::getInstance()->registerPrefilter(['event', 'hascontent', 'lang', 'jslang']);
 		EmailTemplateEngine::getInstance()->assign([
 			'__wcf' => $wcf
 		]);
 	}
-	
+
 	/**
 	 * Wrapper for the getter methods of this class.
-	 * 
+	 *
 	 * @param	string		$name
 	 * @return	mixed		value
 	 * @throws	SystemException
@@ -788,10 +788,10 @@ class WCF {
 		if (method_exists($this, $method)) {
 			return $this->$method();
 		}
-		
+
 		throw new SystemException("method '".$method."' does not exist in class WCF");
 	}
-	
+
 	/**
 	 * Returns true if current application (WCF) is treated as active and was invoked directly.
 	 *
@@ -800,19 +800,19 @@ class WCF {
 	public function isActiveApplication() {
 		return (ApplicationHandler::getInstance()->getActiveApplication()->packageID == 1);
 	}
-	
+
 	/**
 	 * Changes the active language.
-	 * 
+	 *
 	 * @param	integer		$languageID
 	 */
 	public static final function setLanguage($languageID) {
 		if (!$languageID || LanguageFactory::getInstance()->getLanguage($languageID) === null) {
 			$languageID = LanguageFactory::getInstance()->getDefaultLanguageID();
 		}
-		
+
 		self::$languageObj = LanguageFactory::getInstance()->getLanguage($languageID);
-		
+
 		// the template engine may not be available yet, usually happens when
 		// changing the user (and thus the language id) during session init
 		if (self::$tplObj !== null) {
@@ -820,10 +820,10 @@ class WCF {
 			EmailTemplateEngine::getInstance()->setLanguageID(self::getLanguage()->languageID);
 		}
 	}
-	
+
 	/**
 	 * Includes the required util or exception classes automatically.
-	 * 
+	 *
 	 * @param	string		$className
 	 * @see		spl_autoload_register()
 	 */
@@ -836,14 +836,14 @@ class WCF {
 			}
 			if (isset(self::$autoloadDirectories[$applicationPrefix])) {
 				$classPath = self::$autoloadDirectories[$applicationPrefix] . implode('/', $namespaces) . '.class.php';
-				
+
 				// PHP will implicitly check if the file exists when including it, which means that we can save a
 				// redundant syscall/fs access by not checking for existence ourselves. Do not use require_once()!
 				@include_once($classPath);
 			}
 		}
 	}
-	
+
 	/**
 	 * @inheritDoc
 	 */
@@ -852,13 +852,13 @@ class WCF {
 		if (!method_exists($this, $name)) {
 			return self::__callStatic($name, $arguments);
 		}
-		
+
 		throw new \BadMethodCallException("Call to undefined method WCF::{$name}().");
 	}
-	
+
 	/**
 	 * Returns dynamically loaded core objects.
-	 * 
+	 *
 	 * @param	string		$name
 	 * @param	array		$arguments
 	 * @return	object
@@ -866,29 +866,29 @@ class WCF {
 	 */
 	public static final function __callStatic($name, array $arguments) {
 		$className = preg_replace('~^get~', '', $name);
-		
+
 		if (isset(self::$coreObject[$className])) {
 			return self::$coreObject[$className];
 		}
-		
+
 		$objectName = self::getCoreObject($className);
 		if ($objectName === null) {
 			throw new SystemException("Core object '".$className."' is unknown.");
 		}
-		
+
 		if (class_exists($objectName)) {
 			if (!is_subclass_of($objectName, SingletonFactory::class)) {
 				throw new ParentClassException($objectName, SingletonFactory::class);
 			}
-			
+
 			self::$coreObject[$className] = call_user_func([$objectName, 'getInstance']);
 			return self::$coreObject[$className];
 		}
 	}
-	
+
 	/**
 	 * Searches for cached core object definition.
-	 * 
+	 *
 	 * @param	string		$className
 	 * @return	string
 	 */
@@ -896,13 +896,13 @@ class WCF {
 		if (isset(self::$coreObjectCache[$className])) {
 			return self::$coreObjectCache[$className];
 		}
-		
+
 		return null;
 	}
-	
+
 	/**
 	 * Returns true if the debug mode is enabled, otherwise false.
-	 * 
+	 *
 	 * @param	boolean		$ignoreACP
 	 * @return	boolean
 	 */
@@ -914,13 +914,13 @@ class WCF {
 		else if (defined('ENABLE_DEBUG_MODE') && ENABLE_DEBUG_MODE) {
 			return true;
 		}
-		
+
 		return false;
 	}
-	
+
 	/**
 	 * Returns true if benchmarking is enabled, otherwise false.
-	 * 
+	 *
 	 * @return	boolean
 	 */
 	public static function benchmarkIsEnabled() {
@@ -928,10 +928,10 @@ class WCF {
 		if (!defined('ENABLE_BENCHMARK') || ENABLE_BENCHMARK) return true;
 		return false;
 	}
-	
+
 	/**
 	 * Returns domain path for given application.
-	 * 
+	 *
 	 * @param	string		$abbreviation
 	 * @return	string
 	 */
@@ -940,97 +940,97 @@ class WCF {
 		if (!PACKAGE_ID) {
 			return '../';
 		}
-		
+
 		if (!isset(self::$applications[$abbreviation])) {
 			$abbreviation = 'wcf';
 		}
-		
+
 		return self::$applications[$abbreviation]->getPageURL();
 	}
-	
+
 	/**
 	 * Returns the domain path for the currently active application,
 	 * used to avoid CORS requests.
-	 * 
+	 *
 	 * @return      string
 	 */
 	public static function getActivePath() {
 		if (!PACKAGE_ID) {
 			return self::getPath();
 		}
-		
+
 		// We cannot rely on the ApplicationHandler's `getActiveApplication()` because
 		// it uses the requested controller to determine the namespace. However, starting
 		// with WoltLab Suite 5.2, system pages can be virtually assigned to a different
 		// app, resolving against the target app without changing the namespace.
 		return self::getPath(ApplicationHandler::getInstance()->getAbbreviation(PACKAGE_ID));
 	}
-	
+
 	/**
 	 * Returns a fully qualified anchor for current page.
-	 * 
+	 *
 	 * @param	string		$fragment
 	 * @return	string
 	 */
 	public function getAnchor($fragment) {
 		return StringUtil::encodeHTML(self::getRequestURI() . '#' . $fragment);
 	}
-	
+
 	/**
 	 * Returns the currently active page or null if unknown.
-	 * 
+	 *
 	 * @return	Page|null
 	 */
 	public static function getActivePage() {
 		if (self::getActiveRequest() === null) {
 			return null;
 		}
-		
+
 		if (self::getActiveRequest()->getClassName() === CmsPage::class) {
 			$metaData = self::getActiveRequest()->getMetaData();
 			if (isset($metaData['cms'])) {
 				return PageCache::getInstance()->getPage($metaData['cms']['pageID']);
 			}
-			
+
 			return null;
 		}
-		
+
 		return PageCache::getInstance()->getPageByController(self::getActiveRequest()->getClassName());
 	}
-	
+
 	/**
 	 * Returns the currently active request.
-	 * 
+	 *
 	 * @return Request
 	 */
 	public static function getActiveRequest() {
 		return RequestHandler::getInstance()->getActiveRequest();
 	}
-	
+
 	/**
 	 * Returns the URI of the current page.
-	 * 
+	 *
 	 * @return	string
 	 */
 	public static function getRequestURI() {
 		return preg_replace('~^(https?://[^/]+)(?:/.*)?$~', '$1', self::getTPL()->get('baseHref')) . $_SERVER['REQUEST_URI'];
 	}
-	
+
 	/**
 	 * Resets Zend Opcache cache if installed and enabled.
-	 * 
+	 *
 	 * @param	string		$script
 	 */
 	public static function resetZendOpcache($script = '') {
 		if (self::$zendOpcacheEnabled === null) {
 			self::$zendOpcacheEnabled = false;
-			
+
 			if (extension_loaded('Zend Opcache') && @ini_get('opcache.enable')) {
 				self::$zendOpcacheEnabled = true;
 			}
-			
+
 		}
-		
+
 		if (self::$zendOpcacheEnabled) {
 			if (empty($script)) {
 				\opcache_reset();
@@ -1040,16 +1040,16 @@ class WCF {
 			}
 		}
 	}
-	
+
 	/**
 	 * Returns style handler.
-	 * 
+	 *
 	 * @return	StyleHandler
 	 */
 	public function getStyleHandler() {
 		return StyleHandler::getInstance();
 	}
-	
+
 	/**
 	 * Returns box handler.
 	 *
@@ -1059,50 +1059,50 @@ class WCF {
 	public function getBoxHandler() {
 		return BoxHandler::getInstance();
 	}
-	
+
 	/**
 	 * Returns number of available updates.
-	 * 
+	 *
 	 * @return	integer
 	 */
 	public function getAvailableUpdates() {
 		$data = PackageUpdateCacheBuilder::getInstance()->getData();
 		return $data['updates'];
 	}
-	
+
 	/**
 	 * Returns a 8 character prefix for editor autosave.
-	 * 
+	 *
 	 * @return	string
 	 */
 	public function getAutosavePrefix() {
 		return substr(sha1(preg_replace('~^https~', 'http', self::getPath())), 0, 8);
 	}
-	
+
 	/**
 	 * Returns the favicon URL or a base64 encoded image.
-	 * 
+	 *
 	 * @return	string
 	 */
 	public function getFavicon() {
 		$activeApplication = ApplicationHandler::getInstance()->getActiveApplication();
 		$wcf = ApplicationHandler::getInstance()->getWCF();
 		$favicon = StyleHandler::getInstance()->getStyle()->getRelativeFavicon();
-		
+
 		if ($activeApplication->domainName !== $wcf->domainName) {
 			if (file_exists(WCF_DIR.$favicon)) {
 				$favicon = file_get_contents(WCF_DIR.$favicon);
-				
+
 				return 'data:image/x-icon;base64,' . base64_encode($favicon);
 			}
 		}
-		
+
 		return self::getPath() . $favicon;
 	}
-	
+
 	/**
 	 * Returns true if the desktop notifications should be enabled.
-	 * 
+	 *
 	 * @return      boolean
 	 */
 	public function useDesktopNotifications() {
@@ -1113,33 +1113,33 @@ class WCF {
 			$application = ApplicationHandler::getInstance()->getApplicationByID(DESKTOP_NOTIFICATION_PACKAGE_ID);
 			// mismatch, default to Core
 			if ($application === null) $application = ApplicationHandler::getInstance()->getApplicationByID(1);
-			
+
 			$currentApplication = ApplicationHandler::getInstance()->getActiveApplication();
 			if ($currentApplication->domainName != $application->domainName) {
 				// different domain
 				return false;
 			}
 		}
-		
+
 		return true;
 	}
-	
+
 	/**
 	 * Returns true if currently active request represents the landing page.
-	 * 
+	 *
 	 * @return	boolean
 	 */
 	public static function isLandingPage() {
 		if (self::getActiveRequest() === null) {
 			return false;
 		}
-		
+
 		return self::getActiveRequest()->isLandingPage();
 	}
-	
+
 	/**
 	 * Returns true if the given API version is currently supported.
-	 * 
+	 *
 	 * @param       integer         $apiVersion
 	 * @return      boolean
 	 * @deprecated 5.2
@@ -1147,17 +1147,17 @@ class WCF {
 	public static function isSupportedApiVersion($apiVersion) {
 		return ($apiVersion == WSC_API_VERSION) || in_array($apiVersion, self::$supportedLegacyApiVersions);
 	}
-	
+
 	/**
 	 * Returns the list of supported legacy API versions.
-	 * 
+	 *
 	 * @return      integer[]
 	 * @deprecated 5.2
 	 */
 	public static function getSupportedLegacyApiVersions() {
 		return self::$supportedLegacyApiVersions;
 	}
-	
+
 	/**
 	 * Initialises the cronjobs.
 	 */
@@ -1166,15 +1166,15 @@ class WCF {
 			self::getTPL()->assign('executeCronjobs', CronjobScheduler::getInstance()->getNextExec() < TIME_NOW && defined('OFFLINE') && !OFFLINE);
 		}
 	}
-	
+
 	/**
 	 * Checks recursively that the most important system files of `com.woltlab.wcf` are writable.
-	 * 
+	 *
 	 * @throws	\RuntimeException	if any relevant file or directory is not writable
 	 */
 	public static function checkWritability() {
 		$nonWritablePaths = [];
-		
+
 		$nonRecursiveDirectories = [
 			'',
 			'acp/',
@@ -1185,12 +1185,12 @@ class WCF {
 			if ($path === 'tmp/' && !is_dir($path)) {
 				continue;
 			}
-			
+
 			if (!is_writable($path)) {
 				$nonWritablePaths[] = FileUtil::getRelativePath($_SERVER['DOCUMENT_ROOT'], $path);
 				continue;
 			}
-			
+
 			DirectoryUtil::getInstance($path, false)->executeCallback(function($file, \SplFileInfo $fileInfo) use ($path, &$nonWritablePaths) {
 				if ($fileInfo instanceof \DirectoryIterator) {
 					if (!is_writable($fileInfo->getPath())) {
@@ -1202,7 +1202,7 @@ class WCF {
 				}
 			});
 		}
-		
+
 		$recursiveDirectories = [
 			'acp/js/',
 			'acp/style/',
@@ -1216,12 +1216,12 @@ class WCF {
 		];
 		foreach ($recursiveDirectories as $directory) {
 			$path = WCF_DIR . $directory;
-			
+
 			if (!is_writable($path)) {
 				$nonWritablePaths[] = FileUtil::getRelativePath($_SERVER['DOCUMENT_ROOT'], $path);
 				continue;
 			}
-			
+
 			DirectoryUtil::getInstance($path)->executeCallback(function($file, \SplFileInfo $fileInfo) use ($path, &$nonWritablePaths) {
 				if ($fileInfo instanceof \DirectoryIterator) {
 					if (!is_writable($fileInfo->getPath())) {
@@ -1233,7 +1233,7 @@ class WCF {
 				}
 			});
 		}
-		
+
 		if (!empty($nonWritablePaths)) {
 			$maxPaths = 10;
 			throw new \RuntimeException('The following paths are not writable: ' . implode(',', array_slice($nonWritablePaths, 0, $maxPaths)) . (count($nonWritablePaths) > $maxPaths ? ',' . StringUtil::HELLIP : ''));
-- 
2.20.1