[TASK] Re-work/simplify copyright header in PHP files - Part 2
[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
20 /**
21 * Execute "silent" LocalConfiguration upgrades if needed.
22 *
23 * Some LocalConfiguration settings are obsolete or changed over time.
24 * This class handles upgrades of these settings. It is called by
25 * the step controller at an early point.
26 *
27 * Every change is encapsulated in one method an must throw a RedirectException
28 * if new data is written to LocalConfiguration. This is caught by above
29 * step controller to initiate a redirect and start again with adapted configuration.
30 */
31 class SilentConfigurationUpgradeService {
32
33 /**
34 * @var \TYPO3\CMS\Extbase\Object\ObjectManager
35 * @inject
36 */
37 protected $objectManager = NULL;
38
39 /**
40 * @var \TYPO3\CMS\Core\Configuration\ConfigurationManager
41 * @inject
42 */
43 protected $configurationManager = NULL;
44
45 /**
46 * @var array List of obsolete configuration options in LocalConfiguration to be removed
47 */
48 protected $obsoleteLocalConfigurationSettings = array(
49 // #34092
50 'BE/forceCharset',
51 // #26519
52 'BE/loginLabels',
53 // #44506
54 'BE/loginNews',
55 // #52013
56 'BE/TSconfigConditions',
57 // #30613
58 'BE/useOnContextMenuHandler',
59 // #57921
60 'BE/usePHPFileFunctions',
61 // #48179
62 'EXT/em_mirrorListURL',
63 'EXT/em_wsdlURL',
64 // #43094
65 'EXT/extList',
66 // #47018
67 'EXT/extListArray',
68 // #35877
69 'EXT/extList_FE',
70 // #41813
71 'EXT/noEdit',
72 // #47018
73 'EXT/requiredExt',
74 // #26090
75 'FE/defaultTypoScript_editorcfg',
76 'FE/defaultTypoScript_editorcfg.',
77 // #25099
78 'FE/simulateStaticDocuments',
79 // #52786
80 'FE/logfile_dir',
81 // #55549
82 'FE/dontSetCookie',
83 // #52011
84 'GFX/im_combine_filename',
85 // #52088
86 'GFX/im_imvMaskState',
87 // #22687
88 'GFX/gdlib_2',
89 // #52012
90 'GFX/im_mask_temp_ext_noloss',
91 // #52088
92 'GFX/im_negate_mask',
93 // #52010
94 'GFX/im_no_effects',
95 // #18431
96 'GFX/noIconProc',
97 // #17606
98 'GFX/TTFLocaleConv',
99 // #39164
100 'SYS/additionalAllowedClassPrefixes',
101 // #27689
102 'SYS/caching/cacheBackends',
103 'SYS/caching/cacheFrontends',
104 // #38414
105 'SYS/extCache',
106 // #35923
107 'SYS/multiplyDBfieldSize',
108 // #46993
109 'SYS/T3instID',
110 // #52857
111 'SYS/forceReturnPath',
112 // #54930
113 'INSTALL/wizardDone/tx_coreupdates_compressionlevel',
114 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\CompressionLevelUpdate',
115 'INSTALL/wizardDone/tx_coreupdates_installsysexts',
116 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\InstallSysExtsUpdate',
117 'INSTALL/wizardDone/tx_coreupdates_migrateworkspaces',
118 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\MigrateWorkspacesUpdate',
119 );
120
121 /**
122 * Executed configuration upgrades. Single upgrade methods must throw a
123 * RedirectException if something was written to LocalConfiguration.
124 *
125 * @return void
126 */
127 public function execute() {
128 $this->generateEncryptionKeyIfNeeded();
129 $this->configureBackendLoginSecurity();
130 $this->configureSaltedPasswords();
131 $this->migrateOldInstallWizardDoneSettingsToNewClassNames();
132 $this->setProxyAuthScheme();
133 $this->disableImageMagickAndGdlibIfImageProcessingIsDisabled();
134 $this->disableImageMagickDetailSettingsIfImageMagickIsDisabled();
135 $this->setImageMagickDetailSettings();
136 $this->addFileTableToDefaultCategorizedTablesIfAlreadyCustomized();
137 $this->removeObsoleteLocalConfigurationSettings();
138 }
139
140 /**
141 * Some settings in LocalConfiguration vanished in DefaultConfiguration
142 * and have no impact on the core anymore.
143 * To keep the configuration clean, those old settings are just silently
144 * removed from LocalConfiguration if set.
145 *
146 * @return void
147 */
148 protected function removeObsoleteLocalConfigurationSettings() {
149 $removed = $this->configurationManager->removeLocalConfigurationKeysByPath($this->obsoleteLocalConfigurationSettings);
150
151 // The old default value is not needed anymore. So if the user
152 // did not set a different value we can remove it.
153 $currentSetDbInitValue = $this->configurationManager->getConfigurationValueByPath('SYS/setDBinit');
154 if (preg_match('/^\s*SET\s+NAMES\s+[\'"]?utf8[\'"]?\s*[;]?\s*$/i', $currentSetDbInitValue) === 1) {
155 $removed = $removed || $this->configurationManager->removeLocalConfigurationKeysByPath(array('SYS/setDBinit'));
156 }
157
158 // If something was changed: Trigger a reload to have new values in next request
159 if ($removed) {
160 $this->throwRedirectException();
161 }
162 }
163
164 /**
165 * Backend login security is set to rsa if rsaauth
166 * is installed (but not used) otherwise the default value "normal" has to be used.
167 * This forces either 'normal' or 'rsa' to be set in LocalConfiguration.
168 *
169 * @return void
170 */
171 protected function configureBackendLoginSecurity() {
172 try {
173 $currentLoginSecurityLevelValue = $this->configurationManager->getLocalConfigurationValueByPath('BE/loginSecurityLevel');
174 if (ExtensionManagementUtility::isLoaded('rsaauth')
175 && $currentLoginSecurityLevelValue !== 'rsa'
176 ) {
177 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'rsa');
178 $this->throwRedirectException();
179 } elseif (!ExtensionManagementUtility::isLoaded('rsaauth')
180 && $currentLoginSecurityLevelValue !== 'normal'
181 ) {
182 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'normal');
183 $this->throwRedirectException();
184 }
185 } catch (\RuntimeException $e) {
186 // If an exception is thrown, the value is not set in LocalConfiguration
187 if (ExtensionManagementUtility::isLoaded('rsaauth')) {
188 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'rsa');
189 $this->throwRedirectException();
190 } elseif (!ExtensionManagementUtility::isLoaded('rsaauth')) {
191 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'normal');
192 $this->throwRedirectException();
193 }
194 }
195 }
196
197 /**
198 * Check the settings for salted passwords extension to load it as a required extension.
199 * Unset obsolete configuration options if given.
200 *
201 * @return void
202 */
203 protected function configureSaltedPasswords() {
204 $defaultConfiguration = $this->configurationManager->getDefaultConfiguration();
205 $defaultExtensionConfiguration = unserialize($defaultConfiguration['EXT']['extConf']['saltedpasswords']);
206 try {
207 $extensionConfiguration = @unserialize($this->configurationManager->getLocalConfigurationValueByPath('EXT/extConf/saltedpasswords'));
208 } catch (\RuntimeException $e) {
209 $extensionConfiguration = array();
210 }
211 if (is_array($extensionConfiguration) && !empty($extensionConfiguration)) {
212 if (isset($extensionConfiguration['BE.']['enabled'])) {
213 if ($extensionConfiguration['BE.']['enabled']) {
214 unset($extensionConfiguration['BE.']['enabled']);
215 } else {
216 $extensionConfiguration['BE.'] = $defaultExtensionConfiguration['BE.'];
217 }
218 $this->configurationManager->setLocalConfigurationValueByPath(
219 'EXT/extConf/saltedpasswords',
220 serialize($extensionConfiguration)
221 );
222 $this->throwRedirectException();
223 }
224 } else {
225 $this->configurationManager->setLocalConfigurationValueByPath(
226 'EXT/extConf/saltedpasswords',
227 serialize($defaultExtensionConfiguration)
228 );
229 $this->throwRedirectException();
230 }
231 }
232
233 /**
234 * The encryption key is crucial for securing form tokens
235 * and the whole TYPO3 link rendering later on. A random key is set here in
236 * LocalConfiguration if it does not exist yet. This might possible happen
237 * during upgrading and will happen during first install.
238 *
239 * @return void
240 */
241 protected function generateEncryptionKeyIfNeeded() {
242 try{
243 $currentValue = $this->configurationManager->getLocalConfigurationValueByPath('SYS/encryptionKey');
244 } catch (\RuntimeException $e) {
245 // If an exception is thrown, the value is not set in LocalConfiguration
246 $currentValue = '';
247 }
248
249 if (empty($currentValue)) {
250 $randomKey = GeneralUtility::getRandomHexString(96);
251 $this->configurationManager->setLocalConfigurationValueByPath('SYS/encryptionKey', $randomKey);
252 $this->throwRedirectException();
253 }
254 }
255
256 /**
257 * $GLOBALS['TYPO3_CONF_VARS']['HTTP']['proxy_auth_scheme'] must be either
258 * 'digest' or 'basic'. 'basic' is default in DefaultConfiguration, so the
259 * setting can be removed from LocalConfiguration if it is not set to 'digest'.
260 *
261 * @return void
262 */
263 protected function setProxyAuthScheme() {
264 // Get current value from LocalConfiguration
265 try {
266 $currentValueInLocalConfiguration = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_auth_scheme');
267 } catch (\RuntimeException $e) {
268 // If an exception is thrown, the value is not set in LocalConfiguration, so we don't need to do anything
269 return;
270 }
271 if ($currentValueInLocalConfiguration !== 'digest') {
272 $this->configurationManager->removeLocalConfigurationKeysByPath(array('HTTP/proxy_auth_scheme'));
273 $this->throwRedirectException();
274 }
275 }
276
277 /**
278 * GFX/im and GFX/gdlib must be set to 0 if image_processing is disabled.
279 *
280 * "Configuration presets" in install tool is not type safe, so value
281 * comparisons here are not type safe too, to not trigger changes to
282 * LocalConfiguration again.
283 *
284 * @return void
285 */
286 protected function disableImageMagickAndGdlibIfImageProcessingIsDisabled() {
287 $changedValues = array();
288 try {
289 $currentImageProcessingValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/image_processing');
290 } catch (\RuntimeException $e) {
291 $currentImageProcessingValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/image_processing');
292 }
293 try {
294 $currentImValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im');
295 } catch (\RuntimeException $e) {
296 $currentImValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im');
297 }
298 try {
299 $currentGdlibValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/gdlib');
300 } catch (\RuntimeException $e) {
301 $currentGdlibValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/gdlib');
302 }
303 // If image processing is fully disabled, im and gdlib sub settings must be 0
304 if (!$currentImageProcessingValue) {
305 if ($currentImValue != 0) {
306 $changedValues['GFX/im'] = 0;
307 }
308 if ($currentGdlibValue != 0) {
309 $changedValues['GFX/gdlib'] = 0;
310 }
311 }
312 if (count($changedValues) > 0) {
313 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
314 $this->throwRedirectException();
315 }
316 }
317
318 /**
319 * Detail configuration of Image Magick settings must be cleared
320 * if Image Magick handling is disabled.
321 *
322 * "Configuration presets" in install tool is not type safe, so value
323 * comparisons here are not type safe too, to not trigger changes to
324 * LocalConfiguration again.
325 *
326 * @return void
327 */
328 protected function disableImageMagickDetailSettingsIfImageMagickIsDisabled() {
329 $changedValues = array();
330 try {
331 $currentImValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im');
332 }
333 catch (\RuntimeException $e) {
334 $currentImValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im');
335 }
336 try {
337 $currentImPathValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_path');
338 }
339 catch (\RuntimeException $e) {
340 $currentImPathValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_path');
341 }
342 try {
343 $currentImPathLzwValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_path_lzw');
344 }
345 catch (\RuntimeException $e) {
346 $currentImPathLzwValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_path_lzw');
347 }
348 try {
349 $currentImageFileExtValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/imagefile_ext');
350 }
351 catch (\RuntimeException $e) {
352 $currentImageFileExtValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/imagefile_ext');
353 }
354 try {
355 $currentThumbnailsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/thumbnails');
356 }
357 catch (\RuntimeException $e) {
358 $currentThumbnailsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/thumbnails');
359 }
360 if (!$currentImValue) {
361 if ($currentImPathValue != '') {
362 $changedValues['GFX/im_path'] = '';
363 }
364 if ($currentImPathLzwValue != '') {
365 $changedValues['GFX/im_path_lzw'] = '';
366 }
367 if ($currentImageFileExtValue !== 'gif,jpg,jpeg,png') {
368 $changedValues['GFX/imagefile_ext'] = 'gif,jpg,jpeg,png';
369 }
370 if ($currentThumbnailsValue != 0) {
371 $changedValues['GFX/thumbnails'] = 0;
372 }
373 }
374 if (count($changedValues) > 0) {
375 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
376 $this->throwRedirectException();
377 }
378 }
379
380 /**
381 * Detail configuration of Image Magick and Graphics Magick settings
382 * depending on main values.
383 *
384 * "Configuration presets" in install tool is not type safe, so value
385 * comparisons here are not type safe too, to not trigger changes to
386 * LocalConfiguration again.
387 *
388 * @return void
389 */
390 protected function setImageMagickDetailSettings() {
391 $changedValues = array();
392 try {
393 $currentIm5Value = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_version_5');
394 }
395 catch (\RuntimeException $e) {
396 $currentIm5Value = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_version_5');
397 }
398 try {
399 $currentImMaskValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_mask_temp_ext_gif');
400 }
401 catch (\RuntimeException $e) {
402 $currentImMaskValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_mask_temp_ext_gif');
403 }
404 try {
405 $currentIm5EffectsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_v5effects');
406 }
407 catch (\RuntimeException $e) {
408 $currentIm5EffectsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_v5effects');
409 }
410 if (strlen($currentIm5Value) > 0) {
411 if ($currentImMaskValue != 1) {
412 $changedValues['GFX/im_mask_temp_ext_gif'] = 1;
413 }
414 if ($currentIm5Value === 'gm') {
415 if ($currentIm5EffectsValue != -1) {
416 $changedValues['GFX/im_v5effects'] = -1;
417 }
418 }
419 }
420 if (count($changedValues) > 0) {
421 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
422 $this->throwRedirectException();
423 }
424 }
425
426 /**
427 * Make sure file table is categorized as of TYPO3 6.2. To enable DAM Migration
428 * sys_file_metadata table is included in DefaultConfiguration.
429 * If the setting already has been modified but does not contain sys_file_metadata: add it
430 *
431 * @return void
432 */
433 protected function addFileTableToDefaultCategorizedTablesIfAlreadyCustomized() {
434 /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
435 $configurationManager = $this->objectManager->get('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager');
436
437 $default = $configurationManager->getDefaultConfigurationValueByPath('SYS/defaultCategorizedTables');
438 try {
439 $actual = $configurationManager->getLocalConfigurationValueByPath('SYS/defaultCategorizedTables');
440 } catch(\RuntimeException $e) {
441 $actual = '';
442 }
443
444 $tables = GeneralUtility::trimExplode(',', $actual);
445 if ($actual !== '' && $actual !== $default && !in_array('sys_file_metadata', $tables)) {
446 $tables[] = 'sys_file_metadata';
447 $configurationManager->setLocalConfigurationValueByPath('SYS/defaultCategorizedTables', implode(',', $tables));
448 $this->throwRedirectException();
449 }
450 }
451
452 /**
453 * Migrate old Install Tool Wizard "done"-settings to new class names
454 * this happens usually when an existing 6.0/6.1 has called the TceformsUpdateWizard wizard
455 * and has written Tx_Install_Updates_File_TceformsUpdateWizard in TYPO3's LocalConfiguration.php
456 *
457 * @return void
458 */
459 protected function migrateOldInstallWizardDoneSettingsToNewClassNames() {
460 $classNamesToConvert = array();
461 $localConfiguration = $this->configurationManager->getLocalConfiguration();
462 // check for wizards that have been run already and don't start with TYPO3...
463 if (isset($localConfiguration['INSTALL']['wizardDone']) && is_array($localConfiguration['INSTALL']['wizardDone'])) {
464 $classNames = array_keys($localConfiguration['INSTALL']['wizardDone']);
465 foreach ($classNames as $className) {
466 if (!GeneralUtility::isFirstPartOfStr($className, 'TYPO3')) {
467 $classNamesToConvert[] = $className;
468 }
469 }
470 }
471 if (!count($classNamesToConvert)) {
472 return;
473 }
474
475 $migratedClassesMapping = array(
476 'Tx_Install_Updates_File_TceformsUpdateWizard' => 'TYPO3\\CMS\\Install\\Updates\\TceformsUpdateWizard'
477 );
478
479 $migratedSettings = array();
480 $settingsToRemove = array();
481 foreach ($classNamesToConvert as $oldClassName) {
482 if (isset($migratedClassesMapping[$oldClassName])) {
483 $newClassName = $migratedClassesMapping[$oldClassName];
484 } else {
485 continue;
486 }
487 $oldValue = NULL;
488 $newValue = NULL;
489 try {
490 $oldValue = $this->configurationManager->getLocalConfigurationValueByPath('INSTALL/wizardDone/' . $oldClassName);
491 } catch (\RuntimeException $e) {
492 // The old configuration does not exist
493 continue;
494 }
495 try {
496 $newValue = $this->configurationManager->getLocalConfigurationValueByPath('INSTALL/wizardDone/' . $newClassName);
497 } catch (\RuntimeException $e) {
498 // The new configuration does not exist yet
499 }
500 if ($newValue === NULL) {
501 // Migrate the old configuration to the new one
502 $migratedSettings['INSTALL/wizardDone/' . $newClassName] = $oldValue;
503 }
504 $settingsToRemove[] = 'INSTALL/wizardDone/' . $oldClassName;
505
506 }
507
508 if (count($migratedSettings)) {
509 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($migratedSettings);
510 }
511 $this->configurationManager->removeLocalConfigurationKeysByPath($settingsToRemove);
512 if (count($migratedSettings) || count($settingsToRemove)) {
513 $this->throwRedirectException();
514 }
515 }
516
517 /**
518 * Throw exception after configuration change to trigger a redirect.
519 *
520 * @throws \TYPO3\CMS\Install\Controller\Exception\RedirectException
521 */
522 protected function throwRedirectException() {
523 throw new \TYPO3\CMS\Install\Controller\Exception\RedirectException(
524 'Configuration updated, reload needed',
525 1379024938
526 );
527 }
528 }