Passed
Push — master ( f0d038...fb6744 )
by
unknown
27:25 queued 13:24
created

LocalConfigurationValueService   A

Complexity

Total Complexity 37

Size/Duplication

Total Lines 201
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 37
eloc 121
dl 0
loc 201
rs 9.44
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A getDefaultConfigArrayComments() 0 6 1
A getCurrentConfigurationData() 0 22 3
C updateLocalConfigurationValues() 0 65 15
D recursiveConfigurationFetching() 0 72 18
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
namespace TYPO3\CMS\Install\Service;
19
20
use TYPO3\CMS\Core\Configuration\ConfigurationManager;
21
use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader;
22
use TYPO3\CMS\Core\Messaging\FlashMessage;
23
use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
24
use TYPO3\CMS\Core\Utility\GeneralUtility;
25
26
/**
27
 * Service handling bulk read and write of LocalConfiguration values.
28
 *
29
 * Used by "Configure global settings" / "All configuration" view.
30
 * @internal This class is only meant to be used within EXT:install and is not part of the TYPO3 Core API.
31
 */
32
class LocalConfigurationValueService
33
{
34
35
    /**
36
     * Get up configuration data. Prepares main TYPO3_CONF_VARS
37
     * array to be displayed and merges is with the description file
38
     *
39
     * @return array Configuration data
40
     */
41
    public function getCurrentConfigurationData(): array
42
    {
43
        $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
44
        $localConfiguration = $configurationManager->getMergedLocalConfiguration();
45
46
        $data = [];
47
        $commentArray = $this->getDefaultConfigArrayComments();
48
49
        foreach ($localConfiguration as $sectionName => $section) {
50
            if (isset($commentArray[$sectionName])) {
51
                $data[$sectionName]['description'] = $commentArray[$sectionName]['description'] ?? $sectionName;
52
                $data[$sectionName]['items'] = $this->recursiveConfigurationFetching(
53
                    $section,
54
                    $GLOBALS['TYPO3_CONF_VARS'][$sectionName] ?? null,
0 ignored issues
show
Bug introduced by
It seems like $GLOBALS['TYPO3_CONF_VARS'][$sectionName] ?? null can also be of type null; however, parameter $sectionsFromCurrentConfiguration of TYPO3\CMS\Install\Servic...ConfigurationFetching() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

54
                    /** @scrutinizer ignore-type */ $GLOBALS['TYPO3_CONF_VARS'][$sectionName] ?? null,
Loading history...
55
                    $commentArray[$sectionName]
56
                );
57
            }
58
        }
59
60
        ksort($data);
61
62
        return $data;
63
    }
64
65
    /**
66
     * Because configuration entries can be at any sub-array level, we need
67
     * to check entries recursively.
68
     *
69
     * @param array $sections
70
     * @param array $sectionsFromCurrentConfiguration
71
     * @param array $descriptions
72
     * @param array $path
73
     * @return array
74
     */
75
    protected function recursiveConfigurationFetching(array $sections, array $sectionsFromCurrentConfiguration, array $descriptions, array $path = []): array
76
    {
77
        $data = [];
78
79
        foreach ($sections as $key => $value) {
80
            if (!isset($descriptions['items'][$key])) {
81
                // @todo should we do something here?
82
                continue;
83
            }
84
85
            $descriptionInfo = $descriptions['items'][$key];
86
            $descriptionType = $descriptionInfo['type'];
87
88
            $newPath = $path;
89
            $newPath[] = $key;
90
91
            if ($descriptionType === 'container') {
92
                $valueFromCurrentConfiguration = $sectionsFromCurrentConfiguration[$key] ?? null;
93
                $data = array_merge($data, $this->recursiveConfigurationFetching($value, $valueFromCurrentConfiguration, $descriptionInfo, $newPath));
0 ignored issues
show
Bug introduced by
It seems like $valueFromCurrentConfiguration can also be of type null; however, parameter $sectionsFromCurrentConfiguration of TYPO3\CMS\Install\Servic...ConfigurationFetching() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

93
                $data = array_merge($data, $this->recursiveConfigurationFetching($value, /** @scrutinizer ignore-type */ $valueFromCurrentConfiguration, $descriptionInfo, $newPath));
Loading history...
94
            } elseif (!preg_match('/[' . LF . CR . ']/', (string)(is_array($value) ? '' : $value)) || $descriptionType === 'multiline') {
95
                $itemData = [];
96
                $itemData['key'] = implode('/', $newPath);
97
                $itemData['path'] = '[' . implode('][', $newPath) . ']';
98
                $itemData['fieldType'] = $descriptionInfo['type'];
99
                $itemData['description'] = $descriptionInfo['description'] ?? '';
100
                $itemData['allowedValues'] = $descriptionInfo['allowedValues'] ?? [];
101
                $itemData['differentValueInCurrentConfiguration'] = (!isset($descriptionInfo['compareValuesWithCurrentConfiguration']) ||
102
                    $descriptionInfo['compareValuesWithCurrentConfiguration']) &&
103
                    isset($sectionsFromCurrentConfiguration[$key]) &&
104
                    $value !== $sectionsFromCurrentConfiguration[$key];
105
                switch ($descriptionType) {
106
                    case 'multiline':
107
                        $itemData['type'] = 'textarea';
108
                        $itemData['value'] = str_replace(['\' . LF . \'', '\' . LF . \''], [LF, LF], $value);
109
                        break;
110
                    case 'bool':
111
                        $itemData['type'] = 'checkbox';
112
                        $itemData['value'] = $value ? '1' : '0';
113
                        $itemData['checked'] = (bool)$value;
114
                        break;
115
                    case 'int':
116
                        $itemData['type'] = 'number';
117
                        $itemData['value'] = (int)$value;
118
                        break;
119
                    case 'array':
120
                        $itemData['type'] = 'input';
121
                        // @todo The line below should be improved when the array handling is introduced in the global settings manager.
122
                        $itemData['value'] = is_array($value)
123
                            ? implode(',', $value)
124
                            : (string)$value;
125
                        break;
126
                    // Check if the setting is a PHP error code, will trigger a view helper in fluid
127
                    case 'errors':
128
                        $itemData['type'] = 'input';
129
                        $itemData['value'] = $value;
130
                        $itemData['phpErrorCode'] = true;
131
                        break;
132
                    case 'password':
133
                        $itemData['type'] = 'password';
134
                        $itemData['value'] = $value;
135
                        $itemData['hideValue'] = true;
136
                        break;
137
                    default:
138
                        $itemData['type'] = 'input';
139
                        $itemData['value'] = $value;
140
                }
141
142
                $data[] = $itemData;
143
            }
144
        }
145
146
        return $data;
147
    }
148
149
    /**
150
     * Store changed values in LocalConfiguration
151
     *
152
     * @param array $valueList Nested array with key['key'] value
153
     * @return FlashMessageQueue
154
     */
155
    public function updateLocalConfigurationValues(array $valueList): FlashMessageQueue
156
    {
157
        $messageQueue = new FlashMessageQueue('install');
158
        $configurationPathValuePairs = [];
159
        $commentArray = $this->getDefaultConfigArrayComments();
160
        $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
161
        foreach ($valueList as $path => $value) {
162
            $oldValue = $configurationManager->getConfigurationValueByPath($path);
163
            $pathParts = explode('/', $path);
164
            $descriptionData = $commentArray[$pathParts[0]];
165
166
            while ($part = next($pathParts)) {
167
                $descriptionData = $descriptionData['items'][$part];
168
            }
169
170
            $dataType = $descriptionData['type'];
171
172
            if ($dataType === 'multiline') {
173
                $value = str_replace(CR, '', $value);
174
                $valueHasChanged = (string)$oldValue !== (string)$value;
175
            } elseif ($dataType === 'bool') {
176
                // When submitting settings in the Install Tool, values that default to "FALSE" or "TRUE"
177
                // in EXT:core/Configuration/DefaultConfiguration.php will be sent as "0" resp. "1".
178
                $value = $value === '1';
179
                $valueHasChanged = (bool)$oldValue !== $value;
180
            } elseif ($dataType === 'int') {
181
                // Cast integer values to integers (but only for values that can not contain a string as well)
182
                $value = (int)$value;
183
                $valueHasChanged = (int)$oldValue !== $value;
184
            } elseif ($dataType === 'array') {
185
                $oldValueAsString = is_array($oldValue)
186
                    ? implode(',', $oldValue)
187
                    : (string)$oldValue;
188
                $valueHasChanged = $oldValueAsString !== $value;
189
                $value = GeneralUtility::trimExplode(',', $value, true);
190
            } else {
191
                $valueHasChanged = (string)$oldValue !== (string)$value;
192
            }
193
194
            // Save if value changed
195
            if ($valueHasChanged) {
196
                $configurationPathValuePairs[$path] = $value;
197
198
                if (is_bool($value)) {
199
                    $messageBody = 'New value = ' . ($value ? 'true' : 'false');
200
                } elseif (empty($value)) {
201
                    $messageBody = 'New value = none';
202
                } elseif (is_array($value)) {
203
                    $messageBody = "New value = ['" . implode("', '", $value) . "']";
204
                } elseif ($dataType === 'password') {
205
                    $messageBody = 'New value is set';
206
                } else {
207
                    $messageBody = 'New value = ' . $value;
208
                }
209
210
                $messageQueue->enqueue(new FlashMessage(
211
                    $messageBody,
212
                    $path
213
                ));
214
            }
215
        }
216
        if ($messageQueue->count() > 0) {
217
            $configurationManager->setLocalConfigurationValuesByPathValuePairs($configurationPathValuePairs);
218
        }
219
        return $messageQueue;
220
    }
221
222
    /**
223
     * Read descriptions from description file
224
     *
225
     * @return array
226
     */
227
    protected function getDefaultConfigArrayComments(): array
228
    {
229
        $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
230
        $fileName = $configurationManager->getDefaultConfigurationDescriptionFileLocation();
231
        $fileLoader = GeneralUtility::makeInstance(YamlFileLoader::class);
232
        return $fileLoader->load($fileName);
233
    }
234
}
235