/**
* 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);
- }
-}
{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}
<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'}
/**
* 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);
- }
}
});
--- /dev/null
+<?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;
+ }
+}
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.
/**
* @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'];
}