[BUGFIX] Keep configuration of extbase table column cache
[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 */
37 protected $objectManager = null;
38
39 /**
40 * @var \TYPO3\CMS\Core\Configuration\ConfigurationManager
41 */
42 protected $configurationManager = null;
43
44 /**
45 * List of obsolete configuration options in LocalConfiguration to be removed
46 * Example:
47 * // #forge-ticket
48 * 'BE/somesetting',
49 *
50 * @var array
51 */
52 protected $obsoleteLocalConfigurationSettings = array(
53 // #62402
54 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\ExtensionManagerTables',
55 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\FileIdentifierHashUpdate',
56 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\FilemountUpdateWizard',
57 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\FilePermissionUpdate',
58 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\FileTableSplittingUpdate',
59 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\InitUpdateWizard',
60 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\MediaFlexformUpdate',
61 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\ReferenceIntegrityUpdateWizard',
62 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\RteFileLinksUpdateWizard',
63 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\RteMagicImagesUpdateWizard',
64 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\TceformsUpdateWizard',
65 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\TtContentUploadsUpdateWizard',
66 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\TruncateSysFileProcessedFileTable',
67 // #68183
68 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\MigrateShortcutUrlsUpdate',
69 // #63818
70 'BE/staticFileEditPath',
71 // #64226
72 'BE/accessListRenderMode',
73 // #66431
74 'BE/loginNewsTitle',
75 // #24900
76 'SYS/compat_version',
77 // #64643
78 'GFX/enable_typo3temp_db_tracking',
79 // #48542
80 'GFX/TTFdpi',
81 // #64872
82 'SYS/useCachingFramework',
83 // #65912
84 'FE/allowedTempPaths',
85 // #66034
86 'FE/activateContentAdapter',
87 // #66902
88 'SYS/loginCopyrightShowVersion',
89 // #66903
90 'BE/RTEenabled',
91 // #66906
92 'GFX/png_to_gif',
93 // #67411
94 'SYS/caching/cacheConfigurations/cache_classes',
95 // #68178
96 'SYS/form_enctype',
97 // #69904
98 'BE/diff_path',
99 // #69930
100 'SYS/serverTimeZone',
101 // #70138
102 'BE/flexFormXMLincludeDiffBase',
103 // #71110
104 'BE/maxFileSize',
105 );
106
107 /**
108 * @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager
109 */
110 public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
111 {
112 $this->objectManager = $objectManager;
113 }
114
115 /**
116 * @param \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager
117 */
118 public function injectConfigurationManager(\TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager)
119 {
120 $this->configurationManager = $configurationManager;
121 }
122
123 /**
124 * Executed configuration upgrades. Single upgrade methods must throw a
125 * RedirectException if something was written to LocalConfiguration.
126 *
127 * @return void
128 */
129 public function execute()
130 {
131 $this->generateEncryptionKeyIfNeeded();
132 $this->configureBackendLoginSecurity();
133 $this->configureSaltedPasswords();
134 $this->setProxyAuthScheme();
135 $this->transferDeprecatedCurlSettings();
136 $this->disableImageMagickAndGdlibIfImageProcessingIsDisabled();
137 $this->disableImageMagickDetailSettingsIfImageMagickIsDisabled();
138 $this->setImageMagickDetailSettings();
139 $this->removeObsoleteLocalConfigurationSettings();
140 }
141
142 /**
143 * Some settings in LocalConfiguration vanished in DefaultConfiguration
144 * and have no impact on the core anymore.
145 * To keep the configuration clean, those old settings are just silently
146 * removed from LocalConfiguration if set.
147 *
148 * @return void
149 */
150 protected function removeObsoleteLocalConfigurationSettings()
151 {
152 $removed = $this->configurationManager->removeLocalConfigurationKeysByPath($this->obsoleteLocalConfigurationSettings);
153
154 // If something was changed: Trigger a reload to have new values in next request
155 if ($removed) {
156 $this->throwRedirectException();
157 }
158 }
159
160 /**
161 * Backend login security is set to rsa if rsaauth
162 * is installed (but not used) otherwise the default value "normal" has to be used.
163 * This forces either 'normal' or 'rsa' to be set in LocalConfiguration.
164 *
165 * @return void
166 */
167 protected function configureBackendLoginSecurity()
168 {
169 $rsaauthLoaded = ExtensionManagementUtility::isLoaded('rsaauth');
170 try {
171 $currentLoginSecurityLevelValue = $this->configurationManager->getLocalConfigurationValueByPath('BE/loginSecurityLevel');
172 if ($rsaauthLoaded && $currentLoginSecurityLevelValue !== 'rsa') {
173 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'rsa');
174 $this->throwRedirectException();
175 } elseif (!$rsaauthLoaded && $currentLoginSecurityLevelValue !== 'normal') {
176 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'normal');
177 $this->throwRedirectException();
178 }
179 } catch (\RuntimeException $e) {
180 // If an exception is thrown, the value is not set in LocalConfiguration
181 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', $rsaauthLoaded ? 'rsa' : 'normal');
182 $this->throwRedirectException();
183 }
184 }
185
186 /**
187 * Check the settings for salted passwords extension to load it as a required extension.
188 * Unset obsolete configuration options if given.
189 *
190 * @return void
191 */
192 protected function configureSaltedPasswords()
193 {
194 $defaultConfiguration = $this->configurationManager->getDefaultConfiguration();
195 $defaultExtensionConfiguration = unserialize($defaultConfiguration['EXT']['extConf']['saltedpasswords']);
196 try {
197 $extensionConfiguration = @unserialize($this->configurationManager->getLocalConfigurationValueByPath('EXT/extConf/saltedpasswords'));
198 } catch (\RuntimeException $e) {
199 $extensionConfiguration = array();
200 }
201 if (is_array($extensionConfiguration) && !empty($extensionConfiguration)) {
202 if (isset($extensionConfiguration['BE.']['enabled'])) {
203 if ($extensionConfiguration['BE.']['enabled']) {
204 unset($extensionConfiguration['BE.']['enabled']);
205 } else {
206 $extensionConfiguration['BE.'] = $defaultExtensionConfiguration['BE.'];
207 }
208 $this->configurationManager->setLocalConfigurationValueByPath(
209 'EXT/extConf/saltedpasswords',
210 serialize($extensionConfiguration)
211 );
212 $this->throwRedirectException();
213 }
214 } else {
215 $this->configurationManager->setLocalConfigurationValueByPath(
216 'EXT/extConf/saltedpasswords',
217 serialize($defaultExtensionConfiguration)
218 );
219 $this->throwRedirectException();
220 }
221 }
222
223 /**
224 * The encryption key is crucial for securing form tokens
225 * and the whole TYPO3 link rendering later on. A random key is set here in
226 * LocalConfiguration if it does not exist yet. This might possible happen
227 * during upgrading and will happen during first install.
228 *
229 * @return void
230 */
231 protected function generateEncryptionKeyIfNeeded()
232 {
233 try {
234 $currentValue = $this->configurationManager->getLocalConfigurationValueByPath('SYS/encryptionKey');
235 } catch (\RuntimeException $e) {
236 // If an exception is thrown, the value is not set in LocalConfiguration
237 $currentValue = '';
238 }
239
240 if (empty($currentValue)) {
241 $randomKey = GeneralUtility::getRandomHexString(96);
242 $this->configurationManager->setLocalConfigurationValueByPath('SYS/encryptionKey', $randomKey);
243 $this->throwRedirectException();
244 }
245 }
246
247 /**
248 * $GLOBALS['TYPO3_CONF_VARS']['HTTP']['proxy_auth_scheme'] must be either
249 * 'digest' or 'basic'. 'basic' is default in DefaultConfiguration, so the
250 * setting can be removed from LocalConfiguration if it is not set to 'digest'.
251 *
252 * @return void
253 */
254 protected function setProxyAuthScheme()
255 {
256 // Get current value from LocalConfiguration
257 try {
258 $currentValueInLocalConfiguration = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_auth_scheme');
259 } catch (\RuntimeException $e) {
260 // If an exception is thrown, the value is not set in LocalConfiguration, so we don't need to do anything
261 return;
262 }
263 if ($currentValueInLocalConfiguration !== 'digest') {
264 $this->configurationManager->removeLocalConfigurationKeysByPath(array('HTTP/proxy_auth_scheme'));
265 $this->throwRedirectException();
266 }
267 }
268
269 /**
270 * Parse old curl options and set new http ones instead
271 *
272 * @return void
273 */
274 protected function transferDeprecatedCurlSettings()
275 {
276 $changed = false;
277 try {
278 $curlProxyServer = $this->configurationManager->getLocalConfigurationValueByPath('SYS/curlProxyServer');
279 } catch (\RuntimeException $e) {
280 $curlProxyServer = '';
281 }
282 try {
283 $proxyHost = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_host');
284 } catch (\RuntimeException $e) {
285 $proxyHost = '';
286 }
287 if (!empty($curlProxyServer) && empty($proxyHost)) {
288 $curlProxy = rtrim(preg_replace('#^https?://#', '', $curlProxyServer), '/');
289 $proxyParts = GeneralUtility::revExplode(':', $curlProxy, 2);
290 $this->configurationManager->setLocalConfigurationValueByPath('HTTP/proxy_host', $proxyParts[0]);
291 $this->configurationManager->setLocalConfigurationValueByPath('HTTP/proxy_port', $proxyParts[1]);
292 $changed = true;
293 }
294
295 try {
296 $curlProxyUserPass = $this->configurationManager->getLocalConfigurationValueByPath('SYS/curlProxyUserPass');
297 } catch (\RuntimeException $e) {
298 $curlProxyUserPass = '';
299 }
300 try {
301 $proxyUser = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_user');
302 } catch (\RuntimeException $e) {
303 $proxyUser = '';
304 }
305 if (!empty($curlProxyUserPass) && empty($proxyUser)) {
306 $userPassParts = explode(':', $curlProxyUserPass, 2);
307 $this->configurationManager->setLocalConfigurationValueByPath('HTTP/proxy_user', $userPassParts[0]);
308 $this->configurationManager->setLocalConfigurationValueByPath('HTTP/proxy_password', $userPassParts[1]);
309 $changed = true;
310 }
311
312 try {
313 $curlUse = $this->configurationManager->getLocalConfigurationValueByPath('SYS/curlUse');
314 } catch (\RuntimeException $e) {
315 $curlUse = '';
316 }
317 try {
318 $adapter = $this->configurationManager->getConfigurationValueByPath('HTTP/adapter');
319 } catch (\RuntimeException $e) {
320 $adapter = '';
321 }
322 if (!empty($curlUse) && $adapter !== 'curl') {
323 $GLOBALS['TYPO3_CONF_VARS']['HTTP']['adapter'] = 'curl';
324 $this->configurationManager->setLocalConfigurationValueByPath('HTTP/adapter', 'curl');
325 $changed = true;
326 }
327 if ($changed) {
328 $this->throwRedirectException();
329 }
330 }
331
332 /**
333 * GFX/im and GFX/gdlib must be set to 0 if image_processing is disabled.
334 *
335 * "Configuration presets" in install tool is not type safe, so value
336 * comparisons here are not type safe too, to not trigger changes to
337 * LocalConfiguration again.
338 *
339 * @return void
340 */
341 protected function disableImageMagickAndGdlibIfImageProcessingIsDisabled()
342 {
343 $changedValues = array();
344 try {
345 $currentImageProcessingValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/image_processing');
346 } catch (\RuntimeException $e) {
347 $currentImageProcessingValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/image_processing');
348 }
349 try {
350 $currentImValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im');
351 } catch (\RuntimeException $e) {
352 $currentImValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im');
353 }
354 try {
355 $currentGdlibValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/gdlib');
356 } catch (\RuntimeException $e) {
357 $currentGdlibValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/gdlib');
358 }
359 // If image processing is fully disabled, im and gdlib sub settings must be 0
360 if (!$currentImageProcessingValue) {
361 if ($currentImValue != 0) {
362 $changedValues['GFX/im'] = 0;
363 }
364 if ($currentGdlibValue != 0) {
365 $changedValues['GFX/gdlib'] = 0;
366 }
367 }
368 if (!empty($changedValues)) {
369 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
370 $this->throwRedirectException();
371 }
372 }
373
374 /**
375 * Detail configuration of Image Magick settings must be cleared
376 * if Image Magick handling is disabled.
377 *
378 * "Configuration presets" in install tool is not type safe, so value
379 * comparisons here are not type safe too, to not trigger changes to
380 * LocalConfiguration again.
381 *
382 * @return void
383 */
384 protected function disableImageMagickDetailSettingsIfImageMagickIsDisabled()
385 {
386 $changedValues = array();
387 try {
388 $currentImValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im');
389 } catch (\RuntimeException $e) {
390 $currentImValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im');
391 }
392 try {
393 $currentImPathValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_path');
394 } catch (\RuntimeException $e) {
395 $currentImPathValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_path');
396 }
397 try {
398 $currentImPathLzwValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_path_lzw');
399 } catch (\RuntimeException $e) {
400 $currentImPathLzwValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_path_lzw');
401 }
402 try {
403 $currentImageFileExtValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/imagefile_ext');
404 } catch (\RuntimeException $e) {
405 $currentImageFileExtValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/imagefile_ext');
406 }
407 try {
408 $currentThumbnailsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/thumbnails');
409 } catch (\RuntimeException $e) {
410 $currentThumbnailsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/thumbnails');
411 }
412 if (!$currentImValue) {
413 if ($currentImPathValue != '') {
414 $changedValues['GFX/im_path'] = '';
415 }
416 if ($currentImPathLzwValue != '') {
417 $changedValues['GFX/im_path_lzw'] = '';
418 }
419 if ($currentImageFileExtValue !== 'gif,jpg,jpeg,png') {
420 $changedValues['GFX/imagefile_ext'] = 'gif,jpg,jpeg,png';
421 }
422 if ($currentThumbnailsValue != 0) {
423 $changedValues['GFX/thumbnails'] = 0;
424 }
425 }
426 if (!empty($changedValues)) {
427 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
428 $this->throwRedirectException();
429 }
430 }
431
432 /**
433 * Detail configuration of Image Magick and Graphics Magick settings
434 * depending on main values.
435 *
436 * "Configuration presets" in install tool is not type safe, so value
437 * comparisons here are not type safe too, to not trigger changes to
438 * LocalConfiguration again.
439 *
440 * @return void
441 */
442 protected function setImageMagickDetailSettings()
443 {
444 $changedValues = array();
445 try {
446 $currentIm5Value = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_version_5');
447 } catch (\RuntimeException $e) {
448 $currentIm5Value = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_version_5');
449 }
450 try {
451 $currentImMaskValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_mask_temp_ext_gif');
452 } catch (\RuntimeException $e) {
453 $currentImMaskValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_mask_temp_ext_gif');
454 }
455 try {
456 $currentIm5EffectsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_v5effects');
457 } catch (\RuntimeException $e) {
458 $currentIm5EffectsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_v5effects');
459 }
460 if ((string)$currentIm5Value !== '') {
461 if ($currentImMaskValue != 1) {
462 $changedValues['GFX/im_mask_temp_ext_gif'] = 1;
463 }
464 if ($currentIm5Value === 'gm') {
465 if ($currentIm5EffectsValue != -1) {
466 $changedValues['GFX/im_v5effects'] = -1;
467 }
468 }
469 }
470 if (!empty($changedValues)) {
471 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
472 $this->throwRedirectException();
473 }
474 }
475
476 /**
477 * Throw exception after configuration change to trigger a redirect.
478 *
479 * @throws RedirectException
480 */
481 protected function throwRedirectException()
482 {
483 throw new RedirectException(
484 'Configuration updated, reload needed',
485 1379024938
486 );
487 }
488 }