[!!!][TASK] Remove static file edit magic
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Service / SilentConfigurationUpgradeService.php
1 <?php
2 namespace TYPO3\CMS\Install\Service;
3
4 /**
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3\CMS\Install\Controller\Exception\RedirectException;
20
21 /**
22 * Execute "silent" LocalConfiguration upgrades if needed.
23 *
24 * Some LocalConfiguration settings are obsolete or changed over time.
25 * This class handles upgrades of these settings. It is called by
26 * the step controller at an early point.
27 *
28 * Every change is encapsulated in one method an must throw a RedirectException
29 * if new data is written to LocalConfiguration. This is caught by above
30 * step controller to initiate a redirect and start again with adapted configuration.
31 */
32 class SilentConfigurationUpgradeService {
33
34 /**
35 * @var \TYPO3\CMS\Extbase\Object\ObjectManager
36 * @inject
37 */
38 protected $objectManager = NULL;
39
40 /**
41 * @var \TYPO3\CMS\Core\Configuration\ConfigurationManager
42 * @inject
43 */
44 protected $configurationManager = NULL;
45
46 /**
47 * List of obsolete configuration options in LocalConfiguration to be removed
48 * Example:
49 * // #forge-ticket
50 * 'BE/somesetting',
51 *
52 * @var array
53 */
54 protected $obsoleteLocalConfigurationSettings = array(
55 // #62402
56 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\ExtensionManagerTables',
57 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\FileIdentifierHashUpdate',
58 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\FilemountUpdateWizard',
59 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\FilePermissionUpdate',
60 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\FileTableSplittingUpdate',
61 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\InitUpdateWizard',
62 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\MediaFlexformUpdate',
63 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\ReferenceIntegrityUpdateWizard',
64 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\RteFileLinksUpdateWizard',
65 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\RteMagicImagesUpdateWizard',
66 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\TceformsUpdateWizard',
67 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\TtContentUploadsUpdateWizard',
68 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\TruncateSysFileProcessedFileTable',
69 // #63818
70 'BE/staticFileEditPath',
71 );
72
73 /**
74 * Executed configuration upgrades. Single upgrade methods must throw a
75 * RedirectException if something was written to LocalConfiguration.
76 *
77 * @return void
78 */
79 public function execute() {
80 $this->generateEncryptionKeyIfNeeded();
81 $this->configureBackendLoginSecurity();
82 $this->configureSaltedPasswords();
83 $this->setProxyAuthScheme();
84 $this->disableImageMagickAndGdlibIfImageProcessingIsDisabled();
85 $this->disableImageMagickDetailSettingsIfImageMagickIsDisabled();
86 $this->setImageMagickDetailSettings();
87 $this->removeObsoleteLocalConfigurationSettings();
88 }
89
90 /**
91 * Some settings in LocalConfiguration vanished in DefaultConfiguration
92 * and have no impact on the core anymore.
93 * To keep the configuration clean, those old settings are just silently
94 * removed from LocalConfiguration if set.
95 *
96 * @return void
97 */
98 protected function removeObsoleteLocalConfigurationSettings() {
99 $removed = $this->configurationManager->removeLocalConfigurationKeysByPath($this->obsoleteLocalConfigurationSettings);
100
101 // If something was changed: Trigger a reload to have new values in next request
102 if ($removed) {
103 $this->throwRedirectException();
104 }
105 }
106
107 /**
108 * Backend login security is set to rsa if rsaauth
109 * is installed (but not used) otherwise the default value "normal" has to be used.
110 * This forces either 'normal' or 'rsa' to be set in LocalConfiguration.
111 *
112 * @return void
113 */
114 protected function configureBackendLoginSecurity() {
115 try {
116 $currentLoginSecurityLevelValue = $this->configurationManager->getLocalConfigurationValueByPath('BE/loginSecurityLevel');
117 if (ExtensionManagementUtility::isLoaded('rsaauth')
118 && $currentLoginSecurityLevelValue !== 'rsa'
119 ) {
120 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'rsa');
121 $this->throwRedirectException();
122 } elseif (!ExtensionManagementUtility::isLoaded('rsaauth')
123 && $currentLoginSecurityLevelValue !== 'normal'
124 ) {
125 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'normal');
126 $this->throwRedirectException();
127 }
128 } catch (\RuntimeException $e) {
129 // If an exception is thrown, the value is not set in LocalConfiguration
130 if (ExtensionManagementUtility::isLoaded('rsaauth')) {
131 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'rsa');
132 $this->throwRedirectException();
133 } elseif (!ExtensionManagementUtility::isLoaded('rsaauth')) {
134 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'normal');
135 $this->throwRedirectException();
136 }
137 }
138 }
139
140 /**
141 * Check the settings for salted passwords extension to load it as a required extension.
142 * Unset obsolete configuration options if given.
143 *
144 * @return void
145 */
146 protected function configureSaltedPasswords() {
147 $defaultConfiguration = $this->configurationManager->getDefaultConfiguration();
148 $defaultExtensionConfiguration = unserialize($defaultConfiguration['EXT']['extConf']['saltedpasswords']);
149 try {
150 $extensionConfiguration = @unserialize($this->configurationManager->getLocalConfigurationValueByPath('EXT/extConf/saltedpasswords'));
151 } catch (\RuntimeException $e) {
152 $extensionConfiguration = array();
153 }
154 if (is_array($extensionConfiguration) && !empty($extensionConfiguration)) {
155 if (isset($extensionConfiguration['BE.']['enabled'])) {
156 if ($extensionConfiguration['BE.']['enabled']) {
157 unset($extensionConfiguration['BE.']['enabled']);
158 } else {
159 $extensionConfiguration['BE.'] = $defaultExtensionConfiguration['BE.'];
160 }
161 $this->configurationManager->setLocalConfigurationValueByPath(
162 'EXT/extConf/saltedpasswords',
163 serialize($extensionConfiguration)
164 );
165 $this->throwRedirectException();
166 }
167 } else {
168 $this->configurationManager->setLocalConfigurationValueByPath(
169 'EXT/extConf/saltedpasswords',
170 serialize($defaultExtensionConfiguration)
171 );
172 $this->throwRedirectException();
173 }
174 }
175
176 /**
177 * The encryption key is crucial for securing form tokens
178 * and the whole TYPO3 link rendering later on. A random key is set here in
179 * LocalConfiguration if it does not exist yet. This might possible happen
180 * during upgrading and will happen during first install.
181 *
182 * @return void
183 */
184 protected function generateEncryptionKeyIfNeeded() {
185 try{
186 $currentValue = $this->configurationManager->getLocalConfigurationValueByPath('SYS/encryptionKey');
187 } catch (\RuntimeException $e) {
188 // If an exception is thrown, the value is not set in LocalConfiguration
189 $currentValue = '';
190 }
191
192 if (empty($currentValue)) {
193 $randomKey = GeneralUtility::getRandomHexString(96);
194 $this->configurationManager->setLocalConfigurationValueByPath('SYS/encryptionKey', $randomKey);
195 $this->throwRedirectException();
196 }
197 }
198
199 /**
200 * $GLOBALS['TYPO3_CONF_VARS']['HTTP']['proxy_auth_scheme'] must be either
201 * 'digest' or 'basic'. 'basic' is default in DefaultConfiguration, so the
202 * setting can be removed from LocalConfiguration if it is not set to 'digest'.
203 *
204 * @return void
205 */
206 protected function setProxyAuthScheme() {
207 // Get current value from LocalConfiguration
208 try {
209 $currentValueInLocalConfiguration = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_auth_scheme');
210 } catch (\RuntimeException $e) {
211 // If an exception is thrown, the value is not set in LocalConfiguration, so we don't need to do anything
212 return;
213 }
214 if ($currentValueInLocalConfiguration !== 'digest') {
215 $this->configurationManager->removeLocalConfigurationKeysByPath(array('HTTP/proxy_auth_scheme'));
216 $this->throwRedirectException();
217 }
218 }
219
220 /**
221 * GFX/im and GFX/gdlib must be set to 0 if image_processing is disabled.
222 *
223 * "Configuration presets" in install tool is not type safe, so value
224 * comparisons here are not type safe too, to not trigger changes to
225 * LocalConfiguration again.
226 *
227 * @return void
228 */
229 protected function disableImageMagickAndGdlibIfImageProcessingIsDisabled() {
230 $changedValues = array();
231 try {
232 $currentImageProcessingValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/image_processing');
233 } catch (\RuntimeException $e) {
234 $currentImageProcessingValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/image_processing');
235 }
236 try {
237 $currentImValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im');
238 } catch (\RuntimeException $e) {
239 $currentImValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im');
240 }
241 try {
242 $currentGdlibValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/gdlib');
243 } catch (\RuntimeException $e) {
244 $currentGdlibValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/gdlib');
245 }
246 // If image processing is fully disabled, im and gdlib sub settings must be 0
247 if (!$currentImageProcessingValue) {
248 if ($currentImValue != 0) {
249 $changedValues['GFX/im'] = 0;
250 }
251 if ($currentGdlibValue != 0) {
252 $changedValues['GFX/gdlib'] = 0;
253 }
254 }
255 if (count($changedValues) > 0) {
256 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
257 $this->throwRedirectException();
258 }
259 }
260
261 /**
262 * Detail configuration of Image Magick settings must be cleared
263 * if Image Magick handling is disabled.
264 *
265 * "Configuration presets" in install tool is not type safe, so value
266 * comparisons here are not type safe too, to not trigger changes to
267 * LocalConfiguration again.
268 *
269 * @return void
270 */
271 protected function disableImageMagickDetailSettingsIfImageMagickIsDisabled() {
272 $changedValues = array();
273 try {
274 $currentImValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im');
275 }
276 catch (\RuntimeException $e) {
277 $currentImValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im');
278 }
279 try {
280 $currentImPathValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_path');
281 }
282 catch (\RuntimeException $e) {
283 $currentImPathValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_path');
284 }
285 try {
286 $currentImPathLzwValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_path_lzw');
287 }
288 catch (\RuntimeException $e) {
289 $currentImPathLzwValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_path_lzw');
290 }
291 try {
292 $currentImageFileExtValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/imagefile_ext');
293 }
294 catch (\RuntimeException $e) {
295 $currentImageFileExtValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/imagefile_ext');
296 }
297 try {
298 $currentThumbnailsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/thumbnails');
299 }
300 catch (\RuntimeException $e) {
301 $currentThumbnailsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/thumbnails');
302 }
303 if (!$currentImValue) {
304 if ($currentImPathValue != '') {
305 $changedValues['GFX/im_path'] = '';
306 }
307 if ($currentImPathLzwValue != '') {
308 $changedValues['GFX/im_path_lzw'] = '';
309 }
310 if ($currentImageFileExtValue !== 'gif,jpg,jpeg,png') {
311 $changedValues['GFX/imagefile_ext'] = 'gif,jpg,jpeg,png';
312 }
313 if ($currentThumbnailsValue != 0) {
314 $changedValues['GFX/thumbnails'] = 0;
315 }
316 }
317 if (count($changedValues) > 0) {
318 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
319 $this->throwRedirectException();
320 }
321 }
322
323 /**
324 * Detail configuration of Image Magick and Graphics Magick settings
325 * depending on main values.
326 *
327 * "Configuration presets" in install tool is not type safe, so value
328 * comparisons here are not type safe too, to not trigger changes to
329 * LocalConfiguration again.
330 *
331 * @return void
332 */
333 protected function setImageMagickDetailSettings() {
334 $changedValues = array();
335 try {
336 $currentIm5Value = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_version_5');
337 }
338 catch (\RuntimeException $e) {
339 $currentIm5Value = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_version_5');
340 }
341 try {
342 $currentImMaskValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_mask_temp_ext_gif');
343 }
344 catch (\RuntimeException $e) {
345 $currentImMaskValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_mask_temp_ext_gif');
346 }
347 try {
348 $currentIm5EffectsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_v5effects');
349 }
350 catch (\RuntimeException $e) {
351 $currentIm5EffectsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_v5effects');
352 }
353 if (strlen($currentIm5Value) > 0) {
354 if ($currentImMaskValue != 1) {
355 $changedValues['GFX/im_mask_temp_ext_gif'] = 1;
356 }
357 if ($currentIm5Value === 'gm') {
358 if ($currentIm5EffectsValue != -1) {
359 $changedValues['GFX/im_v5effects'] = -1;
360 }
361 }
362 }
363 if (count($changedValues) > 0) {
364 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
365 $this->throwRedirectException();
366 }
367 }
368
369 /**
370 * Throw exception after configuration change to trigger a redirect.
371 *
372 * @throws RedirectException
373 */
374 protected function throwRedirectException() {
375 throw new RedirectException(
376 'Configuration updated, reload needed',
377 1379024938
378 );
379 }
380 }