[TASK] Use FQCN's when registering plugins/modules
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Service / ExtensionService.php
1 <?php
2 namespace TYPO3\CMS\Extbase\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\Context\Context;
18 use TYPO3\CMS\Core\Database\ConnectionPool;
19 use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;
20 use TYPO3\CMS\Core\Utility\ArrayUtility;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22 use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
23
24 /**
25 * Service for determining basic extension params
26 * @internal only to be used within Extbase, not part of TYPO3 Core API.
27 */
28 class ExtensionService implements \TYPO3\CMS\Core\SingletonInterface
29 {
30 const PLUGIN_TYPE_PLUGIN = 'list_type';
31 const PLUGIN_TYPE_CONTENT_ELEMENT = 'CType';
32
33 /**
34 * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
35 */
36 protected $objectManager;
37
38 /**
39 * @var ConfigurationManagerInterface
40 */
41 protected $configurationManager;
42
43 /**
44 * Cache of result for getTargetPidByPlugin()
45 * @var array
46 */
47 protected $targetPidPluginCache = [];
48
49 /**
50 * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
51 */
52 public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
53 {
54 $this->objectManager = $objectManager;
55 }
56
57 /**
58 * @param ConfigurationManagerInterface $configurationManager
59 */
60 public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager)
61 {
62 $this->configurationManager = $configurationManager;
63 }
64
65 /**
66 * Determines the plugin namespace of the specified plugin (defaults to "tx_[extensionname]_[pluginname]")
67 * If plugin.tx_$pluginSignature.view.pluginNamespace is set, this value is returned
68 * If pluginNamespace is not specified "tx_[extensionname]_[pluginname]" is returned.
69 *
70 * @param string $extensionName name of the extension to retrieve the namespace for
71 * @param string $pluginName name of the plugin to retrieve the namespace for
72 * @return string plugin namespace
73 */
74 public function getPluginNamespace($extensionName, $pluginName)
75 {
76 $pluginSignature = strtolower($extensionName . '_' . $pluginName);
77 $defaultPluginNamespace = 'tx_' . $pluginSignature;
78 $frameworkConfiguration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK, $extensionName, $pluginName);
79 if (!isset($frameworkConfiguration['view']['pluginNamespace']) || empty($frameworkConfiguration['view']['pluginNamespace'])) {
80 return $defaultPluginNamespace;
81 }
82 return $frameworkConfiguration['view']['pluginNamespace'];
83 }
84
85 /**
86 * Iterates through the global TypoScript configuration and returns the name of the plugin
87 * that matches specified extensionName, controllerName and actionName.
88 * If no matching plugin was found, NULL is returned.
89 * If more than one plugin matches and the current plugin is not configured to handle the action,
90 * an Exception will be thrown
91 *
92 * @param string $extensionName name of the target extension (UpperCamelCase)
93 * @param string $controllerName name of the target controller (UpperCamelCase)
94 * @param string $actionName name of the target action (lowerCamelCase)
95 * @throws \TYPO3\CMS\Extbase\Exception
96 * @return string name of the target plugin (UpperCamelCase) or NULL if no matching plugin configuration was found
97 */
98 public function getPluginNameByAction($extensionName, $controllerName, $actionName)
99 {
100 $frameworkConfiguration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
101 // check, whether the current plugin is configured to handle the action
102 if (!empty($frameworkConfiguration['extensionName']) && $extensionName === $frameworkConfiguration['extensionName']) {
103 if (isset($frameworkConfiguration['controllerConfiguration'][$controllerName]) && in_array($actionName, $frameworkConfiguration['controllerConfiguration'][$controllerName]['actions'])) {
104 return $frameworkConfiguration['pluginName'];
105 }
106 }
107 $plugins = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'] ?? false;
108 if (!$plugins) {
109 return null;
110 }
111 $pluginNames = [];
112 foreach ($plugins as $pluginName => $pluginConfiguration) {
113 foreach ($pluginConfiguration['controllers'] ?? [] as $pluginControllerName => $pluginControllerActions) {
114 if (strtolower($pluginControllerName) !== strtolower($controllerName)) {
115 continue;
116 }
117 if (in_array($actionName, $pluginControllerActions['actions'])) {
118 $pluginNames[] = $pluginName;
119 }
120 }
121 }
122 if (count($pluginNames) > 1) {
123 throw new \TYPO3\CMS\Extbase\Exception('There is more than one plugin that can handle this request (Extension: "' . $extensionName . '", Controller: "' . $controllerName . '", action: "' . $actionName . '"). Please specify "pluginName" argument', 1280825466);
124 }
125 return !empty($pluginNames) ? $pluginNames[0] : null;
126 }
127
128 /**
129 * Checks if the given action is cacheable or not.
130 *
131 * @param string $extensionName Name of the target extension, without underscores
132 * @param string $pluginName Name of the target plugin
133 * @param string $controllerClassName Name of the target controller
134 * @param string $actionName Name of the action to be called
135 * @return bool TRUE if the specified plugin action is cacheable, otherwise FALSE
136 */
137 public function isActionCacheable($extensionName, $pluginName, $controllerClassName, $actionName)
138 {
139 $frameworkConfiguration = $this->configurationManager->getConfiguration(
140 ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK,
141 $extensionName,
142 $pluginName
143 );
144
145 $nonCacheableActions = $frameworkConfiguration['controllerConfiguration'][$controllerClassName]['nonCacheableActions'] ?? null;
146
147 if (!is_array($nonCacheableActions)) {
148 return true;
149 }
150
151 return !in_array($actionName, $frameworkConfiguration['controllerConfiguration'][$controllerClassName]['nonCacheableActions'], true);
152 }
153
154 /**
155 * Determines the target page of the specified plugin.
156 * If plugin.tx_$pluginSignature.view.defaultPid is set, this value is used as target page id
157 * If defaultPid is set to "auto", a the target pid is determined by loading the tt_content record that contains this plugin
158 * If the page could not be determined, NULL is returned
159 * If defaultPid is "auto" and more than one page contains the specified plugin, an Exception is thrown
160 *
161 * @param string $extensionName name of the extension to retrieve the target PID for
162 * @param string $pluginName name of the plugin to retrieve the target PID for
163 * @throws \TYPO3\CMS\Extbase\Exception
164 * @return int uid of the target page or NULL if target page could not be determined
165 */
166 public function getTargetPidByPlugin($extensionName, $pluginName)
167 {
168 $frameworkConfiguration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK, $extensionName, $pluginName);
169 if (!isset($frameworkConfiguration['view']['defaultPid']) || empty($frameworkConfiguration['view']['defaultPid'])) {
170 return null;
171 }
172 $pluginSignature = strtolower($extensionName . '_' . $pluginName);
173 if ($frameworkConfiguration['view']['defaultPid'] === 'auto') {
174 if (!array_key_exists($pluginSignature, $this->targetPidPluginCache)) {
175 $languageId = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('language', 'id', 0);
176 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
177 ->getQueryBuilderForTable('tt_content');
178 $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
179
180 $pages = $queryBuilder
181 ->select('pid')
182 ->from('tt_content')
183 ->where(
184 $queryBuilder->expr()->eq(
185 'list_type',
186 $queryBuilder->createNamedParameter($pluginSignature, \PDO::PARAM_STR)
187 ),
188 $queryBuilder->expr()->eq(
189 'CType',
190 $queryBuilder->createNamedParameter('list', \PDO::PARAM_STR)
191 ),
192 $queryBuilder->expr()->eq(
193 'sys_language_uid',
194 $queryBuilder->createNamedParameter($languageId, \PDO::PARAM_INT)
195 )
196 )
197 ->setMaxResults(2)
198 ->execute()
199 ->fetchAll();
200
201 if (count($pages) > 1) {
202 throw new \TYPO3\CMS\Extbase\Exception('There is more than one "' . $pluginSignature . '" plugin in the current page tree. Please remove one plugin or set the TypoScript configuration "plugin.tx_' . $pluginSignature . '.view.defaultPid" to a fixed page id', 1280773643);
203 }
204 $this->targetPidPluginCache[$pluginSignature] = !empty($pages) ? $pages[0]['pid'] : null;
205 }
206 return $this->targetPidPluginCache[$pluginSignature];
207 }
208 return (int)$frameworkConfiguration['view']['defaultPid'];
209 }
210
211 /**
212 * This returns the name of the first controller of the given plugin.
213 *
214 * @param string $extensionName name of the extension to retrieve the target PID for
215 * @param string $pluginName name of the plugin to retrieve the target PID for
216 * @return string|null
217 */
218 public function getDefaultControllerNameByPlugin($extensionName, $pluginName)
219 {
220 $controllers = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'] ?? false;
221 return $controllers ? key($controllers) : null;
222 }
223
224 /**
225 * This returns the name of the first action of the given plugin controller.
226 *
227 * @param string $extensionName name of the extension to retrieve the target PID for
228 * @param string $pluginName name of the plugin to retrieve the target PID for
229 * @param string $controllerName name of the controller to retrieve default action for
230 * @return string|null
231 */
232 public function getDefaultActionNameByPluginAndController($extensionName, $pluginName, $controllerName)
233 {
234 $actions = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'][$controllerName]['actions'] ?? false;
235 return $actions ? current($actions) : null;
236 }
237
238 /**
239 * Resolve the page type number to use for building a link for a specific format
240 *
241 * @param string $extensionName name of the extension that has defined the target page type
242 * @param string $format The format for which to look up the page type
243 * @return int Page type number for target page
244 */
245 public function getTargetPageTypeByFormat($extensionName, $format)
246 {
247 // Default behaviour
248 $settings = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK, $extensionName);
249 if (isset($settings['view']['formatToPageTypeMapping']) && is_array($settings['view']['formatToPageTypeMapping'])) {
250 ArrayUtility::mergeRecursiveWithOverrule($formatToPageTypeMapping, $settings['view']['formatToPageTypeMapping']);
251 }
252 return $formatToPageTypeMapping[$format] ?? 0;
253 }
254 }