‪TYPO3CMS  ‪main
BackendConfigurationManager.php
Go to the documentation of this file.
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 
19 
20 use Psr\Http\Message\ServerRequestInterface;
34 use TYPO3\CMS\Core\TypoScript\FrontendTypoScriptFactory;
43 
69 {
73  protected array ‪$configuration = [];
74 
78  protected ?string ‪$extensionName = null;
79 
83  protected ?string ‪$pluginName = null;
84 
88  protected ?int ‪$currentPageId = null;
89 
94  private ?ServerRequestInterface ‪$request = null;
95 
96  public function ‪__construct(
97  private readonly ‪TypoScriptService $typoScriptService,
98  private readonly ‪PhpFrontend $typoScriptCache,
99  private readonly ‪FrontendInterface $runtimeCache,
100  private readonly ‪SysTemplateRepository $sysTemplateRepository,
101  private readonly ‪SiteFinder $siteFinder,
102  private readonly FrontendTypoScriptFactory $frontendTypoScriptFactory,
103  ) {}
104 
105  public function ‪setRequest(ServerRequestInterface ‪$request): void
106  {
107  $this->request = ‪$request;
108  }
109 
116  public function ‪setConfiguration(array ‪$configuration = []): void
117  {
118  $this->extensionName = ‪$configuration['extensionName'] ?? null;
119  $this->pluginName = ‪$configuration['pluginName'] ?? null;
120  $this->configuration = $this->typoScriptService->convertTypoScriptArrayToPlainArray(‪$configuration);
121  }
122 
133  public function ‪getConfiguration(?string ‪$extensionName = null, ?string ‪$pluginName = null): array
134  {
135  $frameworkConfiguration = $this->‪getExtbaseConfiguration();
136  if (!isset($frameworkConfiguration['persistence']['storagePid'])) {
137  $frameworkConfiguration['persistence']['storagePid'] = $this->‪getCurrentPageId();
138  }
139  // only merge $this->configuration and override controller configuration when retrieving configuration of the current plugin
140  if ($extensionName === null || ‪$extensionName === $this->extensionName && ‪$pluginName === $this->pluginName) {
141  $pluginConfiguration = $this->‪getPluginConfiguration((string)$this->extensionName, (string)$this->pluginName);
142  ArrayUtility::mergeRecursiveWithOverrule($pluginConfiguration, $this->configuration);
143  $pluginConfiguration['controllerConfiguration'] = [];
144  } else {
145  $pluginConfiguration = $this->‪getPluginConfiguration((string)$extensionName, (string)‪$pluginName);
146  $pluginConfiguration['controllerConfiguration'] = [];
147  }
148  ArrayUtility::mergeRecursiveWithOverrule($frameworkConfiguration, $pluginConfiguration);
149 
150  if (!empty($frameworkConfiguration['persistence']['storagePid'])) {
151  if (is_array($frameworkConfiguration['persistence']['storagePid'])) {
152  // We simulate the frontend to enable the use of cObjects in
153  // stdWrap. We then convert the configuration to normal TypoScript
154  // and apply the stdWrap to the storagePid
155  // Use makeInstance here since extbase Bootstrap always setContentObject(null) in Backend, no need to call getContentObject().
156  ‪FrontendSimulatorUtility::simulateFrontendEnvironment(GeneralUtility::makeInstance(ContentObjectRenderer::class));
157  $conf = $this->typoScriptService->convertPlainArrayToTypoScriptArray($frameworkConfiguration['persistence']);
158  $frameworkConfiguration['persistence']['storagePid'] = ‪$GLOBALS['TSFE']->cObj->stdWrapValue('storagePid', $conf);
160  }
161 
162  if (!empty($frameworkConfiguration['persistence']['recursive'])) {
163  $storagePids = $this->‪getRecursiveStoragePids(
164  ‪GeneralUtility::intExplode(',', (string)($frameworkConfiguration['persistence']['storagePid'] ?? '')),
165  (int)$frameworkConfiguration['persistence']['recursive']
166  );
167  $frameworkConfiguration['persistence']['storagePid'] = implode(',', $storagePids);
168  }
169  }
170  return $frameworkConfiguration;
171  }
172 
178  public function ‪getTypoScriptSetup(): array
179  {
180  $pageId = $this->‪getCurrentPageId();
181 
182  $cacheIdentifier = 'extbase-backend-typoscript-pageId-' . $pageId;
183  $setupArray = $this->runtimeCache->get($cacheIdentifier);
184  if (is_array($setupArray)) {
185  return $setupArray;
186  }
187 
188  $site = $this->request?->getAttribute('site');
189  if (($site === null || $site instanceof ‪NullSite) && $pageId > 0) {
190  // Due to the weird magic of getting the pid of the first root template when
191  // not having a pageId (extbase BE modules without page tree / no page selected),
192  // we also have no proper site in this case.
193  // So we try to get the site for this pageId. This way, site settings for this
194  // first TS page are turned into constants and can be used in setup and setup
195  // conditions.
196  try {
197  $site = $this->siteFinder->getSiteByPageId($pageId);
198  } catch (‪SiteNotFoundException) {
199  // Keep null / NullSite when no site could be determined for whatever reason.
200  }
201  }
202  if ($site === null) {
203  // If still no site object, have NullSite (usually pid 0).
204  $site = new ‪NullSite();
205  }
206 
207  $rootLine = [];
208  $sysTemplateFakeRow = [
209  'uid' => 0,
210  'pid' => 0,
211  'title' => 'Fake sys_template row to force extension statics loading',
212  'root' => 1,
213  'clear' => 3,
214  'include_static_file' => '',
215  'basedOn' => '',
216  'includeStaticAfterBasedOn' => 0,
217  'static_file_mode' => false,
218  'constants' => '',
219  'config' => '',
220  'deleted' => 0,
221  'hidden' => 0,
222  'starttime' => 0,
223  'endtime' => 0,
224  'sorting' => 0,
225  ];
226  if ($pageId > 0) {
227  $rootLine = GeneralUtility::makeInstance(RootlineUtility::class, $pageId)->get();
228  $sysTemplateRows = $this->sysTemplateRepository->getSysTemplateRowsByRootline($rootLine, $this->request);
229  ksort($rootLine);
230  }
231  if (empty($sysTemplateRows)) {
232  // If there is no page (pid 0 only), or if the first 'is_siteroot' site has no sys_template record,
233  // then we "fake" a sys_template row: This triggers inclusion of 'global' and 'extension static' TypoScript.
234  $sysTemplateRows[] = $sysTemplateFakeRow;
235  }
236 
237  $expressionMatcherVariables = [
238  'request' => ‪$this->request,
239  'pageId' => $pageId,
240  'page' => !empty($rootLine) ? $rootLine[array_key_first($rootLine)] : [],
241  'fullRootLine' => $rootLine,
242  'site' => $site,
243  ];
244 
245  $typoScript = $this->frontendTypoScriptFactory->createSettingsAndSetupConditions($site, $sysTemplateRows, $expressionMatcherVariables, $this->typoScriptCache);
246  $typoScript = $this->frontendTypoScriptFactory->createSetupConfigOrFullSetup(true, $typoScript, $site, $sysTemplateRows, $expressionMatcherVariables, '0', $this->typoScriptCache, null);
247  $setupArray = $typoScript->getSetupArray();
248  $this->runtimeCache->set($cacheIdentifier, $setupArray);
249  return $setupArray;
250  }
251 
255  protected function ‪getExtbaseConfiguration(): array
256  {
257  $setup = $this->‪getTypoScriptSetup();
258  $extbaseConfiguration = [];
259  if (isset($setup['config.']['tx_extbase.'])) {
260  $extbaseConfiguration = $this->typoScriptService->convertTypoScriptArrayToPlainArray($setup['config.']['tx_extbase.']);
261  }
262  return $extbaseConfiguration;
263  }
264 
271  protected function ‪getPluginConfiguration(string ‪$extensionName, string ‪$pluginName = null): array
272  {
273  $setup = $this->‪getTypoScriptSetup();
274  $pluginConfiguration = [];
275  if (is_array($setup['module.']['tx_' . strtolower(‪$extensionName) . '.'] ?? false)) {
276  $pluginConfiguration = $this->typoScriptService->convertTypoScriptArrayToPlainArray($setup['module.']['tx_' . strtolower(‪$extensionName) . '.']);
277  }
278  if (‪$pluginName !== null) {
279  $pluginSignature = strtolower(‪$extensionName . '_' . ‪$pluginName);
280  if (is_array($setup['module.']['tx_' . $pluginSignature . '.'] ?? false)) {
281  $overruleConfiguration = $this->typoScriptService->convertTypoScriptArrayToPlainArray($setup['module.']['tx_' . $pluginSignature . '.']);
282  ArrayUtility::mergeRecursiveWithOverrule($pluginConfiguration, $overruleConfiguration);
283  }
284  }
285  return $pluginConfiguration;
286  }
287 
297  protected function ‪getCurrentPageId(): int
298  {
299  if ($this->currentPageId !== null) {
301  }
302  $this->currentPageId = $this->‪getCurrentPageIdFromRequest();
303  $this->currentPageId = $this->currentPageId ?: $this->‪getCurrentPageIdFromCurrentSiteRoot();
304  $this->currentPageId = $this->currentPageId ?: $this->‪getCurrentPageIdFromRootTemplate();
305  $this->currentPageId = $this->currentPageId ?: 0;
307  }
308 
314  protected function ‪getCurrentPageIdFromRequest(): int
315  {
316  // @todo: This misuses 'id' as a broken convention for pages-uid. The filelist module for instance
317  // uses 'id' as "storage-uid:path", which is only mitigated here by testing the argument
318  // with MU:canBeInterpretedAsInteger().
319  // This is in-line with a similar misuse in BackendModuleValidator.
320  $id = 0;
321  $potentialId = $this->request?->getParsedBody()['id'] ?? $this->request?->getQueryParams()['id'] ?? 0;
322  if (‪MathUtility::canBeInterpretedAsInteger($potentialId) && $potentialId > 0) {
323  $id = (int)$potentialId;
324  }
325  return $id;
326  }
327 
333  protected function ‪getCurrentPageIdFromCurrentSiteRoot(): int
334  {
335  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
336  $queryBuilder->getRestrictions()->removeAll()
337  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
338  ->add(GeneralUtility::makeInstance(HiddenRestriction::class));
339  $rootPage = $queryBuilder
340  ->select('uid')
341  ->from('pages')
342  ->where(
343  $queryBuilder->expr()->eq('is_siteroot', $queryBuilder->createNamedParameter(1, ‪Connection::PARAM_INT)),
344  $queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter(0, ‪Connection::PARAM_INT)),
345  // Only consider live root page IDs, never return a versioned root page ID
346  $queryBuilder->expr()->eq('t3ver_oid', $queryBuilder->createNamedParameter(0, ‪Connection::PARAM_INT)),
347  $queryBuilder->expr()->eq('t3ver_wsid', $queryBuilder->createNamedParameter(0, ‪Connection::PARAM_INT))
348  )
349  ->orderBy('sorting')
350  ->setMaxResults(1)
351  ->executeQuery()
352  ->fetchAssociative();
353  if (empty($rootPage)) {
354  return 0;
355  }
356  return (int)$rootPage['uid'];
357  }
358 
364  protected function ‪getCurrentPageIdFromRootTemplate(): int
365  {
366  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_template');
367  $queryBuilder->getRestrictions()->removeAll()
368  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
369  ->add(GeneralUtility::makeInstance(HiddenRestriction::class));
370  $rootTemplate = $queryBuilder
371  ->select('pid')
372  ->from('sys_template')
373  ->where(
374  $queryBuilder->expr()->eq('root', $queryBuilder->createNamedParameter(1, ‪Connection::PARAM_INT))
375  )
376  ->orderBy('crdate')
377  ->setMaxResults(1)
378  ->executeQuery()
379  ->fetchAssociative();
380  if (empty($rootTemplate)) {
381  return 0;
382  }
383  return (int)$rootTemplate['pid'];
384  }
385 
393  protected function ‪getRecursiveStoragePids(array $storagePids, int $recursionDepth = 0): array
394  {
395  if ($recursionDepth <= 0) {
396  return $storagePids;
397  }
399  $this->‪getBackendUser()->getPagePermsClause(‪Permission::PAGE_SHOW)
400  );
401  $recursiveStoragePids = [];
402  foreach ($storagePids as $startPid) {
403  $startPid = abs($startPid);
404  $recursiveStoragePids = array_merge(
405  $recursiveStoragePids,
406  [ $startPid ],
407  $this->‪getPageChildrenRecursive($startPid, $recursionDepth, 0, $permsClause)
408  );
409  }
410  return array_unique($recursiveStoragePids);
411  }
412 
419  protected function ‪getPageChildrenRecursive(int $pid, int $depth, int $begin, string $permsClause): array
420  {
421  $children = [];
422  if ($pid && $depth > 0) {
423  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
424  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
425  $statement = $queryBuilder->select('uid')
426  ->from('pages')
427  ->where(
428  $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pid, ‪Connection::PARAM_INT)),
429  $queryBuilder->expr()->eq('sys_language_uid', 0),
430  $permsClause
431  )
432  ->orderBy('uid')
433  ->executeQuery();
434  while ($row = $statement->fetchAssociative()) {
435  if ($begin <= 0) {
436  $children[] = (int)$row['uid'];
437  }
438  if ($depth > 1) {
439  $theSubList = $this->‪getPageChildrenRecursive((int)$row['uid'], $depth - 1, $begin - 1, $permsClause);
440  $children = array_merge($children, $theSubList);
441  }
442  }
443  }
444  return $children;
445  }
446 
448  {
449  return ‪$GLOBALS['BE_USER'];
450  }
451 }
‪TYPO3\CMS\Extbase\Utility\FrontendSimulatorUtility
Definition: FrontendSimulatorUtility.php:30
‪TYPO3\CMS\Extbase\Utility\FrontendSimulatorUtility\simulateFrontendEnvironment
‪static simulateFrontendEnvironment(ContentObjectRenderer $cObj=null)
Definition: FrontendSimulatorUtility.php:40
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager\$request
‪ServerRequestInterface $request
Definition: BackendConfigurationManager.php:94
‪TYPO3\CMS\Core\TypoScript\IncludeTree\SysTemplateRepository
Definition: SysTemplateRepository.php:38
‪TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction
Definition: HiddenRestriction.php:27
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:52
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager\getConfiguration
‪array getConfiguration(?string $extensionName=null, ?string $pluginName=null)
Definition: BackendConfigurationManager.php:133
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager
Definition: BackendConfigurationManager.php:69
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager\getCurrentPageId
‪int getCurrentPageId()
Definition: BackendConfigurationManager.php:297
‪TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
Definition: PhpFrontend.php:25
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager\getPluginConfiguration
‪getPluginConfiguration(string $extensionName, string $pluginName=null)
Definition: BackendConfigurationManager.php:271
‪TYPO3\CMS\Core\Utility\RootlineUtility
Definition: RootlineUtility.php:40
‪TYPO3\CMS\Core\Site\Entity\NullSite
Definition: NullSite.php:32
‪TYPO3\CMS\Core\Exception\SiteNotFoundException
Definition: SiteNotFoundException.php:25
‪TYPO3\CMS\Core\Site\SiteFinder
Definition: SiteFinder.php:31
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager\getCurrentPageIdFromRootTemplate
‪int getCurrentPageIdFromRootTemplate()
Definition: BackendConfigurationManager.php:364
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager\$currentPageId
‪int $currentPageId
Definition: BackendConfigurationManager.php:88
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager\getPageChildrenRecursive
‪int[] getPageChildrenRecursive(int $pid, int $depth, int $begin, string $permsClause)
Definition: BackendConfigurationManager.php:419
‪TYPO3\CMS\Core\Type\Bitmask\Permission
Definition: Permission.php:26
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager\$configuration
‪array $configuration
Definition: BackendConfigurationManager.php:73
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager\setRequest
‪setRequest(ServerRequestInterface $request)
Definition: BackendConfigurationManager.php:105
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager\getTypoScriptSetup
‪array getTypoScriptSetup()
Definition: BackendConfigurationManager.php:178
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager\getRecursiveStoragePids
‪int[] getRecursiveStoragePids(array $storagePids, int $recursionDepth=0)
Definition: BackendConfigurationManager.php:393
‪TYPO3\CMS\Core\Database\Query\QueryHelper
Definition: QueryHelper.php:32
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager\getCurrentPageIdFromCurrentSiteRoot
‪int getCurrentPageIdFromCurrentSiteRoot()
Definition: BackendConfigurationManager.php:333
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager\getBackendUser
‪getBackendUser()
Definition: BackendConfigurationManager.php:447
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Core\Type\Bitmask\Permission\PAGE_SHOW
‪const PAGE_SHOW
Definition: Permission.php:35
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager\$pluginName
‪string $pluginName
Definition: BackendConfigurationManager.php:83
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager\$extensionName
‪string $extensionName
Definition: BackendConfigurationManager.php:78
‪TYPO3\CMS\Extbase\Utility\FrontendSimulatorUtility\resetFrontendEnvironment
‪static resetFrontendEnvironment()
Definition: FrontendSimulatorUtility.php:52
‪TYPO3\CMS\Core\TypoScript\TypoScriptService
Definition: TypoScriptService.php:27
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:41
‪TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
Definition: FrontendInterface.php:22
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager\getExtbaseConfiguration
‪getExtbaseConfiguration()
Definition: BackendConfigurationManager.php:255
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager\getCurrentPageIdFromRequest
‪int getCurrentPageIdFromRequest()
Definition: BackendConfigurationManager.php:314
‪TYPO3\CMS\Core\Database\Query\QueryHelper\stripLogicalOperatorPrefix
‪static string stripLogicalOperatorPrefix(string $constraint)
Definition: QueryHelper.php:171
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:26
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
Definition: DeletedRestriction.php:28
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager\setConfiguration
‪setConfiguration(array $configuration=[])
Definition: BackendConfigurationManager.php:116
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
Definition: ContentObjectRenderer.php:102
‪TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager\__construct
‪__construct(private readonly TypoScriptService $typoScriptService, private readonly PhpFrontend $typoScriptCache, private readonly FrontendInterface $runtimeCache, private readonly SysTemplateRepository $sysTemplateRepository, private readonly SiteFinder $siteFinder, private readonly FrontendTypoScriptFactory $frontendTypoScriptFactory,)
Definition: BackendConfigurationManager.php:96
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Extbase\Configuration
Definition: BackendConfigurationManager.php:18
‪TYPO3\CMS\Core\Utility\GeneralUtility\intExplode
‪static list< int > intExplode(string $delimiter, string $string, bool $removeEmptyValues=false)
Definition: GeneralUtility.php:756