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