‪TYPO3CMS  ‪main
TemplateAnalyzerController.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\ResponseFactoryInterface;
21 use Psr\Http\Message\ResponseInterface;
22 use Psr\Http\Message\ServerRequestInterface;
23 use Psr\Http\Message\StreamFactoryInterface;
28 use TYPO3\CMS\Backend\Utility\BackendUtility;
41 use TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeSyntaxScannerVisitor;
45 
52 #[AsController]
54 {
55  public function ‪__construct(
56  private readonly ‪ModuleTemplateFactory $moduleTemplateFactory,
57  private readonly ‪SysTemplateRepository $sysTemplateRepository,
58  private readonly ‪IncludeTreeTraverser $treeTraverser,
59  private readonly ‪ConditionVerdictAwareIncludeTreeTraverser $treeTraverserConditionVerdictAware,
60  private readonly ‪SysTemplateTreeBuilder $treeBuilder,
61  private readonly ‪LosslessTokenizer $losslessTokenizer,
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  if ($pageUid === 0) {
80  // Redirect to template record overview if on page 0.
81  return new ‪RedirectResponse($this->uriBuilder->buildUriFromRoute('web_typoscript_recordsoverview'));
82  }
83  $pageRecord = BackendUtility::readPageAccess($pageUid, '1=1') ?: [];
84  if (empty($pageRecord)) {
85  // Redirect to records overview if page could not be determined.
86  // Edge case if page has been removed meanwhile.
87  BackendUtility::setUpdateSignal('updatePageTree');
88  return new ‪RedirectResponse($this->uriBuilder->buildUriFromRoute('web_typoscript_recordsoverview'));
89  }
90 
91  // Template selection handling for this page
92  $allTemplatesOnPage = $this->‪getAllTemplateRecordsOnPage($pageUid);
93  $selectedTemplateFromModuleData = (array)$moduleData->get('selectedTemplatePerPage');
94  $selectedTemplateUid = (int)($parsedBody['selectedTemplate'] ?? $selectedTemplateFromModuleData[$pageUid] ?? 0);
95  if (!in_array($selectedTemplateUid, array_column($allTemplatesOnPage, 'uid'))) {
96  $selectedTemplateUid = (int)($allTemplatesOnPage[0]['uid'] ?? 0);
97  }
98  if (($moduleData->get('selectedTemplatePerPage')[$pageUid] ?? 0) !== $selectedTemplateUid) {
99  $selectedTemplateFromModuleData[$pageUid] = $selectedTemplateUid;
100  $moduleData->set('selectedTemplatePerPage', $selectedTemplateFromModuleData);
101  $backendUser->pushModuleData($currentModuleIdentifier, $moduleData->toArray());
102  }
103  $templateTitle = '';
104  foreach ($allTemplatesOnPage as $templateRow) {
105  if ((int)$templateRow['uid'] === $selectedTemplateUid) {
106  $templateTitle = $templateRow['title'];
107  }
108  }
109 
110  $rootLine = GeneralUtility::makeInstance(RootlineUtility::class, $pageUid)->get();
111 
112  $sysTemplateRows = $this->sysTemplateRepository->getSysTemplateRowsByRootlineWithUidOverride($rootLine, $request, $selectedTemplateUid);
113 
114  // Build the constant include tree
115  $site = $request->getAttribute('site');
116  $constantIncludeTree = $this->treeBuilder->getTreeBySysTemplateRowsAndSite('constants', $sysTemplateRows, $this->losslessTokenizer, $site);
117  $constantIncludeTree->setIdentifier('constants tstemplate includes');
118  // Set enabled conditions in constant include tree
119  $constantConditions = $this->‪handleToggledConstantConditions($constantIncludeTree, $moduleData, $parsedBody);
120  $conditionEnforcerVisitor = GeneralUtility::makeInstance(IncludeTreeConditionEnforcerVisitor::class);
121  $conditionEnforcerVisitor->setEnabledConditions(array_column(array_filter($constantConditions, static fn($condition) => $condition['active']), 'value'));
122  $treeTraverserVisitors = [];
123  $treeTraverserVisitors[] = $conditionEnforcerVisitor;
124  $constantSyntaxScannerVisitor = new IncludeTreeSyntaxScannerVisitor();
125  $treeTraverserVisitors[] = $constantSyntaxScannerVisitor;
126  $this->treeTraverser->traverse($constantIncludeTree, $treeTraverserVisitors);
127  // Build the constant AST and flatten it. Needed for setup include tree to substitute constants in setup conditions.
128  $constantAstBuilderVisitor = GeneralUtility::makeInstance(IncludeTreeAstBuilderVisitor::class);
129  $this->treeTraverserConditionVerdictAware->traverse($constantIncludeTree, [$constantAstBuilderVisitor]);
130  $constantAst = $constantAstBuilderVisitor->getAst();
131  $flattenedConstants = $constantAst->flatten();
132 
133  // Build the setup include tree
134  $setupIncludeTree = $this->treeBuilder->getTreeBySysTemplateRowsAndSite('setup', $sysTemplateRows, $this->losslessTokenizer, $site);
135  $setupIncludeTree->setIdentifier('setup tstemplate includes');
136  // Set enabled conditions in setup include tree and let it handle constant substitutions in setup conditions.
137  $setupConditions = $this->‪handleToggledSetupConditions($setupIncludeTree, $moduleData, $parsedBody, $flattenedConstants);
138  $conditionEnforcerVisitor = GeneralUtility::makeInstance(IncludeTreeConditionEnforcerVisitor::class);
139  $conditionEnforcerVisitor->setEnabledConditions(array_column(array_filter($setupConditions, static fn($condition) => $condition['active']), 'value'));
140  $treeTraverserVisitors = [];
141  $treeTraverserVisitors[] = $conditionEnforcerVisitor;
142  $setupSyntaxScannerVisitor = new IncludeTreeSyntaxScannerVisitor();
143  $treeTraverserVisitors[] = $setupSyntaxScannerVisitor;
144  $this->treeTraverser->traverse($setupIncludeTree, $treeTraverserVisitors);
145 
146  $view = $this->moduleTemplateFactory->create($request);
147  $view->setTitle($languageService->sL($currentModule->getTitle()), $pageRecord['title']);
148  $view->getDocHeaderComponent()->setMetaInformation($pageRecord);
149  $this->‪addPreviewButtonToDocHeader($view, $pageUid, (int)$pageRecord['doktype']);
150  $this->‪addShortcutButtonToDocHeader($view, $currentModuleIdentifier, $pageRecord, $pageUid);
151  $view->makeDocHeaderModuleMenu(['id' => $pageUid]);
152  $view->assignMultiple([
153  'pageUid' => $pageUid,
154  'allTemplatesOnPage' => $allTemplatesOnPage,
155  'selectedTemplateUid' => $selectedTemplateUid,
156  'templateTitle' => $templateTitle,
157  'constantConditions' => $constantConditions,
158  'constantConditionsActiveCount' => count(array_filter($constantConditions, static fn($condition) => $condition['active'])),
159  'constantIncludeTree' => $constantIncludeTree,
160  'constantErrors' => $constantSyntaxScannerVisitor->getErrors(),
161  'constantErrorCount' => count($constantSyntaxScannerVisitor->getErrors()),
162  'setupConditions' => $setupConditions,
163  'setupConditionsActiveCount' => count(array_filter($setupConditions, static fn($condition) => $condition['active'])),
164  'setupIncludeTree' => $setupIncludeTree,
165  'setupErrors' => $setupSyntaxScannerVisitor->getErrors(),
166  'setupErrorCount' => count($setupSyntaxScannerVisitor->getErrors()),
167  ]);
168 
169  return $view->renderResponse('Analyzer');
170  }
171 
172  public function ‪sourceAction(ServerRequestInterface $request): ResponseInterface
173  {
174  $queryParams = $request->getQueryParams();
175  $pageUid = (int)($queryParams['id'] ?? 0);
176  $type = $queryParams['includeType'] ?? null;
177  $includeIdentifier = $queryParams['identifier'] ?? null;
178  $moduleData = $request->getAttribute('moduleData');
179  $allTemplatesOnPage = $this->‪getAllTemplateRecordsOnPage($pageUid);
180  $selectedTemplateUid = (int)($moduleData->get('selectedTemplatePerPage')[$pageUid] ?? 0);
181  if (!in_array($selectedTemplateUid, array_column($allTemplatesOnPage, 'uid'))) {
182  $selectedTemplateUid = (int)($allTemplatesOnPage[0]['uid'] ?? 0);
183  }
184  if ($pageUid === 0 || $includeIdentifier === null || !in_array($type, ['constants', 'setup'], true)) {
185  return $this->responseFactory->createResponse(400);
186  }
187  $rootLine = GeneralUtility::makeInstance(RootlineUtility::class, $pageUid)->get();
188  $sysTemplateRows = $this->sysTemplateRepository->getSysTemplateRowsByRootlineWithUidOverride($rootLine, $request, $selectedTemplateUid);
189  $site = $request->getAttribute('site');
190  $includeTree = $this->treeBuilder->getTreeBySysTemplateRowsAndSite($type, $sysTemplateRows, $this->losslessTokenizer, $site);
191  $includeTree->setIdentifier($type . ' tstemplate includes');
192 
193  $nodeFinderVisitor = GeneralUtility::makeInstance(IncludeTreeNodeFinderVisitor::class);
194  $nodeFinderVisitor->setNodeIdentifier($includeIdentifier);
195  $this->treeTraverser->traverse($includeTree, [$nodeFinderVisitor]);
196  $foundNode = $nodeFinderVisitor->getFoundNode();
197  if ($foundNode?->getLineStream() === null) {
198  return $this->responseFactory->createResponse(400);
199  }
200 
201  return $this->responseFactory
202  ->createResponse()
203  ->withHeader('Content-Type', 'text/plain')
204  ->withBody($this->streamFactory->createStream((string)$foundNode->getLineStream()));
205  }
206 
207  public function ‪sourceWithIncludesAction(ServerRequestInterface $request): ResponseInterface
208  {
209  $queryParams = $request->getQueryParams();
210  $pageUid = (int)($queryParams['id'] ?? 0);
211  $type = $queryParams['includeType'] ?? null;
212  $includeIdentifier = $queryParams['identifier'] ?? null;
213  $moduleData = $request->getAttribute('moduleData');
214  $allTemplatesOnPage = $this->‪getAllTemplateRecordsOnPage($pageUid);
215  $selectedTemplateUid = (int)($moduleData->get('selectedTemplatePerPage')[$pageUid] ?? 0);
216  if (!in_array($selectedTemplateUid, array_column($allTemplatesOnPage, 'uid'))) {
217  $selectedTemplateUid = (int)($allTemplatesOnPage[0]['uid'] ?? 0);
218  }
219  if ($pageUid === 0 || $includeIdentifier === null || !in_array($type, ['constants', 'setup'], true)) {
220  return $this->responseFactory->createResponse(400);
221  }
222  $rootLine = GeneralUtility::makeInstance(RootlineUtility::class, $pageUid)->get();
223  $sysTemplateRows = $this->sysTemplateRepository->getSysTemplateRowsByRootlineWithUidOverride($rootLine, $request, $selectedTemplateUid);
224  $site = $request->getAttribute('site');
225  $includeTree = $this->treeBuilder->getTreeBySysTemplateRowsAndSite($type, $sysTemplateRows, $this->losslessTokenizer, $site);
226  $includeTree->setIdentifier($type . ' tstemplate includes');
227 
228  $sourceAggregatorVisitor = new ‪IncludeTreeSourceAggregatorVisitor();
229  $sourceAggregatorVisitor->setStartNodeIdentifier($includeIdentifier);
230  $this->treeTraverser->traverse($includeTree, [$sourceAggregatorVisitor]);
231  $source = $sourceAggregatorVisitor->getSource();
232 
233  return $this->responseFactory
234  ->createResponse()
235  ->withHeader('Content-Type', 'text/plain')
236  ->withBody($this->streamFactory->createStream($source));
237  }
238 
244  private function ‪handleToggledConstantConditions(‪RootInclude $constantTree, ‪ModuleData $moduleData, ?array $parsedBody): array
245  {
246  $conditionAggregatorVisitor = GeneralUtility::makeInstance(IncludeTreeConditionAggregatorVisitor::class);
247  $this->treeTraverser->traverse($constantTree, [$conditionAggregatorVisitor]);
248  $constantConditions = $conditionAggregatorVisitor->getConditions();
249  $conditionsFromPost = $parsedBody['constantsConditions'] ?? [];
250  $conditionsFromModuleData = array_flip((array)$moduleData->‪get('constantConditions'));
251  $typoscriptConditions = [];
252  foreach ($constantConditions as $condition) {
253  $conditionHash = sha1($condition['value']);
254  $conditionActive = array_key_exists($conditionHash, $conditionsFromModuleData);
255  // Note we're not feeding the post values directly to module data, but filter
256  // them through available conditions to prevent polluting module data with
257  // manipulated post values.
258  if (($conditionsFromPost[$conditionHash] ?? null) === '0') {
259  unset($conditionsFromModuleData[$conditionHash]);
260  $conditionActive = false;
261  } elseif (($conditionsFromPost[$conditionHash] ?? null) === '1') {
262  $conditionsFromModuleData[$conditionHash] = true;
263  $conditionActive = true;
264  }
265  $typoscriptConditions[] = [
266  'value' => $condition['value'],
267  'hash' => $conditionHash,
268  'active' => $conditionActive,
269  ];
270  }
271  if ($conditionsFromPost) {
272  $moduleData->‪set('constantConditions', array_keys($conditionsFromModuleData));
273  $this->‪getBackendUser()->pushModuleData($moduleData->‪getModuleIdentifier(), $moduleData->‪toArray());
274  }
275  return $typoscriptConditions;
276  }
277 
283  private function ‪handleToggledSetupConditions(‪RootInclude $constantTree, ‪ModuleData $moduleData, ?array $parsedBody, array $flattenedConstants): array
284  {
285  $setupConditionConstantSubstitutionVisitor = new ‪IncludeTreeSetupConditionConstantSubstitutionVisitor();
286  $setupConditionConstantSubstitutionVisitor->setFlattenedConstants($flattenedConstants);
287  $treeTraverserVisitors = [];
288  $treeTraverserVisitors[] = $setupConditionConstantSubstitutionVisitor;
289  $conditionAggregatorVisitor = GeneralUtility::makeInstance(IncludeTreeConditionAggregatorVisitor::class);
290  $treeTraverserVisitors[] = $conditionAggregatorVisitor;
291  $this->treeTraverser->traverse($constantTree, $treeTraverserVisitors);
292  $setupConditions = $conditionAggregatorVisitor->getConditions();
293  $conditionsFromPost = $parsedBody['setupConditions'] ?? [];
294  $conditionsFromModuleData = array_flip((array)$moduleData->‪get('setupConditions'));
295  $typoscriptConditions = [];
296  foreach ($setupConditions as $condition) {
297  $conditionHash = sha1($condition['value']);
298  $conditionActive = array_key_exists($conditionHash, $conditionsFromModuleData);
299  // Note we're not feeding the post values directly to module data, but filter
300  // them through available conditions to prevent polluting module data with
301  // manipulated post values.
302  if (($conditionsFromPost[$conditionHash] ?? null) === '0') {
303  unset($conditionsFromModuleData[$conditionHash]);
304  $conditionActive = false;
305  } elseif (($conditionsFromPost[$conditionHash] ?? null) === '1') {
306  $conditionsFromModuleData[$conditionHash] = true;
307  $conditionActive = true;
308  }
309  $typoscriptConditions[] = [
310  'value' => $condition['value'],
311  'originalValue' => $condition['originalValue'],
312  'hash' => $conditionHash,
313  'active' => $conditionActive,
314  ];
315  }
316  if ($conditionsFromPost) {
317  $moduleData->‪set('setupConditions', array_keys($conditionsFromModuleData));
318  $this->‪getBackendUser()->pushModuleData($moduleData->‪getModuleIdentifier(), $moduleData->‪toArray());
319  }
320  return $typoscriptConditions;
321  }
322 
323  private function ‪addShortcutButtonToDocHeader(‪ModuleTemplate $view, string $moduleIdentifier, array $pageInfo, int $pageUid): void
324  {
325  $languageService = $this->‪getLanguageService();
326  $buttonBar = $view->‪getDocHeaderComponent()->getButtonBar();
327  $shortcutTitle = sprintf(
328  '%s: %s [%d]',
329  $languageService->sL('LLL:EXT:tstemplate/Resources/Private/Language/locallang_analyzer.xlf:submodule.title'),
330  BackendUtility::getRecordTitle('pages', $pageInfo),
331  $pageUid
332  );
333  $shortcutButton = $buttonBar->makeShortcutButton()
334  ->setRouteIdentifier($moduleIdentifier)
335  ->setDisplayName($shortcutTitle)
336  ->setArguments(['id' => $pageUid]);
337  $buttonBar->addButton($shortcutButton);
338  }
339 }
‪TYPO3\CMS\Core\TypoScript\IncludeTree\SysTemplateRepository
Definition: SysTemplateRepository.php:39
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeSetupConditionConstantSubstitutionVisitor
Definition: IncludeTreeSetupConditionConstantSubstitutionVisitor.php:36
‪TYPO3\CMS\Tstemplate\Controller\TemplateAnalyzerController\indexAction
‪indexAction(ServerRequestInterface $request)
Definition: TemplateAnalyzerController.php:66
‪TYPO3\CMS\Tstemplate\Controller\TemplateAnalyzerController\addShortcutButtonToDocHeader
‪addShortcutButtonToDocHeader(ModuleTemplate $view, string $moduleIdentifier, array $pageInfo, int $pageUid)
Definition: TemplateAnalyzerController.php:323
‪TYPO3\CMS\Tstemplate\Controller\TemplateAnalyzerController\sourceAction
‪sourceAction(ServerRequestInterface $request)
Definition: TemplateAnalyzerController.php:172
‪TYPO3\CMS\Core\TypoScript\Tokenizer\LosslessTokenizer
Definition: LosslessTokenizer.php:61
‪TYPO3\CMS\Backend\Template\ModuleTemplateFactory
Definition: ModuleTemplateFactory.php:33
‪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\Core\Utility\RootlineUtility
Definition: RootlineUtility.php:40
‪TYPO3\CMS\Backend\Module\ModuleData
Definition: ModuleData.php:30
‪TYPO3\CMS\Tstemplate\Controller
Definition: AbstractTemplateModuleController.php:18
‪TYPO3\CMS\Backend\Module\ModuleData\get
‪get(string $propertyName, mixed $default=null)
Definition: ModuleData.php:56
‪TYPO3\CMS\Tstemplate\Controller\TemplateAnalyzerController
Definition: TemplateAnalyzerController.php:54
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeAstBuilderVisitor
Definition: IncludeTreeAstBuilderVisitor.php:39
‪TYPO3\CMS\Tstemplate\Controller\AbstractTemplateModuleController\getAllTemplateRecordsOnPage
‪getAllTemplateRecordsOnPage(int $pageId)
Definition: AbstractTemplateModuleController.php:168
‪TYPO3\CMS\Backend\Template\ModuleTemplate
Definition: ModuleTemplate.php:46
‪TYPO3\CMS\Tstemplate\Controller\TemplateAnalyzerController\__construct
‪__construct(private readonly ModuleTemplateFactory $moduleTemplateFactory, private readonly SysTemplateRepository $sysTemplateRepository, private readonly IncludeTreeTraverser $treeTraverser, private readonly ConditionVerdictAwareIncludeTreeTraverser $treeTraverserConditionVerdictAware, private readonly SysTemplateTreeBuilder $treeBuilder, private readonly LosslessTokenizer $losslessTokenizer, private readonly ResponseFactoryInterface $responseFactory, private readonly StreamFactoryInterface $streamFactory,)
Definition: TemplateAnalyzerController.php:55
‪TYPO3\CMS\Tstemplate\Controller\AbstractTemplateModuleController
Definition: AbstractTemplateModuleController.php:46
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeNodeFinderVisitor
Definition: IncludeTreeNodeFinderVisitor.php:31
‪TYPO3\CMS\Tstemplate\Controller\AbstractTemplateModuleController\getBackendUser
‪getBackendUser()
Definition: AbstractTemplateModuleController.php:226
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeSourceAggregatorVisitor
Definition: IncludeTreeSourceAggregatorVisitor.php:36
‪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\Tstemplate\Controller\TemplateAnalyzerController\handleToggledConstantConditions
‪handleToggledConstantConditions(RootInclude $constantTree, ModuleData $moduleData, ?array $parsedBody)
Definition: TemplateAnalyzerController.php:244
‪TYPO3\CMS\Tstemplate\Controller\AbstractTemplateModuleController\getLanguageService
‪getLanguageService()
Definition: AbstractTemplateModuleController.php:221
‪TYPO3\CMS\Backend\Template\ModuleTemplate\getDocHeaderComponent
‪getDocHeaderComponent()
Definition: ModuleTemplate.php:181
‪TYPO3\CMS\Tstemplate\Controller\TemplateAnalyzerController\sourceWithIncludesAction
‪sourceWithIncludesAction(ServerRequestInterface $request)
Definition: TemplateAnalyzerController.php:207
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Traverser\ConditionVerdictAwareIncludeTreeTraverser
Definition: ConditionVerdictAwareIncludeTreeTraverser.php:38
‪TYPO3\CMS\Backend\Attribute\AsController
Definition: AsController.php:25
‪TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode\RootInclude
Definition: RootInclude.php:27
‪TYPO3\CMS\Tstemplate\Controller\TemplateAnalyzerController\handleToggledSetupConditions
‪handleToggledSetupConditions(RootInclude $constantTree, ModuleData $moduleData, ?array $parsedBody, array $flattenedConstants)
Definition: TemplateAnalyzerController.php:283
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Visitor\IncludeTreeConditionAggregatorVisitor
Definition: IncludeTreeConditionAggregatorVisitor.php:32
‪TYPO3\CMS\Tstemplate\Controller\AbstractTemplateModuleController\addPreviewButtonToDocHeader
‪addPreviewButtonToDocHeader(ModuleTemplate $view, int $pageId, int $dokType)
Definition: AbstractTemplateModuleController.php:117
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\TypoScript\IncludeTree\SysTemplateTreeBuilder
Definition: SysTemplateTreeBuilder.php:72
‪TYPO3\CMS\Backend\Module\ModuleData\getModuleIdentifier
‪getModuleIdentifier()
Definition: ModuleData.php:51