[!!!][TASK] Rename GFX settings referring to IM in their name
[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 // #72367
54 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\AccessRightParametersUpdate',
55 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\BackendUserStartModuleUpdate',
56 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\Compatibility6ExtractionUpdate',
57 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\ContentTypesToTextMediaUpdate',
58 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\FileListIsStartModuleUpdate',
59 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\FilesReplacePermissionUpdate',
60 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\LanguageIsoCodeUpdate',
61 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\MediaceExtractionUpdate',
62 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\MigrateMediaToAssetsForTextMediaCe',
63 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\MigrateShortcutUrlsAgainUpdate',
64 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\OpenidExtractionUpdate',
65 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\PageShortcutParentUpdate',
66 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\ProcessedFileChecksumUpdate',
67 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\TableFlexFormToTtContentFieldsUpdate',
68 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\WorkspacesNotificationSettingsUpdate',
69 'INSTALL/wizardDone/TYPO3\\CMS\\Rtehtmlarea\\Hook\\Install\\DeprecatedRteProperties',
70 'INSTALL/wizardDone/TYPO3\\CMS\\Rtehtmlarea\\Hook\\Install\\RteAcronymButtonRenamedToAbbreviation',
71 // #72400
72 'BE/spriteIconGenerator_handler',
73 // #72417
74 'SYS/lockingMode',
75 // #72473
76 'FE/secureFormmail',
77 'FE/strictFormmail',
78 'FE/formmailMaxAttachmentSize',
79 // #72337
80 'SYS/t3lib_cs_utils',
81 'SYS/t3lib_cs_convMethod',
82 // #72604
83 'SYS/maxFileNameLength',
84 // #72602
85 'BE/unzip_path',
86 // #72615
87 'BE/notificationPrefix',
88 // #72616
89 'BE/XCLASS',
90 'FE/XCLASS',
91 // #43085
92 'GFX/image_processing',
93 );
94
95 /**
96 * @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager
97 */
98 public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
99 {
100 $this->objectManager = $objectManager;
101 }
102
103 /**
104 * @param \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager
105 */
106 public function injectConfigurationManager(\TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager)
107 {
108 $this->configurationManager = $configurationManager;
109 }
110
111 /**
112 * Executed configuration upgrades. Single upgrade methods must throw a
113 * RedirectException if something was written to LocalConfiguration.
114 *
115 * @return void
116 */
117 public function execute()
118 {
119 $this->generateEncryptionKeyIfNeeded();
120 $this->configureBackendLoginSecurity();
121 $this->configureSaltedPasswords();
122 $this->setProxyAuthScheme();
123 $this->migrateImageProcessorSetting();
124 $this->transferDeprecatedCurlSettings();
125 $this->disableImageMagickDetailSettingsIfImageMagickIsDisabled();
126 $this->setImageMagickDetailSettings();
127 $this->removeObsoleteLocalConfigurationSettings();
128 }
129
130 /**
131 * Some settings in LocalConfiguration vanished in DefaultConfiguration
132 * and have no impact on the core anymore.
133 * To keep the configuration clean, those old settings are just silently
134 * removed from LocalConfiguration if set.
135 *
136 * @return void
137 */
138 protected function removeObsoleteLocalConfigurationSettings()
139 {
140 $removed = $this->configurationManager->removeLocalConfigurationKeysByPath($this->obsoleteLocalConfigurationSettings);
141
142 // If something was changed: Trigger a reload to have new values in next request
143 if ($removed) {
144 $this->throwRedirectException();
145 }
146 }
147
148 /**
149 * Backend login security is set to rsa if rsaauth
150 * is installed (but not used) otherwise the default value "normal" has to be used.
151 * This forces either 'normal' or 'rsa' to be set in LocalConfiguration.
152 *
153 * @return void
154 */
155 protected function configureBackendLoginSecurity()
156 {
157 $rsaauthLoaded = ExtensionManagementUtility::isLoaded('rsaauth');
158 try {
159 $currentLoginSecurityLevelValue = $this->configurationManager->getLocalConfigurationValueByPath('BE/loginSecurityLevel');
160 if ($rsaauthLoaded && $currentLoginSecurityLevelValue !== 'rsa') {
161 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'rsa');
162 $this->throwRedirectException();
163 } elseif (!$rsaauthLoaded && $currentLoginSecurityLevelValue !== 'normal') {
164 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'normal');
165 $this->throwRedirectException();
166 }
167 } catch (\RuntimeException $e) {
168 // If an exception is thrown, the value is not set in LocalConfiguration
169 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', $rsaauthLoaded ? 'rsa' : 'normal');
170 $this->throwRedirectException();
171 }
172 }
173
174 /**
175 * Check the settings for salted passwords extension to load it as a required extension.
176 * Unset obsolete configuration options if given.
177 *
178 * @return void
179 */
180 protected function configureSaltedPasswords()
181 {
182 $defaultConfiguration = $this->configurationManager->getDefaultConfiguration();
183 $defaultExtensionConfiguration = unserialize($defaultConfiguration['EXT']['extConf']['saltedpasswords']);
184 try {
185 $extensionConfiguration = @unserialize($this->configurationManager->getLocalConfigurationValueByPath('EXT/extConf/saltedpasswords'));
186 } catch (\RuntimeException $e) {
187 $extensionConfiguration = array();
188 }
189 if (is_array($extensionConfiguration) && !empty($extensionConfiguration)) {
190 if (isset($extensionConfiguration['BE.']['enabled'])) {
191 if ($extensionConfiguration['BE.']['enabled']) {
192 unset($extensionConfiguration['BE.']['enabled']);
193 } else {
194 $extensionConfiguration['BE.'] = $defaultExtensionConfiguration['BE.'];
195 }
196 $this->configurationManager->setLocalConfigurationValueByPath(
197 'EXT/extConf/saltedpasswords',
198 serialize($extensionConfiguration)
199 );
200 $this->throwRedirectException();
201 }
202 } else {
203 $this->configurationManager->setLocalConfigurationValueByPath(
204 'EXT/extConf/saltedpasswords',
205 serialize($defaultExtensionConfiguration)
206 );
207 $this->throwRedirectException();
208 }
209 }
210
211 /**
212 * The encryption key is crucial for securing form tokens
213 * and the whole TYPO3 link rendering later on. A random key is set here in
214 * LocalConfiguration if it does not exist yet. This might possible happen
215 * during upgrading and will happen during first install.
216 *
217 * @return void
218 */
219 protected function generateEncryptionKeyIfNeeded()
220 {
221 try {
222 $currentValue = $this->configurationManager->getLocalConfigurationValueByPath('SYS/encryptionKey');
223 } catch (\RuntimeException $e) {
224 // If an exception is thrown, the value is not set in LocalConfiguration
225 $currentValue = '';
226 }
227
228 if (empty($currentValue)) {
229 $randomKey = GeneralUtility::getRandomHexString(96);
230 $this->configurationManager->setLocalConfigurationValueByPath('SYS/encryptionKey', $randomKey);
231 $this->throwRedirectException();
232 }
233 }
234
235 /**
236 * $GLOBALS['TYPO3_CONF_VARS']['HTTP']['proxy_auth_scheme'] must be either
237 * 'digest' or 'basic'. 'basic' is default in DefaultConfiguration, so the
238 * setting can be removed from LocalConfiguration if it is not set to 'digest'.
239 *
240 * @return void
241 */
242 protected function setProxyAuthScheme()
243 {
244 // Get current value from LocalConfiguration
245 try {
246 $currentValueInLocalConfiguration = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_auth_scheme');
247 } catch (\RuntimeException $e) {
248 // If an exception is thrown, the value is not set in LocalConfiguration, so we don't need to do anything
249 return;
250 }
251 if ($currentValueInLocalConfiguration !== 'digest') {
252 $this->configurationManager->removeLocalConfigurationKeysByPath(array('HTTP/proxy_auth_scheme'));
253 $this->throwRedirectException();
254 }
255 }
256
257 /**
258 * Parse old curl options and set new http ones instead
259 *
260 * @return void
261 */
262 protected function transferDeprecatedCurlSettings()
263 {
264 $changed = false;
265 try {
266 $curlProxyServer = $this->configurationManager->getLocalConfigurationValueByPath('SYS/curlProxyServer');
267 } catch (\RuntimeException $e) {
268 $curlProxyServer = '';
269 }
270 try {
271 $proxyHost = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_host');
272 } catch (\RuntimeException $e) {
273 $proxyHost = '';
274 }
275 if (!empty($curlProxyServer) && empty($proxyHost)) {
276 $curlProxy = rtrim(preg_replace('#^https?://#', '', $curlProxyServer), '/');
277 $proxyParts = GeneralUtility::revExplode(':', $curlProxy, 2);
278 $this->configurationManager->setLocalConfigurationValueByPath('HTTP/proxy_host', $proxyParts[0]);
279 $this->configurationManager->setLocalConfigurationValueByPath('HTTP/proxy_port', $proxyParts[1]);
280 $changed = true;
281 }
282
283 try {
284 $curlProxyUserPass = $this->configurationManager->getLocalConfigurationValueByPath('SYS/curlProxyUserPass');
285 } catch (\RuntimeException $e) {
286 $curlProxyUserPass = '';
287 }
288 try {
289 $proxyUser = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_user');
290 } catch (\RuntimeException $e) {
291 $proxyUser = '';
292 }
293 if (!empty($curlProxyUserPass) && empty($proxyUser)) {
294 $userPassParts = explode(':', $curlProxyUserPass, 2);
295 $this->configurationManager->setLocalConfigurationValueByPath('HTTP/proxy_user', $userPassParts[0]);
296 $this->configurationManager->setLocalConfigurationValueByPath('HTTP/proxy_password', $userPassParts[1]);
297 $changed = true;
298 }
299
300 try {
301 $curlUse = $this->configurationManager->getLocalConfigurationValueByPath('SYS/curlUse');
302 } catch (\RuntimeException $e) {
303 $curlUse = '';
304 }
305 try {
306 $adapter = $this->configurationManager->getConfigurationValueByPath('HTTP/adapter');
307 } catch (\RuntimeException $e) {
308 $adapter = '';
309 }
310 if (!empty($curlUse) && $adapter !== 'curl') {
311 $GLOBALS['TYPO3_CONF_VARS']['HTTP']['adapter'] = 'curl';
312 $this->configurationManager->setLocalConfigurationValueByPath('HTTP/adapter', 'curl');
313 $changed = true;
314 }
315 if ($changed) {
316 $this->throwRedirectException();
317 }
318 }
319
320 /**
321 * Detail configuration of Image Magick settings must be cleared
322 * if Image Magick handling is disabled.
323 *
324 * "Configuration presets" in install tool is not type safe, so value
325 * comparisons here are not type safe too, to not trigger changes to
326 * LocalConfiguration again.
327 *
328 * @return void
329 */
330 protected function disableImageMagickDetailSettingsIfImageMagickIsDisabled()
331 {
332 $changedValues = array();
333 try {
334 $currentImValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor_enabled');
335 } catch (\RuntimeException $e) {
336 $currentImValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor_enabled');
337 }
338
339 try {
340 $currentImPathValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor_path');
341 } catch (\RuntimeException $e) {
342 $currentImPathValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor_path');
343 }
344
345 try {
346 $currentImPathLzwValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor_path_lzw');
347 } catch (\RuntimeException $e) {
348 $currentImPathLzwValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor_path_lzw');
349 }
350
351 try {
352 $currentImageFileExtValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/imagefile_ext');
353 } catch (\RuntimeException $e) {
354 $currentImageFileExtValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/imagefile_ext');
355 }
356
357 try {
358 $currentThumbnailsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/thumbnails');
359 } catch (\RuntimeException $e) {
360 $currentThumbnailsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/thumbnails');
361 }
362
363 if (!$currentImValue) {
364 if ($currentImPathValue != '') {
365 $changedValues['GFX/processor_path'] = '';
366 }
367 if ($currentImPathLzwValue != '') {
368 $changedValues['GFX/processor_path_lzw'] = '';
369 }
370 if ($currentImageFileExtValue !== 'gif,jpg,jpeg,png') {
371 $changedValues['GFX/imagefile_ext'] = 'gif,jpg,jpeg,png';
372 }
373 if ($currentThumbnailsValue != 0) {
374 $changedValues['GFX/thumbnails'] = 0;
375 }
376 }
377 if (!empty($changedValues)) {
378 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
379 $this->throwRedirectException();
380 }
381 }
382
383 /**
384 * Detail configuration of Image Magick and Graphics Magick settings
385 * depending on main values.
386 *
387 * "Configuration presets" in install tool is not type safe, so value
388 * comparisons here are not type safe too, to not trigger changes to
389 * LocalConfiguration again.
390 *
391 * @return void
392 */
393 protected function setImageMagickDetailSettings()
394 {
395 $changedValues = array();
396 try {
397 $currentProcessorValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor');
398 } catch (\RuntimeException $e) {
399 $currentProcessorValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor');
400 }
401
402 try {
403 $currentProcessorMaskValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor_allowTemporaryMasksAsPng');
404 } catch (\RuntimeException $e) {
405 $currentProcessorMaskValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor_allowTemporaryMasksAsPng');
406 }
407
408 try {
409 $currentProcessorEffectsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor_effects');
410 } catch (\RuntimeException $e) {
411 $currentProcessorEffectsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor_effects');
412 }
413
414 if ((string)$currentProcessorValue !== '') {
415 if ($currentProcessorMaskValue != 0) {
416 $changedValues['GFX/processor_allowTemporaryMasksAsPng'] = 0;
417 }
418 if ($currentProcessorValue === 'GraphicsMagick') {
419 if ($currentProcessorEffectsValue != -1) {
420 $changedValues['GFX/processor_effects'] = -1;
421 }
422 }
423 }
424 if (!empty($changedValues)) {
425 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
426 $this->throwRedirectException();
427 }
428 }
429
430 /**
431 * Migrate the definition of the image processor from the configuration value
432 * im_version_5 to the setting processor.
433 *
434 * @return void
435 */
436 protected function migrateImageProcessorSetting()
437 {
438 $changedSettings = array();
439 $settingsToRename = array(
440 'GFX/im' => 'GFX/processor_enabled',
441 'GFX/im_version_5' => 'GFX/processor',
442 'GFX/im_v5effects' => 'GFX/processor_effects',
443 'GFX/im_path' => 'GFX/processor_path',
444 'GFX/im_path_lzw' => 'GFX/processor_path_lzw',
445 'GFX/im_mask_temp_ext_gif' => 'GFX/processor_allowTemporaryMasksAsPng',
446 'GFX/im_noScaleUp' => 'GFX/processor_allowUpscaling',
447 'GFX/im_noFramePrepended' => 'GFX/processor_allowFrameSelection',
448 'GFX/im_stripProfileCommand' => 'GFX/processor_stripColorProfileCommand',
449 'GFX/im_useStripProfileByDefault' => 'GFX/processor_stripColorProfileByDefault',
450 'GFX/colorspace' => 'GFX/processor_colorspace',
451 );
452
453 foreach ($settingsToRename as $oldPath => $newPath) {
454 try {
455 $value = $this->configurationManager->getLocalConfigurationValueByPath($oldPath);
456 $this->configurationManager->setLocalConfigurationValueByPath($newPath, $value);
457 $changedSettings[$oldPath] = true;
458 } catch (\RuntimeException $e) {
459 // If an exception is thrown, the value is not set in LocalConfiguration
460 $changedSettings[$oldPath] = false;
461 }
462 }
463
464 if (!empty($changedSettings['GFX/im_version_5'])) {
465 $currentProcessorValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_version_5');
466 $newProcessorValue = $currentProcessorValue === 'gm' ? 'GraphicsMagick' : 'ImageMagick';
467 $this->configurationManager->setLocalConfigurationValueByPath('GFX/processor', $newProcessorValue);
468 }
469
470 if (!empty($changedSettings['GFX/im_noScaleUp'])) {
471 $currentProcessorValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_noScaleUp');
472 $newProcessorValue = !$currentProcessorValue;
473 $this->configurationManager->setLocalConfigurationValueByPath('GFX/processor_allowUpscaling', $newProcessorValue);
474 }
475
476 if (!empty($changedSettings['GFX/im_noFramePrepended'])) {
477 $currentProcessorValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_noFramePrepended');
478 $newProcessorValue = !$currentProcessorValue;
479 $this->configurationManager->setLocalConfigurationValueByPath('GFX/processor_allowFrameSelection', $newProcessorValue);
480 }
481
482 if (!empty($changedSettings['GFX/im_mask_temp_ext_gif'])) {
483 $currentProcessorValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_mask_temp_ext_gif');
484 $newProcessorValue = !$currentProcessorValue;
485 $this->configurationManager->setLocalConfigurationValueByPath('GFX/processor_allowTemporaryMasksAsPng', $newProcessorValue);
486 }
487
488 if (!empty(array_filter($changedSettings))) {
489 $this->configurationManager->removeLocalConfigurationKeysByPath(array_keys($changedSettings));
490 $this->throwRedirectException();
491 }
492 }
493
494 /**
495 * Throw exception after configuration change to trigger a redirect.
496 *
497 * @throws RedirectException
498 */
499 protected function throwRedirectException()
500 {
501 throw new RedirectException(
502 'Configuration updated, reload needed',
503 1379024938
504 );
505 }
506 }