[TASK] Install tool: System maintainer administration 31/53931/6
authorChristian Kuhn <lolli@schwarzbu.ch>
Wed, 6 Sep 2017 17:22:33 +0000 (19:22 +0200)
committerAndreas Fernandez <typo3@scripting-base.de>
Thu, 7 Sep 2017 19:24:45 +0000 (21:24 +0200)
Adds a card to the "Settings" menu to allow configuration of
"System Maintainers" in the install tool writing SYS/systemMaintainers
array to LocalConfiguration.

Change-Id: I0219b60e9a261373befab7b846e55b61ea215e5e
Resolves: #82319
Releases: master
Reviewed-on: https://review.typo3.org/53931
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Matthias Vogel <typo3@kanti.de>
Tested-by: Matthias Vogel <typo3@kanti.de>
Reviewed-by: Łukasz Uznański <l.uznanski@macopedia.pl>
Tested-by: Łukasz Uznański <l.uznanski@macopedia.pl>
Reviewed-by: Andreas Fernandez <typo3@scripting-base.de>
Tested-by: Andreas Fernandez <typo3@scripting-base.de>
typo3/sysext/install/Classes/Controller/Action/Ajax/SystemMaintainerGetList.php [new file with mode: 0644]
typo3/sysext/install/Classes/Controller/Action/Ajax/SystemMaintainerWrite.php [new file with mode: 0644]
typo3/sysext/install/Classes/Controller/Action/Tool/Settings.php
typo3/sysext/install/Classes/Controller/AjaxController.php
typo3/sysext/install/Resources/Private/Partials/Action/Tool/Settings/SystemMaintainer.html [new file with mode: 0644]
typo3/sysext/install/Resources/Private/Templates/Action/Tool/Settings.html
typo3/sysext/install/Resources/Public/JavaScript/Modules/SystemMaintainer.js [new file with mode: 0644]

