‪TYPO3CMS  ‪main
PageTsConfigIncludesController.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\Container\ContainerInterface;
21 use Psr\Http\Message\ResponseFactoryInterface;
22 use Psr\Http\Message\ResponseInterface;
23 use Psr\Http\Message\ServerRequestInterface;
24 use Psr\Http\Message\StreamFactoryInterface;
30 use TYPO3\CMS\Backend\Utility\BackendUtility;
46 use TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeSyntaxScannerVisitor;
47 use TYPO3\CMS\Core\TypoScript\Tokenizer\LosslessTokenizer;
48 
54 #[AsController]
56 {
57  public function ‪__construct(
58  private readonly ContainerInterface $container,
59  private readonly ‪UriBuilder $uriBuilder,
60  private readonly ‪ModuleTemplateFactory $moduleTemplateFactory,
61  private readonly ‪TsConfigTreeBuilder $tsConfigTreeBuilder,
62  private readonly ResponseFactoryInterface $responseFactory,
63  private readonly StreamFactoryInterface $streamFactory,
64  ) {}
65 
66  public function ‪indexAction(ServerRequestInterface $request): ResponseInterface
67  {
68  $backendUser = $this->‪getBackendUser();
69  $languageService = $this->‪getLanguageService();
70 
71  $queryParams = $request->getQueryParams();
72  $parsedBody = $request->getParsedBody();
73 
74  $currentModule = $request->getAttribute('module');
75  $currentModuleIdentifier = $currentModule->getIdentifier();
76  $moduleData = $request->getAttribute('moduleData');
77 
78  $pageUid = (int)($queryParams['id'] ?? 0);
79  $pageRecord = BackendUtility::readPageAccess($pageUid, '1=1') ?: [];
80  if (empty($pageRecord)) {
81  // Redirect to records overview if page could not be determined.
82  // Edge case if page has been removed meanwhile.
83  BackendUtility::setUpdateSignal('updatePageTree');
84  return new ‪RedirectResponse($this->uriBuilder->buildUriFromRoute('pagetsconfig_pages'));
85  }
86 
87  // Prepare site constants if any
88  $site = $request->getAttribute('site');
89  $siteSettingsTree = new ‪RootInclude();
90  $siteSettingsFlat = [];
91  if ($site instanceof ‪Site && !$site->‪getSettings()->isEmpty()) {
92  $siteSettings = $site->getSettings()->getAllFlat();
93  $siteConstants = '';
94  foreach ($siteSettings as $nodeIdentifier => $value) {
95  $siteConstants .= $nodeIdentifier . ' = ' . $value . LF;
96  }
97  $siteSettingsNode = new ‪SiteInclude();
98  $siteSettingsNode->setName('Site constants settings of site "' . $site->getIdentifier() . '"');
99  $siteSettingsNode->setLineStream((new LosslessTokenizer())->tokenize($siteConstants));
100  $siteSettingsTree->addChild($siteSettingsNode);
101  $siteSettingsTree->setIdentifier('pageTsConfig-siteSettingsTree');
103  $astBuilderVisitor = $this->container->get(IncludeTreeAstBuilderVisitor::class);
104  $includeTreeTraverser = new ‪IncludeTreeTraverser();
105  $includeTreeTraverser->traverse($siteSettingsTree, [$astBuilderVisitor]);
106  $siteSettingsAst = $astBuilderVisitor->getAst();
107  $siteSettingsFlat = $siteSettingsAst->flatten();
108  }
109 
110  // Base page TSconfig tree
111  $rootLine = BackendUtility::BEgetRootLine($pageUid, '', true);
112  ksort($rootLine);
113  $pageTsConfigTree = $this->tsConfigTreeBuilder->getPagesTsConfigTree($rootLine, new LosslessTokenizer());
114 
115  // Overload tree with user TSconfig if any
116  $userTsConfig = $backendUser->getUserTsConfig();
117  if ($userTsConfig === null) {
118  throw new \RuntimeException('User TSconfig not initialized', 1675535278);
119  }
120  $userTsConfigAst = $userTsConfig->getUserTsConfigTree();
121  $userTsConfigPageOverrides = '';
122  // @todo: Ugly, similar in PageTsConfigFactory.
123  $userTsConfigFlat = $userTsConfigAst->flatten();
124  foreach ($userTsConfigFlat as $userTsConfigIdentifier => $userTsConfigValue) {
125  if (str_starts_with($userTsConfigIdentifier, 'page.')) {
126  $userTsConfigPageOverrides .= substr($userTsConfigIdentifier, 5) . ' = ' . $userTsConfigValue . chr(10);
127  }
128  }
129  if (!empty($userTsConfigPageOverrides)) {
130  $includeNode = new ‪TsConfigInclude();
131  $includeNode->setName('pageTsConfig-overrides-by-userTsConfig');
132  $includeNode->setLineStream((new LosslessTokenizer())->tokenize($userTsConfigPageOverrides));
133  $pageTsConfigTree->addChild($includeNode);
134  }
135  $pageTsConfigTree->setIdentifier('pageTsConfig-pageTsConfigTree');
136 
137  // Set enabled conditions in page TSconfig include tree and let it handle constant substitutions in page TSconfig conditions.
138  $treeTraverser = new ‪IncludeTreeTraverser();
139  $treeTraverserVisitors = [];
140  $syntaxScannerVisitor = new IncludeTreeSyntaxScannerVisitor();
141  $treeTraverserVisitors[] = $syntaxScannerVisitor;
142  $pageTsConfigConditions = $this->‪handleToggledPageTsConfigConditions($pageTsConfigTree, $moduleData, $parsedBody, $siteSettingsFlat);
143  $conditionEnforcerVisitor = new ‪IncludeTreeConditionEnforcerVisitor();
144  $conditionEnforcerVisitor->setEnabledConditions(array_column(array_filter($pageTsConfigConditions, static fn(array $condition): bool => (bool)$condition['active']), 'value'));
145  $treeTraverserVisitors[] = $conditionEnforcerVisitor;
146  $treeTraverser->traverse($pageTsConfigTree, $treeTraverserVisitors);
147 
148  $view = $this->moduleTemplateFactory->create($request);
149  $view->setTitle($languageService->sL($currentModule->getTitle()), $pageRecord['title'] ?? ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] ?? '');
150  $view->getDocHeaderComponent()->setMetaInformation($pageRecord);
151  $this->‪addShortcutButtonToDocHeader($view, $currentModuleIdentifier, $pageRecord, $pageUid);
152  $view->makeDocHeaderModuleMenu(['id' => $pageUid]);
153  $view->assignMultiple([
154  'pageUid' => $pageUid,
155  'pageTitle' => $pageRecord['title'] ?? ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] ?? '',
156  'siteSettingsTree' => $siteSettingsTree,
157  'pageTsConfigTree' => $pageTsConfigTree,
158  'pageTsConfigConditions' => $pageTsConfigConditions,
159  'pageTsConfigConditionsActiveCount' => count(array_filter($pageTsConfigConditions, static fn(array $condition): bool => (bool)$condition['active'])),
160  'syntaxErrors' => $syntaxScannerVisitor->getErrors(),
161  'syntaxErrorCount' => count($syntaxScannerVisitor->getErrors()),
162  ]);
163  return $view->renderResponse('PageTsConfig/Includes');
164  }
165 
166  public function ‪sourceAction(ServerRequestInterface $request): ResponseInterface
167  {
168  $backendUser = $this->‪getBackendUser();
169  $queryParams = $request->getQueryParams();
170  $pageUid = (int)($queryParams['id'] ?? 0);
171  $type = $queryParams['includeType'] ?? null;
172  $includeIdentifier = $queryParams['identifier'] ?? null;
173  if ($pageUid === 0 || $includeIdentifier === null || !in_array($type, ['constants', 'setup'], true)) {
174  return $this->responseFactory->createResponse(400);
175  }
176 
177  if ($type === 'constants') {
178  // Prepare site constants if any
179  $site = $request->getAttribute('site');
180  $includeTree = new ‪RootInclude();
181  if ($site instanceof ‪Site && !$site->‪getSettings()->isEmpty()) {
182  $siteSettings = $site->getSettings()->getAllFlat();
183  $siteConstants = '';
184  foreach ($siteSettings as $nodeIdentifier => $value) {
185  $siteConstants .= $nodeIdentifier . ' = ' . $value . LF;
186  }
187  $siteSettingsNode = new ‪SiteInclude();
188  $siteSettingsNode->setName('Site constants settings of site "' . $site->getIdentifier() . '"');
189  $siteSettingsNode->setLineStream((new LosslessTokenizer())->tokenize($siteConstants));
190  $includeTree->addChild($siteSettingsNode);
191  $includeTree->setIdentifier('pageTsConfig-siteSettingsTree');
192  }
193  } else {
194  // Base page TSconfig tree
195  $rootLine = BackendUtility::BEgetRootLine($pageUid, '', true);
196  ksort($rootLine);
197  $includeTree = $this->tsConfigTreeBuilder->getPagesTsConfigTree($rootLine, new LosslessTokenizer());
198 
199  // Overload tree with user TSconfig if any
200  $userTsConfig = $backendUser->getUserTsConfig();
201  if ($userTsConfig === null) {
202  throw new \RuntimeException('UserTsConfig not initialized', 1675535279);
203  }
204  $userTsConfigAst = $userTsConfig->getUserTsConfigTree();
205  $userTsConfigPageOverrides = '';
206  // @todo: Ugly, similar in PageTsConfigFactory.
207  $userTsConfigFlat = $userTsConfigAst->flatten();
208  foreach ($userTsConfigFlat as $userTsConfigIdentifier => $userTsConfigValue) {
209  if (str_starts_with($userTsConfigIdentifier, 'page.')) {
210  $userTsConfigPageOverrides .= substr($userTsConfigIdentifier, 5) . ' = ' . $userTsConfigValue . chr(10);
211  }
212  }
213  if (!empty($userTsConfigPageOverrides)) {
214  $includeNode = new ‪TsConfigInclude();
215  $includeNode->setName('pageTsConfig-overrides-by-userTsConfig');
216  $includeNode->setLineStream((new LosslessTokenizer())->tokenize($userTsConfigPageOverrides));
217  $includeTree->addChild($includeNode);
218  }
219  $includeTree->setIdentifier('pageTsConfig-pageTsConfigTree');
220  }
221 
222  $nodeFinderVisitor = new ‪IncludeTreeNodeFinderVisitor();
223  $nodeFinderVisitor->setNodeIdentifier($includeIdentifier);
224  $treeTraverser = new ‪IncludeTreeTraverser();
225  $treeTraverser->traverse($includeTree, [$nodeFinderVisitor]);
226  $lineStream = $nodeFinderVisitor->getFoundNode()?->getLineStream();
227  if ($lineStream === null) {
228  return $this->responseFactory->createResponse(400);
229  }
230 
231  return $this->responseFactory
232  ->createResponse()
233  ->withHeader('Content-Type', 'text/plain')
234  ->withBody($this->streamFactory->createStream((string)$lineStream));
235  }
236 
237  public function ‪sourceWithIncludesAction(ServerRequestInterface $request): ResponseInterface
238  {
239  $backendUser = $this->‪getBackendUser();
240  $queryParams = $request->getQueryParams();
241  $pageUid = (int)($queryParams['id'] ?? 0);
242  $type = $queryParams['includeType'] ?? null;
243  $includeIdentifier = $queryParams['identifier'] ?? null;
244  if ($pageUid === 0 || $includeIdentifier === null || !in_array($type, ['constants', 'setup'], true)) {
245  return $this->responseFactory->createResponse(400);
246  }
247 
248  if ($type === 'constants') {
249  // Prepare site constants if any
250  $site = $request->getAttribute('site');
251  $includeTree = new ‪RootInclude();
252  if ($site instanceof ‪Site && !$site->‪getSettings()->isEmpty()) {
253  $siteSettings = $site->getSettings()->getAllFlat();
254  $siteConstants = '';
255  foreach ($siteSettings as $nodeIdentifier => $value) {
256  $siteConstants .= $nodeIdentifier . ' = ' . $value . LF;
257  }
258  $siteSettingsNode = new ‪SiteInclude();
259  $siteSettingsNode->setName('Site constants settings of site "' . $site->getIdentifier() . '"');
260  $siteSettingsNode->setLineStream((new LosslessTokenizer())->tokenize($siteConstants));
261  $includeTree->addChild($siteSettingsNode);
262  $includeTree->setIdentifier('pageTsConfig-siteSettingsTree');
263  }
264  } else {
265  // Base page TSconfig tree
266  $rootLine = BackendUtility::BEgetRootLine($pageUid, '', true);
267  ksort($rootLine);
268  $includeTree = $this->tsConfigTreeBuilder->getPagesTsConfigTree($rootLine, new LosslessTokenizer());
269 
270  // Overload tree with user TSconfig if any
271  $userTsConfig = $backendUser->getUserTsConfig();
272  if ($userTsConfig === null) {
273  throw new \RuntimeException('UserTsConfig not initialized', 1675535280);
274  }
275  $userTsConfigAst = $userTsConfig->getUserTsConfigTree();
276  $userTsConfigPageOverrides = '';
277  // @todo: Ugly, similar in PageTsConfigFactory.
278  $userTsConfigFlat = $userTsConfigAst->flatten();
279  foreach ($userTsConfigFlat as $userTsConfigIdentifier => $userTsConfigValue) {
280  if (str_starts_with($userTsConfigIdentifier, 'page.')) {
281  $userTsConfigPageOverrides .= substr($userTsConfigIdentifier, 5) . ' = ' . $userTsConfigValue . chr(10);
282  }
283  }
284  if (!empty($userTsConfigPageOverrides)) {
285  $includeNode = new ‪TsConfigInclude();
286  $includeNode->setName('pageTsConfig-overrides-by-userTsConfig');
287  $includeNode->setLineStream((new LosslessTokenizer())->tokenize($userTsConfigPageOverrides));
288  $includeTree->addChild($includeNode);
289  }
290  $includeTree->setIdentifier('pageTsConfig-pageTsConfigTree');
291  }
292 
293  $sourceAggregatorVisitor = new ‪IncludeTreeSourceAggregatorVisitor();
294  $sourceAggregatorVisitor->setStartNodeIdentifier($includeIdentifier);
295  $treeTraverser = new ‪IncludeTreeTraverser();
296  $treeTraverser->traverse($includeTree, [$sourceAggregatorVisitor]);
297  $source = $sourceAggregatorVisitor->getSource();
298 
299  return $this->responseFactory
300  ->createResponse()
301  ->withHeader('Content-Type', 'text/plain')
302  ->withBody($this->streamFactory->createStream($source));
303  }
304 
310  private function ‪handleToggledPageTsConfigConditions(‪RootInclude $pageTsConfigTree, ‪ModuleData $moduleData, ?array $parsedBody, array $flattenedConstants): array
311  {
312  $treeTraverser = new ‪IncludeTreeTraverser();
313  $treeTraverserVisitors = [];
314  $setupConditionConstantSubstitutionVisitor = new ‪IncludeTreeSetupConditionConstantSubstitutionVisitor();
315  $setupConditionConstantSubstitutionVisitor->setFlattenedConstants($flattenedConstants);
316  $treeTraverserVisitors[] = $setupConditionConstantSubstitutionVisitor;
317  $conditionAggregatorVisitor = new ‪IncludeTreeConditionAggregatorVisitor();
318  $treeTraverserVisitors[] = $conditionAggregatorVisitor;
319  $treeTraverser->traverse($pageTsConfigTree, $treeTraverserVisitors);
320  $pageTsConfigConditions = $conditionAggregatorVisitor->getConditions();
321  $conditionsFromPost = $parsedBody['pageTsConfigConditions'] ?? [];
322  $conditionsFromModuleData = array_flip((array)$moduleData->‪get('pageTsConfigConditions'));
323  $conditions = [];
324  foreach ($pageTsConfigConditions as $condition) {
325  $conditionHash = hash('xxh3', $condition['value']);
326  $conditionActive = array_key_exists($conditionHash, $conditionsFromModuleData);
327  // Note we're not feeding the post values directly to module data, but filter
328  // them through available conditions to prevent polluting module data with
329  // manipulated post values.
330  if (($conditionsFromPost[$conditionHash] ?? null) === '0') {
331  unset($conditionsFromModuleData[$conditionHash]);
332  $conditionActive = false;
333  } elseif (($conditionsFromPost[$conditionHash] ?? null) === '1') {
334  $conditionsFromModuleData[$conditionHash] = true;
335  $conditionActive = true;
336  }
337  $conditions[] = [
338  'value' => $condition['value'],
339  'originalValue' => $condition['originalValue'],
340  'hash' => $conditionHash,
341  'active' => $conditionActive,
342  ];
343  }
344  if ($conditionsFromPost) {
345  $moduleData->‪set('pageTsConfigConditions', array_keys($conditionsFromModuleData));
346  $this->‪getBackendUser()->pushModuleData($moduleData->‪getModuleIdentifier(), $moduleData->‪toArray());
347  }
348  return $conditions;
349  }
350 
351  private function ‪addShortcutButtonToDocHeader(‪ModuleTemplate $view, string $moduleIdentifier, array $pageInfo, int $pageUid): void
352  {
353  $languageService = $this->‪getLanguageService();
354  $buttonBar = $view->‪getDocHeaderComponent()->getButtonBar();
355  $shortcutTitle = sprintf(
356  '%s: %s [%d]',
357  $languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang_pagetsconfig.xlf:module.pagetsconfig_includes'),
358  BackendUtility::getRecordTitle('pages', $pageInfo),
359  $pageUid
360  );
361  $shortcutButton = $buttonBar->makeShortcutButton()
362  ->setRouteIdentifier($moduleIdentifier)
363  ->setDisplayName($shortcutTitle)
364  ->setArguments(['id' => $pageUid]);
365  $buttonBar->addButton($shortcutButton);
366  }
367 
369  {
370  return ‪$GLOBALS['LANG'];
371  }
372 
374  {
375  return ‪$GLOBALS['BE_USER'];
376  }
377 }
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeSetupConditionConstantSubstitutionVisitor
Definition: IncludeTreeSetupConditionConstantSubstitutionVisitor.php:40
‪TYPO3\CMS\Backend\Controller\PageTsConfig\PageTsConfigIncludesController
Definition: PageTsConfigIncludesController.php:56
‪TYPO3\CMS\Backend\Controller\PageTsConfig
Definition: PageTsConfigActiveController.php:18
‪TYPO3\CMS\Backend\Template\ModuleTemplateFactory
Definition: ModuleTemplateFactory.php:35
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Traverser\IncludeTreeTraverser
Definition: IncludeTreeTraverser.php:30
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeConditionEnforcerVisitor
Definition: IncludeTreeConditionEnforcerVisitor.php:32
‪TYPO3\CMS\Backend\Module\ModuleData\toArray
‪toArray()
Definition: ModuleData.php:122
‪TYPO3\CMS\Backend\Module\ModuleData
Definition: ModuleData.php:30
‪TYPO3\CMS\Backend\Module\ModuleData\get
‪get(string $propertyName, mixed $default=null)
Definition: ModuleData.php:56
‪TYPO3\CMS\Backend\Controller\PageTsConfig\PageTsConfigIncludesController\sourceWithIncludesAction
‪sourceWithIncludesAction(ServerRequestInterface $request)
Definition: PageTsConfigIncludesController.php:237
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeAstBuilderVisitor
Definition: IncludeTreeAstBuilderVisitor.php:43
‪TYPO3\CMS\Backend\Controller\PageTsConfig\PageTsConfigIncludesController\sourceAction
‪sourceAction(ServerRequestInterface $request)
Definition: PageTsConfigIncludesController.php:166
‪TYPO3\CMS\Backend\Controller\PageTsConfig\PageTsConfigIncludesController\getBackendUser
‪getBackendUser()
Definition: PageTsConfigIncludesController.php:373
‪TYPO3\CMS\Backend\Template\ModuleTemplate
Definition: ModuleTemplate.php:46
‪TYPO3\CMS\Core\Site\Entity\Site
Definition: Site.php:42
‪TYPO3\CMS\Backend\Controller\PageTsConfig\PageTsConfigIncludesController\addShortcutButtonToDocHeader
‪addShortcutButtonToDocHeader(ModuleTemplate $view, string $moduleIdentifier, array $pageInfo, int $pageUid)
Definition: PageTsConfigIncludesController.php:351
‪TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode\SiteInclude
Definition: SiteInclude.php:25
‪TYPO3\CMS\Backend\Controller\PageTsConfig\PageTsConfigIncludesController\indexAction
‪indexAction(ServerRequestInterface $request)
Definition: PageTsConfigIncludesController.php:66
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeNodeFinderVisitor
Definition: IncludeTreeNodeFinderVisitor.php:31
‪TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode\TsConfigInclude
Definition: TsConfigInclude.php:25
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:44
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeSourceAggregatorVisitor
Definition: IncludeTreeSourceAggregatorVisitor.php:36
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Backend\Controller\PageTsConfig\PageTsConfigIncludesController\__construct
‪__construct(private readonly ContainerInterface $container, private readonly UriBuilder $uriBuilder, private readonly ModuleTemplateFactory $moduleTemplateFactory, private readonly TsConfigTreeBuilder $tsConfigTreeBuilder, private readonly ResponseFactoryInterface $responseFactory, private readonly StreamFactoryInterface $streamFactory,)
Definition: PageTsConfigIncludesController.php:57
‪TYPO3\CMS\Backend\Module\ModuleData\set
‪set(string $propertyName, mixed $value)
Definition: ModuleData.php:66
‪TYPO3\CMS\Core\Http\RedirectResponse
Definition: RedirectResponse.php:30
‪TYPO3\CMS\Backend\Controller\PageTsConfig\PageTsConfigIncludesController\getLanguageService
‪getLanguageService()
Definition: PageTsConfigIncludesController.php:368
‪TYPO3\CMS\Backend\Template\ModuleTemplate\getDocHeaderComponent
‪getDocHeaderComponent()
Definition: ModuleTemplate.php:181
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Backend\Controller\PageTsConfig\PageTsConfigIncludesController\handleToggledPageTsConfigConditions
‪handleToggledPageTsConfigConditions(RootInclude $pageTsConfigTree, ModuleData $moduleData, ?array $parsedBody, array $flattenedConstants)
Definition: PageTsConfigIncludesController.php:310
‪TYPO3\CMS\Backend\Attribute\AsController
Definition: AsController.php:25
‪TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode\RootInclude
Definition: RootInclude.php:27
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:46
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeConditionAggregatorVisitor
Definition: IncludeTreeConditionAggregatorVisitor.php:32
‪TYPO3\CMS\Core\Site\Entity\Site\getSettings
‪getSettings()
Definition: Site.php:321
‪TYPO3\CMS\Backend\Module\ModuleData\getModuleIdentifier
‪getModuleIdentifier()
Definition: ModuleData.php:51
‪TYPO3\CMS\Core\TypoScript\IncludeTree\TsConfigTreeBuilder
Definition: TsConfigTreeBuilder.php:48