‪TYPO3CMS  ‪main
BackendLayoutView.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 TYPO3\CMS\Backend\Utility\BackendUtility;
32 
38 {
39  protected array ‪$selectedCombinedIdentifier = [];
40  protected array ‪$selectedBackendLayout = [];
41 
45  public function ‪__construct(
46  private readonly ‪DataProviderCollection $dataProviderCollection,
47  private readonly ‪TypoScriptStringFactory $typoScriptStringFactory,
48  ) {
49  $this->dataProviderCollection->add('default', DefaultDataProvider::class);
50  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['BackendLayoutDataProvider'])) {
51  $dataProviders = (array)‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['BackendLayoutDataProvider'];
52  foreach ($dataProviders as ‪$identifier => $className) {
53  $this->dataProviderCollection->add(‪$identifier, $className);
54  }
55  }
56  }
57 
70  public function ‪addBackendLayoutItems(array &$parameters)
71  {
72  $pageId = $this->‪determinePageId($parameters['table'], $parameters['row']) ?: 0;
73  $pageTsConfig = (array)BackendUtility::getPagesTSconfig($pageId);
74  $identifiersToBeExcluded = $this->‪getIdentifiersToBeExcluded($pageTsConfig);
75 
76  $dataProviderContext = $this->‪createDataProviderContext()
77  ->‪setPageId($pageId)
78  ->‪setData($parameters['row'])
79  ->‪setTableName($parameters['table'])
80  ->‪setFieldName($parameters['field'])
81  ->‪setPageTsConfig($pageTsConfig);
82 
83  $backendLayoutCollections = $this->dataProviderCollection->getBackendLayoutCollections($dataProviderContext);
84  foreach ($backendLayoutCollections as $backendLayoutCollection) {
85  $combinedIdentifierPrefix = '';
86  if ($backendLayoutCollection->getIdentifier() !== 'default') {
87  $combinedIdentifierPrefix = $backendLayoutCollection->getIdentifier() . '__';
88  }
89 
90  foreach ($backendLayoutCollection->getAll() as $backendLayout) {
91  $combinedIdentifier = $combinedIdentifierPrefix . $backendLayout->getIdentifier();
92 
93  if (in_array($combinedIdentifier, $identifiersToBeExcluded, true)) {
94  continue;
95  }
96 
97  $parameters['items'][] = [
98  'label' => $this->‪getLanguageService()->sL($backendLayout->getTitle()),
99  'value' => $combinedIdentifier,
100  'icon' => $backendLayout->getIconPath(),
101  ];
102  }
103  }
104  }
105 
112  protected function ‪determinePageId($tableName, array $data)
113  {
114  if (str_starts_with((string)$data['uid'], 'NEW')) {
115  // negative uid_pid values of content elements indicate that the element
116  // has been inserted after an existing element so there is no pid to get
117  // the backendLayout for and we have to get that first
118  if ($data['pid'] < 0) {
119  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
120  ->getQueryBuilderForTable($tableName);
121  $queryBuilder->getRestrictions()
122  ->removeAll();
123  $pageId = $queryBuilder
124  ->select('pid')
125  ->from($tableName)
126  ->where(
127  $queryBuilder->expr()->eq(
128  'uid',
129  $queryBuilder->createNamedParameter(abs($data['pid']), ‪Connection::PARAM_INT)
130  )
131  )
132  ->executeQuery()
133  ->fetchOne();
134  } else {
135  $pageId = $data['pid'];
136  }
137  } elseif ($tableName === 'pages') {
138  $pageId = $data['uid'];
139  } else {
140  $pageId = $data['pid'];
141  }
142 
143  return $pageId;
144  }
145 
152  public function ‪getSelectedCombinedIdentifier($pageId)
153  {
154  if (!isset($this->selectedCombinedIdentifier[$pageId])) {
155  $page = $this->‪getPage($pageId);
156  $this->selectedCombinedIdentifier[$pageId] = (string)($page['backend_layout'] ?? null);
157 
158  if ($this->selectedCombinedIdentifier[$pageId] === '-1') {
159  // If it is set to "none" - don't use any
160  $this->selectedCombinedIdentifier[$pageId] = false;
161  } elseif ($this->selectedCombinedIdentifier[$pageId] === '' || $this->selectedCombinedIdentifier[$pageId] === '0') {
162  // If it not set check the root-line for a layout on next level and use this
163  // (root-line starts with current page and has page "0" at the end)
164  $rootLine = $this->‪getRootLine($pageId);
165  // Remove first and last element (current and root page)
166  array_shift($rootLine);
167  array_pop($rootLine);
168  foreach ($rootLine as $rootLinePage) {
169  $this->selectedCombinedIdentifier[$pageId] = (string)$rootLinePage['backend_layout_next_level'];
170  if ($this->selectedCombinedIdentifier[$pageId] === '-1') {
171  // If layout for "next level" is set to "none" - don't use any and stop searching
172  $this->selectedCombinedIdentifier[$pageId] = false;
173  break;
174  }
175  if ($this->selectedCombinedIdentifier[$pageId] !== '' && $this->selectedCombinedIdentifier[$pageId] !== '0') {
176  // Stop searching if a layout for "next level" is set
177  break;
178  }
179  }
180  }
181  }
182  // If it is set to a positive value use this
183  return $this->selectedCombinedIdentifier[$pageId];
184  }
185 
191  protected function ‪getIdentifiersToBeExcluded(array $pageTSconfig)
192  {
193  $identifiersToBeExcluded = [];
194 
195  if (‪ArrayUtility::isValidPath($pageTSconfig, 'options./backendLayout./exclude')) {
196  $identifiersToBeExcluded = GeneralUtility::trimExplode(
197  ',',
198  ‪ArrayUtility::getValueByPath($pageTSconfig, 'options./backendLayout./exclude'),
199  true
200  );
201  }
202 
203  return $identifiersToBeExcluded;
204  }
205 
211  public function ‪colPosListItemProcFunc(array $parameters)
212  {
213  $pageId = $this->‪determinePageId($parameters['table'], $parameters['row']);
214 
215  if ($pageId !== false) {
216  $parameters['items'] = $this->‪addColPosListLayoutItems($pageId, $parameters['items']);
217  }
218  }
219 
227  protected function ‪addColPosListLayoutItems($pageId, $items)
228  {
229  $layout = $this->‪getSelectedBackendLayout($pageId);
230  if ($layout && !empty($layout['__items'])) {
231  $items = $layout['__items'];
232  }
233  return $items;
234  }
235 
242  public function ‪getColPosListItemsParsed($id)
243  {
244  $tsConfig = BackendUtility::getPagesTSconfig($id)['TCEFORM.']['tt_content.']['colPos.'] ?? [];
245  $tcaConfig = ‪$GLOBALS['TCA']['tt_content']['columns']['colPos']['config'] ?? [];
246  $tcaItems = $tcaConfig['items'];
247  $tcaItems = $this->‪addItems($tcaItems, $tsConfig['addItems.'] ?? []);
248  if (isset($tcaConfig['itemsProcFunc']) && $tcaConfig['itemsProcFunc']) {
249  $tcaItems = $this->‪addColPosListLayoutItems($id, $tcaItems);
250  }
251  if (!empty($tsConfig['removeItems'])) {
252  foreach (GeneralUtility::trimExplode(',', $tsConfig['removeItems'], true) as $removeId) {
253  foreach ($tcaItems as $key => $item) {
254  if ($item[1] == $removeId) {
255  unset($tcaItems[$key]);
256  }
257  }
258  }
259  }
260  return $tcaItems;
261  }
262 
274  protected function ‪addItems($items, $iArray)
275  {
276  $languageService = $this->‪getLanguageService();
277  if (is_array($iArray)) {
278  foreach ($iArray as $value => $label) {
279  // if the label is an array (that means it is a subelement
280  // like "34.icon = mylabel.png", skip it (see its usage below)
281  if (is_array($label)) {
282  continue;
283  }
284  // check if the value "34 = mylabel" also has a "34.icon = myimage.png"
285  if (isset($iArray[$value . '.']) && $iArray[$value . '.']['icon']) {
286  $icon = $iArray[$value . '.']['icon'];
287  } else {
288  $icon = '';
289  }
290  $items[] = [$languageService->sL($label), $value, $icon];
291  }
292  }
293  return $items;
294  }
295 
302  public function ‪getSelectedBackendLayout($pageId)
303  {
304  $layout = $this->‪getBackendLayoutForPage((int)$pageId);
305  return $layout?->getStructure();
306  }
307 
312  public function ‪getBackendLayoutForPage(int $pageId): ?‪BackendLayout
313  {
314  if (isset($this->selectedBackendLayout[$pageId])) {
315  return $this->selectedBackendLayout[$pageId];
316  }
318  // If no backend layout is selected, use default
319  if (empty(‪$selectedCombinedIdentifier)) {
320  ‪$selectedCombinedIdentifier = 'default';
321  }
322  $backendLayout = $this->dataProviderCollection->getBackendLayout(‪$selectedCombinedIdentifier, $pageId);
323  // If backend layout is not found available anymore, use default
324  if ($backendLayout === null) {
325  $backendLayout = $this->dataProviderCollection->getBackendLayout('default', $pageId);
326  }
327 
328  if ($backendLayout instanceof ‪BackendLayout) {
329  $this->selectedBackendLayout[$pageId] = $backendLayout;
330  }
331  return $backendLayout;
332  }
333 
337  public function ‪parseStructure(‪BackendLayout $backendLayout): array
338  {
339  $typoScriptTree = $this->typoScriptStringFactory->parseFromStringWithIncludes('backend-layout', $backendLayout->‪getConfiguration());
340 
341  $backendLayoutData = [];
342  $backendLayoutData['config'] = $backendLayout->‪getConfiguration();
343  $backendLayoutData['__config'] = $typoScriptTree->toArray();
344  $backendLayoutData['__items'] = [];
345  $backendLayoutData['__colPosList'] = [];
346  $backendLayoutData['usedColumns'] = [];
347  $backendLayoutData['colCount'] = (int)($backendLayoutData['__config']['backend_layout.']['colCount'] ?? 0);
348  $backendLayoutData['rowCount'] = (int)($backendLayoutData['__config']['backend_layout.']['rowCount'] ?? 0);
349 
350  // create items and colPosList
351  if (!empty($backendLayoutData['__config']['backend_layout.']['rows.'])) {
352  $rows = $backendLayoutData['__config']['backend_layout.']['rows.'];
353  ksort($rows);
354  foreach ($rows as $row) {
355  if (!empty($row['columns.'])) {
356  foreach ($row['columns.'] as $column) {
357  if (!isset($column['colPos'])) {
358  continue;
359  }
360  $backendLayoutData['__items'][] = [
361  'label' => $this->‪getColumnName($column),
362  'value' => $column['colPos'],
363  'icon' => null,
364  ];
365  $backendLayoutData['__colPosList'][] = $column['colPos'];
366  $backendLayoutData['usedColumns'][(int)$column['colPos']] = $column['name'];
367  }
368  }
369  }
370  }
371  return $backendLayoutData;
372  }
373 
380  public static function ‪getDefaultColumnLayout()
381  {
382  return '
383  backend_layout {
384  colCount = 1
385  rowCount = 1
386  rows {
387  1 {
388  columns {
389  1 {
390  name = LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:colPos.I.1
391  colPos = 0
392  }
393  }
394  }
395  }
396  }
397  ';
398  }
399 
406  protected function ‪getPage($pageId)
407  {
408  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
409  ->getQueryBuilderForTable('pages');
410  $queryBuilder->getRestrictions()
411  ->removeAll();
412  $page = $queryBuilder
413  ->select('uid', 'pid', 'backend_layout')
414  ->from('pages')
415  ->where(
416  $queryBuilder->expr()->eq(
417  'uid',
418  $queryBuilder->createNamedParameter($pageId, ‪Connection::PARAM_INT)
419  )
420  )
421  ->executeQuery()
422  ->fetchAssociative();
423  BackendUtility::workspaceOL('pages', $page);
424 
425  return $page;
426  }
427 
434  protected function ‪getRootLine($pageId)
435  {
436  return BackendUtility::BEgetRootLine($pageId, '', true);
437  }
438 
442  protected function ‪createDataProviderContext()
443  {
444  return GeneralUtility::makeInstance(DataProviderContext::class);
445  }
446 
448  {
449  return ‪$GLOBALS['LANG'];
450  }
451 
458  protected function ‪getColumnName($column)
459  {
460  $columnName = $column['name'];
461  $columnName = $this->‪getLanguageService()->sL($columnName);
462  return $columnName;
463  }
464 }
‪TYPO3\CMS\Backend\View\BackendLayout\DataProviderContext\setPageId
‪DataProviderContext setPageId($pageId)
Definition: DataProviderContext.php:57
‪TYPO3\CMS\Backend\View\BackendLayoutView\determinePageId
‪int false determinePageId($tableName, array $data)
Definition: BackendLayoutView.php:112
‪TYPO3\CMS\Backend\View\BackendLayout\DataProviderContext\setTableName
‪DataProviderContext setTableName($tableName)
Definition: DataProviderContext.php:75
‪TYPO3\CMS\Backend\View\BackendLayoutView\getSelectedBackendLayout
‪array null getSelectedBackendLayout($pageId)
Definition: BackendLayoutView.php:302
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:50
‪TYPO3\CMS\Backend\View\BackendLayoutView\addColPosListLayoutItems
‪array addColPosListLayoutItems($pageId, $items)
Definition: BackendLayoutView.php:227
‪TYPO3\CMS\Backend\View\BackendLayoutView\getLanguageService
‪getLanguageService()
Definition: BackendLayoutView.php:447
‪TYPO3\CMS\Backend\View
Definition: AuthenticationStyleInformation.php:18
‪TYPO3\CMS\Backend\View\BackendLayoutView\colPosListItemProcFunc
‪colPosListItemProcFunc(array $parameters)
Definition: BackendLayoutView.php:211
‪TYPO3\CMS\Backend\View\BackendLayoutView\getIdentifiersToBeExcluded
‪array getIdentifiersToBeExcluded(array $pageTSconfig)
Definition: BackendLayoutView.php:191
‪TYPO3\CMS\Backend\View\BackendLayoutView\getRootLine
‪array getRootLine($pageId)
Definition: BackendLayoutView.php:434
‪TYPO3\CMS\Backend\View\BackendLayoutView\$selectedBackendLayout
‪array $selectedBackendLayout
Definition: BackendLayoutView.php:40
‪TYPO3\CMS\Backend\View\BackendLayoutView\createDataProviderContext
‪DataProviderContext createDataProviderContext()
Definition: BackendLayoutView.php:442
‪TYPO3\CMS\Backend\View\BackendLayout\DataProviderCollection
Definition: DataProviderCollection.php:25
‪TYPO3\CMS\Core\Utility\ArrayUtility\isValidPath
‪static bool isValidPath(array $array, array|string $path, string $delimiter='/')
Definition: ArrayUtility.php:141
‪TYPO3\CMS\Backend\View\BackendLayout\DataProviderContext\setFieldName
‪DataProviderContext setFieldName($fieldName)
Definition: DataProviderContext.php:93
‪TYPO3\CMS\Backend\View\BackendLayout\DataProviderContext\setPageTsConfig
‪DataProviderContext setPageTsConfig(array $pageTsConfig)
Definition: DataProviderContext.php:124
‪TYPO3\CMS\Core\Utility\ArrayUtility\getValueByPath
‪static getValueByPath(array $array, array|string $path, string $delimiter='/')
Definition: ArrayUtility.php:176
‪TYPO3\CMS\Backend\View\BackendLayoutView\getPage
‪array false null getPage($pageId)
Definition: BackendLayoutView.php:406
‪TYPO3\CMS\Backend\View\BackendLayoutView\getColumnName
‪string getColumnName($column)
Definition: BackendLayoutView.php:458
‪TYPO3\CMS\Backend\View\BackendLayoutView\getDefaultColumnLayout
‪static string getDefaultColumnLayout()
Definition: BackendLayoutView.php:380
‪TYPO3\CMS\Backend\View\BackendLayout\BackendLayout\getConfiguration
‪string getConfiguration()
Definition: BackendLayout.php:164
‪TYPO3\CMS\Backend\View\BackendLayoutView\parseStructure
‪parseStructure(BackendLayout $backendLayout)
Definition: BackendLayoutView.php:337
‪TYPO3\CMS\Backend\View\BackendLayoutView\addItems
‪array addItems($items, $iArray)
Definition: BackendLayoutView.php:274
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:39
‪TYPO3\CMS\Backend\View\BackendLayoutView
Definition: BackendLayoutView.php:38
‪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\Backend\View\BackendLayout\BackendLayout
Definition: BackendLayout.php:25
‪TYPO3\CMS\Backend\View\BackendLayoutView\getColPosListItemsParsed
‪array getColPosListItemsParsed($id)
Definition: BackendLayoutView.php:242
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:46
‪TYPO3\CMS\Backend\View\BackendLayoutView\__construct
‪__construct(private readonly DataProviderCollection $dataProviderCollection, private readonly TypoScriptStringFactory $typoScriptStringFactory,)
Definition: BackendLayoutView.php:45
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:48
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪TYPO3\CMS\Backend\View\BackendLayoutView\addBackendLayoutItems
‪addBackendLayoutItems(array &$parameters)
Definition: BackendLayoutView.php:70
‪TYPO3\CMS\Core\TypoScript\TypoScriptStringFactory
Definition: TypoScriptStringFactory.php:37
‪TYPO3\CMS\Backend\View\BackendLayout\DataProviderContext\setData
‪DataProviderContext setData(array $data)
Definition: DataProviderContext.php:110
‪TYPO3\CMS\Backend\View\BackendLayoutView\getSelectedCombinedIdentifier
‪bool string getSelectedCombinedIdentifier($pageId)
Definition: BackendLayoutView.php:152
‪TYPO3\CMS\Backend\View\BackendLayout\DataProviderContext
Definition: DataProviderContext.php:24
‪TYPO3\CMS\Backend\View\BackendLayoutView\getBackendLayoutForPage
‪BackendLayout getBackendLayoutForPage(int $pageId)
Definition: BackendLayoutView.php:312
‪TYPO3\CMS\Webhooks\Message\$identifier
‪identifier readonly string $identifier
Definition: FileAddedMessage.php:37
‪TYPO3\CMS\Backend\View\BackendLayout\DefaultDataProvider
Definition: DefaultDataProvider.php:32
‪TYPO3\CMS\Backend\View\BackendLayoutView\$selectedCombinedIdentifier
‪array $selectedCombinedIdentifier
Definition: BackendLayoutView.php:39