diff --git a/typo3/sysext/install/Classes/Controller/Action/Ajax/SystemMaintainerGetList.php b/typo3/sysext/install/Classes/Controller/Action/Ajax/SystemMaintainerGetList.php
new file mode 100644 (file)
index 0000000..5caebd9
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+declare(strict_types=1);
+namespace TYPO3\CMS\Install\Controller\Action\Ajax;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+/**
+ * Get list of backend admin users with information if they are system maintainers
+ */
+class SystemMaintainerGetList extends AbstractAjaxAction
+{
+    /**
+     * Get backend admin user list
+     *
+     * @return array
+     */
+    protected function executeAction(): array
+    {
+        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
+
+        // We have to respect the enable fields here by our own because no TCA is loaded in standalone mode
+        $queryBuilder = $connectionPool->getQueryBuilderForTable('be_users');
+        $queryBuilder->getRestrictions()->removeAll();
+
+        $users = $queryBuilder
+            ->select('uid', 'username', 'disable', 'starttime', 'endtime')
+            ->from('be_users')
+            ->where(
+                $queryBuilder->expr()->andX(
+                    $queryBuilder->expr()->eq('deleted', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
+                    $queryBuilder->expr()->eq('admin', $queryBuilder->createNamedParameter(1, \PDO::PARAM_INT))
+                )
+            )
+            ->orderBy('uid')
+            ->execute()
+            ->fetchAll();
+
+        $systemMaintainerList = $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemMaintainers'] ?? [];
+        $currentTime = time();
+        foreach ($users as &$user) {
+            $user['disable'] = $user['disable'] ||
+                ((int)$user['starttime'] !== 0 && $user['starttime'] > $currentTime) ||
+                ((int)$user['endtime'] !== 0 && $user['endtime'] < $currentTime);
+            $user['isSystemMaintainer'] = in_array((int)$user['uid'], $systemMaintainerList, true);
+        }
+        $this->view->assignMultiple([
+            'success' => true,
+            'status' => [],
+            'users' => $users,
+        ]);
+        return $this->view->render();
+    }
+}
diff --git a/typo3/sysext/install/Classes/Controller/Action/Ajax/SystemMaintainerWrite.php b/typo3/sysext/install/Classes/Controller/Action/Ajax/SystemMaintainerWrite.php
new file mode 100644 (file)
index 0000000..ecded37
--- /dev/null
@@ -0,0 +1,87 @@
+<?php
+declare(strict_types=1);
+namespace TYPO3\CMS\Install\Controller\Action\Ajax;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Configuration\ConfigurationManager;
+use TYPO3\CMS\Core\Database\Connection;
+use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Messaging\FlashMessage;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\MathUtility;
+
+/**
+ * Write out system maintainer list to LocalConfiguration
+ */
+class SystemMaintainerWrite extends AbstractAjaxAction
+{
+    /**
+     * Write system maintainer list
+     *
+     * @return array
+     */
+    protected function executeAction(): array
+    {
+        // Sanitize given user list and write out
+        $newUserList = [];
+        if (isset($this->postValues['users']) && is_array($this->postValues['users'])) {
+            foreach ($this->postValues['users'] as $uid) {
+                if (MathUtility::canBeInterpretedAsInteger($uid)) {
+                    $newUserList[] = (int)$uid;
+                }
+            }
+        }
+
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_users');
+        $validatedUserList = $queryBuilder
+            ->select('uid')
+            ->from('be_users')
+            ->where(
+                $queryBuilder->expr()->andX(
+                    $queryBuilder->expr()->eq('deleted', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
+                    $queryBuilder->expr()->eq('admin', $queryBuilder->createNamedParameter(1, \PDO::PARAM_INT)),
+                    $queryBuilder->expr()->in('uid', $queryBuilder->createNamedParameter($newUserList, Connection::PARAM_INT_ARRAY))
+                )
+            )->execute()->fetchAll();
+
+        $validatedUserList = array_column($validatedUserList, 'uid');
+
+        $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
+        $configurationManager->setLocalConfigurationValuesByPathValuePairs(
+            [ 'SYS/systemMaintainers' => $validatedUserList ]
+        );
+
+        $messages = [];
+        if (empty($validatedUserList)) {
+            $messages[] = new FlashMessage(
+                '',
+                'Set system maintainer list to an empty array',
+                FlashMessage::INFO
+            );
+        } else {
+            $messages[] = new FlashMessage(
+                implode(', ', $validatedUserList),
+                'New system maintainer uid list',
+                FlashMessage::INFO
+            );
+        }
+
+        $this->view->assignMultiple([
+            'success' => true,
+            'status' => $messages
+        ]);
+        return $this->view->render();
+    }
+}
index 4114873..1520e26 100644 (file)
@@ -50,6 +50,7 @@ class Settings extends Action\AbstractAction
         $presetFeatures = $this->featureManager->getInitializedFeatures($this->postValues['values'] ?? []);
         $localConfigurationValueService = new LocalConfigurationValueService();
         $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
+
         $this->view->assignMultiple([
             'changeInstallToolPasswordToken' => $formProtection->generateToken('installTool', 'changeInstallToolPassword'),
 
@@ -59,6 +60,9 @@ class Settings extends Action\AbstractAction
 
             'presetActivateToken' => $formProtection->generateToken('installTool', 'presetActivate'),
             'presetFeatures' => $presetFeatures,
+
+            'systemMaintainerWriteToken' => $formProtection->generateToken('installTool', 'systemMaintainerWrite'),
+            'systemMaintainerIsDevelopmentContext' => GeneralUtility::getApplicationContext()->isDevelopment(),
         ]);
         return $this->view->render();
     }
index b1d2316..8631ec6 100644 (file)
@@ -66,6 +66,10 @@ class AjaxController extends AbstractController
         'mailTest',
         'presetActivate',
         'resetBackendUserUc',
+
+        'systemMaintainerGetList',
+        'systemMaintainerWrite',
+
         'tcaExtTablesCheck',
         'tcaMigrationsCheck',
 
diff --git a/typo3/sysext/install/Resources/Private/Partials/Action/Tool/Settings/SystemMaintainer.html b/typo3/sysext/install/Resources/Private/Partials/Action/Tool/Settings/SystemMaintainer.html
new file mode 100644 (file)
index 0000000..4384385
--- /dev/null
@@ -0,0 +1,43 @@
+<p>
+       Backend admin users listed here will see the system maintenance related main
+       module entries "Maintenance", "Settings", "Upgrade" and "Environment" in the
+       backend and are allowed to use them without further log in.
+       Note that all backend admin users see these menu entries if the system is in
+       development (not production) context.
+</p>
+
+<f:if condition="{systemMaintainerIsDevelopmentContext}">
+       <div class="typo3-message alert alert-info">
+               <div class="message-body">
+                       This TYPO3 instance is set to "Development" context, all backend admin users
+                       can see the system maintenance related main module entries.
+               </div>
+       </div>
+</f:if>
+
+<div style="display:none;">
+       <div id="t3js-systemMaintainer-write-token">{systemMaintainerWriteToken}</div>
+</div>
+
+<div class="form-group">
+       <div class="input-group t3js-systemMaintainer-chosen" style="display:none">
+               <span class="input-group-addon">System Maintainer:</span>
+               <select
+                       class="chosen-select t3js-systemMaintainer-chosen-select"
+                       data-placeholder="none"
+                       style="width:100%;"
+                       multiple
+                       tabindex=""
+               >
+               </select>
+       </div>
+</div>
+
+<div class="t3js-systemMaintainer-output"></div>
+
+<button
+       class="btn btn-default t3js-systemMaintainer-write"
+       type="button"
+>
+       Save system maintainer list
+</button>
index 55750a3..7c590df 100644 (file)
                        category: 'Access',
                        description: 'Set a new install tool password.'
                },
