Adds a quick template filter and copy support for template groups
authorAlexander Ebert <ebert@woltlab.com>
Sat, 25 Nov 2017 18:43:38 +0000 (19:43 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Sat, 25 Nov 2017 18:43:46 +0000 (19:43 +0100)
Closes #2479

wcfsetup/install/files/acp/templates/templateGroupAdd.tpl
wcfsetup/install/files/acp/templates/templateGroupList.tpl
wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Template/Group/Copy.js [new file with mode: 0644]
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Confirmation.js
wcfsetup/install/files/lib/data/template/group/TemplateGroupAction.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index 7eff8cbb96824d0954dd658053463e129c94048b..90607ee32a0ee3dc79676e3d603767e86cbe71f2 100644 (file)
@@ -1,5 +1,22 @@
 {include file='header' pageTitle='wcf.acp.template.group.'|concat:$action}
 
+{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': '{lang}wcf.acp.template.group.copy{/lang}',
+                               'wcf.acp.template.group.name.error.notUnique': '{lang}wcf.acp.template.group.name.error.notUnique{/lang}',
+                               'wcf.acp.template.group.folderName': '{lang}wcf.acp.template.group.folderName{/lang}',
+                               'wcf.acp.template.group.folderName.error.invalid': '{lang}wcf.acp.template.group.folderName.error.invalid{/lang}',
+                               'wcf.acp.template.group.folderName.error.notUnique': '{lang}wcf.acp.template.group.folderName.error.notUnique{/lang}',
+                               'wcf.global.name': '{lang}wcf.global.name{/lang}'
+                       });
+                       
+                       AcpUiTemplateGroupCopy.init({$templateGroupID});
+               });
+       </script>
+{/if}
+
 <header class="contentHeader">
        <div class="contentHeaderTitle">
                <h1 class="contentTitle">{lang}wcf.acp.template.group.{$action}{/lang}</h1>
@@ -7,6 +24,7 @@
        
        <nav class="contentHeaderNavigation">
                <ul>
+                       {if $action === 'edit'}<li><a href="#" class="jsButtonCopy button"><span class="icon icon16 fa-files-o"></span> <span>{lang}wcf.acp.template.group.copy{/lang}</span></a></li>{/if}
                        <li><a href="{link controller='TemplateGroupList'}{/link}" class="button"><span class="icon icon16 fa-list"></span> <span>{lang}wcf.acp.menu.link.template.group.list{/lang}</span></a></li>
                        
                        {event name='contentHeaderNavigation'}
@@ -33,9 +51,9 @@
                                        {if $errorField == 'parentTemplateGroupID'}
                                                <small class="innerError">
                                                        {if $errorType == 'empty'}
-                                                       {lang}wcf.global.form.error.empty{/lang}
+                                                               {lang}wcf.global.form.error.empty{/lang}
                                                        {else}
-                                                       {lang}wcf.acp.template.group.parentTemplateGroupID.error.{@$errorType}{/lang}
+                                                               {lang}wcf.acp.template.group.parentTemplateGroupID.error.{@$errorType}{/lang}
                                                        {/if}
                                                </small>
                                        {/if}
index 93c27a07f1e9480c50d200e3e6e7b13d350e81d1..3e25e849ff445166039622e1440d8bc13343612c 100644 (file)
                                {foreach from=$objects item=templateGroup}
                                        <tr class="jsTemplateGroupRow">
                                                <td class="columnIcon">
-                                                       {if !$templateGroup->isImmutable()}
-                                                               <a href="{link controller='TemplateGroupEdit' id=$templateGroup->templateGroupID}{/link}" title="{lang}wcf.global.button.edit{/lang}" class="jsTooltip"><span class="icon icon16 fa-pencil"></span></a>
-                                                               <span class="icon icon16 fa-times jsDeleteButton jsTooltip pointer" title="{lang}wcf.global.button.delete{/lang}" data-object-id="{@$templateGroup->templateGroupID}" data-confirm-message-html="{lang __encode=true}wcf.acp.template.group.delete.sure{/lang}"></span>
-                                                       {else}
+                                                       {if $templateGroup->isImmutable()}
                                                                <span class="icon icon16 fa-pencil disabled" title="{lang}wcf.global.button.edit{/lang}"></span>
+                                                       {else}
+                                                               <a href="{link controller='TemplateGroupEdit' id=$templateGroup->templateGroupID}{/link}" title="{lang}wcf.global.button.edit{/lang}" class="jsTooltip"><span class="icon icon16 fa-pencil"></span></a>
+                                                       {/if}
+                                                       
+                                                       <a href="{link controller='TemplateList' templateGroupID=$templateGroup->templateGroupID}{/link}" title="{lang}wcf.acp.template.list{/lang}" class="jsTooltip"><span class="icon icon16 fa-list"></span></a>
+                                                       
+                                                       {if $templateGroup->isImmutable()}
                                                                <span class="icon icon16 fa-times disabled" title="{lang}wcf.global.button.delete{/lang}"></span>
