[TASK] Install tool fails if LocalConfiguration EXT/extConf does not exist
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Controller / LayoutController.php
1 <?php
2 declare(strict_types=1);
3 namespace TYPO3\CMS\Install\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\Core\Configuration\ConfigurationManager;
21 use TYPO3\CMS\Core\Http\HtmlResponse;
22 use TYPO3\CMS\Core\Http\JsonResponse;
23 use TYPO3\CMS\Install\Service\Exception\ConfigurationChangedException;
24 use TYPO3\CMS\Install\Service\ExtensionConfigurationService;
25 use TYPO3\CMS\Install\Service\SilentConfigurationUpgradeService;
26
27 /**
28 * Layout controller
29 *
30 * Renders a first "load the Javascript in <head>" view, and the
31 * main layout of the install tool in second action.
32 */
33 class LayoutController extends AbstractController
34 {
35 /**
36 * The init action renders an HTML response with HTML view having <head> section
37 * containing resources to main .js routing.
38 *
39 * @param ServerRequestInterface $request
40 * @return ResponseInterface
41 */
42 public function initAction(ServerRequestInterface $request): ResponseInterface
43 {
44 $view = $this->initializeStandaloneView($request, 'Layout/Init.html');
45 $view->assignMultiple([
46 // time is used as cache bust for js and css resources
47 'time' => time(),
48 'siteName' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'],
49 ]);
50 return new HtmlResponse(
51 $view->render(),
52 200,
53 [
54 'Cache-Control' => 'no-cache, must-revalidate',
55 'Pragma' => 'no-cache'
56 ]
57 );
58 }
59
60 /**
61 * Return a json response with the main HTML layout body: Toolbar, main menu and
62 * doc header in standalone, doc header only in backend context. Silent updaters
63 * are executed before this main view is loaded.
64 *
65 * @param ServerRequestInterface $request
66 * @return ResponseInterface
67 */
68 public function mainLayoutAction(ServerRequestInterface $request): ResponseInterface
69 {
70 $view = $this->initializeStandaloneView($request, 'Layout/MainLayout.html');
71 return new JsonResponse([
72 'success' => true,
73 'html' => $view->render(),
74 ]);
75 }
76
77 /**
78 * Execute silent configuration update. May be called multiple times until success = true is returned.
79 *
80 * @return ResponseInterface success = true if no change has been done
81 */
82 public function executeSilentConfigurationUpdateAction(): ResponseInterface
83 {
84 $silentUpdate = new SilentConfigurationUpgradeService();
85 $success = true;
86 try {
87 $silentUpdate->execute();
88 } catch (ConfigurationChangedException $e) {
89 $success = false;
90 }
91 return new JsonResponse([
92 'success' => $success,
93 ]);
94 }
95
96 /**
97 * Legacy ajax call. This silent updater takes care that all extensions configured in LocalConfiguration
98 * EXT/extConf serialized array are "upmerged" to arrays within EXTENSIONS if this extension does not
99 * exist in EXTENSIONS yet.
100 *
101 * @return ResponseInterface
102 * @deprecated since core v9, will be removed with core v10
103 */
104 public function executeSilentLegacyExtConfExtensionConfigurationUpdateAction(): ResponseInterface
105 {
106 $configurationManager = new ConfigurationManager();
107 try {
108 $oldExtConfSettings = $configurationManager->getConfigurationValueByPath('EXT/extConf');
109 } catch (\RuntimeException $e) {
110 // The old 'extConf' array may not exist anymore, set to empty array if so.
111 $oldExtConfSettings = [];
112 }
113 $newExtensionSettings = $configurationManager->getConfigurationValueByPath('EXTENSIONS');
114 foreach ($oldExtConfSettings as $extensionName => $extensionSettings) {
115 if (!array_key_exists($extensionName, $newExtensionSettings)) {
116 $newExtensionSettings = $this->removeDotsFromArrayKeysRecursive(unserialize($extensionSettings, ['allowed_classes' => false]));
117 $configurationManager->setLocalConfigurationValueByPath('EXTENSIONS/' . $extensionName, $newExtensionSettings);
118 }
119 }
120 return new JsonResponse([
121 'success' => true,
122 ]);
123 }
124
125 /**
126 * Synchronize TYPO3_CONF_VARS['EXTENSIONS'] with possibly new defaults from extensions
127 * ext_conf_template.txt files. This make LocalConfiguration the only source of truth for
128 * extension configuration and it is always up to date, also if an extension has been
129 * updated.
130 *
131 * @return ResponseInterface
132 */
133 public function executeSilentExtensionConfigurationSynchronizationAction(): ResponseInterface
134 {
135 $extensionConfigurationService = new ExtensionConfigurationService();
136 $extensionConfigurationService->synchronizeExtConfTemplateWithLocalConfigurationOfAllExtensions();
137 return new JsonResponse([
138 'success' => true,
139 ]);
140 }
141
142 /**
143 * Helper method for executeSilentLegacyExtConfExtensionConfigurationUpdateAction(). Old EXT/extConf
144 * settings have dots at the end of array keys if nested arrays were used. The new configuration does
145 * not use this funny nested representation anymore. The method removes all dots at the end of given
146 * array keys recursive to do this transition.
147 *
148 * @param array $settings
149 * @return array New settings
150 * @deprecated since core v9, will be removed with core v10 along with executeSilentLegacyExtConfExtensionConfigurationUpdateAction()
151 */
152 private function removeDotsFromArrayKeysRecursive(array $settings): array
153 {
154 $settingsWithoutDots = [];
155 foreach ($settings as $key => $value) {
156 if (is_array($value)) {
157 $settingsWithoutDots[rtrim($key, '.')] = $this->removeDotsFromArrayKeysRecursive($value);
158 } else {
159 $settingsWithoutDots[$key] = $value;
160 }
161 }
162 return $settingsWithoutDots;
163 }
164 }