-               1: {partial: 'Action/Tool/Settings/LocalConfiguration',
+               1: {partial: 'Action/Tool/Settings/SystemMaintainer',
+                       require: 'TYPO3/CMS/Install/SystemMaintainer',
+                       baseClass: 't3js-systemMaintainer',
+                       title: 'Configure system maintainer',
+                       category: 'Access',
+                       description: 'Backend admin users with access to install tool.'
+               },
+               2: {partial: 'Action/Tool/Settings/LocalConfiguration',
                        require: 'TYPO3/CMS/Install/LocalConfiguration',
                        baseClass: 't3js-localConfiguration',
                        title: 'Configure Global Settings',
                        category: 'LocalConfiguration',
                        description: 'Modify LocalConfiguration.php settings.'
                },
-               2: {partial: 'Action/Tool/Settings/Presets',
+               3: {partial: 'Action/Tool/Settings/Presets',
                        require: 'TYPO3/CMS/Install/Presets',
                        baseClass: 't3js-presets',
                        title: 'Configure Presets',
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Modules/SystemMaintainer.js b/typo3/sysext/install/Resources/Public/JavaScript/Modules/SystemMaintainer.js
new file mode 100644 (file)
index 0000000..8bee973
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Module: TYPO3/CMS/Install/SystemMaintainer
+ */
+define([
+       'jquery',
+       'TYPO3/CMS/Install/FlashMessage',
+       'TYPO3/CMS/Install/ProgressBar',
+       'TYPO3/CMS/Install/InfoBox',
+       'TYPO3/CMS/Install/Severity',
+       'bootstrap',
+       'chosen'
+], function($, FlashMessage, ProgressBar, InfoBox, Severity) {
+       'use strict';
+
+       return {
+               selectorGridderOpener: 't3js-systemMaintainer-open',
+               selectorWriteTrigger: '.t3js-systemMaintainer-write',
+               selectorWriteToken: '#t3js-systemMaintainer-write-token',
+               selectorOutputContainer: '.t3js-systemMaintainer-output',
+               selectorChosenContainer: '.t3js-systemMaintainer-chosen',
+               selectorChosenField: '.t3js-systemMaintainer-chosen-select',
+
+               initialize: function() {
+                       var self = this;
+
+                       // Get current system maintainer list on card open
+                       $(document).on('cardlayout:card-opened', function(event, $card) {
+                               if ($card.hasClass(self.selectorGridderOpener)) {
+                                       self.getList();
+                               }
+                       });
+
+                       $(document).on('click', this.selectorWriteTrigger, function(e) {
+                               e.preventDefault();
+                               self.write();
+                       });
+               },
+
+               getList: function() {
+                       var self = this;
+                       var url = location.href + '&install[controller]=ajax&install[action]=systemMaintainerGetList';
+                       var $chosenContainer = $(this.selectorChosenContainer);
+                       var $outputContainer = $(this.selectorOutputContainer);
+                       var $chosenField = $(self.selectorChosenField);
+                       var message = ProgressBar.render(Severity.loading, 'Loading...', '');
+                       $outputContainer.empty().append(message);
+                       $chosenContainer.hide();
+                       $chosenField.empty();
+                       $.ajax({
+                               url: url,
+                               cache: false,
+                               success: function (data) {
+                                       if (data.success === true) {
+                                               $outputContainer.find('.alert-loading').remove();
+                                               if (Array.isArray(data.status)) {
+                                                       data.status.forEach(function(element) {
+                                                               var message = InfoBox.render(element.severity, element.title, element.message);
+                                                               $outputContainer.append(message);
+                                                       });
+                                               }
+                                               if (Array.isArray(data.users)) {
+                                                       data.users.forEach(function(element) {
+                                                               var name = element.username;
+                                                               if (element.disable) {
+                                                                       name = '[DISABLED] ' + name;
+                                                               }
+                                                               var selected = '';
+                                                               if (element.isSystemMaintainer) {
+                                                                       selected = 'selected="selected"';
+                                                               }
+                                                               $chosenField.append(
+                                                                       '<option value="' + element.uid + '" ' + selected + '>' + name + '</option>'
+                                                               );
+                                                       });
+                                               }
+                                               var config = {
+                                                       '.chosen-select': {width: "100%", placeholder_text_multiple: "users"},
+                                                       '.chosen-select-deselect': {allow_single_deselect: true},
+                                                       '.chosen-select-width': {width: "100%"}
+                                               };
+                                               for (var selector in config) {
+                                                       $(selector).chosen(config[selector]);
+                                               }
+                                               $chosenContainer.show();
+                                               $chosenField.trigger('chosen:updated');
+                                       }
+                               },
+                               error: function() {
+                                       var message = InfoBox.render(Severity.error, 'Something went wrong', '');
+                                       $outputContainer.empty().html(message);
+                               }
+                       });
+               },
+
+               write: function() {
+                       var $outputContainer = $(this.selectorOutputContainer);
+                       var selectedUsers = $(this.selectorChosenField).val();
+                       var message = ProgressBar.render(Severity.loading, 'Loading...', '');
+                       $outputContainer.append(message);
+                       $.ajax({
+                               method: 'POST',
+                               url: location.href + '&install[controller]=ajax',
+                               data: {
+                                       'install': {
+                                               'users': selectedUsers,
+                                               'token': $(this.selectorWriteToken).text(),
+                                               'action': 'systemMaintainerWrite'
+                                       }
+                               },
+                               success: function (data) {
+                                       if (data.success === true) {
+                                               $outputContainer.find('.alert-loading').remove();
+                                               if (Array.isArray(data.status)) {
+                                                       data.status.forEach(function(element) {
+                                                               var message = InfoBox.render(element.severity, element.title, element.message);
+                                                               $outputContainer.empty().append(message);
+                                                       });
+                                               }
+                                       }
+                               },
+                               error: function() {
+                                       var message = InfoBox.render(Severity.error, 'Something went wrong', '');
+                                       $outputContainer.empty().html(message);
+                               }
+                       });
+               }
+       };
+});