Use FormBuilder to copy template groups
authorCyperghost <olaf_schmitz_1@t-online.de>
Thu, 14 Nov 2024 09:13:56 +0000 (10:13 +0100)
committerCyperghost <olaf_schmitz_1@t-online.de>
Thu, 14 Nov 2024 09:13:56 +0000 (10:13 +0100)
ts/WoltLabSuite/Core/Acp/Ui/Template/Group/Copy.ts
wcfsetup/install/files/acp/templates/templateGroupAdd.tpl
wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Template/Group/Copy.js
wcfsetup/install/files/lib/acp/action/TemplateGroupCopyAction.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/template/group/TemplateGroupAction.class.php

index 1650d84c098f4542d2cd17b0cd9b6c653ae93f7e..ad6da2484cfbdb7faddd56b0f5fb6f11b75e510e 100644 (file)
 /**
  * Provides a dialog to copy an existing template group.
  *
- * @author  Alexander Ebert
- * @copyright  2001-2019 WoltLab GmbH
+ * @author  Olaf Braun, Alexander Ebert
+ * @copyright  2001-2024 WoltLab GmbH
  * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  */
 
-import * as Ajax from "../../../../Ajax";
-import { AjaxCallbackObject, AjaxCallbackSetup } from "../../../../Ajax/Data";
-import { DialogCallbackObject, DialogCallbackSetup } from "../../../../Ui/Dialog/Data";
-import * as Language from "../../../../Language";
-import UiDialog from "../../../../Ui/Dialog";
 import * as UiNotification from "../../../../Ui/Notification";
-import DomUtil from "../../../../Dom/Util";
+import { dialogFactory } from "WoltLabSuite/Core/Component/Dialog";
 
