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