90295e8172d9992e69ea3a2b2f459dd899822101
[Packages/TYPO3.CMS.git] / typo3 / sysext / lowlevel / Classes / Controller / ConfigurationController.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Lowlevel\Controller;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use Psr\Http\Message\ResponseInterface;
19 use Psr\Http\Message\ServerRequestInterface;
20 use TYPO3\CMS\Backend\Routing\Router;
21 use TYPO3\CMS\Backend\Template\ModuleTemplate;
22 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
23 use TYPO3\CMS\Core\Cache\Backend\NullBackend;
24 use TYPO3\CMS\Core\Cache\Frontend\PhpFrontend;
25 use TYPO3\CMS\Core\Http\MiddlewareStackResolver;
26 use TYPO3\CMS\Core\Localization\LanguageService;
27 use TYPO3\CMS\Core\Package\PackageManager;
28 use TYPO3\CMS\Core\Service\DependencyOrderingService;
29 use TYPO3\CMS\Core\Utility\ArrayUtility;
30 use TYPO3\CMS\Core\Utility\GeneralUtility;
31 use TYPO3\CMS\Fluid\View\StandaloneView;
32 use TYPO3\CMS\Lowlevel\Utility\ArrayBrowser;
33
34 /**
35 * View configuration arrays in the backend
36 */
37 class ConfigurationController
38 {
39 /**
40 * Available trees to render.
41 * * label is an LLL identifier
42 * * type is used to identify the data source type
43 * * globalKey (only for type=global) is the name of a global variable
44 *
45 * @var array
46 */
47 protected $treeSetup = [
48 'confVars' => [
49 'label' => 'typo3ConfVars',
50 'type' => 'global',
51 'globalKey' => 'TYPO3_CONF_VARS',
52 ],
53 'tca' => [
54 'label' => 'tca',
55 'type' => 'global',
56 'globalKey' => 'TCA',
57 ],
58 'tcaDescr' => [
59 'label' => 'tcaDescr',
60 'type' => 'global',
61 'globalKey' => 'TCA_DESCR',
62 ],
63 'loadedExt' => [
64 'label' => 'loadedExt',
65 'type' => 'global',
66 'globalKey' => 'TYPO3_LOADED_EXT',
67 ],
68 'services' => [
69 'label' => 't3services',
70 'key' => 'services',
71 'type' => 'global',
72 'globalKey' => 'T3_SERVICES',
73 ],
74 'tbeModules' => [
75 'label' => 'tbemodules',
76 'type' => 'global',
77 'globalKey' => 'TBE_MODULES',
78 ],
79 'tbeModulesExt' => [
80 'label' => 'tbemodulesext',
81 'type' => 'global',
82 'globalKey' => 'TBE_MODULES_EXT',
83 ],
84 'tbeStyles' => [
85 'label' => 'tbeStyles',
86 'type' => 'global',
87 'globalKey' => 'TBE_STYLES',
88 ],
89 'userSettings' => [
90 'label' => 'usersettings',
91 'type' => 'global',
92 'globalKey' => 'TYPO3_USER_SETTINGS',
93 ],
94 'pagesTypes' => [
95 'label' => 'pagesTypes',
96 'type' => 'global',
97 'globalKey' => 'PAGES_TYPES',
98 ],
99 'beUserUc' => [
100 'label' => 'beUser',
101 'type' => 'uc',
102 ],
103 'beRoutes' => [
104 'label' => 'routes',
105 'type' => 'routes',
106 ],
107 'httpMiddlewareStacks' => [
108 'label' => 'httpMiddlewareStacks',
109 'type' => 'httpMiddlewareStacks',
110 ],
111 ];
112
113 /**
114 * Blind configurations which should not be visible to mortal admins
115 *
116 * @var array
117 */
118 protected $blindedConfigurationOptions = [
119 'TYPO3_CONF_VARS' => [
120 'DB' => [
121 'database' => '******',
122 'host' => '******',
123 'password' => '******',
124 'port' => '******',
125 'socket' => '******',
126 'username' => '******',
127 'Connections' => [
128 'Default' => [
129 'dbname' => '******',
130 'host' => '******',
131 'password' => '******',
132 'port' => '******',
133 'user' => '******',
134 'unix_socket' => '******',
135 ],
136 ],
137 ],
138 'SYS' => [
139 'encryptionKey' => '******'
140 ],
141 ],
142 ];
143
144 /**
145 * Main controller action determines get/post values, takes care of
146 * stored backend user settings for this module, determines tree
147 * and renders it.
148 *
149 * @param ServerRequestInterface $request the current request
150 * @param ResponseInterface $response
151 * @return ResponseInterface the response with the content
152 * @throws \RuntimeException
153 */
154 public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
155 {
156 $backendUser = $this->getBackendUser();
157 $languageService = $this->getLanguageService();
158
159 $queryParams = $request->getQueryParams();
160 $postValues = $request->getParsedBody();
161
162 $moduleState = $backendUser->uc['moduleData']['system_config'] ?? [];
163
164 // Determine validated tree key and tree detail setup
165 $selectedTreeKey = $this->treeSetup[$queryParams['tree']] ? $queryParams['tree']
166 : ($this->treeSetup[$moduleState['tree']] ? $moduleState['tree'] : key($this->treeSetup));
167 $selectedTreeDetails = $this->treeSetup[$selectedTreeKey];
168 $moduleState['tree'] = $selectedTreeKey;
169
170 // Search string given or regex search enabled?
171 $searchString = (string)($postValues['searchString'] ? trim($postValues['searchString']) : '');
172 $moduleState['regexSearch'] = (bool)($postValues['regexSearch'] ?? $moduleState['regexSearch'] ?? false);
173
174 // Prepare main array
175 $sortKeysByName = true;
176 if ($selectedTreeDetails['type'] === 'global') {
177 $globalArrayKey = $selectedTreeDetails['globalKey'];
178 $renderArray = $GLOBALS[$globalArrayKey];
179
180 // Hook for Processing blindedConfigurationOptions
181 $blindedConfigurationOptions = $this->blindedConfigurationOptions;
182
183 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['modifyBlindedConfigurationOptions'] ?? [] as $classReference) {
184 $processingObject = GeneralUtility::makeInstance($classReference);
185 $blindedConfigurationOptions = $processingObject->modifyBlindedConfigurationOptions($blindedConfigurationOptions, $this);
186 }
187
188 if (isset($blindedConfigurationOptions[$globalArrayKey])) {
189 // Prepare blinding for all database connection types
190 foreach (array_keys($GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']) as $connectionName) {
191 if ($connectionName !== 'Default') {
192 $blindedConfigurationOptions['TYPO3_CONF_VARS']['DB']['Connections'][$connectionName] =
193 $blindedConfigurationOptions['TYPO3_CONF_VARS']['DB']['Connections']['Default'];
194 }
195 }
196 ArrayUtility::mergeRecursiveWithOverrule(
197 $renderArray,
198 ArrayUtility::intersectRecursive($blindedConfigurationOptions[$globalArrayKey], $renderArray)
199 );
200 }
201 } elseif ($selectedTreeDetails['type'] === 'uc') {
202 $renderArray = $backendUser->uc;
203 } elseif ($selectedTreeDetails['type'] === 'routes') {
204 $router = GeneralUtility::makeInstance(Router::class);
205 $routes = $router->getRoutes();
206 $renderArray = [];
207 foreach ($routes as $identifier => $route) {
208 /** @var $route \TYPO3\CMS\Backend\Routing\Route */
209 $renderArray[$identifier] = [
210 'path' => $route->getPath(),
211 'options' => $route->getOptions()
212 ];
213 }
214 } elseif ($selectedTreeDetails['type'] === 'httpMiddlewareStacks') {
215 // Keep the order of the keys
216 $sortKeysByName = false;
217 // Fake a PHP frontend with a null backend to avoid PHP Opcache conflicts
218 // When using >requireOnce() multiple times in one request
219 $cache = GeneralUtility::makeInstance(
220 PhpFrontend::class,
221 'middleware',
222 GeneralUtility::makeInstance(NullBackend::class, 'Production')
223 );
224 $stackResolver = GeneralUtility::makeInstance(
225 MiddlewareStackResolver::class,
226 GeneralUtility::makeInstance(PackageManager::class),
227 GeneralUtility::makeInstance(DependencyOrderingService::class),
228 $cache
229 );
230 $renderArray = [];
231 foreach (['frontend', 'backend'] as $stackName) {
232 // reversing the array allows the admin to read the stack from top to bottom
233 $renderArray[$stackName] = array_reverse($stackResolver->resolve($stackName));
234 }
235 } else {
236 throw new \RuntimeException('Unknown array type "' . $selectedTreeDetails['type'] . '"', 1507845662);
237 }
238 if ($sortKeysByName) {
239 ArrayUtility::naturalKeySortRecursive($renderArray);
240 }
241
242 // Prepare array renderer class, apply search and expand / collapse states
243 $arrayBrowser = GeneralUtility::makeInstance(ArrayBrowser::class);
244 $arrayBrowser->dontLinkVar = true;
245 $arrayBrowser->searchKeysToo = true;
246 $arrayBrowser->regexMode = $moduleState['regexSearch'];
247 $node = $queryParams['node'];
248 if ($searchString) {
249 $arrayBrowser->depthKeys = $arrayBrowser->getSearchKeys($renderArray, '', $searchString, []);
250 } elseif (is_array($node)) {
251 $newExpandCollapse = $arrayBrowser->depthKeys($node, $moduleState['node_' . $selectedTreeKey]);
252 $arrayBrowser->depthKeys = $newExpandCollapse;
253 $moduleState['node_' . $selectedTreeKey] = $newExpandCollapse;
254 } else {
255 $arrayBrowser->depthKeys = $moduleState['node_' . $selectedTreeKey] ?? [];
256 }
257
258 // Store new state
259 $backendUser->uc['moduleData']['system_config'] = $moduleState;
260 $backendUser->writeUC();
261
262 // Render main body
263 $view = GeneralUtility::makeInstance(StandaloneView::class);
264 $view->getRequest()->setControllerExtensionName('lowlevel');
265 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName(
266 'EXT:lowlevel/Resources/Private/Templates/Backend/Configuration.html'
267 ));
268 $view->assignMultiple([
269 'treeName' => $selectedTreeDetails['label'],
270 'searchString' => $searchString,
271 'regexSearch' => $moduleState['regexSearch'],
272 'tree' => $arrayBrowser->tree($renderArray, ''),
273 ]);
274
275 // Prepare module setup
276 $moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
277 $moduleTemplate->setContent($view->render());
278 $moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Lowlevel/ConfigurationView');
279
280 // Shortcut in doc header
281 $shortcutButton = $moduleTemplate->getDocHeaderComponent()->getButtonBar()->makeShortcutButton();
282 $shortcutButton->setModuleName('system_config')
283 ->setDisplayName($languageService->sL(
284 'LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:' . $selectedTreeDetails['label']
285 ))
286 ->setSetVariables(['tree']);
287 $moduleTemplate->getDocHeaderComponent()->getButtonBar()->addButton($shortcutButton);
288
289 // Main drop down in doc header
290 $menu = $moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
291 $menu->setIdentifier('tree');
292 foreach ($this->treeSetup as $treeKey => $treeDetails) {
293 $menuItem = $menu->makeMenuItem();
294 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
295 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
296 $menuItem->setHref((string)$uriBuilder->buildUriFromRoute('system_config', ['tree' => $treeKey]))
297 ->setTitle($languageService->sL(
298 'LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:' . $treeDetails['label']
299 ));
300 if ($selectedTreeKey === $treeKey) {
301 $menuItem->setActive(true);
302 }
303 $menu->addMenuItem($menuItem);
304 }
305 $moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu);
306
307 $response->getBody()->write($moduleTemplate->renderContent());
308 return $response;
309 }
310
311 /**
312 * Returns the Backend User
313 * @return BackendUserAuthentication
314 */
315 protected function getBackendUser()
316 {
317 return $GLOBALS['BE_USER'];
318 }
319
320 /**
321 * @return LanguageService
322 */
323 protected function getLanguageService()
324 {
325 return $GLOBALS['LANG'];
326 }
327 }