Add a proper error message for incompatible Plugin-Store packages
authorAlexander Ebert <ebert@woltlab.com>
Fri, 14 Jun 2024 11:26:59 +0000 (13:26 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Fri, 14 Jun 2024 11:26:59 +0000 (13:26 +0200)
Fixes #5800
See https://www.woltlab.com/community/thread/306394-error-message-when-trying-to-install-a-package-by-storecode/

ts/WoltLabSuite/Core/Acp/Ui/Package/QuickInstallation.ts
wcfsetup/install/files/acp/templates/packageStartInstall.tpl
wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Package/QuickInstallation.js
wcfsetup/install/files/lib/acp/form/PackageStartInstallForm.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index d2c465867b975fa6e4fcc8aece8eb9da62d0d072..6416a4e74154ffbdfef115e5ebe164c18b85a12e 100644 (file)
@@ -13,6 +13,7 @@ import { isPlainObject } from "../../../Core";
 import * as Language from "../../../Language";
 import { innerError } from "../../../Dom/Util";
 import UiDialog from "../../../Ui/Dialog";
+import { StatusNotOk } from "WoltLabSuite/Core/Ajax/Error";
 
 let codeInput: HTMLInputElement;
 
@@ -32,7 +33,7 @@ type Response =
       type: string;
     };
 
