Skip to content
Snippets Groups Projects
Commit 594e0841 authored by Christian Kuhn's avatar Christian Kuhn Committed by Andreas Fernandez
Browse files

[TASK] Clean up ext:lowlevel ConfigurationController

Modernize the code base of the lowlevel ConfigurationController:
* No more _GP usages
* Get rid of MOD_SETTINGS and friends
* Don't use BackendUtility::funcModule and other crude helpers
* Get and apply changes to be_user->uc directly
* Move last html pieces to view

Change-Id: I24af6c53b9c55c82e0e1fc1ee79de97768fd9b2c
Resolves: #82754
Releases: master
Reviewed-on: https://review.typo3.org/54389


Tested-by: default avatarTYPO3com <no-reply@typo3.com>
Reviewed-by: default avatarFrank Naegler <frank.naegler@typo3.org>
Tested-by: default avatarFrank Naegler <frank.naegler@typo3.org>
Reviewed-by: default avatarAndreas Fernandez <typo3@scripting-base.de>
Tested-by: default avatarAndreas Fernandez <typo3@scripting-base.de>
parent da9da511
Branches
Tags
No related merge requests found
<?php <?php
declare(strict_types=1);
namespace TYPO3\CMS\Lowlevel\Controller; namespace TYPO3\CMS\Lowlevel\Controller;
/* /*
...@@ -20,44 +21,89 @@ use TYPO3\CMS\Backend\Routing\Router; ...@@ -20,44 +21,89 @@ use TYPO3\CMS\Backend\Routing\Router;
use TYPO3\CMS\Backend\Template\ModuleTemplate; use TYPO3\CMS\Backend\Template\ModuleTemplate;
use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\ArrayUtility; use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3\CMS\Fluid\View\StandaloneView; use TYPO3\CMS\Fluid\View\StandaloneView;
use TYPO3\CMS\Lowlevel\Utility\ArrayBrowser; use TYPO3\CMS\Lowlevel\Utility\ArrayBrowser;
/** /**
* Script class for the Config module * View configuration arrays in the backend
*/ */
class ConfigurationController class ConfigurationController
{ {
/** /**
* The name of the module * Available trees to render.
* * label is an LLL identifier
* * type is used to identify the data source type
* * globalKey (only for type=global) is the name of a global variable
* *
* @var string
*/
protected $moduleName = 'system_config';
/**
* The module menu items array. Each key represents a key for which values can range between the items in the array of that key.
*
* @see init()
* @var array * @var array
*/ */
protected $MOD_MENU = [ protected $treeSetup = [
'function' => [] 'confVars' => [
'label' => 'typo3ConfVars',
'type' => 'global',
'globalKey' => 'TYPO3_CONF_VARS',
],
'tca' => [
'label' => 'tca',
'type' => 'global',
'globalKey' => 'TCA',
],
'tcaDescr' => [
'label' => 'tcaDescr',
'type' => 'global',
'globalKey' => 'TCA_DESCR',
],
'loadedExt' => [
'label' => 'loadedExt',
'type' => 'global',
'globalKey' => 'TYPO3_LOADED_EXT',
],
'services' => [
'label' => 't3services',
'key' => 'services',
'type' => 'global',
'globalKey' => 'T3_SERVICES',
],
'tbeModules' => [
'label' => 'tbemodules',
'type' => 'global',
'globalKey' => 'TBE_MODULES',
],
'tbeModulesExt' => [
'label' => 'tbemodulesext',
'type' => 'global',
'globalKey' => 'TBE_MODULES_EXT',
],
'tbeStyles' => [
'label' => 'tbeStyles',
'type' => 'global',
'globalKey' => 'TBE_STYLES',
],
'userSettings' => [
'label' => 'usersettings',
'type' => 'global',
'globalKey' => 'TYPO3_USER_SETTINGS',
],
'pagesTypes' => [
'label' => 'pagesTypes',
'type' => 'global',
'globalKey' => 'PAGES_TYPES',
],
'beUserUc' => [
'label' => 'beUser',
'type' => 'uc',
],
'beRoutes' => [
'label' => 'routes',
'type' => 'routes',
],
]; ];
/** /**
* Current settings for the keys of the MOD_MENU array * Blind configurations which should not be visible to mortal admins
*
* @see $MOD_MENU
* @var array
*/
protected $MOD_SETTINGS = [];
/**
* Blind configurations which should not be visible
* *
* @var array * @var array
*/ */
...@@ -83,219 +129,139 @@ class ConfigurationController ...@@ -83,219 +129,139 @@ class ConfigurationController
], ],
'SYS' => [ 'SYS' => [
'encryptionKey' => '******' 'encryptionKey' => '******'
] ],
] ],
]; ];
/** /**
* Injects the request object for the current request or subrequest * Main controller action determines get/post values, takes care of
* Simply calls main() and init() and outputs the content * stored backend user settings for this module, determines tree
* and renders it.
* *
* @param ServerRequestInterface $request the current request * @param ServerRequestInterface $request the current request
* @param ResponseInterface $response * @param ResponseInterface $response
* @return ResponseInterface the response with the content * @return ResponseInterface the response with the content
* @throws \RuntimeException
*/ */
public function mainAction(ServerRequestInterface $request, ResponseInterface $response) public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
{ {
$view = GeneralUtility::makeInstance(StandaloneView::class); $backendUser = $this->getBackendUser();
$view->getRequest()->setControllerExtensionName('lowlevel'); $languageService = $this->getLanguageService();
// Prepare blinding for all database connection types
foreach (array_keys($GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']) as $connectionName) {
if ($connectionName !== 'Default') {
$this->blindedConfigurationOptions['TYPO3_CONF_VARS']['DB']['Connections'][$connectionName] =
$this->blindedConfigurationOptions['TYPO3_CONF_VARS']['DB']['Connections']['Default'];
}
}
// MENU-ITEMS: $queryParams = $request->getQueryParams();
// If array, then it's a selector box menu $postValues = $request->getParsedBody();
// If empty string it's just a variable, that'll be saved.
// Values NOT in this array will not be saved in the settings-array for the module.
$this->MOD_MENU = [
'function' => [
0 => LocalizationUtility::translate('typo3ConfVars', 'lowlevel'),
1 => LocalizationUtility::translate('tca', 'lowlevel'),
2 => LocalizationUtility::translate('tcaDescr', 'lowlevel'),
3 => LocalizationUtility::translate('loadedExt', 'lowlevel'),
4 => LocalizationUtility::translate('t3services', 'lowlevel'),
5 => LocalizationUtility::translate('tbemodules', 'lowlevel'),
6 => LocalizationUtility::translate('tbemodulesext', 'lowlevel'),
7 => LocalizationUtility::translate('tbeStyles', 'lowlevel'),
8 => LocalizationUtility::translate('beUser', 'lowlevel'),
9 => LocalizationUtility::translate('usersettings', 'lowlevel'),
10 => LocalizationUtility::translate('pagesTypes', 'lowlevel'),
11 => LocalizationUtility::translate('routes', 'lowlevel'),
],
'regexsearch' => '',
'fixedLgd' => ''
];
// CLEANSE SETTINGS
$this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, GeneralUtility::_GP('SET'), $this->moduleName);
$moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class); $moduleState = $backendUser->uc['moduleData']['system_config'] ?? [];
$moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Lowlevel/ConfigurationView');
/** @var ArrayBrowser $arrayBrowser */ // Determine validated tree key and tree detail setup
$arrayBrowser = GeneralUtility::makeInstance(ArrayBrowser::class); $selectedTreeKey = $this->treeSetup[$queryParams['tree']] ? $queryParams['tree']
$label = $this->MOD_MENU['function'][$this->MOD_SETTINGS['function']]; : ($this->treeSetup[$moduleState['tree']] ? $moduleState['tree'] : key($this->treeSetup));
$search_field = GeneralUtility::_GP('search_field'); $selectedTreeDetails = $this->treeSetup[$selectedTreeKey];
$moduleState['tree'] = $selectedTreeKey;
$templatePathAndFilename = GeneralUtility::getFileAbsFileName('EXT:lowlevel/Resources/Private/Templates/Backend/Configuration.html'); // Search string given or regex search enabled?
$view->setTemplatePathAndFilename($templatePathAndFilename); $searchString = (string)($postValues['searchString'] ? trim($postValues['searchString']) : '');
$view->assign('label', $label); $moduleState['regexSearch'] = (bool)($postValues['regexSearch'] ?? $moduleState['regexSearch'] ?? false);
$view->assign('search_field', $search_field);
$view->assign('checkbox_checkRegexsearch', BackendUtility::getFuncCheck(0, 'SET[regexsearch]', $this->MOD_SETTINGS['regexsearch'], '', '', 'id="checkRegexsearch"'));
switch ($this->MOD_SETTINGS['function']) { // Prepare main array
case 0: if ($selectedTreeDetails['type'] === 'global') {
$theVar = $GLOBALS['TYPO3_CONF_VARS']; $globalArrayKey = $selectedTreeDetails['globalKey'];
ArrayUtility::naturalKeySortRecursive($theVar); $renderArray = $GLOBALS[$globalArrayKey];
$arrayBrowser->varName = '$TYPO3_CONF_VARS'; $blindedConfigurationOptions = $this->blindedConfigurationOptions;
break; if (isset($blindedConfigurationOptions[$globalArrayKey])) {
case 1: // Prepare blinding for all database connection types
$theVar = $GLOBALS['TCA']; foreach (array_keys($GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']) as $connectionName) {
ArrayUtility::naturalKeySortRecursive($theVar); if ($connectionName !== 'Default') {
$arrayBrowser->varName = '$TCA'; $blindedConfigurationOptions['TYPO3_CONF_VARS']['DB']['Connections'][$connectionName] =
break; $blindedConfigurationOptions['TYPO3_CONF_VARS']['DB']['Connections']['Default'];
case 2: }
$theVar = $GLOBALS['TCA_DESCR'];
ArrayUtility::naturalKeySortRecursive($theVar);
$arrayBrowser->varName = '$TCA_DESCR';
break;
case 3:
$theVar = $GLOBALS['TYPO3_LOADED_EXT'];
ArrayUtility::naturalKeySortRecursive($theVar);
$arrayBrowser->varName = '$TYPO3_LOADED_EXT';
break;
case 4:
$theVar = $GLOBALS['T3_SERVICES'];
ArrayUtility::naturalKeySortRecursive($theVar);
$arrayBrowser->varName = '$T3_SERVICES';
break;
case 5:
$theVar = $GLOBALS['TBE_MODULES'];
ArrayUtility::naturalKeySortRecursive($theVar);
$arrayBrowser->varName = '$TBE_MODULES';
break;
case 6:
$theVar = $GLOBALS['TBE_MODULES_EXT'];
ArrayUtility::naturalKeySortRecursive($theVar);
$arrayBrowser->varName = '$TBE_MODULES_EXT';
break;
case 7:
$theVar = $GLOBALS['TBE_STYLES'];
ArrayUtility::naturalKeySortRecursive($theVar);
$arrayBrowser->varName = '$TBE_STYLES';
break;
case 8:
$theVar = $GLOBALS['BE_USER']->uc;
ArrayUtility::naturalKeySortRecursive($theVar);
$arrayBrowser->varName = '$BE_USER->uc';
break;
case 9:
$theVar = $GLOBALS['TYPO3_USER_SETTINGS'];
ArrayUtility::naturalKeySortRecursive($theVar);
$arrayBrowser->varName = '$TYPO3_USER_SETTINGS';
break;
case 10:
$theVar = $GLOBALS['PAGES_TYPES'];
ArrayUtility::naturalKeySortRecursive($theVar);
$arrayBrowser->varName = '$PAGES_TYPES';
break;
case 11:
$router = GeneralUtility::makeInstance(Router::class);
$routes = $router->getRoutes();
$theVar = [];
foreach ($routes as $identifier => $route) {
$theVar[$identifier] = [
'path' => $route->getPath(),
'options' => $route->getOptions()
];
} }
ArrayUtility::naturalKeySortRecursive($theVar); ArrayUtility::mergeRecursiveWithOverrule(
$arrayBrowser->varName = 'BackendRoutes'; $renderArray,
break; ArrayUtility::intersectRecursive($blindedConfigurationOptions[$globalArrayKey], $renderArray)
default: );
$theVar = []; }
} } elseif ($selectedTreeDetails['type'] === 'uc') {
// Update node: $renderArray = $backendUser->uc;
$update = 0; } elseif ($selectedTreeDetails['type'] === 'routes') {
$node = GeneralUtility::_GET('node'); $router = GeneralUtility::makeInstance(Router::class);
// If any plus-signs were clicked, it's registered. $routes = $router->getRoutes();
if (is_array($node)) { $renderArray = [];
$this->MOD_SETTINGS['node_' . $this->MOD_SETTINGS['function']] = $arrayBrowser->depthKeys($node, $this->MOD_SETTINGS['node_' . $this->MOD_SETTINGS['function']]); foreach ($routes as $identifier => $route) {
$update = 1; /** @var $route \TYPO3\CMS\Backend\Routing\Route */
} $renderArray[$identifier] = [
if ($update) { 'path' => $route->getPath(),
$this->getBackendUser()->pushModuleData($this->moduleName, $this->MOD_SETTINGS); 'options' => $route->getOptions()
];
}
} else {
throw new \RuntimeException('Unknown array type "' . $selectedTreeDetails['type'] . '"', 1507845662);
} }
ArrayUtility::naturalKeySortRecursive($renderArray);
// Prepare array renderer class, apply search and expand / collapse states
$arrayBrowser = GeneralUtility::makeInstance(ArrayBrowser::class);
$arrayBrowser->dontLinkVar = true; $arrayBrowser->dontLinkVar = true;
$arrayBrowser->depthKeys = $this->MOD_SETTINGS['node_' . $this->MOD_SETTINGS['function']];
$arrayBrowser->regexMode = $this->MOD_SETTINGS['regexsearch'];
$arrayBrowser->fixedLgd = $this->MOD_SETTINGS['fixedLgd'];
$arrayBrowser->searchKeysToo = true; $arrayBrowser->searchKeysToo = true;
$arrayBrowser->regexMode = $moduleState['regexSearch'];
// If any POST-vars are send, update the condition array $node = $queryParams['node'];
if (GeneralUtility::_POST('search') && trim($search_field)) { if ($searchString) {
$arrayBrowser->depthKeys = $arrayBrowser->getSearchKeys($theVar, '', $search_field, []); $arrayBrowser->depthKeys = $arrayBrowser->getSearchKeys($renderArray, '', $searchString, []);
} elseif (is_array($node)) {
$newExpandCollapse = $arrayBrowser->depthKeys($node, $moduleState['node_' . $selectedTreeKey]);
$arrayBrowser->depthKeys = $newExpandCollapse;
$moduleState['node_' . $selectedTreeKey] = $newExpandCollapse;
} else {
$arrayBrowser->depthKeys = $moduleState['node_' . $selectedTreeKey] ?? [];
} }
// mask sensitive information // Store new state
$varName = trim($arrayBrowser->varName, '$'); $backendUser->uc['moduleData']['system_config'] = $moduleState;
if (isset($this->blindedConfigurationOptions[$varName])) { $backendUser->writeUC();
ArrayUtility::mergeRecursiveWithOverrule($theVar, ArrayUtility::intersectRecursive($this->blindedConfigurationOptions[$varName], $theVar));
}
$tree = $arrayBrowser->tree($theVar, '');
$view->assign('tree', $tree);
// Setting up the shortcut button for docheader // Render main body
$buttonBar = $moduleTemplate->getDocHeaderComponent()->getButtonBar(); $view = GeneralUtility::makeInstance(StandaloneView::class);
// Shortcut $view->getRequest()->setControllerExtensionName('lowlevel');
$shortcutButton = $buttonBar->makeShortcutButton() $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName(
->setModuleName($this->moduleName) 'EXT:lowlevel/Resources/Private/Templates/Backend/Configuration.html'
->setDisplayName($this->MOD_MENU['function'][$this->MOD_SETTINGS['function']]) ));
->setSetVariables(['function']); $view->assignMultiple([
$buttonBar->addButton($shortcutButton); 'treeName' => $selectedTreeDetails['label'],
'searchString' => $searchString,
'regexSearch' => $moduleState['regexSearch'],
'tree' => $arrayBrowser->tree($renderArray, ''),
]);
$menu = $moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu(); // Prepare module setup
$menu->setIdentifier('ConfigurationJumpMenu'); $moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
$moduleTemplate->setContent($view->render());
$moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Lowlevel/ConfigurationView');
// Shortcut in doc header
$shortcutButton = $moduleTemplate->getDocHeaderComponent()->getButtonBar()->makeShortcutButton();
$shortcutButton->setModuleName('system_config')
->setDisplayName($languageService->sL(
'LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:' . $selectedTreeDetails['label']
))
->setSetVariables(['tree']);
$moduleTemplate->getDocHeaderComponent()->getButtonBar()->addButton($shortcutButton);
foreach ($this->MOD_MENU['function'] as $controller => $title) { // Main drop down in doc header
$item = $menu $menu = $moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
->makeMenuItem() $menu->setIdentifier('tree');
->setHref( foreach ($this->treeSetup as $treeKey => $treeDetails) {
BackendUtility::getModuleUrl( $menuItem = $menu->makeMenuItem();
$this->moduleName, $menuItem->setHref(BackendUtility::getModuleUrl('system_config', ['tree' => $treeKey]))
[ ->setTitle($languageService->sL(
'id' => 0, 'LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:' . $treeDetails['label']
'SET' => [ ));
'function' => $controller if ($selectedTreeKey === $treeKey) {
] $menuItem->setActive(true);
]
)
)
->setTitle($title);
if ($controller === (int)$this->MOD_SETTINGS['function']) {
$item->setActive(true);
} }
$menu->addMenuItem($item); $menu->addMenuItem($menuItem);
} }
$moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu); $moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu);
$moduleTemplate->addJavaScriptCode(
'jumpToUrl',
'
function jumpToUrl(URL) {
window.location.href = URL;
return false;
}
'
);
$content = '<form action="" id="ConfigurationView" method="post">';
$content .= $view->render();
$content .= '</form>';
$moduleTemplate->setContent($content);
$response->getBody()->write($moduleTemplate->renderContent()); $response->getBody()->write($moduleTemplate->renderContent());
return $response; return $response;
} }
...@@ -308,4 +274,12 @@ class ConfigurationController ...@@ -308,4 +274,12 @@ class ConfigurationController
{ {
return $GLOBALS['BE_USER']; return $GLOBALS['BE_USER'];
} }
/**
* @return LanguageService
*/
protected function getLanguageService()
{
return $GLOBALS['LANG'];
}
} }
<h1><f:translate key="configuration" /></h1> <form action="" id="ConfigurationView" method="post">
<h1><f:translate key="configuration" /></h1>
<h2>{label}</h2> <h2><f:translate key="{treeName}" /></h2>
<div id="lowlevel-config"> <div id="lowlevel-config">
<div class="form-group"> <div class="form-group">
<label for="lowlevel-search"> <label for="lowlevel-searchString">
<f:translate key="enterSearchPhrase" /> <f:translate key="enterSearchPhrase" />
</label>
<input class="form-control" type="search" id="lowlevel-search" name="search_field" value="{search_field}" />
</div>
<div class="form-group">
<div class="checkbox">
<label for="checkRegexsearch">
<f:format.raw>{checkbox_checkRegexsearch}</f:format.raw>
<f:translate key="useRegExp" />
</label> </label>
<input class="form-control" type="search" id="lowlevel-searchString" name="searchString" value="{searchString}" />
</div>
<div class="form-group">
<div class="checkbox">
<label for="lowlevel-regexSearch">
<input type="hidden" name="regexSearch" value="0" />
<input
type="checkbox"
class="checkbox"
name="regexSearch"
id="lowlevel-regexSearch"
value="1"
{f:if(condition:'{regexSearch}', then: ' checked')}
>
<f:translate key="useRegExp" />
</label>
</div>
</div>
<div class="form-group">
<input class="btn btn-default" type="submit" name="search" id="search" value="{f:translate(key: 'search')}"/>
</div> </div>
</div> </div>
<div class="form-group">
<input class="btn btn-default" type="submit" name="search" id="search" value="{f:translate(key: 'search')}"/>
</div>
</div>
<div class="nowrap"> <div class="nowrap">
<f:format.raw>{tree}</f:format.raw> <f:format.raw>{tree}</f:format.raw>
</div> </div>
</form>
\ No newline at end of file
...@@ -18,21 +18,20 @@ ...@@ -18,21 +18,20 @@
*/ */
define(['jquery', 'TYPO3/CMS/Backend/jquery.clearable'], function($) { define(['jquery', 'TYPO3/CMS/Backend/jquery.clearable'], function($) {
var $searchFields = $('input[name="search_field"]'); var $searchFields = $('input[name="searchString"]');
var searchResultShown = ('' !== $searchFields.first().val()); var searchResultShown = ('' !== $searchFields.first().val());
// make search field clearable // make search field clearable
$searchFields.clearable({ $searchFields.clearable({
onClear: function() { onClear: function() {
if (searchResultShown) { if (searchResultShown) {
$(this).closest('form').submit(); $(this).closest('form').submit();
} }
} }
}); });
// scroll page down, so the just opened subtree is visible after reload if (self.location.hash) {
// and not hidden by doc header // scroll page down, so the just opened subtree is visible after reload and not hidden by doc header
if (self.location.hash) { $("html, body").scrollTop((document.documentElement.scrollTop || document.body.scrollTop) - 80);
window.scrollTo(window.pageXOffset, window.pageYOffset - 80); }
}
}); });
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment