1<?php 2namespace TYPO3\CMS\Extbase\Configuration; 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/** 18 * Abstract base class for a general purpose configuration manager 19 * @internal only to be used within Extbase, not part of TYPO3 Core API. 20 */ 21abstract class AbstractConfigurationManager implements \TYPO3\CMS\Core\SingletonInterface 22{ 23 /** 24 * Default backend storage PID 25 */ 26 const DEFAULT_BACKEND_STORAGE_PID = 0; 27 28 /** 29 * Storage of the raw TypoScript configuration 30 * 31 * @var array 32 */ 33 protected $configuration = []; 34 35 /** 36 * @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer 37 */ 38 protected $contentObject; 39 40 /** 41 * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface 42 */ 43 protected $objectManager; 44 45 /** 46 * @var \TYPO3\CMS\Core\TypoScript\TypoScriptService 47 */ 48 protected $typoScriptService; 49 50 /** 51 * name of the extension this Configuration Manager instance belongs to 52 * 53 * @var string 54 */ 55 protected $extensionName; 56 57 /** 58 * name of the plugin this Configuration Manager instance belongs to 59 * 60 * @var string 61 */ 62 protected $pluginName; 63 64 /** 65 * 1st level configuration cache 66 * 67 * @var array 68 */ 69 protected $configurationCache = []; 70 71 /** 72 * @var \TYPO3\CMS\Extbase\Service\EnvironmentService 73 */ 74 protected $environmentService; 75 76 /** 77 * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager 78 */ 79 public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager) 80 { 81 $this->objectManager = $objectManager; 82 } 83 84 /** 85 * @param \TYPO3\CMS\Core\TypoScript\TypoScriptService $typoScriptService 86 */ 87 public function injectTypoScriptService(\TYPO3\CMS\Core\TypoScript\TypoScriptService $typoScriptService) 88 { 89 $this->typoScriptService = $typoScriptService; 90 } 91 92 /** 93 * @param \TYPO3\CMS\Extbase\Service\EnvironmentService $environmentService 94 */ 95 public function injectEnvironmentService(\TYPO3\CMS\Extbase\Service\EnvironmentService $environmentService) 96 { 97 $this->environmentService = $environmentService; 98 } 99 100 /** 101 * @param \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer $contentObject 102 */ 103 public function setContentObject(\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer $contentObject = null) 104 { 105 $this->contentObject = $contentObject; 106 } 107 108 /** 109 * @return \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer|null 110 */ 111 public function getContentObject() 112 { 113 if ($this->contentObject !== null) { 114 return $this->contentObject; 115 } 116 return null; 117 } 118 119 /** 120 * Sets the specified raw configuration coming from the outside. 121 * Note that this is a low level method and only makes sense to be used by Extbase internally. 122 * 123 * @param array $configuration The new configuration 124 */ 125 public function setConfiguration(array $configuration = []) 126 { 127 // reset 1st level cache 128 $this->configurationCache = []; 129 $this->extensionName = $configuration['extensionName'] ?? null; 130 $this->pluginName = $configuration['pluginName'] ?? null; 131 $this->configuration = $this->typoScriptService->convertTypoScriptArrayToPlainArray($configuration); 132 } 133 134 /** 135 * Loads the Extbase Framework configuration. 136 * 137 * The Extbase framework configuration HAS TO be retrieved using this method, as they are come from different places than the normal settings. 138 * Framework configuration is, in contrast to normal settings, needed for the Extbase framework to operate correctly. 139 * 140 * @param string $extensionName if specified, the configuration for the given extension will be returned (plugin.tx_extensionname) 141 * @param string $pluginName if specified, the configuration for the given plugin will be returned (plugin.tx_extensionname_pluginname) 142 * @return array the Extbase framework configuration 143 */ 144 public function getConfiguration($extensionName = null, $pluginName = null) 145 { 146 // 1st level cache 147 $configurationCacheKey = strtolower(($extensionName ?: $this->extensionName) . '_' . ($pluginName ?: $this->pluginName)); 148 if (isset($this->configurationCache[$configurationCacheKey])) { 149 return $this->configurationCache[$configurationCacheKey]; 150 } 151 $frameworkConfiguration = $this->getExtbaseConfiguration(); 152 if (!isset($frameworkConfiguration['persistence']['storagePid'])) { 153 $frameworkConfiguration['persistence']['storagePid'] = $this->getDefaultBackendStoragePid(); 154 } 155 // only merge $this->configuration and override switchableControllerActions when retrieving configuration of the current plugin 156 if ($extensionName === null || $extensionName === $this->extensionName && $pluginName === $this->pluginName) { 157 $pluginConfiguration = $this->getPluginConfiguration($this->extensionName, $this->pluginName); 158 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($pluginConfiguration, $this->configuration); 159 $pluginConfiguration['controllerConfiguration'] = $this->getSwitchableControllerActions($this->extensionName, $this->pluginName); 160 if (isset($this->configuration['switchableControllerActions'])) { 161 $this->overrideSwitchableControllerActions($pluginConfiguration, $this->configuration['switchableControllerActions']); 162 } 163 } else { 164 $pluginConfiguration = $this->getPluginConfiguration($extensionName, $pluginName); 165 $pluginConfiguration['controllerConfiguration'] = $this->getSwitchableControllerActions($extensionName, $pluginName); 166 } 167 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($frameworkConfiguration, $pluginConfiguration); 168 // only load context specific configuration when retrieving configuration of the current plugin 169 if ($extensionName === null || $extensionName === $this->extensionName && $pluginName === $this->pluginName) { 170 $frameworkConfiguration = $this->getContextSpecificFrameworkConfiguration($frameworkConfiguration); 171 } 172 173 if (!empty($frameworkConfiguration['persistence']['storagePid'])) { 174 if (is_array($frameworkConfiguration['persistence']['storagePid'])) { 175 // We simulate the frontend to enable the use of cObjects in 176 // stdWrap. Than we convert the configuration to normal TypoScript 177 // and apply the stdWrap to the storagePid 178 if (!$this->environmentService->isEnvironmentInFrontendMode()) { 179 \TYPO3\CMS\Extbase\Utility\FrontendSimulatorUtility::simulateFrontendEnvironment($this->getContentObject()); 180 } 181 $conf = $this->typoScriptService->convertPlainArrayToTypoScriptArray($frameworkConfiguration['persistence']); 182 $frameworkConfiguration['persistence']['storagePid'] = $GLOBALS['TSFE']->cObj->stdWrap($conf['storagePid'], $conf['storagePid.']); 183 if (!$this->environmentService->isEnvironmentInFrontendMode()) { 184 \TYPO3\CMS\Extbase\Utility\FrontendSimulatorUtility::resetFrontendEnvironment(); 185 } 186 } 187 188 if (!empty($frameworkConfiguration['persistence']['recursive'])) { 189 // All implementations of getTreeList allow to pass the ids negative to include them into the result 190 // otherwise only childpages are returned 191 $storagePids = \TYPO3\CMS\Core\Utility\GeneralUtility::intExplode(',', $frameworkConfiguration['persistence']['storagePid']); 192 array_walk($storagePids, function (&$storagePid) { 193 if ($storagePid > 0) { 194 $storagePid = -$storagePid; 195 } 196 }); 197 $frameworkConfiguration['persistence']['storagePid'] = $this->getRecursiveStoragePids( 198 implode(',', $storagePids), 199 (int)$frameworkConfiguration['persistence']['recursive'] 200 ); 201 } 202 } 203 // 1st level cache 204 $this->configurationCache[$configurationCacheKey] = $frameworkConfiguration; 205 return $frameworkConfiguration; 206 } 207 208 /** 209 * Returns the TypoScript configuration found in config.tx_extbase 210 * 211 * @return array 212 */ 213 protected function getExtbaseConfiguration() 214 { 215 $setup = $this->getTypoScriptSetup(); 216 $extbaseConfiguration = []; 217 if (isset($setup['config.']['tx_extbase.'])) { 218 $extbaseConfiguration = $this->typoScriptService->convertTypoScriptArrayToPlainArray($setup['config.']['tx_extbase.']); 219 } 220 return $extbaseConfiguration; 221 } 222 223 /** 224 * Returns the default backend storage pid 225 * 226 * @return string 227 */ 228 public function getDefaultBackendStoragePid() 229 { 230 return self::DEFAULT_BACKEND_STORAGE_PID; 231 } 232 233 /** 234 * @param array &$frameworkConfiguration 235 * @param array $switchableControllerActions 236 */ 237 protected function overrideSwitchableControllerActions(array &$frameworkConfiguration, array $switchableControllerActions) 238 { 239 $overriddenSwitchableControllerActions = []; 240 foreach ($switchableControllerActions as $controllerName => $actions) { 241 if (!isset($frameworkConfiguration['controllerConfiguration'][$controllerName])) { 242 continue; 243 } 244 $overriddenSwitchableControllerActions[$controllerName] = ['actions' => $actions]; 245 $nonCacheableActions = $frameworkConfiguration['controllerConfiguration'][$controllerName]['nonCacheableActions'] ?? null; 246 if (!is_array($nonCacheableActions)) { 247 // There are no non-cacheable actions, thus we can directly continue 248 // with the next controller name. 249 continue; 250 } 251 $overriddenNonCacheableActions = array_intersect($nonCacheableActions, $actions); 252 if (!empty($overriddenNonCacheableActions)) { 253 $overriddenSwitchableControllerActions[$controllerName]['nonCacheableActions'] = $overriddenNonCacheableActions; 254 } 255 } 256 $frameworkConfiguration['controllerConfiguration'] = $overriddenSwitchableControllerActions; 257 } 258 259 /** 260 * The context specific configuration returned by this method 261 * will override the framework configuration which was 262 * obtained from TypoScript. This can be used f.e. to override the storagePid 263 * with the value set inside the Plugin Instance. 264 * 265 * WARNING: Make sure this method ALWAYS returns an array! 266 * 267 * @param array $frameworkConfiguration The framework configuration until now 268 * @return array context specific configuration which will override the configuration obtained by TypoScript 269 */ 270 abstract protected function getContextSpecificFrameworkConfiguration(array $frameworkConfiguration); 271 272 /** 273 * Returns TypoScript Setup array from current Environment. 274 * 275 * @return array the TypoScript setup 276 */ 277 abstract public function getTypoScriptSetup(); 278 279 /** 280 * Returns the TypoScript configuration found in plugin.tx_yourextension_yourplugin / module.tx_yourextension_yourmodule 281 * merged with the global configuration of your extension from plugin.tx_yourextension / module.tx_yourextension 282 * 283 * @param string $extensionName 284 * @param string $pluginName in FE mode this is the specified plugin name, in BE mode this is the full module signature 285 * @return array 286 */ 287 abstract protected function getPluginConfiguration($extensionName, $pluginName = null); 288 289 /** 290 * Returns the configured controller/action pairs of the specified plugin/module in the format 291 * array( 292 * 'Controller1' => array('action1', 'action2'), 293 * 'Controller2' => array('action3', 'action4') 294 * ) 295 * 296 * @param string $extensionName 297 * @param string $pluginName in FE mode this is the specified plugin name, in BE mode this is the full module signature 298 * @return array 299 */ 300 abstract protected function getSwitchableControllerActions($extensionName, $pluginName); 301 302 /** 303 * The implementation of the methods to return a list of storagePid that are below a certain 304 * storage pid. 305 * 306 * @param string $storagePid Storage PID to start at; multiple PIDs possible as comma-separated list 307 * @param int $recursionDepth Maximum number of levels to search, 0 to disable recursive lookup 308 * @return string storage PIDs 309 */ 310 abstract protected function getRecursiveStoragePids($storagePid, $recursionDepth = 0); 311} 312