-function detectCode(): void {
+function detectCode(versionNumber: string): void {
   const value = codeInput.value.trim();
   if (value === "") {
     innerError(codeInput, false);
@@ -55,7 +56,7 @@ function detectCode(): void {
       if (json.package && json.password && json.username) {
         isValid = true;
 
-        void prepareInstallation(json);
+        void prepareInstallation(json, versionNumber);
       }
     }
   }
@@ -78,7 +79,7 @@ function refreshPackageDatabase() {
   return refreshedPackageDatabase;
 }
 
-async function prepareInstallation(data: InstallationCode): Promise<void> {
+async function prepareInstallation(data: InstallationCode, versionNumber: string): Promise<void> {
   try {
     AjaxStatus.show();
     await refreshPackageDatabase();
@@ -86,19 +87,41 @@ async function prepareInstallation(data: InstallationCode): Promise<void> {
     AjaxStatus.hide();
   }
 
-  const response = (await dboAction("prepareInstallation", "wcf\\data\\package\\update\\PackageUpdateAction")
-    .payload({
-      packages: {
-        [data.package]: "",
-      },
-      authData: {
-        username: data.username,
-        password: data.password,
-        saveCredentials: false,
-        isStoreCode: true,
-      },
-    })
-    .dispatch()) as Response;
+  let response: Response;
+  try {
+    response = (await dboAction("prepareInstallation", "wcf\\data\\package\\update\\PackageUpdateAction")
+      .payload({
+        packages: {
+          [data.package]: "",
+        },
+        authData: {
+          username: data.username,
+          password: data.password,
+          saveCredentials: false,
+          isStoreCode: true,
+        },
+      })
+      .dispatch()) as Response;
+  } catch (e) {
+    if (e instanceof StatusNotOk) {
+      try {
+        const json = await e.response.clone().json();
+        if (typeof json.message === "string" && json.message.startsWith("Cannot find the package '")) {
+          codeInput.value = "";
+          innerError(
+            codeInput,
+            Language.getPhrase("wcf.acp.package.error.incompatibleStoreProduct", { versionNumber }),
+          );
+
+          return;
+        }
+      } catch {
+        throw e;
+      }
+    }
+
+    throw e;
+  }
 
   if ("queueID" in response) {
     if (response.queueID === null) {
@@ -129,7 +152,7 @@ async function prepareInstallation(data: InstallationCode): Promise<void> {
   }
 }
 
-export function setup(): void {
+export function setup(versionNumber: string): void {
   codeInput = document.getElementById("quickInstallationCode") as HTMLInputElement;
 
   codeInput.addEventListener("focus", () => {
@@ -140,6 +163,6 @@ export function setup(): void {
   });
 
   codeInput.addEventListener("input", () => {
-    detectCode();
+    detectCode(versionNumber);
   });
 }
index ca9856ae67cc21f61092f8339debc925cfbaf1e8..c56f58518846d3a863defa7fd2eeda5d679059f9 100644 (file)
@@ -9,6 +9,7 @@
        require([
                "WoltLabSuite/Core/Acp/Ui/Package/QuickInstallation", "WoltLabSuite/Core/Acp/Ui/Package/Search"],
                (AcpUiPackageQuickInstallation, AcpUiPackageSearch) => {
+               {jsphrase name='wcf.acp.package.error.incompatibleStoreProduct'}
                {jsphrase name='wcf.acp.package.error.uniqueAlreadyInstalled'}
                {jsphrase name='wcf.acp.package.install.title'}
                {jsphrase name='wcf.acp.package.quickInstallation.code.error.invalid'}
@@ -16,7 +17,7 @@
                {jsphrase name='wcf.acp.package.update.title'}
                {jsphrase name='wcf.acp.package.update.unauthorized'}
                
-               AcpUiPackageQuickInstallation.setup();
+               AcpUiPackageQuickInstallation.setup('{$majorMinorVersion}');
                new AcpUiPackageSearch();
                
                {if $errorField === 'uploadPackage'}
index ad500753b8cd2e8862d44848289a300be03be46b..3190d9fc63ea43a8bd2a422cbadd4cb65cc1ae4e 100644 (file)
@@ -6,7 +6,7 @@
  * @copyright 2001-2022 WoltLab GmbH
  * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  */
-define(["require", "exports", "tslib", "../../../Ajax", "../../../Ajax/Status", "../../../Core", "../../../Language", "../../../Dom/Util", "../../../Ui/Dialog"], function (require, exports, tslib_1, Ajax_1, AjaxStatus, Core_1, Language, Util_1, Dialog_1) {
+define(["require", "exports", "tslib", "../../../Ajax", "../../../Ajax/Status", "../../../Core", "../../../Language", "../../../Dom/Util", "../../../Ui/Dialog", "WoltLabSuite/Core/Ajax/Error"], function (require, exports, tslib_1, Ajax_1, AjaxStatus, Core_1, Language, Util_1, Dialog_1, Error_1) {
     "use strict";
     Object.defineProperty(exports, "__esModule", { value: true });
     exports.setup = void 0;
@@ -14,7 +14,7 @@ define(["require", "exports", "tslib", "../../../Ajax", "../../../Ajax/Status",
     Language = tslib_1.__importStar(Language);
     Dialog_1 = tslib_1.__importDefault(Dialog_1);
     let codeInput;
-    function detectCode() {
+    function detectCode(versionNumber) {
         const value = codeInput.value.trim();
         if (value === "") {
             (0, Util_1.innerError)(codeInput, false);
@@ -34,7 +34,7 @@ define(["require", "exports", "tslib", "../../../Ajax", "../../../Ajax/Status",
                 const json = maybeJson;
                 if (json.package && json.password && json.username) {
                     isValid = true;
-                    void prepareInstallation(json);
+                    void prepareInstallation(json, versionNumber);
                 }
             }
         }
@@ -54,7 +54,7 @@ define(["require", "exports", "tslib", "../../../Ajax", "../../../Ajax/Status",
         }
         return refreshedPackageDatabase;
     }
-    async function prepareInstallation(data) {
+    async function prepareInstallation(data, versionNumber) {
         try {
             AjaxStatus.show();
             await refreshPackageDatabase();
@@ -62,19 +62,38 @@ define(["require", "exports", "tslib", "../../../Ajax", "../../../Ajax/Status",
         finally {
             AjaxStatus.hide();
         }
-        const response = (await (0, Ajax_1.dboAction)("prepareInstallation", "wcf\\data\\package\\update\\PackageUpdateAction")
-            .payload({
-            packages: {
-                [data.package]: "",
-            },
-            authData: {
-                username: data.username,
-                password: data.password,
-                saveCredentials: false,
-                isStoreCode: true,
-            },
-        })
-            .dispatch());
+        let response;
+        try {
+            response = (await (0, Ajax_1.dboAction)("prepareInstallation", "wcf\\data\\package\\update\\PackageUpdateAction")
+                .payload({
+                packages: {
+                    [data.package]: "",
+                },
+                authData: {
+                    username: data.username,
+                    password: data.password,
+                    saveCredentials: false,
+                    isStoreCode: true,
+                },
+            })
+                .dispatch());
+        }
+        catch (e) {
+            if (e instanceof Error_1.StatusNotOk) {
+                try {
+                    const json = await e.response.clone().json();
+                    if (typeof json.message === "string" && json.message.startsWith("Cannot find the package '")) {
+                        codeInput.value = "";
+                        (0, Util_1.innerError)(codeInput, Language.getPhrase("wcf.acp.package.error.incompatibleStoreProduct", { versionNumber }));
+                        return;
+                    }
+                }
+                catch {
+                    throw e;
+                }
+            }
+            throw e;
+        }
         if ("queueID" in response) {
             if (response.queueID === null) {
                 codeInput.value = "";
@@ -102,7 +121,7 @@ define(["require", "exports", "tslib", "../../../Ajax", "../../../Ajax/Status",
             throw new Error("Unreachable");
         }
     }
-    function setup() {
+    function setup(versionNumber) {
         codeInput = document.getElementById("quickInstallationCode");
         codeInput.addEventListener("focus", () => {
             // Refresh the package database when focusing the input to hide the latency of the package
@@ -111,7 +130,7 @@ define(["require", "exports", "tslib", "../../../Ajax", "../../../Ajax/Status",
             void refreshPackageDatabase();
         });
         codeInput.addEventListener("input", () => {
-            detectCode();
+            detectCode(versionNumber);
         });
     }
     exports.setup = setup;
index 504923fd9befe562dc25f5380b39f814e8481079..5d9fd0f0eea35f027ac224b1634b2accb1d2f355 100755 (executable)
@@ -241,9 +241,12 @@ class PackageStartInstallForm extends AbstractForm
     {
         parent::assignVariables();
 
+        $majorMinorVersion = \preg_replace('/^(\d+\.\d+)\..*$/', '\\1', \WCF_VERSION);
+
         WCF::getTPL()->assign([
             'package' => $this->package,
             'installingImportedStyle' => $this->stylePackageImportLocation != '',
+            'majorMinorVersion' => $majorMinorVersion,
         ]);
     }
 
index a45d0ef629c1b43069c9dfee52a82e318fe1c6d3..34beb4d64cfcdd12ddb46ab42f49b390a3926e2f 100644 (file)
@@ -1824,6 +1824,7 @@ Die Datenbestände werden sorgfältig gepflegt, aber es ist nicht ausgeschlossen
                <item name="wcf.acp.package.error.noValidInstall"><![CDATA[Das angegebene Paket lässt keine Neuinstallation zu.]]></item>
                <item name="wcf.acp.package.error.noValidUpdate"><![CDATA[Paket „{$package->getName()}“ kann mit dem angegebenen Archiv nicht aktualisiert werden.]]></item>
                <item name="wcf.acp.package.error.majorUpgrade"><![CDATA[Das Paket aktualisiert die Installation auf eine neue Major-Version. Aus Stabilitätsgründen können Major-Upgrades ausschließlich über die Paketserver durchgeführt werden. Detaillierte Informationen zur Durchführung des Upgrades finden sich <a href="https://manual.woltlab.com/de/updates/" class="externalURL">in unserem Handbuch</a>.]]></item>
+               <item name="wcf.acp.package.error.incompatibleStoreProduct"><![CDATA[Das zu installierende Paket ist nicht kompatibel. {if LANGUAGE_USE_INFORMAL_VARIANT}Du kannst{else}Sie können{/if} nur Pakete installieren, die als kompatibel mit WoltLab Suite {literal}{$versionNumber}{/literal} angegeben werden.]]></item>
                <item name="wcf.acp.package.identifier"><![CDATA[Bezeichner]]></item>
                <item name="wcf.acp.package.information.properties"><![CDATA[Eigenschaften]]></item>
                <item name="wcf.acp.package.information.title"><![CDATA[Informationen]]></item>
index 99e1a0a57d4bcde67f7aab3ebcc754779c9a8332..b4b3999f3243352af350c171a4c19df49b13e0fc 100644 (file)
@@ -1807,6 +1807,7 @@ The database is carefully maintained, but there will be always be a margin of er
                <item name="wcf.acp.package.error.noValidInstall"><![CDATA[The selected package does not support an installation.]]></item>
                <item name="wcf.acp.package.error.noValidUpdate"><![CDATA[Package “{$package->getName()}” cannot be updated using the selected archive.]]></item>
                <item name="wcf.acp.package.error.majorUpgrade"><![CDATA[The package updates your community to a new major version. For stability reasons major upgrades may only be performed via the update servers. Detailed information regarding the upgrade process can be found <a href="https://manual.woltlab.com/en/updates/" class="externalURL">in our manual</a>.]]></item>
+               <item name="wcf.acp.package.error.incompatibleStoreProduct"><![CDATA[The package is incompatible and cannot be installed. You can only install packages that are listed as compatible with WoltLab Suite {literal}{$versionNumber}{/literal}.]]></item>
                <item name="wcf.acp.package.identifier"><![CDATA[Identifier]]></item>
                <item name="wcf.acp.package.information.properties"><![CDATA[Properties]]></item>
                <item name="wcf.acp.package.information.title"><![CDATA[Details]]></item>