[TASK] Move LoginProvider logic into a separate class
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Configuration / BackendConfigurationManager.php
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\Extbase\Configuration;
19
20 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
21 use TYPO3\CMS\Core\Database\ConnectionPool;
22 use TYPO3\CMS\Core\Database\Query\QueryHelper;
23 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
24 use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
25 use TYPO3\CMS\Core\Type\Bitmask\Permission;
26 use TYPO3\CMS\Core\TypoScript\TemplateService;
27 use TYPO3\CMS\Core\Utility\ArrayUtility;
28 use TYPO3\CMS\Core\Utility\GeneralUtility;
29 use TYPO3\CMS\Core\Utility\RootlineUtility;
30
31 /**
32 * A general purpose configuration manager used in backend mode.
33 * @internal only to be used within Extbase, not part of TYPO3 Core API.
34 */
35 class BackendConfigurationManager extends AbstractConfigurationManager
36 {
37 /**
38 * @var array
39 */
40 protected $typoScriptSetupCache = [];
41
42 /**
43 * stores the current page ID
44 * @var int
45 */
46 protected $currentPageId;
47
48 /**
49 * Returns TypoScript Setup array from current Environment.
50 *
51 * @return array the raw TypoScript setup
52 */
53 public function getTypoScriptSetup(): array
54 {
55 $pageId = $this->getCurrentPageId();
56
57 if (!array_key_exists($pageId, $this->typoScriptSetupCache)) {
58 /** @var TemplateService $template */
59 $template = GeneralUtility::makeInstance(TemplateService::class);
60 // do not log time-performance information
61 $template->tt_track = false;
62 // Explicitly trigger processing of extension static files
63 $template->setProcessExtensionStatics(true);
64 // Get the root line
65 $rootline = [];
66 if ($pageId > 0) {
67 try {
68 $rootline = GeneralUtility::makeInstance(RootlineUtility::class, $pageId)->get();
69 } catch (\RuntimeException $e) {
70 $rootline = [];
71 }
72 }
73 // This generates the constants/config + hierarchy info for the template.
74 $template->runThroughTemplates($rootline, 0);
75 $template->generateConfig();
76 $this->typoScriptSetupCache[$pageId] = $template->setup;
77 }
78 return $this->typoScriptSetupCache[$pageId];
79 }
80
81 /**
82 * Returns the TypoScript configuration found in module.tx_yourextension_yourmodule
83 * merged with the global configuration of your extension from module.tx_yourextension
84 *
85 * @param string $extensionName
86 * @param string $pluginName in BE mode this is actually the module signature. But we're using it just like the plugin name in FE
87 * @return array
88 */
89 protected function getPluginConfiguration(string $extensionName, string $pluginName = null): array
90 {
91 $setup = $this->getTypoScriptSetup();
92 $pluginConfiguration = [];
93 if (is_array($setup['module.']['tx_' . strtolower($extensionName) . '.'] ?? false)) {
94 $pluginConfiguration = $this->typoScriptService->convertTypoScriptArrayToPlainArray($setup['module.']['tx_' . strtolower($extensionName) . '.']);
95 }
96 if ($pluginName !== null) {
97 $pluginSignature = strtolower($extensionName . '_' . $pluginName);
98 if (is_array($setup['module.']['tx_' . $pluginSignature . '.'] ?? false)) {
99 $overruleConfiguration = $this->typoScriptService->convertTypoScriptArrayToPlainArray($setup['module.']['tx_' . $pluginSignature . '.']);
100 ArrayUtility::mergeRecursiveWithOverrule($pluginConfiguration, $overruleConfiguration);
101 }
102 }
103 return $pluginConfiguration;
104 }
105
106 /**
107 * Returns the configured controller/action configuration of the specified module in the format
108 * array(
109 * 'Controller1' => array('action1', 'action2'),
110 * 'Controller2' => array('action3', 'action4')
111 * )
112 *
113 * @param string $extensionName
114 * @param string $pluginName in BE mode this is actually the module signature. But we're using it just like the plugin name in FE
115 * @return array
116 */
117 protected function getControllerConfiguration(string $extensionName, string $pluginName): array
118 {
119 $controllerConfiguration = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['modules'][$pluginName]['controllers'] ?? false;
120 if (!is_array($controllerConfiguration)) {
121 $controllerConfiguration = [];
122 }
123 return $controllerConfiguration;
124 }
125
126 /**
127 * Returns the page uid of the current page.
128 * If no page is selected, we'll return the uid of the first root page.
129 *
130 * @return int current page id. If no page is selected current root page id is returned
131 */
132 protected function getCurrentPageId(): int
133 {
134 if ($this->currentPageId !== null) {
135 return $this->currentPageId;
136 }
137
138 $this->currentPageId = $this->getCurrentPageIdFromGetPostData() ?: $this->getCurrentPageIdFromCurrentSiteRoot();
139 $this->currentPageId = $this->currentPageId ?: $this->getCurrentPageIdFromRootTemplate();
140 $this->currentPageId = $this->currentPageId ?: self::DEFAULT_BACKEND_STORAGE_PID;
141
142 return $this->currentPageId;
143 }
144
145 /**
146 * Gets the current page ID from the GET/POST data.
147 *
148 * @return int the page UID, will be 0 if none has been set
149 */
150 protected function getCurrentPageIdFromGetPostData(): int
151 {
152 return (int)GeneralUtility::_GP('id');
153 }
154
155 /**
156 * Gets the current page ID from the first site root in tree.
157 *
158 * @return int the page UID, will be 0 if none has been set
159 */
160 protected function getCurrentPageIdFromCurrentSiteRoot(): int
161 {
162 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
163 ->getQueryBuilderForTable('pages');
164
165 $queryBuilder
166 ->getRestrictions()
167 ->removeAll()
168 ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
169 ->add(GeneralUtility::makeInstance(HiddenRestriction::class));
170
171 $rootPage = $queryBuilder
172 ->select('uid')
173 ->from('pages')
174 ->where(
175 $queryBuilder->expr()->eq('is_siteroot', $queryBuilder->createNamedParameter(1, \PDO::PARAM_INT)),
176 $queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
177 // Only consider live root page IDs, never return a versioned root page ID
178 $queryBuilder->expr()->eq('t3ver_oid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
179 $queryBuilder->expr()->eq('t3ver_wsid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT))
180 )
181 ->orderBy('sorting')
182 ->execute()
183 ->fetch();
184
185 if (empty($rootPage)) {
186 return 0;
187 }
188
189 return (int)$rootPage['uid'];
190 }
191
192 /**
193 * Gets the current page ID from the first created root template.
194 *
195 * @return int the page UID, will be 0 if none has been set
196 */
197 protected function getCurrentPageIdFromRootTemplate(): int
198 {
199 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
200 ->getQueryBuilderForTable('sys_template');
201
202 $queryBuilder
203 ->getRestrictions()
204 ->removeAll()
205 ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
206 ->add(GeneralUtility::makeInstance(HiddenRestriction::class));
207
208 $rootTemplate = $queryBuilder
209 ->select('pid')
210 ->from('sys_template')
211 ->where(
212 $queryBuilder->expr()->eq('root', $queryBuilder->createNamedParameter(1, \PDO::PARAM_INT))
213 )
214 ->orderBy('crdate')
215 ->execute()
216 ->fetch();
217
218 if (empty($rootTemplate)) {
219 return 0;
220 }
221
222 return (int)$rootTemplate['pid'];
223 }
224
225 /**
226 * Returns the default backend storage pid
227 *
228 * @return int
229 */
230 public function getDefaultBackendStoragePid(): int
231 {
232 // todo: fallback to parent::getDefaultBackendStoragePid() would make sense here.
233 return $this->getCurrentPageId();
234 }
235
236 /**
237 * We need to set some default request handler if the framework configuration
238 * could not be loaded; to make sure Extbase also works in Backend modules
239 * in all contexts.
240 *
241 * @param array $frameworkConfiguration
242 * @return array
243 */
244 protected function getContextSpecificFrameworkConfiguration(array $frameworkConfiguration): array
245 {
246 return $frameworkConfiguration;
247 }
248
249 /**
250 * Returns an array of storagePIDs that are below a list of storage pids.
251 *
252 * @param int[] $storagePids Storage PIDs to start at; multiple PIDs possible as comma-separated list
253 * @param int $recursionDepth Maximum number of levels to search, 0 to disable recursive lookup
254 * @return int[] Uid list including the start $storagePids
255 */
256 protected function getRecursiveStoragePids(array $storagePids, int $recursionDepth = 0): array
257 {
258 if ($recursionDepth <= 0) {
259 return $storagePids;
260 }
261 $permsClause = QueryHelper::stripLogicalOperatorPrefix(
262 $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW)
263 );
264 $recursiveStoragePids = [];
265 foreach ($storagePids as $startPid) {
266 $startPid = abs($startPid);
267 $recursiveStoragePids = array_merge(
268 $recursiveStoragePids,
269 [ $startPid ],
270 $this->getPageChildrenRecursive($startPid, $recursionDepth, 0, $permsClause)
271 );
272 }
273 return array_unique($recursiveStoragePids);
274 }
275
276 /**
277 * Recursively fetch all children of a given page
278 *
279 * @param int $pid uid of the page
280 * @param int $depth
281 * @param int $begin
282 * @param string $permsClause
283 * @return int[] List of child row $uid's
284 */
285 protected function getPageChildrenRecursive(int $pid, int $depth, int $begin, string $permsClause): array
286 {
287 $children = [];
288 if ($pid && $depth > 0) {
289 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
290 $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
291 $statement = $queryBuilder->select('uid')
292 ->from('pages')
293 ->where(
294 $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pid, \PDO::PARAM_INT)),
295 $queryBuilder->expr()->eq('sys_language_uid', 0),
296 $permsClause
297 )
298 ->orderBy('uid')
299 ->execute();
300 while ($row = $statement->fetch()) {
301 if ($begin <= 0) {
302 $children[] = (int)$row['uid'];
303 }
304 if ($depth > 1) {
305 $theSubList = $this->getPageChildrenRecursive((int)$row['uid'], $depth - 1, $begin - 1, $permsClause);
306 $children = array_merge($children, $theSubList);
307 }
308 }
309 }
310 return $children;
311 }
312
313 /**
314 * @return BackendUserAuthentication
315 */
316 protected function getBackendUser(): BackendUserAuthentication
317 {
318 return $GLOBALS['BE_USER'];
319 }
320 }