From 32a6b45582418c616248502f3f57f694a5991edd Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Fri, 22 Sep 2023 17:31:03 +0200 Subject: [PATCH 01/21] Move the handling of the license data into a separate API --- .../files/lib/acp/page/LicensePage.class.php | 73 +++-------------- .../package/license/LicenseApi.class.php | 81 +++++++++++++++++++ 2 files changed, 91 insertions(+), 63 deletions(-) create mode 100644 wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php diff --git a/wcfsetup/install/files/lib/acp/page/LicensePage.class.php b/wcfsetup/install/files/lib/acp/page/LicensePage.class.php index ef23f57517f..f7ffccdfa0f 100644 --- a/wcfsetup/install/files/lib/acp/page/LicensePage.class.php +++ b/wcfsetup/install/files/lib/acp/page/LicensePage.class.php @@ -2,9 +2,6 @@ namespace wcf\acp\page; -use CuyZ\Valinor\Mapper\Source\Source; -use CuyZ\Valinor\MapperBuilder; -use GuzzleHttp\Psr7\Request; use Laminas\Diactoros\Response\RedirectResponse; use wcf\acp\form\LicenseEditForm; use wcf\data\package\Package; @@ -13,7 +10,7 @@ use wcf\data\package\update\server\PackageUpdateServer; use wcf\page\AbstractPage; use wcf\system\database\util\PreparedStatementConditionBuilder; -use wcf\system\io\HttpFactory; +use wcf\system\package\license\LicenseApi; use wcf\system\request\LinkHandler; use wcf\system\WCF; @@ -31,9 +28,11 @@ final class LicensePage extends AbstractPage public $neededPermissions = ['admin.configuration.package.canInstallPackage']; + private LicenseApi $licenseApi; + private array $licenseData; - private string $licenseNumber; + private int $licenseNumber; private array $installedPackages; @@ -41,8 +40,6 @@ final class LicensePage extends AbstractPage private array $packageUpdates = []; - private PackageUpdateServer $updateServer; - private array $requiresLicenseExtension = []; private const CURRENT_MAJOR = '6.0'; @@ -51,9 +48,9 @@ public function readData() { parent::readData(); - $this->updateServer = PackageUpdateServer::getWoltLabUpdateServer(); + $this->licenseApi = new LicenseApi(); - if (!$this->hasLicenseCredentials()) { + if (!$this->licenseApi->hasLicenseCredentials()) { return new RedirectResponse( LinkHandler::getInstance()->getControllerLink( LicenseEditForm::class, @@ -66,7 +63,10 @@ public function readData() (new PackageUpdateAction([], 'refreshDatabase'))->executeAction(); - $this->licenseData = $this->fetchLicenseData(); + $this->licenseData = $this->licenseApi->fetchLicenseData(); + if (isset($this->licenseData['license']['licenseID'])) { + $this->licenseNumber = $this->licenseData['license']['licenseID']; + } $identifiers = \array_merge( \array_keys($this->licenseData['woltlab']), @@ -119,16 +119,6 @@ function (string $package) { } } - private function hasLicenseCredentials(): bool - { - $authData = $this->updateServer->getAuthData(); - if (empty($authData['username']) || empty($authData['password'])) { - return false; - } - - return true; - } - public function assignVariables() { parent::assignVariables(); @@ -625,47 +615,4 @@ protected function canInstall( return $packageUpdates; } - - // This code was stolen from "FirstTimeSetupLicenseForm" and - // should propably be moved into a helper class. We might even want to refresh - // the data with requests to the package servers to implicitly fetch the - // latest purchases. - private function fetchLicenseData(): array|object - { - $authData = $this->updateServer->getAuthData(); - $this->licenseNumber = $authData['username']; - - $request = new Request( - 'POST', - 'https://api.woltlab.com/2.1/customer/license/list.json', - [ - 'content-type' => 'application/x-www-form-urlencoded', - ], - \http_build_query([ - 'licenseNo' => $this->licenseNumber, - 'serialNo' => $authData['password'], - 'instanceId' => \hash_hmac('sha256', 'api.woltlab.com', \WCF_UUID), - ], '', '&', \PHP_QUERY_RFC1738) - ); - - $response = HttpFactory::makeClientWithTimeout(5)->send($request); - return (new MapperBuilder()) - ->allowSuperfluousKeys() - ->mapper() - ->map( - <<<'EOT' - array { - status: 200, - license: array { - authCode?: string, - type: string, - expiryDates?: array, - }, - pluginstore: array, - woltlab: array, - } - EOT, - Source::json($response->getBody()) - ); - } } diff --git a/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php b/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php new file mode 100644 index 00000000000..eafd783589f --- /dev/null +++ b/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php @@ -0,0 +1,81 @@ + + * @since 6.0 + */ +final class LicenseApi +{ + private readonly PackageUpdateServer $packageUpdateServer; + + public function __construct() + { + $this->packageUpdateServer = PackageUpdateServer::getWoltLabUpdateServer(); + } + + public function fetchLicenseData(): array|object + { + if (!$this->hasLicenseCredentials()) { + // TODO + throw new \Exception("no credentials"); + } + + $authData = $this->packageUpdateServer->getAuthData(); + + $request = new Request( + 'POST', + 'https://api.woltlab.com/2.1/customer/license/list.json', + [ + 'content-type' => 'application/x-www-form-urlencoded', + ], + \http_build_query([ + 'licenseNo' => $authData['username'], + 'serialNo' => $authData['password'], + 'instanceId' => \hash_hmac('sha256', 'api.woltlab.com', \WCF_UUID), + ], '', '&', \PHP_QUERY_RFC1738) + ); + + $response = HttpFactory::makeClientWithTimeout(5)->send($request); + return (new MapperBuilder()) + ->allowSuperfluousKeys() + ->mapper() + ->map( + <<<'EOT' + array { + status: 200, + license: array { + authCode?: string, + licenseID?: int, + type: string, + expiryDates?: array, + }, + pluginstore: array, + woltlab: array, + } + EOT, + Source::json($response->getBody()) + ); + } + + public function hasLicenseCredentials(): bool + { + $authData = $this->packageUpdateServer->getAuthData(); + if (empty($authData['username']) || empty($authData['password'])) { + return false; + } + + return true; + } +} From 51b1a47cfd8091871cbd57fc009a2c732a393b3f Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Mon, 25 Sep 2023 16:03:26 +0200 Subject: [PATCH 02/21] Add support for the persistent storage of the license data --- .../files/lib/acp/page/LicensePage.class.php | 8 +- .../package/license/LicenseApi.class.php | 113 +++++++++++++----- .../exception/MissingCredentials.class.php | 20 ++++ .../license/exception/ParsingFailed.class.php | 21 ++++ 4 files changed, 127 insertions(+), 35 deletions(-) create mode 100644 wcfsetup/install/files/lib/system/package/license/exception/MissingCredentials.class.php create mode 100644 wcfsetup/install/files/lib/system/package/license/exception/ParsingFailed.class.php diff --git a/wcfsetup/install/files/lib/acp/page/LicensePage.class.php b/wcfsetup/install/files/lib/acp/page/LicensePage.class.php index f7ffccdfa0f..5811e65e4bc 100644 --- a/wcfsetup/install/files/lib/acp/page/LicensePage.class.php +++ b/wcfsetup/install/files/lib/acp/page/LicensePage.class.php @@ -28,8 +28,6 @@ final class LicensePage extends AbstractPage public $neededPermissions = ['admin.configuration.package.canInstallPackage']; - private LicenseApi $licenseApi; - private array $licenseData; private int $licenseNumber; @@ -48,9 +46,7 @@ public function readData() { parent::readData(); - $this->licenseApi = new LicenseApi(); - - if (!$this->licenseApi->hasLicenseCredentials()) { + if (!LicenseApi::hasLicenseCredentials()) { return new RedirectResponse( LinkHandler::getInstance()->getControllerLink( LicenseEditForm::class, @@ -63,7 +59,7 @@ public function readData() (new PackageUpdateAction([], 'refreshDatabase'))->executeAction(); - $this->licenseData = $this->licenseApi->fetchLicenseData(); + $this->licenseData = LicenseApi::fetchFromRemote()->getData(); if (isset($this->licenseData['license']['licenseID'])) { $this->licenseNumber = $this->licenseData['license']['licenseID']; } diff --git a/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php b/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php index eafd783589f..0b592ce7a8e 100644 --- a/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php +++ b/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php @@ -2,11 +2,14 @@ namespace wcf\system\package\license; +use CuyZ\Valinor\Mapper\MappingError; use CuyZ\Valinor\Mapper\Source\Source; use CuyZ\Valinor\MapperBuilder; use GuzzleHttp\Psr7\Request; use wcf\data\package\update\server\PackageUpdateServer; use wcf\system\io\HttpFactory; +use wcf\system\package\license\exception\MissingCredentials; +use wcf\system\package\license\exception\ParsingFailed; /** * Provides access to the license data. @@ -18,21 +21,75 @@ */ final class LicenseApi { - private readonly PackageUpdateServer $packageUpdateServer; + private readonly array $data; + private readonly string $json; - public function __construct() + private const LICENSE_FILE = \WCF_DIR . 'license.php'; + + private function __construct(string $json) + { + $this->json = $json; + $this->data = $this->parseLicenseData($this->json); + + $this->updateLicenseFile(); + } + + public function getData(): array + { + return $this->data; + } + + private function updateLicenseFile(): void + { + @\file_put_contents( + self::LICENSE_FILE, + \sprintf( + <<<'EOT' + return '%s'; + EOT, + $this->json, + ) + ); + } + + private function parseLicenseData(string $json): array { - $this->packageUpdateServer = PackageUpdateServer::getWoltLabUpdateServer(); + try { + /** @var array $result */ + $result = (new MapperBuilder()) + ->allowSuperfluousKeys() + ->mapper() + ->map( + <<<'EOT' + array { + status: 200, + license: array { + authCode?: string, + licenseID?: int, + type: string, + expiryDates?: array, + ckeditorLicenseKey?: string, + }, + pluginstore: array, + woltlab: array, + } + EOT, + Source::json($json) + ); + } catch (MappingError $e) { + throw new ParsingFailed($e); + } + + return $result; } - public function fetchLicenseData(): array|object + public static function fetchFromRemote(): LicenseApi { - if (!$this->hasLicenseCredentials()) { - // TODO - throw new \Exception("no credentials"); + if (!self::hasLicenseCredentials()) { + throw new MissingCredentials(); } - $authData = $this->packageUpdateServer->getAuthData(); + $authData = PackageUpdateServer::getWoltLabUpdateServer()->getAuthData(); $request = new Request( 'POST', @@ -48,30 +105,28 @@ public function fetchLicenseData(): array|object ); $response = HttpFactory::makeClientWithTimeout(5)->send($request); - return (new MapperBuilder()) - ->allowSuperfluousKeys() - ->mapper() - ->map( - <<<'EOT' - array { - status: 200, - license: array { - authCode?: string, - licenseID?: int, - type: string, - expiryDates?: array, - }, - pluginstore: array, - woltlab: array, - } - EOT, - Source::json($response->getBody()) - ); + + return new LicenseApi($response->getBody()); + } + + public static function readFromFile(): ?LicenseApi + { + if (!\is_readable(self::LICENSE_FILE)) { + return null; + } + + $content = \file_get_contents(self::LICENSE_FILE); + + try { + return new LicenseApi($content); + } catch (ParsingFailed) { + return null; + } } - public function hasLicenseCredentials(): bool + public static function hasLicenseCredentials(): bool { - $authData = $this->packageUpdateServer->getAuthData(); + $authData = PackageUpdateServer::getWoltLabUpdateServer()->getAuthData(); if (empty($authData['username']) || empty($authData['password'])) { return false; } diff --git a/wcfsetup/install/files/lib/system/package/license/exception/MissingCredentials.class.php b/wcfsetup/install/files/lib/system/package/license/exception/MissingCredentials.class.php new file mode 100644 index 00000000000..34868e2de55 --- /dev/null +++ b/wcfsetup/install/files/lib/system/package/license/exception/MissingCredentials.class.php @@ -0,0 +1,20 @@ + + * @since 6.0 + */ +final class MissingCredentials extends \Exception +{ + public function __construct() + { + parent::__construct('Cannot fetch the license data without any stored credentials.'); + } +} diff --git a/wcfsetup/install/files/lib/system/package/license/exception/ParsingFailed.class.php b/wcfsetup/install/files/lib/system/package/license/exception/ParsingFailed.class.php new file mode 100644 index 00000000000..de6e93342d1 --- /dev/null +++ b/wcfsetup/install/files/lib/system/package/license/exception/ParsingFailed.class.php @@ -0,0 +1,21 @@ + + * @since 6.0 + */ +final class ParsingFailed extends \Exception +{ + public function __construct(MappingError $previous) + { + parent::__construct('The provided license data cannot be parsed.', 0, $previous); + } +} From b4ae33c12ddefc1cfc203268a58f1c88200e60a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 26 Sep 2023 15:06:09 +0200 Subject: [PATCH 03/21] Update composer dependencies --- .../install/files/lib/system/api/bin/pscss | 5 ++- .../files/lib/system/api/composer.json | 2 +- .../files/lib/system/api/composer.lock | 30 ++++++++-------- .../lib/system/api/composer/installed.json | 30 ++++++++-------- .../lib/system/api/composer/installed.php | 12 +++---- .../system/api/psr/http-client/CHANGELOG.md | 8 +++++ .../system/api/psr/http-client/composer.json | 3 ++ .../system/api/scssphp/scssphp/composer.json | 2 +- .../api/scssphp/scssphp/src/Compiler.php | 34 ++++++++++++++----- .../system/api/scssphp/scssphp/src/Parser.php | 5 +++ .../api/scssphp/scssphp/src/Version.php | 2 +- 11 files changed, 82 insertions(+), 51 deletions(-) diff --git a/wcfsetup/install/files/lib/system/api/bin/pscss b/wcfsetup/install/files/lib/system/api/bin/pscss index 9743992aa56..e57bed8fcd1 100644 --- a/wcfsetup/install/files/lib/system/api/bin/pscss +++ b/wcfsetup/install/files/lib/system/api/bin/pscss @@ -112,9 +112,8 @@ if (PHP_VERSION_ID < 80000) { (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) ) { - include("phpvfscomposer://" . __DIR__ . '/..'.'/scssphp/scssphp/bin/pscss'); - exit(0); + return include("phpvfscomposer://" . __DIR__ . '/..'.'/scssphp/scssphp/bin/pscss'); } } -include __DIR__ . '/..'.'/scssphp/scssphp/bin/pscss'; +return include __DIR__ . '/..'.'/scssphp/scssphp/bin/pscss'; diff --git a/wcfsetup/install/files/lib/system/api/composer.json b/wcfsetup/install/files/lib/system/api/composer.json index 1b9e25e7ac6..0a61bc27747 100644 --- a/wcfsetup/install/files/lib/system/api/composer.json +++ b/wcfsetup/install/files/lib/system/api/composer.json @@ -27,7 +27,7 @@ "psr/http-server-handler": "^1.0.2", "psr/http-server-middleware": "^1.0.2", "psr/log": "^3.0", - "scssphp/scssphp": "^1.11", + "scssphp/scssphp": "^1.11.1", "sebastian/diff": "^5.0.3", "symfony/polyfill-php82": "^1.28.0", "symfony/polyfill-php83": "^1.28", diff --git a/wcfsetup/install/files/lib/system/api/composer.lock b/wcfsetup/install/files/lib/system/api/composer.lock index eb5fbfa863a..53bacda9254 100644 --- a/wcfsetup/install/files/lib/system/api/composer.lock +++ b/wcfsetup/install/files/lib/system/api/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7603e774da8e3f3475ec8d497eb28556", + "content-hash": "8ed824326be4ed2b68fdb8ea8258400a", "packages": [ { "name": "cuyz/valinor", @@ -1093,16 +1093,16 @@ }, { "name": "psr/http-client", - "version": "1.0.2", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/php-fig/http-client.git", - "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31" + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31", - "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", "shasum": "" }, "require": { @@ -1139,9 +1139,9 @@ "psr-18" ], "support": { - "source": "https://github.com/php-fig/http-client/tree/1.0.2" + "source": "https://github.com/php-fig/http-client" }, - "time": "2023-04-10T20:12:12+00:00" + "time": "2023-09-23T14:17:50+00:00" }, { "name": "psr/http-factory", @@ -1564,16 +1564,16 @@ }, { "name": "scssphp/scssphp", - "version": "v1.11.0", + "version": "v1.11.1", "source": { "type": "git", "url": "https://github.com/scssphp/scssphp.git", - "reference": "33749d12c2569bb24071f94e9af828662dabb068" + "reference": "ace2503684bab0dcc817d7614c8a54b865122414" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/scssphp/scssphp/zipball/33749d12c2569bb24071f94e9af828662dabb068", - "reference": "33749d12c2569bb24071f94e9af828662dabb068", + "url": "https://api.github.com/repos/scssphp/scssphp/zipball/ace2503684bab0dcc817d7614c8a54b865122414", + "reference": "ace2503684bab0dcc817d7614c8a54b865122414", "shasum": "" }, "require": { @@ -1590,7 +1590,7 @@ "thoughtbot/bourbon": "^7.0", "twbs/bootstrap": "~5.0", "twbs/bootstrap4": "4.6.1", - "zurb/foundation": "~6.5" + "zurb/foundation": "~6.7.0" }, "suggest": { "ext-iconv": "Can be used as fallback when ext-mbstring is not available", @@ -1638,9 +1638,9 @@ ], "support": { "issues": "https://github.com/scssphp/scssphp/issues", - "source": "https://github.com/scssphp/scssphp/tree/v1.11.0" + "source": "https://github.com/scssphp/scssphp/tree/v1.11.1" }, - "time": "2022-09-02T21:24:55+00:00" + "time": "2023-09-24T13:38:17+00:00" }, { "name": "sebastian/diff", @@ -2127,5 +2127,5 @@ "php": "8.1.2", "ext-gd": "0" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/wcfsetup/install/files/lib/system/api/composer/installed.json b/wcfsetup/install/files/lib/system/api/composer/installed.json index a52b927a876..733270fc1ef 100644 --- a/wcfsetup/install/files/lib/system/api/composer/installed.json +++ b/wcfsetup/install/files/lib/system/api/composer/installed.json @@ -1132,24 +1132,24 @@ }, { "name": "psr/http-client", - "version": "1.0.2", - "version_normalized": "1.0.2.0", + "version": "1.0.3", + "version_normalized": "1.0.3.0", "source": { "type": "git", "url": "https://github.com/php-fig/http-client.git", - "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31" + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31", - "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", "shasum": "" }, "require": { "php": "^7.0 || ^8.0", "psr/http-message": "^1.0 || ^2.0" }, - "time": "2023-04-10T20:12:12+00:00", + "time": "2023-09-23T14:17:50+00:00", "type": "library", "extra": { "branch-alias": { @@ -1181,7 +1181,7 @@ "psr-18" ], "support": { - "source": "https://github.com/php-fig/http-client/tree/1.0.2" + "source": "https://github.com/php-fig/http-client" }, "install-path": "../psr/http-client" }, @@ -1630,17 +1630,17 @@ }, { "name": "scssphp/scssphp", - "version": "v1.11.0", - "version_normalized": "1.11.0.0", + "version": "v1.11.1", + "version_normalized": "1.11.1.0", "source": { "type": "git", "url": "https://github.com/scssphp/scssphp.git", - "reference": "33749d12c2569bb24071f94e9af828662dabb068" + "reference": "ace2503684bab0dcc817d7614c8a54b865122414" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/scssphp/scssphp/zipball/33749d12c2569bb24071f94e9af828662dabb068", - "reference": "33749d12c2569bb24071f94e9af828662dabb068", + "url": "https://api.github.com/repos/scssphp/scssphp/zipball/ace2503684bab0dcc817d7614c8a54b865122414", + "reference": "ace2503684bab0dcc817d7614c8a54b865122414", "shasum": "" }, "require": { @@ -1657,13 +1657,13 @@ "thoughtbot/bourbon": "^7.0", "twbs/bootstrap": "~5.0", "twbs/bootstrap4": "4.6.1", - "zurb/foundation": "~6.5" + "zurb/foundation": "~6.7.0" }, "suggest": { "ext-iconv": "Can be used as fallback when ext-mbstring is not available", "ext-mbstring": "For best performance, mbstring should be installed as it is faster than ext-iconv" }, - "time": "2022-09-02T21:24:55+00:00", + "time": "2023-09-24T13:38:17+00:00", "bin": [ "bin/pscss" ], @@ -1707,7 +1707,7 @@ ], "support": { "issues": "https://github.com/scssphp/scssphp/issues", - "source": "https://github.com/scssphp/scssphp/tree/v1.11.0" + "source": "https://github.com/scssphp/scssphp/tree/v1.11.1" }, "install-path": "../scssphp/scssphp" }, diff --git a/wcfsetup/install/files/lib/system/api/composer/installed.php b/wcfsetup/install/files/lib/system/api/composer/installed.php index 95ab7dd1e54..bc7a8f591db 100644 --- a/wcfsetup/install/files/lib/system/api/composer/installed.php +++ b/wcfsetup/install/files/lib/system/api/composer/installed.php @@ -161,9 +161,9 @@ 'dev_requirement' => false, ), 'psr/http-client' => array( - 'pretty_version' => '1.0.2', - 'version' => '1.0.2.0', - 'reference' => '0955afe48220520692d2d09f7ab7e0f93ffd6a31', + 'pretty_version' => '1.0.3', + 'version' => '1.0.3.0', + 'reference' => 'bb5906edc1c324c9a05aa0873d40117941e5fa90', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/http-client', 'aliases' => array(), @@ -262,9 +262,9 @@ 'dev_requirement' => false, ), 'scssphp/scssphp' => array( - 'pretty_version' => 'v1.11.0', - 'version' => '1.11.0.0', - 'reference' => '33749d12c2569bb24071f94e9af828662dabb068', + 'pretty_version' => 'v1.11.1', + 'version' => '1.11.1.0', + 'reference' => 'ace2503684bab0dcc817d7614c8a54b865122414', 'type' => 'library', 'install_path' => __DIR__ . '/../scssphp/scssphp', 'aliases' => array(), diff --git a/wcfsetup/install/files/lib/system/api/psr/http-client/CHANGELOG.md b/wcfsetup/install/files/lib/system/api/psr/http-client/CHANGELOG.md index e2dc25f519b..babba7c7b2e 100644 --- a/wcfsetup/install/files/lib/system/api/psr/http-client/CHANGELOG.md +++ b/wcfsetup/install/files/lib/system/api/psr/http-client/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. +## 1.0.3 + +Add `source` link in composer.json. No code changes. + +## 1.0.2 + +Allow PSR-7 (psr/http-message) 2.0. No code changes. + ## 1.0.1 Allow installation with PHP 8. No code changes. diff --git a/wcfsetup/install/files/lib/system/api/psr/http-client/composer.json b/wcfsetup/install/files/lib/system/api/psr/http-client/composer.json index e4cab2f3ea4..6fed350beb8 100644 --- a/wcfsetup/install/files/lib/system/api/psr/http-client/composer.json +++ b/wcfsetup/install/files/lib/system/api/psr/http-client/composer.json @@ -10,6 +10,9 @@ "homepage": "https://www.php-fig.org/" } ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, "require": { "php": "^7.0 || ^8.0", "psr/http-message": "^1.0 || ^2.0" diff --git a/wcfsetup/install/files/lib/system/api/scssphp/scssphp/composer.json b/wcfsetup/install/files/lib/system/api/scssphp/scssphp/composer.json index f81203dd7af..d17ffb92428 100644 --- a/wcfsetup/install/files/lib/system/api/scssphp/scssphp/composer.json +++ b/wcfsetup/install/files/lib/system/api/scssphp/scssphp/composer.json @@ -43,7 +43,7 @@ "thoughtbot/bourbon": "^7.0", "twbs/bootstrap": "~5.0", "twbs/bootstrap4": "4.6.1", - "zurb/foundation": "~6.5" + "zurb/foundation": "~6.7.0" }, "repositories": [ { diff --git a/wcfsetup/install/files/lib/system/api/scssphp/scssphp/src/Compiler.php b/wcfsetup/install/files/lib/system/api/scssphp/scssphp/src/Compiler.php index ecafc8cb953..c534c0a9090 100644 --- a/wcfsetup/install/files/lib/system/api/scssphp/scssphp/src/Compiler.php +++ b/wcfsetup/install/files/lib/system/api/scssphp/scssphp/src/Compiler.php @@ -1508,6 +1508,7 @@ protected function filterScopeWithWithout($scope, $with, $without) // start from the root while ($scope->parent && $scope->parent->type !== Type::T_ROOT) { array_unshift($childStash, $scope); + \assert($scope->parent !== null); $scope = $scope->parent; } @@ -2090,6 +2091,11 @@ protected function collapseSelectors($selectors) foreach ($selector as $node) { $compound = ''; + if (!is_array($node)) { + $output[] = $node; + continue; + } + array_walk_recursive( $node, function ($value, $key) use (&$compound) { @@ -2124,12 +2130,16 @@ private function collapseSelectorsAsList($selectors) foreach ($selector as $node) { $compound = ''; - array_walk_recursive( - $node, - function ($value, $key) use (&$compound) { - $compound .= $value; - } - ); + if (!is_array($node)) { + $compound .= $node; + } else { + array_walk_recursive( + $node, + function ($value, $key) use (&$compound) { + $compound .= $value; + } + ); + } if ($this->isImmediateRelationshipCombinator($compound)) { if (\count($output)) { @@ -2885,7 +2895,7 @@ protected function compileChild($child, OutputBlock $out) { if (isset($child[Parser::SOURCE_LINE])) { $this->sourceIndex = isset($child[Parser::SOURCE_INDEX]) ? $child[Parser::SOURCE_INDEX] : null; - $this->sourceLine = isset($child[Parser::SOURCE_LINE]) ? $child[Parser::SOURCE_LINE] : -1; + $this->sourceLine = $child[Parser::SOURCE_LINE]; $this->sourceColumn = isset($child[Parser::SOURCE_COLUMN]) ? $child[Parser::SOURCE_COLUMN] : -1; } elseif (\is_array($child) && isset($child[1]->sourceLine) && $child[1] instanceof Block) { $this->sourceIndex = $child[1]->sourceIndex; @@ -4529,8 +4539,10 @@ public function compileValue($value, $quote = true) return $colorName; } - if (is_numeric($alpha)) { + if (\is_int($alpha) || \is_float($alpha)) { $a = new Number($alpha, ''); + } elseif (is_numeric($alpha)) { + $a = new Number((float) $alpha, ''); } else { $a = $alpha; } @@ -6984,10 +6996,14 @@ protected function coerceValue($value) return static::$null; } - if (is_numeric($value)) { + if (\is_int($value) || \is_float($value)) { return new Number($value, ''); } + if (is_numeric($value)) { + return new Number((float) $value, ''); + } + if ($value === '') { return static::$emptyString; } diff --git a/wcfsetup/install/files/lib/system/api/scssphp/scssphp/src/Parser.php b/wcfsetup/install/files/lib/system/api/scssphp/scssphp/src/Parser.php index 1c76e7c6b04..c5888f56aa4 100644 --- a/wcfsetup/install/files/lib/system/api/scssphp/scssphp/src/Parser.php +++ b/wcfsetup/install/files/lib/system/api/scssphp/scssphp/src/Parser.php @@ -273,6 +273,11 @@ public function parse($buffer) $this->saveEncoding(); $this->extractLineNumbers($buffer); + if ($this->utf8 && !preg_match('//u', $buffer)) { + $message = $this->sourceName ? 'Invalid UTF-8 file: ' . $this->sourceName : 'Invalid UTF-8 file'; + throw new ParserException($message); + } + $this->pushBlock(null); // root block $this->whitespace(); $this->pushBlock(null); diff --git a/wcfsetup/install/files/lib/system/api/scssphp/scssphp/src/Version.php b/wcfsetup/install/files/lib/system/api/scssphp/scssphp/src/Version.php index d604a505764..15c682d3d74 100644 --- a/wcfsetup/install/files/lib/system/api/scssphp/scssphp/src/Version.php +++ b/wcfsetup/install/files/lib/system/api/scssphp/scssphp/src/Version.php @@ -19,5 +19,5 @@ */ class Version { - const VERSION = '1.11.0'; + const VERSION = '1.11.1'; } From 5a7d5783d1591761cc07c3c938cbbbd83fcd9026 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Tue, 26 Sep 2023 16:54:58 +0200 Subject: [PATCH 04/21] Make use of the `LicenseApi` to validate the license --- .../lib/acp/form/LicenseEditForm.class.php | 69 ++++--------------- .../package/license/LicenseApi.class.php | 30 +++++--- 2 files changed, 36 insertions(+), 63 deletions(-) diff --git a/wcfsetup/install/files/lib/acp/form/LicenseEditForm.class.php b/wcfsetup/install/files/lib/acp/form/LicenseEditForm.class.php index ae465e4a96d..4a1d1a831b5 100644 --- a/wcfsetup/install/files/lib/acp/form/LicenseEditForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/LicenseEditForm.class.php @@ -2,11 +2,7 @@ namespace wcf\acp\form; -use CuyZ\Valinor\Mapper\MappingError; -use CuyZ\Valinor\Mapper\Source\Source; -use CuyZ\Valinor\MapperBuilder; use GuzzleHttp\Exception\ConnectException; -use GuzzleHttp\Psr7\Request; use Laminas\Diactoros\Response\RedirectResponse; use Psr\Http\Client\ClientExceptionInterface; use wcf\data\option\Option; @@ -23,9 +19,9 @@ use wcf\system\form\builder\field\TextFormField; use wcf\system\form\builder\field\validation\FormFieldValidationError; use wcf\system\form\builder\field\validation\FormFieldValidator; -use wcf\system\io\HttpFactory; +use wcf\system\package\license\exception\ParsingFailed; +use wcf\system\package\license\LicenseApi; use wcf\system\request\LinkHandler; -use wcf\system\WCF; /** * Set up or edit the license data. @@ -52,7 +48,7 @@ final class LicenseEditForm extends AbstractFormBuilderForm */ public $templateName = 'licenseEdit'; - private array $apiResponse; + private LicenseApi $licenseApi; private string $url; @@ -109,13 +105,16 @@ protected function createForm() \assert($licenseNo instanceof TextFormField); try { - $this->apiResponse = $this->getLicenseData($licenseNo->getValue(), $serialNo->getValue()); + $this->licenseApi = LicenseApi::fetchFromRemote([ + 'username' => $licenseNo->getValue(), + 'password' => $serialNo->getValue(), + ]); } catch (ConnectException) { $serialNo->addValidationError(new FormFieldValidationError( 'failedConnect', 'wcf.acp.firstTimeSetup.license.credentials.error.failedConnect' )); - } catch (ClientExceptionInterface | MappingError) { + } catch (ClientExceptionInterface | ParsingFailed) { $serialNo->addValidationError(new FormFieldValidationError( 'failedValidation', 'wcf.acp.firstTimeSetup.license.credentials.error.failedValidation' @@ -161,43 +160,6 @@ protected function setFormAction() ); } - private function getLicenseData(string $licenseNo, string $serialNo): array - { - $request = new Request( - 'POST', - 'https://api.woltlab.com/2.0/customer/license/list.json', - [ - 'content-type' => 'application/x-www-form-urlencoded', - ], - \http_build_query([ - 'licenseNo' => $licenseNo, - 'serialNo' => $serialNo, - 'instanceId' => \hash_hmac('sha256', 'api.woltlab.com', \WCF_UUID), - ], '', '&', \PHP_QUERY_RFC1738) - ); - - $response = HttpFactory::makeClientWithTimeout(5)->send($request); - - return (new MapperBuilder()) - ->allowSuperfluousKeys() - ->mapper() - ->map( - <<<'EOT' - array { - status: 200, - license: array { - authCode?: string, - type: string, - expiryDates?: array, - }, - pluginstore: array, - woltlab: array, - } - EOT, - Source::json($response->getBody()) - ); - } - /** * @inheritDoc */ @@ -238,21 +200,20 @@ public function save() $objectAction->executeAction(); } - if (isset($this->apiResponse) && isset($this->apiResponse['license']['authCode'])) { - $optionData = [ - Option::getOptionByName('package_server_auth_code')->optionID => $this->apiResponse['license']['authCode'], - ]; + $authCode = ''; + if (isset($this->licenseApi)) { + $authCode = $this->licenseApi->getData()['license']['authCode'] ?? ''; } else { - $optionData = [ - Option::getOptionByName('package_server_auth_code')->optionID => '', - ]; + LicenseApi::removeLicenseFile(); } $objectAction = new OptionAction( [], 'updateAll', [ - 'data' => $optionData, + 'data' => [ + Option::getOptionByName('package_server_auth_code')->optionID => $authCode + ], ] ); $objectAction->executeAction(); diff --git a/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php b/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php index 0b592ce7a8e..80f059e592f 100644 --- a/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php +++ b/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php @@ -30,8 +30,6 @@ private function __construct(string $json) { $this->json = $json; $this->data = $this->parseLicenseData($this->json); - - $this->updateLicenseFile(); } public function getData(): array @@ -39,7 +37,7 @@ public function getData(): array return $this->data; } - private function updateLicenseFile(): void + public function updateLicenseFile(): void { @\file_put_contents( self::LICENSE_FILE, @@ -83,13 +81,15 @@ private function parseLicenseData(string $json): array return $result; } - public static function fetchFromRemote(): LicenseApi + public static function fetchFromRemote(array $authData = []): LicenseApi { - if (!self::hasLicenseCredentials()) { - throw new MissingCredentials(); - } + if ($authData === []) { + if (!self::hasLicenseCredentials()) { + throw new MissingCredentials(); + } - $authData = PackageUpdateServer::getWoltLabUpdateServer()->getAuthData(); + $authData = PackageUpdateServer::getWoltLabUpdateServer()->getAuthData(); + } $request = new Request( 'POST', @@ -106,7 +106,10 @@ public static function fetchFromRemote(): LicenseApi $response = HttpFactory::makeClientWithTimeout(5)->send($request); - return new LicenseApi($response->getBody()); + $licenseApi = new LicenseApi($response->getBody()); + $licenseApi->updateLicenseFile(); + + return $licenseApi; } public static function readFromFile(): ?LicenseApi @@ -124,6 +127,15 @@ public static function readFromFile(): ?LicenseApi } } + public static function removeLicenseFile(): void + { + if (!\file_exists(self::LICENSE_FILE)) { + return; + } + + \unlink(self::LICENSE_FILE); + } + public static function hasLicenseCredentials(): bool { $authData = PackageUpdateServer::getWoltLabUpdateServer()->getAuthData(); From 2ef8729be834f6b18c4d9d1c4fb5bc644cd58177 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Tue, 26 Sep 2023 17:03:50 +0200 Subject: [PATCH 05/21] Add support for the CKEditor license key --- com.woltlab.wcf/templates/wysiwyg.tpl | 2 +- ts/WoltLabSuite/Core/Component/Ckeditor.ts | 6 +++++- wcfsetup/install/files/acp/templates/wysiwyg.tpl | 2 +- .../js/WoltLabSuite/Core/Component/Ckeditor.js | 5 ++++- .../lib/system/bbcode/BBCodeHandler.class.php | 14 ++++++++++++++ .../system/package/license/LicenseApi.class.php | 9 +++++++-- 6 files changed, 32 insertions(+), 6 deletions(-) diff --git a/com.woltlab.wcf/templates/wysiwyg.tpl b/com.woltlab.wcf/templates/wysiwyg.tpl index c5783014ba2..f8875342848 100644 --- a/com.woltlab.wcf/templates/wysiwyg.tpl +++ b/com.woltlab.wcf/templates/wysiwyg.tpl @@ -106,6 +106,6 @@ {/foreach} ]; - void setupCkeditor(element, features, bbcodes, codeBlockLanguages); + void setupCkeditor(element, features, bbcodes, codeBlockLanguages, '{@$__wcf->getBBCodeHandler()->getCkeditorLicenseKey()|encodeJS}'); }); diff --git a/ts/WoltLabSuite/Core/Component/Ckeditor.ts b/ts/WoltLabSuite/Core/Component/Ckeditor.ts index 88e5cd32409..0eaa6b303c0 100644 --- a/ts/WoltLabSuite/Core/Component/Ckeditor.ts +++ b/ts/WoltLabSuite/Core/Component/Ckeditor.ts @@ -207,7 +207,7 @@ function initializeConfiguration( languages: codeBlockLanguages, }; - (configuration as any).woltlabBbcode = bbcodes; + configuration.woltlabBbcode = bbcodes; if (features.autosave !== "") { initializeAutosave(element, configuration, features.autosave); @@ -252,6 +252,7 @@ export async function setupCkeditor( features: Features, bbcodes: WoltlabBbcodeItem[], codeBlockLanguages: CKEditor5.CodeBlock.CodeBlockConfig["languages"], + licenseKey: string, ): Promise { if (instances.has(element)) { throw new TypeError(`Cannot initialize the editor for '${element.id}' twice.`); @@ -279,6 +280,9 @@ export async function setupCkeditor( } const configuration = initializeConfiguration(element, features, bbcodes, codeBlockLanguages, CKEditor5); + if (licenseKey) { + configuration.licenseKey = licenseKey; + } normalizeLegacyMessage(element); diff --git a/wcfsetup/install/files/acp/templates/wysiwyg.tpl b/wcfsetup/install/files/acp/templates/wysiwyg.tpl index c5783014ba2..f8875342848 100644 --- a/wcfsetup/install/files/acp/templates/wysiwyg.tpl +++ b/wcfsetup/install/files/acp/templates/wysiwyg.tpl @@ -106,6 +106,6 @@ {/foreach} ]; - void setupCkeditor(element, features, bbcodes, codeBlockLanguages); + void setupCkeditor(element, features, bbcodes, codeBlockLanguages, '{@$__wcf->getBBCodeHandler()->getCkeditorLicenseKey()|encodeJS}'); }); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Ckeditor.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Ckeditor.js index e3b713d4cb2..5b968abdd82 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Ckeditor.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Ckeditor.js @@ -179,7 +179,7 @@ define(["require", "exports", "tslib", "./Ckeditor/Attachment", "./Ckeditor/Medi } return false; } - async function setupCkeditor(element, features, bbcodes, codeBlockLanguages) { + async function setupCkeditor(element, features, bbcodes, codeBlockLanguages, licenseKey) { if (instances.has(element)) { throw new TypeError(`Cannot initialize the editor for '${element.id}' twice.`); } @@ -200,6 +200,9 @@ define(["require", "exports", "tslib", "./Ckeditor/Attachment", "./Ckeditor/Medi (0, Quote_1.setup)(element); } const configuration = initializeConfiguration(element, features, bbcodes, codeBlockLanguages, CKEditor5); + if (licenseKey) { + configuration.licenseKey = licenseKey; + } (0, Normalizer_1.normalizeLegacyMessage)(element); const cke = await createEditor(element, configuration); const ckeditor = new Ckeditor(cke, features); diff --git a/wcfsetup/install/files/lib/system/bbcode/BBCodeHandler.class.php b/wcfsetup/install/files/lib/system/bbcode/BBCodeHandler.class.php index a8597db42e2..b7b80806cb8 100644 --- a/wcfsetup/install/files/lib/system/bbcode/BBCodeHandler.class.php +++ b/wcfsetup/install/files/lib/system/bbcode/BBCodeHandler.class.php @@ -5,6 +5,7 @@ use wcf\data\bbcode\BBCode; use wcf\data\bbcode\BBCodeCache; use wcf\system\application\ApplicationHandler; +use wcf\system\package\license\LicenseApi; use wcf\system\SingletonFactory; use wcf\system\WCF; use wcf\util\ArrayUtil; @@ -321,4 +322,17 @@ public function getEditorLocalization(): string // yield any module if this locale is (implicitly) requested. return ""; } + + /** + * @since 6.0 + */ + public function getCkeditorLicenseKey(): string + { + $licenseApi = LicenseApi::readFromFile(); + if ($licenseApi === null) { + return ''; + } + + return $licenseApi->getData()['license']['ckeditorLicenseKey'] ?? ''; + } } diff --git a/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php b/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php index 80f059e592f..dbe9962c346 100644 --- a/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php +++ b/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php @@ -43,8 +43,13 @@ public function updateLicenseFile(): void self::LICENSE_FILE, \sprintf( <<<'EOT' - return '%s'; + json, ) ); @@ -118,7 +123,7 @@ public static function readFromFile(): ?LicenseApi return null; } - $content = \file_get_contents(self::LICENSE_FILE); + $content = require(self::LICENSE_FILE); try { return new LicenseApi($content); From a5bec565435db4c439a0dad24d018a611cffc403 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Tue, 26 Sep 2023 17:05:55 +0200 Subject: [PATCH 06/21] Make use of the license API in the first time setup --- .../form/FirstTimeSetupLicenseForm.class.php | 57 ++++--------------- 1 file changed, 10 insertions(+), 47 deletions(-) diff --git a/wcfsetup/install/files/lib/acp/form/FirstTimeSetupLicenseForm.class.php b/wcfsetup/install/files/lib/acp/form/FirstTimeSetupLicenseForm.class.php index f5427d06f73..374e30ab7f4 100644 --- a/wcfsetup/install/files/lib/acp/form/FirstTimeSetupLicenseForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/FirstTimeSetupLicenseForm.class.php @@ -2,11 +2,7 @@ namespace wcf\acp\form; -use CuyZ\Valinor\Mapper\MappingError; -use CuyZ\Valinor\Mapper\Source\Source; -use CuyZ\Valinor\MapperBuilder; use GuzzleHttp\Exception\ConnectException; -use GuzzleHttp\Psr7\Request; use Psr\Http\Client\ClientExceptionInterface; use wcf\data\option\Option; use wcf\data\option\OptionAction; @@ -21,7 +17,8 @@ use wcf\system\form\builder\field\TextFormField; use wcf\system\form\builder\field\validation\FormFieldValidationError; use wcf\system\form\builder\field\validation\FormFieldValidator; -use wcf\system\io\HttpFactory; +use wcf\system\package\license\exception\ParsingFailed; +use wcf\system\package\license\LicenseApi; use wcf\system\request\LinkHandler; use wcf\util\HeaderUtil; @@ -40,7 +37,7 @@ final class FirstTimeSetupLicenseForm extends AbstractFormBuilderForm */ public $neededPermissions = ['admin.configuration.package.canEditServer']; - private array $apiResponse; + private LicenseApi $licenseApi; /** * @inheritDoc @@ -84,13 +81,16 @@ protected function createForm() \assert($licenseNo instanceof TextFormField); try { - $this->apiResponse = $this->getLicenseData($licenseNo->getValue(), $serialNo->getValue()); + $this->licenseApi = LicenseApi::fetchFromRemote([ + 'username' => $licenseNo->getValue(), + 'password' => $serialNo->getValue(), + ]); } catch (ConnectException) { $serialNo->addValidationError(new FormFieldValidationError( 'failedConnect', 'wcf.acp.firstTimeSetup.license.credentials.error.failedConnect' )); - } catch (ClientExceptionInterface | MappingError) { + } catch (ClientExceptionInterface | ParsingFailed) { $serialNo->addValidationError(new FormFieldValidationError( 'failedValidation', 'wcf.acp.firstTimeSetup.license.credentials.error.failedValidation' @@ -113,43 +113,6 @@ protected function createForm() ); } - private function getLicenseData(string $licenseNo, string $serialNo): array - { - $request = new Request( - 'POST', - 'https://api.woltlab.com/2.0/customer/license/list.json', - [ - 'content-type' => 'application/x-www-form-urlencoded', - ], - \http_build_query([ - 'licenseNo' => $licenseNo, - 'serialNo' => $serialNo, - 'instanceId' => \hash_hmac('sha256', 'api.woltlab.com', \WCF_UUID), - ], '', '&', \PHP_QUERY_RFC1738) - ); - - $response = HttpFactory::makeClientWithTimeout(5)->send($request); - - return (new MapperBuilder()) - ->allowSuperfluousKeys() - ->mapper() - ->map( - <<<'EOT' - array { - status: 200, - license: array { - authCode?: string, - type: string, - expiryDates?: array, - }, - pluginstore: array, - woltlab: array, - } - EOT, - Source::json($response->getBody()) - ); - } - /** * @inheritDoc */ @@ -189,8 +152,8 @@ public function save() Option::getOptionByName('first_time_setup_state')->optionID => 1, ]; - if (isset($this->apiResponse) && isset($this->apiResponse['license']['authCode'])) { - $optionData[Option::getOptionByName('package_server_auth_code')->optionID] = $this->apiResponse['license']['authCode']; + if (isset($this->licenseApi) && isset($this->licenseApi->getData()['license']['authCode'])) { + $optionData[Option::getOptionByName('package_server_auth_code')->optionID] = $this->licenseApi->getData()['license']['authCode']; } $objectAction = new OptionAction( From 0f5abeda1423f783b9449caa87ce40f2b00a524f Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Tue, 26 Sep 2023 17:28:20 +0200 Subject: [PATCH 07/21] Dynamically apply the branding free license --- .../files/lib/bootstrap/com.woltlab.wcf.php | 19 +++++++++++++++++++ .../lib/data/option/OptionEditor.class.php | 4 ++++ 2 files changed, 23 insertions(+) diff --git a/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php b/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php index 59732e3992c..c6a3ea339d2 100644 --- a/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php +++ b/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php @@ -15,6 +15,7 @@ use wcf\system\language\preload\PhrasePreloader; use wcf\system\package\event\PackageInstallationPluginSynced; use wcf\system\package\event\PackageListChanged; +use wcf\system\package\license\LicenseApi; use wcf\system\user\authentication\event\UserLoggedIn; use wcf\system\user\event\UsernameValidating; use wcf\system\WCF; @@ -62,4 +63,22 @@ $event->register(\wcf\system\worker\SitemapRebuildWorker::class, 500); $event->register(\wcf\system\worker\StatDailyRebuildDataWorker::class, 800); }); + + try { + $licenseApi = LicenseApi::readFromFile(); + if ($licenseApi !== null) { + $licenseData = $licenseApi->getData(); + $brandingFree = $licenseData['woltlab']['com.woltlab.brandingFree'] ?? '0.0'; + $expiresAt = $licenseData['expiryDates']['com.woltlab.brandingFree'] ?? \TIME_NOW; + if ($brandingFree !== '0.0' && $expiresAt >= \TIME_NOW) { + define('WOLTLAB_BRANDING', false); + } + } + } catch (\Throwable) { + // Reading the license file must never cause any errors. + } + + if (!defined('WOLTLAB_BRANDING')) { + define('WOLTLAB_BRANDING', true); + } }; diff --git a/wcfsetup/install/files/lib/data/option/OptionEditor.class.php b/wcfsetup/install/files/lib/data/option/OptionEditor.class.php index 8cc79d37c15..dece6ba6638 100644 --- a/wcfsetup/install/files/lib/data/option/OptionEditor.class.php +++ b/wcfsetup/install/files/lib/data/option/OptionEditor.class.php @@ -158,6 +158,10 @@ public static function rebuild() // get all options $options = $secretOptions + Option::getOptions(); foreach ($options as $optionName => $option) { + if ($optionName === 'WOLTLAB_BRANDING') { + continue; + } + $writeValue = $option->optionValue; if ($writeValue === null) { $writeValue = "''"; From b2492662c97d2c4f373d0054a6594ee795e1b0c5 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Tue, 26 Sep 2023 17:40:52 +0200 Subject: [PATCH 08/21] Add the update script to write the license file and sync credentials --- com.woltlab.wcf/package.xml | 7 ++++ .../acp/update_com.woltlab.wcf_6.0.0.rc.1.php | 22 ------------ .../update_com.woltlab.wcf_6.0_license.php | 35 +++++++++++++++++++ 3 files changed, 42 insertions(+), 22 deletions(-) delete mode 100644 wcfsetup/install/files/acp/update_com.woltlab.wcf_6.0.0.rc.1.php create mode 100644 wcfsetup/install/files/acp/update_com.woltlab.wcf_6.0_license.php diff --git a/com.woltlab.wcf/package.xml b/com.woltlab.wcf/package.xml index 77c86e5e341..2e0a1992641 100644 --- a/com.woltlab.wcf/package.xml +++ b/com.woltlab.wcf/package.xml @@ -123,6 +123,7 @@ tar cvf com.woltlab.wcf/files_pre_check.tar -C wcfsetup/install/files/ \ acp/update_com.woltlab.wcf_6.0_favicon.php acp/update_com.woltlab.wcf_6.0_trophies.php acp/update_com.woltlab.wcf_6.0_removeDownloadedGravatars.php + acp/update_com.woltlab.wcf_6.0_license.php acp/update_com.woltlab.wcf_6.0_removeLegacyAppConfig.php @@ -130,6 +131,12 @@ tar cvf com.woltlab.wcf/files_pre_check.tar -C wcfsetup/install/files/ \ + + acptemplates_update.tar files_update.tar diff --git a/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.0.0.rc.1.php b/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.0.0.rc.1.php deleted file mode 100644 index 084d17bc6ff..00000000000 --- a/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.0.0.rc.1.php +++ /dev/null @@ -1,22 +0,0 @@ - - */ - -use wcf\system\WCF; - -// Follow-up for https://github.com/WoltLab/WCF/commit/ccf4bcc71444a0c75c7543483585706895b51bd8 -$sql = "UPDATE wcf1_style_variable_value - SET variableValueDarkMode = ? - WHERE variableID = ( - SELECT variableID - FROM wcf1_style_variable - WHERE variableName = ? - )"; -$statement = WCF::getDB()->prepare($sql); -$statement->execute([null, 'individualScssDarkMode']); diff --git a/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.0_license.php b/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.0_license.php new file mode 100644 index 00000000000..c9a4afefd98 --- /dev/null +++ b/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.0_license.php @@ -0,0 +1,35 @@ + + */ + +use wcf\data\package\update\server\PackageUpdateServer; +use wcf\data\package\update\server\PackageUpdateServerEditor; +use wcf\system\package\license\LicenseApi; + +try { + // Side-effect: Writes the license file. + LicenseApi::fetchFromRemote(); + + // If we’re still here it means that the credentials are actually valid. Now + // we can check if the credentials for both servers are in sync, because + // traditionally users could use their account credentials to authenticate. + $updateServer = PackageUpdateServer::getWoltLabUpdateServer(); + $storeServer = PackageUpdateServer::getPluginStoreServer(); + + if ($updateServer->getAuthData() !== $storeServer->getAuthData()) { + $authData = $updateServer->getAuthData(); + + (new PackageUpdateServerEditor($storeServer))->update([ + 'username' => $authData['username'], + 'password' => $authData['password'], + ]); + } +} catch (\Throwable) { + // This action must be silent, failing to execute is not an issue here. +} From fb9b8a919f92a77f26be6e7f70911315bfac12fc Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Wed, 27 Sep 2023 13:58:52 +0200 Subject: [PATCH 09/21] Remove the side effect of fetching the license data from the remote --- .../files/acp/update_com.woltlab.wcf_6.0_license.php | 4 ++-- .../lib/acp/form/FirstTimeSetupLicenseForm.class.php | 8 ++++++-- .../install/files/lib/acp/form/LicenseEditForm.class.php | 2 ++ wcfsetup/install/files/lib/acp/page/LicensePage.class.php | 5 ++++- .../files/lib/system/package/license/LicenseApi.class.php | 5 +---- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.0_license.php b/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.0_license.php index c9a4afefd98..4280047a299 100644 --- a/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.0_license.php +++ b/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.0_license.php @@ -13,8 +13,8 @@ use wcf\system\package\license\LicenseApi; try { - // Side-effect: Writes the license file. - LicenseApi::fetchFromRemote(); + $licenseApi = LicenseApi::fetchFromRemote(); + $licenseApi->updateLicenseFile(); // If we’re still here it means that the credentials are actually valid. Now // we can check if the credentials for both servers are in sync, because diff --git a/wcfsetup/install/files/lib/acp/form/FirstTimeSetupLicenseForm.class.php b/wcfsetup/install/files/lib/acp/form/FirstTimeSetupLicenseForm.class.php index 374e30ab7f4..a0e791341b7 100644 --- a/wcfsetup/install/files/lib/acp/form/FirstTimeSetupLicenseForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/FirstTimeSetupLicenseForm.class.php @@ -152,8 +152,12 @@ public function save() Option::getOptionByName('first_time_setup_state')->optionID => 1, ]; - if (isset($this->licenseApi) && isset($this->licenseApi->getData()['license']['authCode'])) { - $optionData[Option::getOptionByName('package_server_auth_code')->optionID] = $this->licenseApi->getData()['license']['authCode']; + if (isset($this->licenseApi)) { + $this->licenseApi->updateLicenseFile(); + + if (isset($this->licenseApi->getData()['license']['authCode'])) { + $optionData[Option::getOptionByName('package_server_auth_code')->optionID] = $this->licenseApi->getData()['license']['authCode']; + } } $objectAction = new OptionAction( diff --git a/wcfsetup/install/files/lib/acp/form/LicenseEditForm.class.php b/wcfsetup/install/files/lib/acp/form/LicenseEditForm.class.php index 4a1d1a831b5..df8f4883eba 100644 --- a/wcfsetup/install/files/lib/acp/form/LicenseEditForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/LicenseEditForm.class.php @@ -202,6 +202,8 @@ public function save() $authCode = ''; if (isset($this->licenseApi)) { + $this->licenseApi->updateLicenseFile(); + $authCode = $this->licenseApi->getData()['license']['authCode'] ?? ''; } else { LicenseApi::removeLicenseFile(); diff --git a/wcfsetup/install/files/lib/acp/page/LicensePage.class.php b/wcfsetup/install/files/lib/acp/page/LicensePage.class.php index 5811e65e4bc..ed489c9d54c 100644 --- a/wcfsetup/install/files/lib/acp/page/LicensePage.class.php +++ b/wcfsetup/install/files/lib/acp/page/LicensePage.class.php @@ -59,7 +59,10 @@ public function readData() (new PackageUpdateAction([], 'refreshDatabase'))->executeAction(); - $this->licenseData = LicenseApi::fetchFromRemote()->getData(); + $licenseApi = LicenseApi::fetchFromRemote(); + $licenseApi->updateLicenseFile(); + + $this->licenseData = $licenseApi->getData(); if (isset($this->licenseData['license']['licenseID'])) { $this->licenseNumber = $this->licenseData['license']['licenseID']; } diff --git a/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php b/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php index dbe9962c346..74a15b41475 100644 --- a/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php +++ b/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php @@ -111,10 +111,7 @@ public static function fetchFromRemote(array $authData = []): LicenseApi $response = HttpFactory::makeClientWithTimeout(5)->send($request); - $licenseApi = new LicenseApi($response->getBody()); - $licenseApi->updateLicenseFile(); - - return $licenseApi; + return new LicenseApi($response->getBody()); } public static function readFromFile(): ?LicenseApi From bdad6cfaa65262ab123a3e40c47fb24f4dd1b86d Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Wed, 27 Sep 2023 14:00:28 +0200 Subject: [PATCH 10/21] Reformat the code to use an early return --- .../cronjob/GetUpdateInfoCronjob.class.php | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/wcfsetup/install/files/lib/system/cronjob/GetUpdateInfoCronjob.class.php b/wcfsetup/install/files/lib/system/cronjob/GetUpdateInfoCronjob.class.php index ddae3ea4b27..55d938e0c70 100644 --- a/wcfsetup/install/files/lib/system/cronjob/GetUpdateInfoCronjob.class.php +++ b/wcfsetup/install/files/lib/system/cronjob/GetUpdateInfoCronjob.class.php @@ -23,19 +23,21 @@ public function execute(Cronjob $cronjob) { parent::execute($cronjob); - if (!ENABLE_BENCHMARK) { - try { - $currentLanguage = WCF::getLanguage(); - // Always fetch package information using the default language. - if ($currentLanguage->languageID !== LanguageFactory::getInstance()->getDefaultLanguage()->languageID) { - WCF::setLanguage(LanguageFactory::getInstance()->getDefaultLanguage()); - } + if (ENABLE_BENCHMARK) { + return; + } + + try { + $currentLanguage = WCF::getLanguage(); + // Always fetch package information using the default language. + if ($currentLanguage->languageID !== LanguageFactory::getInstance()->getDefaultLanguage()->languageID) { + WCF::setLanguage(LanguageFactory::getInstance()->getDefaultLanguage()); + } - PackageUpdateDispatcher::getInstance()->refreshPackageDatabase([], true); - } finally { - if ($currentLanguage->languageID !== LanguageFactory::getInstance()->getDefaultLanguage()->languageID) { - WCF::setLanguage($currentLanguage); - } + PackageUpdateDispatcher::getInstance()->refreshPackageDatabase([], true); + } finally { + if ($currentLanguage->languageID !== LanguageFactory::getInstance()->getDefaultLanguage()->languageID) { + WCF::setLanguage($currentLanguage); } } } From 261fcf074e06d6371aef68d0b675bf9713345ef2 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Wed, 27 Sep 2023 14:05:09 +0200 Subject: [PATCH 11/21] Refresh the license file when searching for updates --- .../update/PackageUpdateAction.class.php | 7 +++++++ .../cronjob/GetUpdateInfoCronjob.class.php | 17 +++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/wcfsetup/install/files/lib/data/package/update/PackageUpdateAction.class.php b/wcfsetup/install/files/lib/data/package/update/PackageUpdateAction.class.php index 1371c38bf07..0b2f0c490ad 100644 --- a/wcfsetup/install/files/lib/data/package/update/PackageUpdateAction.class.php +++ b/wcfsetup/install/files/lib/data/package/update/PackageUpdateAction.class.php @@ -13,6 +13,7 @@ use wcf\system\exception\NamedUserException; use wcf\system\exception\SystemException; use wcf\system\exception\UserInputException; +use wcf\system\package\license\LicenseApi; use wcf\system\package\PackageInstallationScheduler; use wcf\system\package\PackageUpdateDispatcher; use wcf\system\package\PackageUpdateUnauthorizedException; @@ -610,6 +611,12 @@ public function searchForUpdates() { PackageUpdateDispatcher::getInstance()->refreshPackageDatabase([], $this->parameters['ignoreCache']); + // Try to update the cached license data to check for recent purchases. + if (LicenseApi::hasLicenseCredentials()) { + $licenseApi = LicenseApi::fetchFromRemote(); + $licenseApi->updateLicenseFile(); + } + $updates = PackageUpdateDispatcher::getInstance()->getAvailableUpdates(); $url = ''; if (!empty($updates)) { diff --git a/wcfsetup/install/files/lib/system/cronjob/GetUpdateInfoCronjob.class.php b/wcfsetup/install/files/lib/system/cronjob/GetUpdateInfoCronjob.class.php index 55d938e0c70..9d0bde92edf 100644 --- a/wcfsetup/install/files/lib/system/cronjob/GetUpdateInfoCronjob.class.php +++ b/wcfsetup/install/files/lib/system/cronjob/GetUpdateInfoCronjob.class.php @@ -4,6 +4,7 @@ use wcf\data\cronjob\Cronjob; use wcf\system\language\LanguageFactory; +use wcf\system\package\license\LicenseApi; use wcf\system\package\PackageUpdateDispatcher; use wcf\system\WCF; @@ -40,5 +41,21 @@ public function execute(Cronjob $cronjob) WCF::setLanguage($currentLanguage); } } + + $this->refreshLicenseFile(); + } + + /** + * Refresh the license file to update any recently made purchases. + */ + private function refreshLicenseFile(): void + { + try { + $licenseApi = LicenseApi::fetchFromRemote(); + $licenseApi->updateLicenseFile(); + } catch (\Throwable) { + // This is a “silent” operation that should not interrupt the + // execution of cronjobs in case of an error. + } } } From 974f03632655753bfe954ecefd81913f52a67fc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 27 Sep 2023 15:03:21 +0200 Subject: [PATCH 12/21] Map the license data into an object --- .../install/files/acp/templates/license.tpl | 6 ++-- .../form/FirstTimeSetupLicenseForm.class.php | 4 +-- .../lib/acp/form/LicenseEditForm.class.php | 2 +- .../files/lib/acp/page/LicensePage.class.php | 22 ++++++++----- .../files/lib/bootstrap/com.woltlab.wcf.php | 4 +-- .../lib/system/bbcode/BBCodeHandler.class.php | 2 +- .../package/license/LicenseApi.class.php | 26 +++------------ .../package/license/LicenseData.class.php | 33 +++++++++++++++++++ 8 files changed, 60 insertions(+), 39 deletions(-) create mode 100644 wcfsetup/install/files/lib/system/package/license/LicenseData.class.php diff --git a/wcfsetup/install/files/acp/templates/license.tpl b/wcfsetup/install/files/acp/templates/license.tpl index 4ba0759db55..a1f1306596a 100644 --- a/wcfsetup/install/files/acp/templates/license.tpl +++ b/wcfsetup/install/files/acp/templates/license.tpl @@ -59,7 +59,7 @@ {/hascontent} -{if $licenseData[license][type] === 'developer'} +{if $licenseData->license[type] === 'developer'}

{lang}wcf.acp.license.developerLicense{/lang}

{/if} @@ -76,7 +76,7 @@ {content} - {foreach from=$licenseData[woltlab] key=package item=majorVersion} + {foreach from=$availablePackages[woltlab] key=package item=majorVersion} {if $installedPackages[$package]|isset} @@ -135,7 +135,7 @@ {content} - {foreach from=$licenseData[pluginstore] key=package item=majorVersion} + {foreach from=$availablePackages[pluginstore] key=package item=majorVersion} {if $installedPackages[$package]|isset} diff --git a/wcfsetup/install/files/lib/acp/form/FirstTimeSetupLicenseForm.class.php b/wcfsetup/install/files/lib/acp/form/FirstTimeSetupLicenseForm.class.php index a0e791341b7..41f60590dc0 100644 --- a/wcfsetup/install/files/lib/acp/form/FirstTimeSetupLicenseForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/FirstTimeSetupLicenseForm.class.php @@ -155,8 +155,8 @@ public function save() if (isset($this->licenseApi)) { $this->licenseApi->updateLicenseFile(); - if (isset($this->licenseApi->getData()['license']['authCode'])) { - $optionData[Option::getOptionByName('package_server_auth_code')->optionID] = $this->licenseApi->getData()['license']['authCode']; + if (isset($this->licenseApi->getData()->license['authCode'])) { + $optionData[Option::getOptionByName('package_server_auth_code')->optionID] = $this->licenseApi->getData()->license['authCode']; } } diff --git a/wcfsetup/install/files/lib/acp/form/LicenseEditForm.class.php b/wcfsetup/install/files/lib/acp/form/LicenseEditForm.class.php index df8f4883eba..7a932838a97 100644 --- a/wcfsetup/install/files/lib/acp/form/LicenseEditForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/LicenseEditForm.class.php @@ -204,7 +204,7 @@ public function save() if (isset($this->licenseApi)) { $this->licenseApi->updateLicenseFile(); - $authCode = $this->licenseApi->getData()['license']['authCode'] ?? ''; + $authCode = $this->licenseApi->getData()->license['authCode'] ?? ''; } else { LicenseApi::removeLicenseFile(); } diff --git a/wcfsetup/install/files/lib/acp/page/LicensePage.class.php b/wcfsetup/install/files/lib/acp/page/LicensePage.class.php index ed489c9d54c..baec0f028a6 100644 --- a/wcfsetup/install/files/lib/acp/page/LicensePage.class.php +++ b/wcfsetup/install/files/lib/acp/page/LicensePage.class.php @@ -11,6 +11,7 @@ use wcf\page\AbstractPage; use wcf\system\database\util\PreparedStatementConditionBuilder; use wcf\system\package\license\LicenseApi; +use wcf\system\package\license\LicenseData; use wcf\system\request\LinkHandler; use wcf\system\WCF; @@ -28,7 +29,9 @@ final class LicensePage extends AbstractPage public $neededPermissions = ['admin.configuration.package.canInstallPackage']; - private array $licenseData; + private LicenseData $licenseData; + + private array $availablePackages = []; private int $licenseNumber; @@ -63,13 +66,13 @@ public function readData() $licenseApi->updateLicenseFile(); $this->licenseData = $licenseApi->getData(); - if (isset($this->licenseData['license']['licenseID'])) { - $this->licenseNumber = $this->licenseData['license']['licenseID']; + if (isset($this->licenseData->license['licenseID'])) { + $this->licenseNumber = $this->licenseData->license['licenseID']; } $identifiers = \array_merge( - \array_keys($this->licenseData['woltlab']), - \array_keys($this->licenseData['pluginstore']) + \array_keys($this->licenseData->woltlab), + \array_keys($this->licenseData->pluginstore) ); $this->installedPackages = $this->getInstalledPackages($identifiers); @@ -82,8 +85,8 @@ public function readData() } foreach (['woltlab', 'pluginstore'] as $type) { - $this->licenseData[$type] = \array_filter( - $this->licenseData[$type], + $this->availablePackages[$type] = \array_filter( + $this->licenseData->{$type}, function (string $package) { if (isset($this->installedPackages[$package])) { return true; @@ -94,7 +97,7 @@ function (string $package) { \ARRAY_FILTER_USE_KEY ); - \uksort($this->licenseData[$type], function ($packageA, $packageB) { + \uksort($this->availablePackages[$type], function ($packageA, $packageB) { $a = $this->installedPackages[$packageA] ?? $this->packageUpdates[$packageA]; $b = $this->installedPackages[$packageB] ?? $this->packageUpdates[$packageB]; @@ -105,7 +108,7 @@ function (string $package) { }); } - foreach ($this->licenseData['woltlab'] as $identifier => $accessibleVersion) { + foreach ($this->availablePackages['woltlab'] as $identifier => $accessibleVersion) { if ($accessibleVersion === '*') { continue; } @@ -124,6 +127,7 @@ public function assignVariables() WCF::getTPL()->assign([ 'licenseData' => $this->licenseData, + 'availablePackages' => $this->availablePackages, 'licenseNumber' => $this->licenseNumber, 'installedPackages' => $this->installedPackages, 'installablePackages' => $this->installablePackages, diff --git a/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php b/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php index c6a3ea339d2..fb03c436f7c 100644 --- a/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php +++ b/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php @@ -68,8 +68,8 @@ $licenseApi = LicenseApi::readFromFile(); if ($licenseApi !== null) { $licenseData = $licenseApi->getData(); - $brandingFree = $licenseData['woltlab']['com.woltlab.brandingFree'] ?? '0.0'; - $expiresAt = $licenseData['expiryDates']['com.woltlab.brandingFree'] ?? \TIME_NOW; + $brandingFree = $licenseData->woltlab['com.woltlab.brandingFree'] ?? '0.0'; + $expiresAt = $licenseData->license['expiryDates']['com.woltlab.brandingFree'] ?? \TIME_NOW; if ($brandingFree !== '0.0' && $expiresAt >= \TIME_NOW) { define('WOLTLAB_BRANDING', false); } diff --git a/wcfsetup/install/files/lib/system/bbcode/BBCodeHandler.class.php b/wcfsetup/install/files/lib/system/bbcode/BBCodeHandler.class.php index b7b80806cb8..f4a7c11e230 100644 --- a/wcfsetup/install/files/lib/system/bbcode/BBCodeHandler.class.php +++ b/wcfsetup/install/files/lib/system/bbcode/BBCodeHandler.class.php @@ -333,6 +333,6 @@ public function getCkeditorLicenseKey(): string return ''; } - return $licenseApi->getData()['license']['ckeditorLicenseKey'] ?? ''; + return $licenseApi->getData()->license['ckeditorLicenseKey'] ?? ''; } } diff --git a/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php b/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php index 74a15b41475..bd669d3cd15 100644 --- a/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php +++ b/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php @@ -21,7 +21,7 @@ */ final class LicenseApi { - private readonly array $data; + private readonly LicenseData $data; private readonly string $json; private const LICENSE_FILE = \WCF_DIR . 'license.php'; @@ -32,7 +32,7 @@ private function __construct(string $json) $this->data = $this->parseLicenseData($this->json); } - public function getData(): array + public function getData(): LicenseData { return $this->data; } @@ -55,35 +55,19 @@ public function updateLicenseFile(): void ); } - private function parseLicenseData(string $json): array + private function parseLicenseData(string $json): LicenseData { try { - /** @var array $result */ - $result = (new MapperBuilder()) + return (new MapperBuilder()) ->allowSuperfluousKeys() ->mapper() ->map( - <<<'EOT' - array { - status: 200, - license: array { - authCode?: string, - licenseID?: int, - type: string, - expiryDates?: array, - ckeditorLicenseKey?: string, - }, - pluginstore: array, - woltlab: array, - } - EOT, + LicenseData::class, Source::json($json) ); } catch (MappingError $e) { throw new ParsingFailed($e); } - - return $result; } public static function fetchFromRemote(array $authData = []): LicenseApi diff --git a/wcfsetup/install/files/lib/system/package/license/LicenseData.class.php b/wcfsetup/install/files/lib/system/package/license/LicenseData.class.php new file mode 100644 index 00000000000..bfdc351f9bd --- /dev/null +++ b/wcfsetup/install/files/lib/system/package/license/LicenseData.class.php @@ -0,0 +1,33 @@ + + * @since 6.0 + */ +final class LicenseData +{ + /** + * @param array{ + * authCode?: string, + * licenseID?: int, + * type: string, + * expiryDates?: array, + * ckeditorLicenseKey?: string, + * } $license + * @param array $pluginstore + * @param array $woltlab + */ + public function __construct( + public readonly array $license, + public readonly array $pluginstore, + public readonly array $woltlab, + ) + { + } +} From 8796b7de71e5fd5e904617de1d61a772767dff33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 27 Sep 2023 15:09:06 +0200 Subject: [PATCH 13/21] Store the license data in serialized form This avoids expensive remapping on every request. --- .../package/license/LicenseApi.class.php | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php b/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php index bd669d3cd15..82c9d77b644 100644 --- a/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php +++ b/wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php @@ -22,14 +22,12 @@ final class LicenseApi { private readonly LicenseData $data; - private readonly string $json; private const LICENSE_FILE = \WCF_DIR . 'license.php'; - private function __construct(string $json) + private function __construct(LicenseData $data) { - $this->json = $json; - $this->data = $this->parseLicenseData($this->json); + $this->data = $data; } public function getData(): LicenseData @@ -45,17 +43,15 @@ public function updateLicenseFile(): void <<<'EOT' json, + \var_export(\serialize($this->data), true), ) ); } - private function parseLicenseData(string $json): LicenseData + private static function parseLicenseData(string $json): LicenseData { try { return (new MapperBuilder()) @@ -95,7 +91,7 @@ public static function fetchFromRemote(array $authData = []): LicenseApi $response = HttpFactory::makeClientWithTimeout(5)->send($request); - return new LicenseApi($response->getBody()); + return new LicenseApi(self::parseLicenseData($response->getBody())); } public static function readFromFile(): ?LicenseApi @@ -104,13 +100,7 @@ public static function readFromFile(): ?LicenseApi return null; } - $content = require(self::LICENSE_FILE); - - try { - return new LicenseApi($content); - } catch (ParsingFailed) { - return null; - } + return new LicenseApi(require(self::LICENSE_FILE)); } public static function removeLicenseFile(): void From 7575b48acd2bba068f580b31e349b371fb390616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 27 Sep 2023 15:16:08 +0200 Subject: [PATCH 14/21] Add helper methods to LicenseData --- wcfsetup/install/files/acp/templates/license.tpl | 8 ++++---- .../install/files/lib/acp/page/LicensePage.class.php | 6 ------ .../lib/system/package/license/LicenseData.class.php | 10 ++++++++++ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/wcfsetup/install/files/acp/templates/license.tpl b/wcfsetup/install/files/acp/templates/license.tpl index a1f1306596a..0f1b81b98aa 100644 --- a/wcfsetup/install/files/acp/templates/license.tpl +++ b/wcfsetup/install/files/acp/templates/license.tpl @@ -34,8 +34,8 @@

{lang}wcf.acp.license{/lang}

- {if $licenseNumber} -

{lang}wcf.acp.license.licenseNo{/lang}

+ {if $licenseData->getLicenseNumber()} +

{lang licenseNumber=$licenseData->getLicenseNumber()}wcf.acp.license.licenseNo{/lang}

{/if}
@@ -59,7 +59,7 @@ {/hascontent}
-{if $licenseData->license[type] === 'developer'} +{if $licenseData->getLicenseType() === 'developer'}

{lang}wcf.acp.license.developerLicense{/lang}

{/if} @@ -104,7 +104,7 @@ {lang accessibleVersion=$requiresLicenseExtension[$package]}wcf.acp.license.package.outdated{/lang} {/if} - {lang}wcf.acp.license.extend{/lang} + {lang}wcf.acp.license.extend{/lang} {else}