+                                                       {else}
+                                                               <span class="icon icon16 fa-times jsDeleteButton jsTooltip pointer" title="{lang}wcf.global.button.delete{/lang}" data-object-id="{@$templateGroup->templateGroupID}" data-confirm-message-html="{lang __encode=true}wcf.acp.template.group.delete.sure{/lang}"></span>
                                                        {/if}
                                                        
                                                        {event name='rowButtons'}
diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Template/Group/Copy.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Template/Group/Copy.js
new file mode 100644 (file)
index 0000000..0c9186e
--- /dev/null
@@ -0,0 +1,142 @@
+/**
+ * Provides a dialog to copy an existing template group.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2017 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module     WoltLabSuite/Core/Acp/Ui/Template/Group/Copy
+ */
+define(['Ajax', 'EventKey', 'Language', 'Ui/Dialog', 'Ui/Notification'], function(Ajax, EventKey, Language, UiDialog, UiNotification) {
+       "use strict";
+       
+       var _name = null;
+       var _folderName = null;
+       var _templateGroupId = 0;
+       
+       /**
+        * @exports     WoltLabSuite/Core/Acp/Ui/Template/Group/Copy
+        */
+       return {
+               /**
+                * Initializes the dialog handler.
+                * 
+                * @param       {int}           templateGroupId
+                */
+               init: function (templateGroupId) {
+                       _templateGroupId = templateGroupId;
+                       
+                       elBySel('.jsButtonCopy').addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+               },
+               
+               /**
+                * Handles clicks on the 'Copy Template Group' button.
+                * 
+                * @param       {Event}         event
+                * @protected
+                */
+               _click: function (event) {
+                       event.preventDefault();
+                       
+                       UiDialog.open(this);
+               },
+               
+               /**
+                * Submits the values for the new template group.
+                * 
+                * @param       {Event}         event
+                * @protected
+                */
+               _submit: function (event) {
+                       event.preventDefault();
+                       
+                       var valid = true;
+                       [_name, _folderName].forEach(function (input) {
+                               if (input.value.trim() === '') {
+                                       elInnerError(input, Language.get('wcf.global.form.error.empty'));
+                                       valid = false;
+                               }
+                               else {
+                                       elInnerError(input, false);
+                               }
+                       });
+                       
+                       if (valid) {
+                               Ajax.api(this, {
+                                       parameters: {
+                                               templateGroupName: _name.value,
+                                               templateGroupFolderName: _folderName.value
+                                       }
+                               });
+                       }
+               },
+               
+               _ajaxSuccess: function (data) {
+                       UiDialog.close(this);
+                       
+                       UiNotification.show(undefined, function () {
+                               //noinspection JSUnresolvedVariable
+                               window.location = data.returnValues.redirectURL;
+                       });
+               },
+               
+               _dialogSetup: function () {
+                       return {
+                               id: 'templateGroupCopy',
+                               options: {
+                                       onSetup: (function (content) {
+                                               ['Name', 'FolderName'].forEach((function(type) {
+                                                       var input = elById('copyTemplateGroup' + type);
+                                                       input.value = elById('templateGroup' + type).value;
+                                                       
+                                                       if (type === 'Name') _name = input;
+                                                       else _folderName = input;
+                                                       
+                                                       input.addEventListener('keydown', (function (event) {
+                                                               if (EventKey.Enter(event)) {
+                                                                       this._submit(event);
+                                                               }
+                                                       }).bind(this));
+                                               }).bind(this));
+                                               
+                                               elBySel('.formSubmit > button[data-type="submit"]', content).addEventListener(WCF_CLICK_EVENT, this._submit.bind(this));
+                                       }).bind(this),
+                                       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"></dd>' +
+                               '</dl>' +
+                               '<dl>' +
+                                       '<dt><label for="copyTemplateGroupFolderName">' + Language.get('wcf.acp.template.group.folderName') + '</label></dt>' +
+                                       '<dd><input type="text" id="copyTemplateGroupFolderName" class="long"></dd>' +
+                               '</dl>' +
+                               '<div class="formSubmit">' +
+                                       '<button class="buttonPrimary" data-type="submit">' + Language.get('wcf.global.button.submit') + '</button>' +
+                               '</div>'
+                       }
+               },
+               
+               _ajaxSetup: function () {
+                       return {
+                               data: {
+                                       actionName: 'copy',
+                                       className: 'wcf\\data\\template\\group\\TemplateGroupAction',
+                                       objectIDs: [_templateGroupId]
+                               },
+                               /** @var {{returnValues:{fieldName: string, errorType: string}}} data */
+                               failure: function (data) {
+                                       if (data && data.returnValues && data.returnValues.fieldName && data.returnValues.errorType) {
+                                               if (data.returnValues.fieldName === 'templateGroupName') {
+                                                       elInnerError(_name, Language.get('wcf.acp.template.group.name.error.' + data.returnValues.errorType));
+                                               }
+                                               else {
+                                                       elInnerError(_folderName, Language.get('wcf.acp.template.group.folderName.error.' + data.returnValues.errorType));
+                                               }
+                                               
+                                               return false;
+                                       }
+                               }
+                       }
+               }
+       };
+});
index f749ea0a3eab7b1082d96e8c5093c6622289746d..bc2e79514a6bf59222deda92740081d210925ad2 100644 (file)
@@ -20,7 +20,7 @@ define(['Core', 'Language', 'Ui/Dialog'], function(Core, Language, UiDialog) {
         * 
         * @exports     WoltLabSuite/Core/Ui/Confirmation
         */
-       var UiConfirmation = {
+       return {
                /**
                 * Shows the confirmation dialog.
                 * 
@@ -166,6 +166,4 @@ define(['Core', 'Language', 'Ui/Dialog'], function(Core, Language, UiDialog) {
                        _confirmButton.focus();
                }
        };
-       
-       return UiConfirmation;
 });
index dd6a95cbf9597a53f1a5a9f68192c16dc1e94acd..9e821adc3d79ac0cb4317f501c45c24b6a0bc9ac 100644 (file)
@@ -1,6 +1,12 @@
 <?php
 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.
@@ -38,5 +44,95 @@ class TemplateGroupAction extends AbstractDatabaseObjectAction {
        /**
         * @inheritDoc
         */
-       protected $requireACP = ['create', 'delete', 'update'];
+       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    wcf".WCF_N."_template_group
+                       WHERE   templateGroupName = ?";
+               $statement = WCF::getDB()->prepareStatement($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    wcf".WCF_N."_template_group
+                       WHERE   templateGroupFolderName = ?";
+               $statement = WCF::getDB()->prepareStatement($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 TemplateGroupAction([], '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
+                               ]
+                       )
+               ];
+       }
 }
index 86f018947a2e08bf8c295ac6f347b1b572346f26..d5f2226f9e1d4f10fe2956eb2b0036ddeeef9ed8 100644 (file)
@@ -2036,6 +2036,7 @@ Als Benachrichtigungs-URL in der Konfiguration der sofortigen Zahlungsbestätigu
                <item name="wcf.acp.template.lastModificationTime"><![CDATA[Letzte Änderung]]></item>
                <item name="wcf.acp.template.group.list"><![CDATA[Templategruppen]]></item>
                <item name="wcf.acp.template.group.add"><![CDATA[Templategruppe hinzufügen]]></item>
+               <item name="wcf.acp.template.group.copy"><![CDATA[Templategruppe kopieren]]></item>
                <item name="wcf.acp.template.group.edit"><![CDATA[Templategruppe bearbeiten]]></item>
                <item name="wcf.acp.template.group.templates"><![CDATA[Templates]]></item>
                <item name="wcf.acp.template.error.noGroups"><![CDATA[Bevor {if LANGUAGE_USE_INFORMAL_VARIANT}du{else}Sie{/if} ein eigenes Template erstellen {if LANGUAGE_USE_INFORMAL_VARIANT}kannst{else}können{/if}, {if LANGUAGE_USE_INFORMAL_VARIANT}musst du{else}müssen Sie{/if} eine <a href="{link controller='TemplateGroupAdd'}{/link}">Templategruppe hinzufügen</a>.]]></item>
index 22821d7d1b60f510fea568204f178904f854c857..d22bbeec2c42bd1c57536dac11b501d8fd677149 100644 (file)
@@ -1977,6 +1977,7 @@ When prompted for the notification URL for the instant payment notifications, pl
                <item name="wcf.acp.template.lastModificationTime"><![CDATA[Last Modification]]></item>
                <item name="wcf.acp.template.group.list"><![CDATA[Template Groups]]></item>
                <item name="wcf.acp.template.group.add"><![CDATA[Add Template Group]]></item>
+               <item name="wcf.acp.template.group.copy"><![CDATA[Copy Template Group]]></item>
                <item name="wcf.acp.template.group.edit"><![CDATA[Edit Template Group]]></item>
                <item name="wcf.acp.template.group.templates"><![CDATA[Templates]]></item>
                <item name="wcf.acp.template.error.noGroups"><![CDATA[You must create a <a href="{link controller='TemplateGroupAdd'}{/link}">template group</a> first.]]></item>