-interface AjaxResponse {
-  returnValues: {
-    redirectURL: string;
-  };
+interface Response {
+  redirectURL: string;
 }
 
-interface AjaxResponseError {
-  returnValues?: {
-    fieldName?: string;
-    errorType?: string;
-  };
+export function init(): void {
+  const button = document.querySelector(".jsButtonCopy") as HTMLAnchorElement;
+  button.addEventListener("click", () => void click(button));
 }
 
-class AcpUiTemplateGroupCopy implements AjaxCallbackObject, DialogCallbackObject {
-  private folderName?: HTMLInputElement = undefined;
-  private name?: HTMLInputElement = undefined;
-  private readonly templateGroupId: number;
-
-  constructor(templateGroupId: number) {
-    this.templateGroupId = templateGroupId;
-
-    const button = document.querySelector(".jsButtonCopy") as HTMLAnchorElement;
-    button.addEventListener("click", (ev) => this.click(ev));
-  }
-
-  private click(event: MouseEvent): void {
-    event.preventDefault();
-
-    UiDialog.open(this);
-  }
-
-  _dialogSubmit(): void {
-    Ajax.api(this, {
-      parameters: {
-        templateGroupName: this.name!.value,
-        templateGroupFolderName: this.folderName!.value,
-      },
-    });
-  }
-
-  _ajaxSuccess(data: AjaxResponse): void {
-    UiDialog.close(this);
-
+async function click(button: HTMLAnchorElement): Promise<void> {
+  const result = await dialogFactory().usingFormBuilder().fromEndpoint<Response>(button.dataset.endpoint!);
+  if (result.ok) {
     UiNotification.show(undefined, () => {
-      window.location.href = data.returnValues.redirectURL;
+      window.location.href = result.result.redirectURL;
     });
   }
-
-  _dialogSetup(): ReturnType<DialogCallbackSetup> {
-    return {
-      id: "templateGroupCopy",
-      options: {
-        onSetup: () => {
-          ["Name", "FolderName"].forEach((type) => {
-            const input = document.getElementById("copyTemplateGroup" + type) as HTMLInputElement;
-            input.value = (document.getElementById("templateGroup" + type) as HTMLInputElement).value;
-
-            if (type === "Name") {
-              this.name = input;
-            } else {
-              this.folderName = input;
-            }
-          });
-        },
-        title: Language.get("wcf.acp.template.group.copy"),
-      },
-      source: `<dl>
-  <dt>
-    <label for="copyTemplateGroupName">${Language.get("wcf.global.name")}</label>
-  </dt>
-  <dd>
-    <input type="text" id="copyTemplateGroupName" class="long" data-dialog-submit-on-enter="true" required>
-  </dd>
-</dl>
-<dl>
-  <dt>
-    <label for="copyTemplateGroupFolderName">${Language.get("wcf.acp.template.group.folderName")}</label>
-  </dt>
-  <dd>
-    <input type="text" id="copyTemplateGroupFolderName" class="long" data-dialog-submit-on-enter="true" required>
-  </dd>
-</dl>
-<div class="formSubmit">
-  <button type="button" class="button buttonPrimary" data-type="submit">${Language.get(
-    "wcf.global.button.submit",
-  )}</button>
-</div>`,
-    };
-  }
-
-  _ajaxSetup(): ReturnType<AjaxCallbackSetup> {
-    return {
-      data: {
-        actionName: "copy",
-        className: "wcf\\data\\template\\group\\TemplateGroupAction",
-        objectIDs: [this.templateGroupId],
-      },
-      failure: (data: AjaxResponseError) => {
-        if (data && data.returnValues && data.returnValues.fieldName && data.returnValues.errorType) {
-          if (data.returnValues.fieldName === "templateGroupName") {
-            DomUtil.innerError(
-              this.name!,
-              Language.get(`wcf.acp.template.group.name.error.${data.returnValues.errorType}`),
-            );
-          } else {
-            DomUtil.innerError(
-              this.folderName!,
-              Language.get(`wcf.acp.template.group.folderName.error.${data.returnValues.errorType}`),
-            );
-          }
-
-          return false;
-        }
-
-        return true;
-      },
-    };
-  }
 }
 
-let acpUiTemplateGroupCopy: AcpUiTemplateGroupCopy;
-
-export function init(templateGroupId: number): void {
-  if (!acpUiTemplateGroupCopy) {
-    acpUiTemplateGroupCopy = new AcpUiTemplateGroupCopy(templateGroupId);
-  }
-}
index 0b891494215410fb2ed8a92b9f2c78ccc0c2d6e8..62fce1b22ac918dd7fa013d2c4d9633c9f5676e2 100644 (file)
@@ -2,17 +2,8 @@
 
 {if $action === 'edit'}
        <script data-relocate="true">
-               require(['Language', 'WoltLabSuite/Core/Acp/Ui/Template/Group/Copy'], function (Language, AcpUiTemplateGroupCopy) {
-                       Language.addObject({
-                               'wcf.acp.template.group.copy': '{jslang}wcf.acp.template.group.copy{/jslang}',
-                               'wcf.acp.template.group.name.error.notUnique': '{jslang}wcf.acp.template.group.name.error.notUnique{/jslang}',
-                               'wcf.acp.template.group.folderName': '{jslang}wcf.acp.template.group.folderName{/jslang}',
-                               'wcf.acp.template.group.folderName.error.invalid': '{jslang}wcf.acp.template.group.folderName.error.invalid{/jslang}',
-                               'wcf.acp.template.group.folderName.error.notUnique': '{jslang}wcf.acp.template.group.folderName.error.notUnique{/jslang}',
-                               'wcf.global.name': '{jslang}wcf.global.name{/jslang}'
-                       });
-
-                       AcpUiTemplateGroupCopy.init({$formObject->templateGroupID});
+               require(['WoltLabSuite/Core/Acp/Ui/Template/Group/Copy'], (AcpUiTemplateGroupCopy) => {
+                       AcpUiTemplateGroupCopy.init();
                });
        </script>
 {/if}
@@ -24,7 +15,7 @@
        
        <nav class="contentHeaderNavigation">
                <ul>
-                       {if $action === 'edit'}<li><a href="#" class="jsButtonCopy button">{icon name='copy'} <span>{lang}wcf.acp.template.group.copy{/lang}</span></a></li>{/if}
+                       {if $action === 'edit'}<li><button type="button" class="jsButtonCopy button" data-endpoint="{link controller="TemplateGroupCopy" id=$formObject->templateGroupID}{/link}">{icon name='copy'} <span>{lang}wcf.acp.template.group.copy{/lang}</span></button></li>{/if}
                        <li><a href="{link controller='TemplateGroupList'}{/link}" class="button">{icon name='list'} <span>{lang}wcf.acp.menu.link.template.group.list{/lang}</span></a></li>
                        
                        {event name='contentHeaderNavigation'}
index 05c72549c2d03bdcc04af47563d584fea0da550b..b8ee293ff78882a72214f7a3650811ada3885b72 100644 (file)
 /**
  * Provides a dialog to copy an existing template group.
  *
- * @author  Alexander Ebert
- * @copyright  2001-2019 WoltLab GmbH
+ * @author  Olaf Braun, Alexander Ebert
+ * @copyright  2001-2024 WoltLab GmbH
  * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  */
-define(["require", "exports", "tslib", "../../../../Ajax", "../../../../Language", "../../../../Ui/Dialog", "../../../../Ui/Notification", "../../../../Dom/Util"], function (require, exports, tslib_1, Ajax, Language, Dialog_1, UiNotification, Util_1) {
+define(["require", "exports", "tslib", "../../../../Ui/Notification", "WoltLabSuite/Core/Component/Dialog"], function (require, exports, tslib_1, UiNotification, Dialog_1) {
     "use strict";
     Object.defineProperty(exports, "__esModule", { value: true });
     exports.init = init;
-    Ajax = tslib_1.__importStar(Ajax);
-    Language = tslib_1.__importStar(Language);
-    Dialog_1 = tslib_1.__importDefault(Dialog_1);
     UiNotification = tslib_1.__importStar(UiNotification);
-    Util_1 = tslib_1.__importDefault(Util_1);
-    class AcpUiTemplateGroupCopy {
-        folderName = undefined;
-        name = undefined;
-        templateGroupId;
-        constructor(templateGroupId) {
-            this.templateGroupId = templateGroupId;
-            const button = document.querySelector(".jsButtonCopy");
-            button.addEventListener("click", (ev) => this.click(ev));
-        }
-        click(event) {
-            event.preventDefault();
-            Dialog_1.default.open(this);
-        }
-        _dialogSubmit() {
-            Ajax.api(this, {
-                parameters: {
-                    templateGroupName: this.name.value,
-                    templateGroupFolderName: this.folderName.value,
-                },
-            });
-        }
-        _ajaxSuccess(data) {
-            Dialog_1.default.close(this);
+    function init() {
+        const button = document.querySelector(".jsButtonCopy");
+        button.addEventListener("click", () => void click(button));
+    }
+    async function click(button) {
+        const result = await (0, Dialog_1.dialogFactory)().usingFormBuilder().fromEndpoint(button.dataset.endpoint);
+        if (result.ok) {
             UiNotification.show(undefined, () => {
-                window.location.href = data.returnValues.redirectURL;
+                window.location.href = result.result.redirectURL;
             });
         }
-        _dialogSetup() {
-            return {
-                id: "templateGroupCopy",
-                options: {
-                    onSetup: () => {
-                        ["Name", "FolderName"].forEach((type) => {
-                            const input = document.getElementById("copyTemplateGroup" + type);
-                            input.value = document.getElementById("templateGroup" + type).value;
-                            if (type === "Name") {
-                                this.name = input;
-                            }
-                            else {
-                                this.folderName = input;
-                            }
-                        });
-                    },
-                    title: Language.get("wcf.acp.template.group.copy"),
-                },
-                source: `<dl>
-  <dt>
-    <label for="copyTemplateGroupName">${Language.get("wcf.global.name")}</label>
-  </dt>
-  <dd>
-    <input type="text" id="copyTemplateGroupName" class="long" data-dialog-submit-on-enter="true" required>
-  </dd>
-</dl>
-<dl>
-  <dt>
-    <label for="copyTemplateGroupFolderName">${Language.get("wcf.acp.template.group.folderName")}</label>
-  </dt>
-  <dd>
-    <input type="text" id="copyTemplateGroupFolderName" class="long" data-dialog-submit-on-enter="true" required>
-  </dd>
-</dl>
-<div class="formSubmit">
-  <button type="button" class="button buttonPrimary" data-type="submit">${Language.get("wcf.global.button.submit")}</button>
-</div>`,
-            };
-        }
-        _ajaxSetup() {
-            return {
-                data: {
-                    actionName: "copy",
-                    className: "wcf\\data\\template\\group\\TemplateGroupAction",
-                    objectIDs: [this.templateGroupId],
-                },
-                failure: (data) => {
-                    if (data && data.returnValues && data.returnValues.fieldName && data.returnValues.errorType) {
-                        if (data.returnValues.fieldName === "templateGroupName") {
-                            Util_1.default.innerError(this.name, Language.get(`wcf.acp.template.group.name.error.${data.returnValues.errorType}`));
-                        }
-                        else {
-                            Util_1.default.innerError(this.folderName, Language.get(`wcf.acp.template.group.folderName.error.${data.returnValues.errorType}`));
-                        }
-                        return false;
-                    }
-                    return true;
-                },
-            };
-        }
-    }
-    let acpUiTemplateGroupCopy;
-    function init(templateGroupId) {
-        if (!acpUiTemplateGroupCopy) {
-            acpUiTemplateGroupCopy = new AcpUiTemplateGroupCopy(templateGroupId);
-        }
     }
 });
diff --git a/wcfsetup/install/files/lib/acp/action/TemplateGroupCopyAction.class.php b/wcfsetup/install/files/lib/acp/action/TemplateGroupCopyAction.class.php
new file mode 100644 (file)
index 0000000..7210121
--- /dev/null
@@ -0,0 +1,185 @@
+<?php
+
+namespace wcf\acp\action;
+
+use CuyZ\Valinor\Mapper\MappingError;
+use Laminas\Diactoros\Response\JsonResponse;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+use wcf\data\template\group\TemplateGroup;
+use wcf\data\template\group\TemplateGroupAction;
+use wcf\data\template\TemplateAction;
+use wcf\data\template\TemplateList;
+use wcf\http\Helper;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\exception\PermissionDeniedException;
+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\form\builder\Psr15DialogForm;
+use wcf\system\request\LinkHandler;
+use wcf\system\WCF;
+use wcf\util\FileUtil;
+
+/**
+ * Handles the copying of template groups.
+ *
+ * @author      Olaf Braun
+ * @copyright   2001-2023 WoltLab GmbH
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since       6.2
+ */
+final class TemplateGroupCopyAction implements RequestHandlerInterface
+{
+    #[\Override]
+    public function handle(ServerRequestInterface $request): ResponseInterface
+    {
+        if (!WCF::getSession()->getPermission('admin.template.canManageTemplate')) {
+            throw new PermissionDeniedException();
+        }
+
+        try {
+            $queryParameters = Helper::mapQueryParameters(
+                $_GET,
+                <<<'EOT'
+                    array {
+                        id: positive-int
+                    }
+                    EOT
+            );
+            $templateGroup = new TemplateGroup($queryParameters['id']);
+
+            if (!$templateGroup->getObjectID()) {
+                throw new IllegalLinkException();
+            }
+        } catch (MappingError) {
+            throw new IllegalLinkException();
+        }
+
+        $form = $this->getForm($templateGroup);
+
+        if ($request->getMethod() === 'GET') {
+            return $form->toResponse();
+        } elseif ($request->getMethod() === 'POST') {
+            $response = $form->validateRequest($request);
+            if ($response !== null) {
+                return $response;
+            }
+
+            $data = $form->getData()['data'];
+            $data['parentTemplateGroupID'] = $templateGroup->parentTemplateGroupID ?: null;
+
+            $returnValues = (new TemplateGroupAction([], 'create', ['data' => $data]))->executeAction();
+            /** @var TemplateGroup $templateGroup */
+            $templateGroup = $returnValues['returnValues'];
+
+            $templateList = new TemplateList();
+            $templateList->getConditionBuilder()->add(
+                "template.templateGroupID = ?",
+                [$templateGroup->templateGroupID]
+            );
+            $templateList->readObjects();
+
+            foreach ($templateList as $template) {
+                (new TemplateAction([], 'create', [
+                    'data' => [
+                        'application' => $template->application,
+                        'templateName' => $template->templateName,
+                        'packageID' => $template->packageID,
+                        'templateGroupID' => $templateGroup->templateGroupID,
+                    ],
+                    'source' => $template->getSource(),
+                ]))->executeAction();
+            }
+
+            return new JsonResponse([
+                'result' => [
+                    'redirectURL' => LinkHandler::getInstance()->getLink(
+                        'TemplateGroupEdit',
+                        [
+                            'isACP' => true,
+                            'id' => $templateGroup->templateGroupID,
+                        ]
+                    ),
+                ]
+            ]);
+        } else {
+            throw new \LogicException('Unreachable');
+        }
+    }
+
+    private function getForm(TemplateGroup $templateGroup): Psr15DialogForm
+    {
+        $form = new Psr15DialogForm(
+            TemplateGroupCopyAction::class,
+            WCF::getLanguage()->get('wcf.acp.template.group.copy')
+        );
+        $form->appendChildren([
+            TextFormField::create('templateGroupName')
+                ->label('wcf.global.name')
+                ->required()
+                ->value($templateGroup->templateGroupName)
+                ->addValidator(
+                    new FormFieldValidator('templateNameValidator', function (TextFormField $formField) {
+                        $sql = "SELECT  COUNT(*)
+                                FROM    wcf1_template_group
+                                WHERE   templateGroupName = ?";
+                        $statement = WCF::getDB()->prepare($sql);
+                        $statement->execute([$formField->getValue()]);
+
+                        if ($statement->fetchSingleColumn()) {
+                            $formField->addValidationError(
+                                new FormFieldValidationError(
+                                    'notUnique',
+                                    'wcf.acp.template.group.name.error.notUnique'
+                                )
+                            );
+                        }
+                    })
+                ),
+            TextFormField::create('templateGroupFolderName')
+                ->label('wcf.acp.template.group.folderName')
+                ->required()
+                ->value($templateGroup->templateGroupFolderName)
+                ->addValidator(
+                    new FormFieldValidator('folderNameValidator', function (TextFormField $formField) {
+                        $formField->value(FileUtil::addTrailingSlash($formField->getValue()));
+
+                        if (!\preg_match('/^[a-z0-9_\- ]+\/$/i', $formField->getValue())) {
+                            $formField->addValidationError(
+                                new FormFieldValidationError(
+                                    'invalid',
+                                    'wcf.acp.template.group.folderName.error.invalid'
+                                )
+                            );
+                        }
+                    })
+                )
+                ->addValidator(
+                    new FormFieldValidator('uniqueFolderNameValidator', function (TextFormField $formField) {
+                        $formField->value(FileUtil::addTrailingSlash($formField->getValue()));
+
+                        $sql = "SELECT  COUNT(*)
+                                FROM    wcf1_template_group
+                                WHERE   templateGroupFolderName = ?";
+                        $statement = WCF::getDB()->prepare($sql);
+                        $statement->execute([$formField->getValue()]);
+
+                        if ($statement->fetchSingleColumn()) {
+                            $formField->addValidationError(
+                                new FormFieldValidationError(
+                                    'notUnique',
+                                    'wcf.acp.template.group.folderName.error.notUnique'
+                                )
+                            );
+                        }
+                    })
+                ),
+        ]);
+
+        $form->build();
+
+        return $form;
+    }
+}
index 34f99aacd9d06e43edd49ffd92cc9c3cf280a72c..b5e17958ef2a6b24f3663ee1523b16b48da139f6 100644 (file)
@@ -3,12 +3,6 @@
 namespace wcf\data\template\group;
 
 use wcf\data\AbstractDatabaseObjectAction;
-use wcf\data\template\Template;
-use wcf\data\template\TemplateAction;
-use wcf\data\template\TemplateList;
-use wcf\system\exception\UserInputException;
-use wcf\system\request\LinkHandler;
-use wcf\system\WCF;
 
 /**
  * Executes template group-related actions.
@@ -46,101 +40,5 @@ class TemplateGroupAction extends AbstractDatabaseObjectAction
     /**
      * @inheritDoc
      */
-    protected $requireACP = ['copy', 'create', 'delete', 'update'];
-
-    /**
-     * @var TemplateGroupEditor
-     */
-    public $templateGroupEditor;
-
-    /**
-     * Validates the parameters to copy an existing template group.
-     *
-     * @throws      UserInputException
-     */
-    public function validateCopy()
-    {
-        WCF::getSession()->checkPermissions(['admin.template.canManageTemplate']);
-
-        $this->readString('templateGroupName');
-        $this->readString('templateGroupFolderName');
-
-        $this->templateGroupEditor = $this->getSingleObject();
-
-        // validate name
-        $sql = "SELECT  COUNT(*)
-                FROM    wcf1_template_group
-                WHERE   templateGroupName = ?";
-        $statement = WCF::getDB()->prepare($sql);
-        $statement->execute([$this->parameters['templateGroupName']]);
-
-        if ($statement->fetchSingleColumn()) {
-            throw new UserInputException('templateGroupName', 'notUnique');
-        }
-
-        // validate folder name
-        if (!\preg_match('/^[a-z0-9_\- ]+\/$/i', $this->parameters['templateGroupFolderName'])) {
-            throw new UserInputException('templateGroupFolderName', 'invalid');
-        }
-
-        $sql = "SELECT  COUNT(*)
-                FROM    wcf1_template_group
-                WHERE   templateGroupFolderName = ?";
-        $statement = WCF::getDB()->prepare($sql);
-        $statement->execute([$this->parameters['templateGroupFolderName']]);
-
-        if ($statement->fetchSingleColumn()) {
-            throw new UserInputException('templateGroupFolderName', 'notUnique');
-        }
-    }
-
-    /**
-     * Copies an existing template group.
-     *
-     * @return      string[]
-     */
-    public function copy()
-    {
-        // create a new template group
-        $returnValues = (new self([], 'create', [
-            'data' => [
-                'parentTemplateGroupID' => ($this->templateGroupEditor->parentTemplateGroupID ?: null),
-                'templateGroupName' => $this->parameters['templateGroupName'],
-                'templateGroupFolderName' => $this->parameters['templateGroupFolderName'],
-            ],
-        ]))->executeAction();
-        /** @var TemplateGroup $templateGroup */
-        $templateGroup = $returnValues['returnValues'];
-
-        // copy over the templates
-        $templateList = new TemplateList();
-        $templateList->getConditionBuilder()->add(
-            "template.templateGroupID = ?",
-            [$this->templateGroupEditor->templateGroupID]
-        );
-        $templateList->readObjects();
-
-        /** @var Template $template */
-        foreach ($templateList as $template) {
-            (new TemplateAction([], 'create', [
-                'data' => [
-                    'application' => $template->application,
-                    'templateName' => $template->templateName,
-                    'packageID' => $template->packageID,
-                    'templateGroupID' => $templateGroup->templateGroupID,
-                ],
-                'source' => $template->getSource(),
-            ]))->executeAction();
-        }
-
-        return [
-            'redirectURL' => LinkHandler::getInstance()->getLink(
-                'TemplateGroupEdit',
-                [
-                    'isACP' => true,
-                    'id' => $templateGroup->templateGroupID,
-                ]
-            ),
-        ];
-    }
+    protected $requireACP = ['create', 'delete', 'update'];
 }