[CLEANUP] Streamline code in UpgradeWizard
[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 );
70
71 /**
72 * Executed configuration upgrades. Single upgrade methods must throw a
73 * RedirectException if something was written to LocalConfiguration.
74 *
75 * @return void
76 */
77 public function execute() {
78 $this->generateEncryptionKeyIfNeeded();
79 $this->configureBackendLoginSecurity();
80 $this->configureSaltedPasswords();
81 $this->setProxyAuthScheme();
82 $this->disableImageMagickAndGdlibIfImageProcessingIsDisabled();
83 $this->disableImageMagickDetailSettingsIfImageMagickIsDisabled();
84 $this->setImageMagickDetailSettings();
85 $this->removeObsoleteLocalConfigurationSettings();
86 }
87
88 /**
89 * Some settings in LocalConfiguration vanished in DefaultConfiguration
90 * and have no impact on the core anymore.
91 * To keep the configuration clean, those old settings are just silently
92 * removed from LocalConfiguration if set.
93 *
94 * @return void
95 */
96 protected function removeObsoleteLocalConfigurationSettings() {
97 $removed = $this->configurationManager->removeLocalConfigurationKeysByPath($this->obsoleteLocalConfigurationSettings);
98
99 // If something was changed: Trigger a reload to have new values in next request
100 if ($removed) {
101 $this->throwRedirectException();
102 }
103 }
104
105 /**
106 * Backend login security is set to rsa if rsaauth
107 * is installed (but not used) otherwise the default value "normal" has to be used.
108 * This forces either 'normal' or 'rsa' to be set in LocalConfiguration.
109 *
110 * @return void
111 */
112 protected function configureBackendLoginSecurity() {
113 try {
114 $currentLoginSecurityLevelValue = $this->configurationManager->getLocalConfigurationValueByPath('BE/loginSecurityLevel');
115 if (ExtensionManagementUtility::isLoaded('rsaauth')
116 && $currentLoginSecurityLevelValue !== 'rsa'
117 ) {
118 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'rsa');
119 $this->throwRedirectException();
120 } elseif (!ExtensionManagementUtility::isLoaded('rsaauth')
121 && $currentLoginSecurityLevelValue !== 'normal'
122 ) {
123 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'normal');
124 $this->throwRedirectException();
125 }
126 } catch (\RuntimeException $e) {
127 // If an exception is thrown, the value is not set in LocalConfiguration
128 if (ExtensionManagementUtility::isLoaded('rsaauth')) {
129 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'rsa');
130 $this->throwRedirectException();
131 } elseif (!ExtensionManagementUtility::isLoaded('rsaauth')) {
132 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'normal');
133 $this->throwRedirectException();
134 }
135 }
136 }
137
138 /**
139 * Check the settings for salted passwords extension to load it as a required extension.
140 * Unset obsolete configuration options if given.
141 *
142 * @return void
143 */
144 protected function configureSaltedPasswords() {
145 $defaultConfiguration = $this->configurationManager->getDefaultConfiguration();
146 $defaultExtensionConfiguration = unserialize($defaultConfiguration['EXT']['extConf']['saltedpasswords']);
147 try {
148 $extensionConfiguration = @unserialize($this->configurationManager->getLocalConfigurationValueByPath('EXT/extConf/saltedpasswords'));
149 } catch (\RuntimeException $e) {
150 $extensionConfiguration = array();
151 }
152 if (is_array($extensionConfiguration) && !empty($extensionConfiguration)) {
153 if (isset($extensionConfiguration['BE.']['enabled'])) {
154 if ($extensionConfiguration['BE.']['enabled']) {
155 unset($extensionConfiguration['BE.']['enabled']);
156 } else {
157 $extensionConfiguration['BE.'] = $defaultExtensionConfiguration['BE.'];
158 }
159 $this->configurationManager->setLocalConfigurationValueByPath(
160 'EXT/extConf/saltedpasswords',
161 serialize($extensionConfiguration)
162 );
163 $this->throwRedirectException();
164 }
165 } else {
166 $this->configurationManager->setLocalConfigurationValueByPath(
167 'EXT/extConf/saltedpasswords',
168 serialize($defaultExtensionConfiguration)
169 );
170 $this->throwRedirectException();
171 }
172 }
173
174 /**
175 * The encryption key is crucial for securing form tokens
176 * and the whole TYPO3 link rendering later on. A random key is set here in
177 * LocalConfiguration if it does not exist yet. This might possible happen
178 * during upgrading and will happen during first install.
179 *
180 * @return void
181 */
182 protected function generateEncryptionKeyIfNeeded() {
183 try{
184 $currentValue = $this->configurationManager->getLocalConfigurationValueByPath('SYS/encryptionKey');
185 } catch (\RuntimeException $e) {
186 // If an exception is thrown, the value is not set in LocalConfiguration
187 $currentValue = '';
188 }
189
190 if (empty($currentValue)) {
191 $randomKey = GeneralUtility::getRandomHexString(96);
192 $this->configurationManager->setLocalConfigurationValueByPath('SYS/encryptionKey', $randomKey);
193 $this->throwRedirectException();
194 }
195 }
196
197 /**
198 * $GLOBALS['TYPO3_CONF_VARS']['HTTP']['proxy_auth_scheme'] must be either
199 * 'digest' or 'basic'. 'basic' is default in DefaultConfiguration, so the
200 * setting can be removed from LocalConfiguration if it is not set to 'digest'.
201 *
202 * @return void
203 */
204 protected function setProxyAuthScheme() {
205 // Get current value from LocalConfiguration
206 try {
207 $currentValueInLocalConfiguration = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_auth_scheme');
208 } catch (\RuntimeException $e) {
209 // If an exception is thrown, the value is not set in LocalConfiguration, so we don't need to do anything
210 return;
211 }
212 if ($currentValueInLocalConfiguration !== 'digest') {
213 $this->configurationManager->removeLocalConfigurationKeysByPath(array('HTTP/proxy_auth_scheme'));
214 $this->throwRedirectException();
215 }
216 }
217
218 /**
219 * GFX/im and GFX/gdlib must be set to 0 if image_processing is disabled.
220 *
221 * "Configuration presets" in install tool is not type safe, so value
222 * comparisons here are not type safe too, to not trigger changes to
223 * LocalConfiguration again.
224 *
225 * @return void
226 */
227 protected function disableImageMagickAndGdlibIfImageProcessingIsDisabled() {
228 $changedValues = array();
229 try {
230 $currentImageProcessingValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/image_processing');
231 } catch (\RuntimeException $e) {
232 $currentImageProcessingValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/image_processing');
233 }
234 try {
235 $currentImValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im');
236 } catch (\RuntimeException $e) {
237 $currentImValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im');
238 }
239 try {
240 $currentGdlibValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/gdlib');
241 } catch (\RuntimeException $e) {
242 $currentGdlibValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/gdlib');
243 }
244 // If image processing is fully disabled, im and gdlib sub settings must be 0
245 if (!$currentImageProcessingValue) {
246 if ($currentImValue != 0) {
247 $changedValues['GFX/im'] = 0;
248 }
249 if ($currentGdlibValue != 0) {
250 $changedValues['GFX/gdlib'] = 0;
251 }
252 }
253 if (count($changedValues) > 0) {
254 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
255 $this->throwRedirectException();
256 }
257 }
258
259 /**
260 * Detail configuration of Image Magick settings must be cleared
261 * if Image Magick handling is disabled.
262 *
263 * "Configuration presets" in install tool is not type safe, so value
264 * comparisons here are not type safe too, to not trigger changes to
265 * LocalConfiguration again.
266 *
267 * @return void
268 */
269 protected function disableImageMagickDetailSettingsIfImageMagickIsDisabled() {
270 $changedValues = array();
271 try {
272 $currentImValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im');
273 }
274 catch (\RuntimeException $e) {
275 $currentImValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im');
276 }
277 try {
278 $currentImPathValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_path');
279 }
280 catch (\RuntimeException $e) {
281 $currentImPathValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_path');
282 }
283 try {
284 $currentImPathLzwValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_path_lzw');
285 }
286 catch (\RuntimeException $e) {
287 $currentImPathLzwValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_path_lzw');
288 }
289 try {
290 $currentImageFileExtValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/imagefile_ext');
291 }
292 catch (\RuntimeException $e) {
293 $currentImageFileExtValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/imagefile_ext');
294 }
295 try {
296 $currentThumbnailsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/thumbnails');
297 }
298 catch (\RuntimeException $e) {
299 $currentThumbnailsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/thumbnails');
300 }
301 if (!$currentImValue) {
302 if ($currentImPathValue != '') {
303 $changedValues['GFX/im_path'] = '';
304 }
305 if ($currentImPathLzwValue != '') {
306 $changedValues['GFX/im_path_lzw'] = '';
307 }
308 if ($currentImageFileExtValue !== 'gif,jpg,jpeg,png') {
309 $changedValues['GFX/imagefile_ext'] = 'gif,jpg,jpeg,png';
310 }
311 if ($currentThumbnailsValue != 0) {
312 $changedValues['GFX/thumbnails'] = 0;
313 }
314 }
315 if (count($changedValues) > 0) {
316 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
317 $this->throwRedirectException();
318 }
319 }
320
321 /**
322 * Detail configuration of Image Magick and Graphics Magick settings
323 * depending on main values.
324 *
325 * "Configuration presets" in install tool is not type safe, so value
326 * comparisons here are not type safe too, to not trigger changes to
327 * LocalConfiguration again.
328 *
329 * @return void
330 */
331 protected function setImageMagickDetailSettings() {
332 $changedValues = array();
333 try {
334 $currentIm5Value = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_version_5');
335 }
336 catch (\RuntimeException $e) {
337 $currentIm5Value = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_version_5');
338 }
339 try {
340 $currentImMaskValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_mask_temp_ext_gif');
341 }
342 catch (\RuntimeException $e) {
343 $currentImMaskValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_mask_temp_ext_gif');
344 }
345 try {
346 $currentIm5EffectsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_v5effects');
347 }
348 catch (\RuntimeException $e) {
349 $currentIm5EffectsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_v5effects');
350 }
351 if (strlen($currentIm5Value) > 0) {
352 if ($currentImMaskValue != 1) {
353 $changedValues['GFX/im_mask_temp_ext_gif'] = 1;
354 }
355 if ($currentIm5Value === 'gm') {
356 if ($currentIm5EffectsValue != -1) {
357 $changedValues['GFX/im_v5effects'] = -1;
358 }
359 }
360 }
361 if (count($changedValues) > 0) {
362 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
363 $this->throwRedirectException();
364 }
365 }
366
367 /**
368 * Throw exception after configuration change to trigger a redirect.
369 *
370 * @throws RedirectException
371 */
372 protected function throwRedirectException() {
373 throw new RedirectException(
374 'Configuration updated, reload needed',
375 1379024938
376 );
377 }
378 }