From 22a2627e0e2244662eb0567937c33f4a52f0c085 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Thu, 20 Nov 2025 17:57:06 +0100 Subject: [PATCH 1/2] Sync the package version when synchronizing an entire project --- .../Core/Acp/Ui/Devtools/Project/Sync.ts | 27 +++++++- .../Core/Api/DevtoolsProjects/SyncVersion.ts | 19 ++++++ .../Core/Acp/Ui/Devtools/Project/Sync.js | 25 ++++++- .../Core/Api/DevtoolsProjects/SyncVersion.js | 20 ++++++ .../files/lib/bootstrap/com.woltlab.wcf.php | 1 + .../devtoolsProjects/SyncVersion.class.php | 68 +++++++++++++++++++ 6 files changed, 154 insertions(+), 6 deletions(-) create mode 100644 ts/WoltLabSuite/Core/Api/DevtoolsProjects/SyncVersion.ts create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Api/DevtoolsProjects/SyncVersion.js create mode 100644 wcfsetup/install/files/lib/system/endpoint/controller/core/devtoolsProjects/SyncVersion.class.php diff --git a/ts/WoltLabSuite/Core/Acp/Ui/Devtools/Project/Sync.ts b/ts/WoltLabSuite/Core/Acp/Ui/Devtools/Project/Sync.ts index 9e84a1c25e9..1d63c36f186 100644 --- a/ts/WoltLabSuite/Core/Acp/Ui/Devtools/Project/Sync.ts +++ b/ts/WoltLabSuite/Core/Acp/Ui/Devtools/Project/Sync.ts @@ -4,6 +4,7 @@ import { AjaxCallbackSetup, AjaxResponseException } from "../../../../Ajax/Data" import { DialogCallbackSetup } from "../../../../Ui/Dialog/Data"; import { dialogFactory } from "../../../../Component/Dialog"; import { showDefaultSuccessSnackbar } from "WoltLabSuite/Core/Component/Snackbar"; +import { syncVersion } from "WoltLabSuite/Core/Api/DevtoolsProjects/SyncVersion"; interface PipData { dependencies: string[]; @@ -37,6 +38,7 @@ class AcpUiDevtoolsProjectSync { private readonly pips: PipData[] = []; private readonly projectId: number; private queue: PendingPip[] = []; + #syncVersionAfterCompletion = false; constructor(projectId: number) { this.projectId = projectId; @@ -162,6 +164,7 @@ class AcpUiDevtoolsProjectSync { } this.buttonSyncAll.classList.add("disabled"); + this.#syncVersionAfterCompletion = true; this.queue = []; this.pips.forEach((pip) => { @@ -174,9 +177,23 @@ class AcpUiDevtoolsProjectSync { private syncNext(): void { if (this.queue.length === 0) { - this.buttonSyncAll.classList.remove("disabled"); - - showDefaultSuccessSnackbar(); + const showSuccess = () => { + this.buttonSyncAll.classList.remove("disabled"); + + showDefaultSuccessSnackbar(); + }; + + if (this.#syncVersionAfterCompletion) { + void this.#syncPackageVersion() + .then(() => { + showSuccess(); + }) + .finally(() => { + this.#syncVersionAfterCompletion = false; + }); + } else { + showSuccess(); + } return; } @@ -189,6 +206,10 @@ class AcpUiDevtoolsProjectSync { return `${pluginName}-${target}`; } + async #syncPackageVersion(): Promise { + await syncVersion(this.projectId); + } + _ajaxSuccess(data: AjaxResponse): void { const identifier = this.getButtonIdentifier(data.returnValues.pluginName, data.returnValues.target); this.buttons.get(identifier)!.disabled = false; diff --git a/ts/WoltLabSuite/Core/Api/DevtoolsProjects/SyncVersion.ts b/ts/WoltLabSuite/Core/Api/DevtoolsProjects/SyncVersion.ts new file mode 100644 index 00000000000..738a49db80e --- /dev/null +++ b/ts/WoltLabSuite/Core/Api/DevtoolsProjects/SyncVersion.ts @@ -0,0 +1,19 @@ +/** + * Synchronizes the version of an installed package with the versio number in + * the `package.xml` of the project. + * + * @author Alexander Ebert + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.2 + * @woltlabExcludeBundle all + */ + +import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend"; +import { fromInfallibleApiRequest } from "../Result"; + +export async function syncVersion(projectId: number): Promise<[]> { + return fromInfallibleApiRequest(() => { + return prepareRequest(`${window.WSC_RPC_API_URL}core/devtools-projects/${projectId}/sync-version`).post().fetchAsJson(); + }); +} diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Devtools/Project/Sync.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Devtools/Project/Sync.js index 1fbedfa13fb..39a2b6be546 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Devtools/Project/Sync.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Devtools/Project/Sync.js @@ -1,4 +1,4 @@ -define(["require", "exports", "tslib", "../../../../Ajax", "../../../../Language", "../../../../Component/Dialog", "WoltLabSuite/Core/Component/Snackbar"], function (require, exports, tslib_1, Ajax, Language, Dialog_1, Snackbar_1) { +define(["require", "exports", "tslib", "../../../../Ajax", "../../../../Language", "../../../../Component/Dialog", "WoltLabSuite/Core/Component/Snackbar", "WoltLabSuite/Core/Api/DevtoolsProjects/SyncVersion"], function (require, exports, tslib_1, Ajax, Language, Dialog_1, Snackbar_1, SyncVersion_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.init = init; @@ -12,6 +12,7 @@ define(["require", "exports", "tslib", "../../../../Ajax", "../../../../Language pips = []; projectId; queue = []; + #syncVersionAfterCompletion = false; constructor(projectId) { this.projectId = projectId; const restrictedSync = document.getElementById("syncShowOnlyMatches"); @@ -108,6 +109,7 @@ define(["require", "exports", "tslib", "../../../../Ajax", "../../../../Language return; } this.buttonSyncAll.classList.add("disabled"); + this.#syncVersionAfterCompletion = true; this.queue = []; this.pips.forEach((pip) => { pip.targets.forEach((target) => { @@ -118,8 +120,22 @@ define(["require", "exports", "tslib", "../../../../Ajax", "../../../../Language } syncNext() { if (this.queue.length === 0) { - this.buttonSyncAll.classList.remove("disabled"); - (0, Snackbar_1.showDefaultSuccessSnackbar)(); + const showSuccess = () => { + this.buttonSyncAll.classList.remove("disabled"); + (0, Snackbar_1.showDefaultSuccessSnackbar)(); + }; + if (this.#syncVersionAfterCompletion) { + void this.#syncPackageVersion() + .then(() => { + showSuccess(); + }) + .finally(() => { + this.#syncVersionAfterCompletion = false; + }); + } + else { + showSuccess(); + } return; } const next = this.queue.shift(); @@ -128,6 +144,9 @@ define(["require", "exports", "tslib", "../../../../Ajax", "../../../../Language getButtonIdentifier(pluginName, target) { return `${pluginName}-${target}`; } + async #syncPackageVersion() { + await (0, SyncVersion_1.syncVersion)(this.projectId); + } _ajaxSuccess(data) { const identifier = this.getButtonIdentifier(data.returnValues.pluginName, data.returnValues.target); this.buttons.get(identifier).disabled = false; diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Api/DevtoolsProjects/SyncVersion.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Api/DevtoolsProjects/SyncVersion.js new file mode 100644 index 00000000000..b7c15f01bc2 --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Api/DevtoolsProjects/SyncVersion.js @@ -0,0 +1,20 @@ +/** + * Synchronizes the version of an installed package with the versio number in + * the `package.xml` of the project. + * + * @author Alexander Ebert + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.2 + * @woltlabExcludeBundle all + */ +define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "../Result"], function (require, exports, Backend_1, Result_1) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.syncVersion = syncVersion; + async function syncVersion(projectId) { + return (0, Result_1.fromInfallibleApiRequest)(() => { + return (0, Backend_1.prepareRequest)(`${window.WSC_RPC_API_URL}core/devtools-projects/${projectId}/sync-version`).post().fetchAsJson(); + }); + } +}); diff --git a/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php b/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php index c903d76b659..581ddf686f3 100644 --- a/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php +++ b/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php @@ -152,6 +152,7 @@ static function (\wcf\event\endpoint\ControllerCollecting $event) { $event->register(new \wcf\system\endpoint\controller\core\comments\responses\RenderResponse()); $event->register(new \wcf\system\endpoint\controller\core\comments\responses\RenderResponses()); $event->register(new \wcf\system\endpoint\controller\core\comments\responses\UpdateResponse()); + $event->register(new \wcf\system\endpoint\controller\core\devtoolsProjects\SyncVersion()); $event->register(new \wcf\system\endpoint\controller\core\exceptions\RenderException()); $event->register(new \wcf\system\endpoint\controller\core\gridViews\GetRows()); $event->register(new \wcf\system\endpoint\controller\core\gridViews\GetGridView()); diff --git a/wcfsetup/install/files/lib/system/endpoint/controller/core/devtoolsProjects/SyncVersion.class.php b/wcfsetup/install/files/lib/system/endpoint/controller/core/devtoolsProjects/SyncVersion.class.php new file mode 100644 index 00000000000..f401c2c8678 --- /dev/null +++ b/wcfsetup/install/files/lib/system/endpoint/controller/core/devtoolsProjects/SyncVersion.class.php @@ -0,0 +1,68 @@ + + * @since 6.2 + */ +#[PostRequest('/core/devtools-projects/{id:\d+}/sync-version')] +final class SyncVersion implements IController +{ + #[\Override] + public function __invoke(ServerRequestInterface $request, array $variables): ResponseInterface + { + $project = Helper::fetchObjectFromRequestParameter($variables['id'], DevtoolsProject::class); + + $this->assertProjectCanBeManaged(); + + $package = $project->getPackage(); + if ($package === null) { + throw new UserInputException('projectID'); + } + + $this->syncVersion($project, $package); + + return new JsonResponse([]); + } + + private function syncVersion(DevtoolsProject $project, Package $package): void + { + $projectVersion = $project->getPackageArchive()->getPackageInfo('version'); + if (Package::compareVersion($package->packageVersion, $projectVersion, '>=')) { + return; + } + + (new PackageEditor($package))->update([ + 'packageVersion' => $projectVersion, + ]); + + PackageCacheBuilder::getInstance()->reset(); + } + + private function assertProjectCanBeManaged(): void + { + if (!ENABLE_DEVELOPER_TOOLS || !WCF::getSession()->getPermission('admin.configuration.package.canInstallPackage')) { + throw new PermissionDeniedException(); + } + } +} From 4975d980665955434f04593ded300bd362c4b397 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Fri, 21 Nov 2025 16:09:20 +0100 Subject: [PATCH 2/2] Fix the namespace of the endpoint --- ts/WoltLabSuite/Core/Acp/Ui/Devtools/Project/Sync.ts | 2 +- .../{DevtoolsProjects => Devtools/Projects}/SyncVersion.ts | 6 +++--- .../js/WoltLabSuite/Core/Acp/Ui/Devtools/Project/Sync.js | 2 +- .../{DevtoolsProjects => Devtools/Projects}/SyncVersion.js | 6 +++--- wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php | 2 +- .../projects}/SyncVersion.class.php | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) rename ts/WoltLabSuite/Core/Api/{DevtoolsProjects => Devtools/Projects}/SyncVersion.ts (81%) rename wcfsetup/install/files/js/WoltLabSuite/Core/Api/{DevtoolsProjects => Devtools/Projects}/SyncVersion.js (83%) rename wcfsetup/install/files/lib/system/endpoint/controller/core/{devtoolsProjects => devtools/projects}/SyncVersion.class.php (94%) diff --git a/ts/WoltLabSuite/Core/Acp/Ui/Devtools/Project/Sync.ts b/ts/WoltLabSuite/Core/Acp/Ui/Devtools/Project/Sync.ts index 1d63c36f186..36bdc3cfd39 100644 --- a/ts/WoltLabSuite/Core/Acp/Ui/Devtools/Project/Sync.ts +++ b/ts/WoltLabSuite/Core/Acp/Ui/Devtools/Project/Sync.ts @@ -4,7 +4,7 @@ import { AjaxCallbackSetup, AjaxResponseException } from "../../../../Ajax/Data" import { DialogCallbackSetup } from "../../../../Ui/Dialog/Data"; import { dialogFactory } from "../../../../Component/Dialog"; import { showDefaultSuccessSnackbar } from "WoltLabSuite/Core/Component/Snackbar"; -import { syncVersion } from "WoltLabSuite/Core/Api/DevtoolsProjects/SyncVersion"; +import { syncVersion } from "WoltLabSuite/Core/Api/Devtools/Projects/SyncVersion"; interface PipData { dependencies: string[]; diff --git a/ts/WoltLabSuite/Core/Api/DevtoolsProjects/SyncVersion.ts b/ts/WoltLabSuite/Core/Api/Devtools/Projects/SyncVersion.ts similarity index 81% rename from ts/WoltLabSuite/Core/Api/DevtoolsProjects/SyncVersion.ts rename to ts/WoltLabSuite/Core/Api/Devtools/Projects/SyncVersion.ts index 738a49db80e..7cd3a38ee30 100644 --- a/ts/WoltLabSuite/Core/Api/DevtoolsProjects/SyncVersion.ts +++ b/ts/WoltLabSuite/Core/Api/Devtools/Projects/SyncVersion.ts @@ -1,5 +1,5 @@ /** - * Synchronizes the version of an installed package with the versio number in + * Synchronizes the version of an installed package with the version number in * the `package.xml` of the project. * * @author Alexander Ebert @@ -10,10 +10,10 @@ */ import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend"; -import { fromInfallibleApiRequest } from "../Result"; +import { fromInfallibleApiRequest } from "../../Result"; export async function syncVersion(projectId: number): Promise<[]> { return fromInfallibleApiRequest(() => { - return prepareRequest(`${window.WSC_RPC_API_URL}core/devtools-projects/${projectId}/sync-version`).post().fetchAsJson(); + return prepareRequest(`${window.WSC_RPC_API_URL}core/devtools/projects/${projectId}/sync-version`).post().fetchAsJson(); }); } diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Devtools/Project/Sync.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Devtools/Project/Sync.js index 39a2b6be546..b58b8d0a47c 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Devtools/Project/Sync.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Devtools/Project/Sync.js @@ -1,4 +1,4 @@ -define(["require", "exports", "tslib", "../../../../Ajax", "../../../../Language", "../../../../Component/Dialog", "WoltLabSuite/Core/Component/Snackbar", "WoltLabSuite/Core/Api/DevtoolsProjects/SyncVersion"], function (require, exports, tslib_1, Ajax, Language, Dialog_1, Snackbar_1, SyncVersion_1) { +define(["require", "exports", "tslib", "../../../../Ajax", "../../../../Language", "../../../../Component/Dialog", "WoltLabSuite/Core/Component/Snackbar", "WoltLabSuite/Core/Api/Devtools/Projects/SyncVersion"], function (require, exports, tslib_1, Ajax, Language, Dialog_1, Snackbar_1, SyncVersion_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.init = init; diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Api/DevtoolsProjects/SyncVersion.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Api/Devtools/Projects/SyncVersion.js similarity index 83% rename from wcfsetup/install/files/js/WoltLabSuite/Core/Api/DevtoolsProjects/SyncVersion.js rename to wcfsetup/install/files/js/WoltLabSuite/Core/Api/Devtools/Projects/SyncVersion.js index b7c15f01bc2..ade15f34585 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Api/DevtoolsProjects/SyncVersion.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Api/Devtools/Projects/SyncVersion.js @@ -1,5 +1,5 @@ /** - * Synchronizes the version of an installed package with the versio number in + * Synchronizes the version of an installed package with the version number in * the `package.xml` of the project. * * @author Alexander Ebert @@ -8,13 +8,13 @@ * @since 6.2 * @woltlabExcludeBundle all */ -define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "../Result"], function (require, exports, Backend_1, Result_1) { +define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "../../Result"], function (require, exports, Backend_1, Result_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.syncVersion = syncVersion; async function syncVersion(projectId) { return (0, Result_1.fromInfallibleApiRequest)(() => { - return (0, Backend_1.prepareRequest)(`${window.WSC_RPC_API_URL}core/devtools-projects/${projectId}/sync-version`).post().fetchAsJson(); + return (0, Backend_1.prepareRequest)(`${window.WSC_RPC_API_URL}core/devtools/projects/${projectId}/sync-version`).post().fetchAsJson(); }); } }); diff --git a/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php b/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php index 581ddf686f3..589a84ac347 100644 --- a/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php +++ b/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php @@ -152,7 +152,7 @@ static function (\wcf\event\endpoint\ControllerCollecting $event) { $event->register(new \wcf\system\endpoint\controller\core\comments\responses\RenderResponse()); $event->register(new \wcf\system\endpoint\controller\core\comments\responses\RenderResponses()); $event->register(new \wcf\system\endpoint\controller\core\comments\responses\UpdateResponse()); - $event->register(new \wcf\system\endpoint\controller\core\devtoolsProjects\SyncVersion()); + $event->register(new \wcf\system\endpoint\controller\core\devtools\projects\SyncVersion()); $event->register(new \wcf\system\endpoint\controller\core\exceptions\RenderException()); $event->register(new \wcf\system\endpoint\controller\core\gridViews\GetRows()); $event->register(new \wcf\system\endpoint\controller\core\gridViews\GetGridView()); diff --git a/wcfsetup/install/files/lib/system/endpoint/controller/core/devtoolsProjects/SyncVersion.class.php b/wcfsetup/install/files/lib/system/endpoint/controller/core/devtools/projects/SyncVersion.class.php similarity index 94% rename from wcfsetup/install/files/lib/system/endpoint/controller/core/devtoolsProjects/SyncVersion.class.php rename to wcfsetup/install/files/lib/system/endpoint/controller/core/devtools/projects/SyncVersion.class.php index f401c2c8678..ba4b303cd82 100644 --- a/wcfsetup/install/files/lib/system/endpoint/controller/core/devtoolsProjects/SyncVersion.class.php +++ b/wcfsetup/install/files/lib/system/endpoint/controller/core/devtools/projects/SyncVersion.class.php @@ -1,6 +1,6 @@ * @since 6.2 */ -#[PostRequest('/core/devtools-projects/{id:\d+}/sync-version')] +#[PostRequest('/core/devtools/projects/{id:\d+}/sync-version')] final class SyncVersion implements IController { #[\Override]