Add quick setup of projects
authorMatthias Schmidt <gravatronics@live.com>
Sun, 23 Jul 2017 09:09:18 +0000 (11:09 +0200)
committerMatthias Schmidt <gravatronics@live.com>
Sun, 23 Jul 2017 09:09:18 +0000 (11:09 +0200)
Close #2350

wcfsetup/install/files/acp/templates/devtoolsProjectList.tpl
wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Devtools/Project/QuickSetup.js [new file with mode: 0644]
wcfsetup/install/files/lib/data/devtools/project/DevtoolsProjectAction.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index c4d6fce2734bc775b4423f87acdff52b195b0818..bc5fbebabdb22256b472debe9585c48d0d194282 100644 (file)
@@ -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();
+       });
 </script>
 
 <header class="contentHeader">
@@ -13,6 +19,7 @@
        
        <nav class="contentHeaderNavigation">
                <ul>
+                       <li><a href="#" class="button jsDevtoolsProjectQuickSetupButton"><span class="icon icon16 fa-search"></span> <span>{lang}wcf.acp.devtools.project.quickSetup{/lang}</span></a></li>
                        <li><a href="{link controller='DevtoolsProjectAdd'}{/link}" class="button"><span class="icon icon16 fa-plus"></span> <span>{lang}wcf.acp.devtools.project.add{/lang}</span></a></li>
                        
                        {event name='contentHeaderNavigation'}
        </nav>
 </footer>
 
+<div id="projectQuickSetup" style="display: none;">
+       <dl>
+               <dt>{lang}wcf.acp.devtools.project.quickSetup.path{/lang}</dt>
+               <dd>
+                       <input type="text" name="projectQuickSetupPath" id="projectQuickSetupPath" class="long" />
+                       <small>{lang}wcf.acp.devtools.project.quickSetup.path.description{/lang}</small>
+               </dd>
+       </dl>
+       
+       <div class="formSubmit">
+               <button id="projectQuickSetupSubmit" class="buttonPrimary">{lang}wcf.global.button.submit{/lang}</button>
+       </div>
+</div>
+
 {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 (file)
index 0000000..aba21f3
--- /dev/null
@@ -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 <http://opensource.org/licenses/lgpl-license.php>
+ * @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
index 4154b6eb57171f6e9fffe60a06935118a479e00e..67735aae31c3bae4c6b068ef18ae2b4806c0f5ae 100644 (file)
@@ -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
+                       ])
+               ];
+       }
 }
index 1c4afae0fa692567c456231adf16c9f2ad575771..b3dcc17f4a3be5f53ac8b3690bb29facb90f0a3e 100644 (file)
                <item name="wcf.acp.devtools.sync.status.idle"><![CDATA[Bereit.]]></item>
                <item name="wcf.acp.devtools.sync.status.success"><![CDATA[{$timeElapsed}s ({@TIME_NOW|time})]]></item>
                <item name="wcf.acp.devtools.sync.syncAll"><![CDATA[Alles Abgleichen]]></item>
+               <item name="wcf.acp.devtools.project.quickSetup"><![CDATA[Pfad durchsuchen]]></item>
+               <item name="wcf.acp.devtools.project.quickSetup.path"><![CDATA[Pfad]]></item>
+               <item name="wcf.acp.devtools.project.quickSetup.path.description"><![CDATA[Alle Ordner, die sich direkt in dem angegebenen Pfad befinden, werden überprüft, ob sie ein Paket enthalten.]]></item>
+               <item name="wcf.acp.devtools.project.quickSetup.path.error.noPackages"><![CDATA[Der Pfad enthält keine neuen Pakete.]]></item>
+               <item name="wcf.acp.devtools.project.quickSetup.success"><![CDATA[Es {if $count > 1}}wurden {#$count} neue Projekte{else}ein neues Projekt{/if} hinzugefügt.]]></item>
        </category>
        
        <category name="wcf.acp.email">
index 1cc943a6f9fdac5829bbc966f63257400ba15f97..2dc57a3ebccff6ad1540f60d3079d34692856034 100644 (file)
                <item name="wcf.acp.devtools.sync.status.idle"><![CDATA[Ready.]]></item>
                <item name="wcf.acp.devtools.sync.status.success"><![CDATA[{$timeElapsed}s ({@TIME_NOW|time})]]></item>
                <item name="wcf.acp.devtools.sync.syncAll"><![CDATA[Sync All]]></item>
+               <item name="wcf.acp.devtools.project.quickSetup"><![CDATA[Search Path]]></item>
+               <item name="wcf.acp.devtools.project.quickSetup.path"><![CDATA[Path]]></item>
+               <item name="wcf.acp.devtools.project.quickSetup.path.description"><![CDATA[All folders directly contained in the path will be checked whether they contain a package.]]></item>
+               <item name="wcf.acp.devtools.project.quickSetup.path.error.noPackages"><![CDATA[The path contains no new packages.]]></item>
+               <item name="wcf.acp.devtools.project.quickSetup.success"><![CDATA[{#$count} new project{if $count > 1}s have{else} has{/if} been added.]]></item>
        </category>
        
        <category name="wcf.acp.email">