8dcda814339c81f2ce37568c22b5a7c6db5e9049
[Packages/TYPO3.CMS.git] / typo3 / sysext / form / Classes / Mvc / Configuration / ConfigurationManager.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Form\Mvc\Configuration;
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 use TYPO3\CMS\Core\Cache\CacheManager;
19 use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
20 use TYPO3\CMS\Core\Configuration\Loader\FalYamlFileLoader;
21 use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader;
22 use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\Configuration;
23 use TYPO3\CMS\Core\Utility\ArrayUtility;
24 use TYPO3\CMS\Core\Utility\GeneralUtility;
25 use TYPO3\CMS\Extbase\Configuration\ConfigurationManager as ExtbaseConfigurationManager;
26 use TYPO3\CMS\Form\Mvc\Configuration\Exception\ExtensionNameRequiredException;
27 use TYPO3\CMS\Form\Mvc\Configuration\Exception\NoConfigurationFoundException;
28
29 /**
30 * Extend the ExtbaseConfigurationManager to read YAML configurations.
31 *
32 * Scope: frontend / backend
33 * @internal
34 */
35 class ConfigurationManager extends ExtbaseConfigurationManager implements ConfigurationManagerInterface
36 {
37
38 /**
39 * @var \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
40 */
41 protected $cache;
42
43 /**
44 * @param string $configurationType The kind of configuration to fetch - must be one of the CONFIGURATION_TYPE_* constants
45 * @param string $extensionName if specified, the configuration for the given extension will be returned.
46 * @param string $pluginName if specified, the configuration for the given plugin will be returned.
47 * @return array The configuration
48 * @internal
49 */
50 public function getConfiguration($configurationType, $extensionName = null, $pluginName = null)
51 {
52 switch ($configurationType) {
53 case self::CONFIGURATION_TYPE_YAML_SETTINGS:
54 return $this->getConfigurationFromYamlFile($extensionName);
55 default:
56 return parent::getConfiguration($configurationType, $extensionName, $pluginName);
57 }
58 }
59
60 /**
61 * Load and parse a YAML configuration which is configured within
62 * plugin.tx_form.settings.configurationFile
63 *
64 * The following steps will be done:
65 *
66 * * load a YAML file into an array
67 * * resolve all declared inheritances
68 * * convert all boolean strings ('true' / 'false') into boolean values
69 * * remove all keys if their values are NULL
70 * * return all configuration paths within TYPO3.CMS
71 * * sort by array keys, if all keys within the current nesting level are numerical keys
72 * * resolve possible TypoScript settings in FE mode
73 *
74 * @param string $extensionName
75 * @return array
76 * @throws ExtensionNameRequiredException
77 * @throws NoConfigurationFoundException
78 */
79 protected function getConfigurationFromYamlFile(string $extensionName): array
80 {
81 if (empty($extensionName)) {
82 throw new ExtensionNameRequiredException(
83 'Please specify an extension key to load a YAML configuration',
84 1471473377
85 );
86 }
87 $ucFirstExtensioName = ucfirst($extensionName);
88
89 $typoscriptSettings = $this->getTypoScriptSettings($extensionName);
90
91 $cacheKeySuffix = $extensionName;
92 if (isset($typoscriptSettings['configurationFile'])) {
93 $cacheKeySuffix .= md5($typoscriptSettings['configurationFile']);
94 } elseif (isset($typoscriptSettings['yamlConfigurations'])) {
95 $cacheKeySuffix .= md5(json_encode($typoscriptSettings['yamlConfigurations']));
96 }
97
98 $yamlSettings = $this->getYamlSettingsFromCache($cacheKeySuffix);
99 if (!empty($yamlSettings)) {
100 return $this->overrideConfigurationByTypoScript($yamlSettings, $extensionName);
101 }
102
103 $configuration = $this->objectManager->get(Configuration::class)
104 ->setMergeLists(false);
105 if (isset($typoscriptSettings['configurationFile'])) {
106 $yamlSettings = $this->objectManager->get(FalYamlFileLoader::class, $configuration)
107 ->load($typoscriptSettings['configurationFile']);
108 } elseif (isset($typoscriptSettings['yamlConfigurations'])) {
109 trigger_error('EXT:form configuration registration via "<module|plugin>.tx_form.settings.yamlConfigurations" has been deprecated in v9 and will be removed in v10. Use "<module|plugin>.tx_form.settings.configurationFile" instead.', E_USER_DEPRECATED);
110 $yamlContent = $this->generateYamlFromLegacyYamlConfigurations(
111 $typoscriptSettings['yamlConfigurations']
112 );
113
114 $yamlSettings = $this->objectManager->get(YamlFileLoader::class, $configuration)
115 ->loadFromContent($yamlContent);
116 } else {
117 throw new NoConfigurationFoundException(
118 'No YAML configurations could be found for extension ' . $extensionName,
119 1471473378
120 );
121 }
122
123 $yamlSettings = ArrayUtility::convertBooleanStringsToBooleanRecursive($yamlSettings);
124 $yamlSettings = ArrayUtility::removeNullValuesRecursive($yamlSettings);
125 $yamlSettings = InheritancesResolverService::create($yamlSettings)
126 ->getResolvedConfiguration();
127
128 $yamlSettings = is_array($yamlSettings['TYPO3']['CMS'][$ucFirstExtensioName])
129 ? $yamlSettings['TYPO3']['CMS'][$ucFirstExtensioName]
130 : [];
131 $yamlSettings = ArrayUtility::sortArrayWithIntegerKeysRecursive($yamlSettings);
132
133 $this->setYamlSettingsIntoCache($cacheKeySuffix, $yamlSettings);
134
135 return $this->overrideConfigurationByTypoScript($yamlSettings, $extensionName);
136 }
137
138 /**
139 * @param array $yamlSettings
140 * @param string $extensionName
141 * @return array
142 */
143 protected function overrideConfigurationByTypoScript(array $yamlSettings, string $extensionName): array
144 {
145 $typoScript = parent::getConfiguration(self::CONFIGURATION_TYPE_SETTINGS, $extensionName);
146 if (is_array($typoScript['yamlSettingsOverrides']) && !empty($typoScript['yamlSettingsOverrides'])) {
147 ArrayUtility::mergeRecursiveWithOverrule(
148 $yamlSettings,
149 $typoScript['yamlSettingsOverrides']
150 );
151
152 if ($this->environmentService->isEnvironmentInFrontendMode()) {
153 $yamlSettings = $this->objectManager->get(TypoScriptService::class)
154 ->resolvePossibleTypoScriptConfiguration($yamlSettings);
155 }
156 }
157 return $yamlSettings;
158 }
159
160 /**
161 * @return \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
162 */
163 protected function getCacheFrontend(): FrontendInterface
164 {
165 if ($this->cache === null) {
166 $this->cache = GeneralUtility::makeInstance(CacheManager::class)->getCache('assets');
167 }
168 return $this->cache;
169 }
170
171 /**
172 * @param string $cacheKeySuffix
173 * @return string
174 */
175 protected function getConfigurationCacheKey(string $cacheKeySuffix): string
176 {
177 return strtolower(self::CONFIGURATION_TYPE_YAML_SETTINGS . '_' . $cacheKeySuffix);
178 }
179
180 /**
181 * @param string $cacheKeySuffix
182 * @return mixed
183 */
184 protected function getYamlSettingsFromCache(string $cacheKeySuffix)
185 {
186 return $this->getCacheFrontend()->get(
187 $this->getConfigurationCacheKey($cacheKeySuffix)
188 );
189 }
190
191 /**
192 * @param string $cacheKeySuffix
193 * @param array $yamlSettings
194 */
195 protected function setYamlSettingsIntoCache(
196 string $cacheKeySuffix,
197 array $yamlSettings
198 ) {
199 $this->getCacheFrontend()->set(
200 $this->getConfigurationCacheKey($cacheKeySuffix),
201 $yamlSettings
202 );
203 }
204
205 /**
206 * @param string $extensionName
207 * @return null|[]
208 */
209 protected function getTypoScriptSettings(string $extensionName)
210 {
211 return parent::getConfiguration(
212 ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS,
213 $extensionName
214 );
215 }
216
217 /**
218 * Compatibility layer for the deprecated TypoScript option
219 * "plugin.tx_form.settings.yamlConfigurations"
220 *
221 * @param array $yamlConfigurations
222 * @return string
223 * @internal
224 */
225 protected function generateYamlFromLegacyYamlConfigurations(array $yamlConfigurations): string
226 {
227 $yamlConfigurations = ArrayUtility::sortArrayWithIntegerKeys($yamlConfigurations);
228 $yamlContent = 'imports:' . LF;
229
230 $baseExtFormConfigurations = [
231 'EXT:form/Configuration/Yaml/BaseSetup.yaml',
232 'EXT:form/Configuration/Yaml/FormEditorSetup.yaml',
233 'EXT:form/Configuration/Yaml/FormEngineSetup.yaml',
234 'EXT:form/Configuration/Yaml/FormSetup.yaml',
235 ];
236
237 $imports = '';
238 $baseExtFormConfigurationsExists = false;
239 foreach ($yamlConfigurations as $yamlConfiguration) {
240 if (in_array($yamlConfiguration, $baseExtFormConfigurations)) {
241 $baseExtFormConfigurationsExists = true;
242 } else {
243 $imports .= ' - { resource: "' . $yamlConfiguration . '" }' . LF;
244 }
245 }
246
247 // We assume that if one of the files defined within $baseExtFormConfigurations exists
248 // within plugin.tx_form.settings.yamlConfigurations, someone wants to load
249 // the base EXT:form setup files (old or new) and afterwards extend it with his own configuration.
250 // In this case, we define the new EXT:form/Configuration/Yaml/FormSetup.yaml file as the
251 // first file to import from.
252 if ($baseExtFormConfigurationsExists) {
253 $yamlContent .= ' - { resource: "EXT:form/Configuration/Yaml/FormSetup.yaml" }' . LF . $imports;
254 } else {
255 $yamlContent .= $imports;
256 }
257
258 return $yamlContent;
259 }
260 }