From 60ceebf3f4268bdcfb25f966950668bcb6fe9b5a Mon Sep 17 00:00:00 2001 From: Matthias Schmidt Date: Sun, 23 Jul 2017 11:09:18 +0200 Subject: [PATCH] Add quick setup of projects Close #2350 --- .../acp/templates/devtoolsProjectList.tpl | 21 ++ .../Acp/Ui/Devtools/Project/QuickSetup.js | 184 ++++++++++++++++++ .../project/DevtoolsProjectAction.class.php | 89 +++++++++ wcfsetup/install/lang/de.xml | 5 + wcfsetup/install/lang/en.xml | 5 + 5 files changed, 304 insertions(+) create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Devtools/Project/QuickSetup.js diff --git a/wcfsetup/install/files/acp/templates/devtoolsProjectList.tpl b/wcfsetup/install/files/acp/templates/devtoolsProjectList.tpl index c4d6fce273..bc5fbebabd 100644 --- a/wcfsetup/install/files/acp/templates/devtoolsProjectList.tpl +++ b/wcfsetup/install/files/acp/templates/devtoolsProjectList.tpl @@ -4,6 +4,12 @@ $(function() { new WCF.Action.Delete('wcf\\data\\devtools\\project\\DevtoolsProjectAction', '.jsObjectRow'); }); + + require(['WoltLabSuite/Core/Acp/Ui/Devtools/Project/QuickSetup', 'Language'], function(AcpUiDevtoolsProjectQuickSetup, Language) { + Language.add('wcf.acp.devtools.project.quickSetup', '{lang}wcf.acp.devtools.project.quickSetup{/lang}'); + + AcpUiDevtoolsProjectQuickSetup.init(); + });
@@ -13,6 +19,7 @@ + + {include file='footer'} diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Devtools/Project/QuickSetup.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Devtools/Project/QuickSetup.js new file mode 100644 index 0000000000..aba21f3f45 --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Devtools/Project/QuickSetup.js @@ -0,0 +1,184 @@ +/** + * Handles quick setup of all projects within a path. + * + * @author Matthias Schmidt + * @copyright 2001-2017 WoltLab GmbH + * @license GNU Lesser General Public License + * @module WoltLabSuite/Core/Acp/Ui/Devtools/Project/QuickSetup + */ +define([ + 'Ajax', + 'Dom/Traverse', + 'Dom/Util', + 'EventKey', + 'Language', + 'Ui/Dialog', + 'Ui/Notification' +], function ( + Ajax, + DomTraverse, + DomUtil, + EventKey, + Language, + UiDialog, + UiNotification +) { + "use strict"; + + var _setupButtons = elByClass('jsDevtoolsProjectQuickSetupButton'); + var _submitButton = elById('projectQuickSetupSubmit'); + var _pathInput = elById('projectQuickSetupPath'); + + return { + /** + * Initializes the project quick setup handler. + */ + init: function() { + // add event listeners to open dialog + Array.prototype.forEach.call(_setupButtons, function(button) { + button.addEventListener('click', this._showDialog.bind(this)); + }.bind(this)); + + // add event listener to submit dialog + _submitButton.addEventListener('click', this._submit.bind(this)); + + // add event listener to input field to submit dialog by pressing enter + _pathInput.addEventListener('keypress', this._keyPress.bind(this)); + }, + + /** + * Returns the data used to setup the AJAX request object. + * + * @return {object} setup data + */ + _ajaxSetup: function () { + return { + data: { + actionName: 'quickSetup', + className: 'wcf\\data\\devtools\\project\\DevtoolsProjectAction' + } + }; + }, + + /** + * Handles successful AJAX request. + * + * @param {object} data response data + */ + _ajaxSuccess: function(data) { + if (data.returnValues.errorMessage) { + this._showPathError(data.returnValues.errorMessage); + + _submitButton.disabled = false; + + return; + } + + UiDialog.close(this); + + UiNotification.show(data.returnValues.successMessage, function() { + window.location.reload(); + }); + }, + + /** + * Returns the data used to setup the dialog. + * + * @return {object} setup data + */ + _dialogSetup: function() { + return { + id: 'projectQuickSetup', + options: { + onShow: this._onDialogShow.bind(this), + title: Language.get('wcf.acp.devtools.project.quickSetup') + } + }; + }, + + /** + * Returns the error element for the path input in the dialog or + * `null` if no exists yet. + * + * @param {boolean} createPathError if `true` and inner error element does not exist, it will be created + * @return {HTMLElement?} path error element + */ + _getPathError: function(createPathError) { + var innerError = DomTraverse.nextByClass(_pathInput, 'innerError'); + + if (createPathError && innerError === null) { + innerError = elCreate('small'); + innerError.classList = 'innerError'; + + DomUtil.insertAfter(innerError, _pathInput); + } + + return innerError; + }, + + /** + * Handles the `[ENTER]` key to submit the form. + * + * @param {object} event event object + */ + _keyPress: function(event) { + if (EventKey.Enter(event)) { + this._submit(); + } + }, + + /** + * Is called every time the dialog is shown. + */ + _onDialogShow: function() { + // reset path input + _pathInput.value = ''; + _pathInput.focus(); + + // hide error + var innerError = this._getPathError(); + if (innerError) { + elHide(innerError); + } + }, + + /** + * Shows the dialog after clicking on the related button. + */ + _showDialog: function() { + UiDialog.open(this); + }, + + /** + * Shows the path error message. + * + * @param {string} errorMessage path error emssage + */ + _showPathError: function(errorMessage) { + var innerError = this._getPathError(true); + innerError.textContent = errorMessage; + + elShow(innerError); + }, + + /** + * Is called if the dialog form is submitted. + */ + _submit: function() { + // check if path is empty + if (_pathInput.value === '') { + this._showPathError(Language.get('wcf.global.form.error.empty')); + + return; + } + + Ajax.api(this, { + parameters: { + path: _pathInput.value + } + }); + + _submitButton.disabled = true; + } + }; +}); \ No newline at end of file diff --git a/wcfsetup/install/files/lib/data/devtools/project/DevtoolsProjectAction.class.php b/wcfsetup/install/files/lib/data/devtools/project/DevtoolsProjectAction.class.php index 4154b6eb57..67735aae31 100644 --- a/wcfsetup/install/files/lib/data/devtools/project/DevtoolsProjectAction.class.php +++ b/wcfsetup/install/files/lib/data/devtools/project/DevtoolsProjectAction.class.php @@ -2,6 +2,9 @@ namespace wcf\data\devtools\project; use wcf\data\AbstractDatabaseObjectAction; use wcf\system\exception\IllegalLinkException; +use wcf\system\WCF; +use wcf\util\DirectoryUtil; +use wcf\util\FileUtil; /** * Executes devtools project related actions. @@ -41,4 +44,90 @@ class DevtoolsProjectAction extends AbstractDatabaseObjectAction { parent::validateDelete(); } + + /** + * Validates the 'quickSetup' action. + * + * @throws IllegalLinkException + */ + public function validateQuickSetup() { + if (!ENABLE_DEVELOPER_TOOLS) { + throw new IllegalLinkException(); + } + + WCF::getSession()->checkPermissions(['admin.configuration.package.canInstallPackage']); + + $this->readString('path'); + } + + /** + * Quickly setups multiple projects by scanning a directory. + * + * @return array + */ + public function quickSetup() { + if (!is_dir($this->parameters['path'])) { + return [ + 'errorMessage' => WCF::getLanguage()->get('wcf.acp.devtools.project.path.error.notFound'), + 'errorType' => 'notFound' + ]; + } + + $path = FileUtil::addTrailingSlash(FileUtil::unifyDirSeparator($this->parameters['path'])); + + // read all project names and paths + $projectList = new DevtoolsProjectList(); + $projectList->readObjects(); + + $projectNames = $projectPaths = []; + foreach ($projectList as $project) { + $projectNames[] = $project->name; + $projectPaths[] = $project->path; + } + + $projectCount = 0; + + $directoryUtil = DirectoryUtil::getInstance($path, false); + $directoryUtil->executeCallback(function($directory) use ($path, $projectNames, $projectPaths, &$projectCount) { + $projectPath = $path . $directory . '/'; + + // validate path + if (DevtoolsProject::validatePath($projectPath) !== '') { + return; + } + + // only consider paths that are not already used by a different project + if (in_array($projectPath, $projectPaths)) { + return; + } + + // make sure that project name is unique + $name = $directory; + + $iteration = 1; + while (in_array($name, $projectNames)) { + $name = $directory . ' (' . $iteration++ . ')'; + } + + (new DevtoolsProjectAction([], 'create', ['data' => [ + 'name' => $name, + 'path' => $projectPath + ]]))->executeAction(); + + $projectCount++; + }); + + if (!$projectCount) { + return [ + 'errorMessage' => WCF::getLanguage()->get('wcf.acp.devtools.project.quickSetup.path.error.noPackages'), + 'errorType' => 'noPackages' + ]; + } + + return [ + 'successMessage' => WCF::getLanguage()->getDynamicVariable('wcf.acp.devtools.project.quickSetup.success', [ + 'count' => $projectCount + ]) + ]; + } } diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml index 1c4afae0fa..b3dcc17f4a 100644 --- a/wcfsetup/install/lang/de.xml +++ b/wcfsetup/install/lang/de.xml @@ -398,6 +398,11 @@ + + + + + 1}}wurden {#$count} neue Projekte{else}ein neues Projekt{/if} hinzugefügt.]]> diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index 1cc943a6f9..2dc57a3ebc 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -381,6 +381,11 @@ + + + + + 1}s have{else} has{/if} been added.]]> -- 2.20.1