From 0e464ae4c3f8ce2989535568c24b58025da4499a Mon Sep 17 00:00:00 2001 From: Benni Mack <benni@typo3.org> Date: Wed, 25 Mar 2020 18:00:48 +0100 Subject: [PATCH] [TASK] Streamline BackendLayout API class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change moves the ConditionMatching back to the BackendLayoutView. The main goal is to make BackendLayout more of an entity class again, whereas BackendLayoutView should be the process to build the BackendLayout objects, and configuration and return them, and act more as a BackendLayoutResolver - this could / should be renamed in the future. This change thus reactivates runtime-caching for fetching a backend layout configuration again. In addition, the new method "getBackendLayoutForPage()" now actually returns a "ready-to-use" Backend Layout instance, which could also used for the Nested Content Grid in the future. The previous "configurationArray" is now defined as "stucture". A short-hand function "getUsedColumns()" is added. Resolves: #90839 Releases: master Change-Id: Ic6e65317a5faa6d70a181fc3f24b3716a1137c39 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/63918 Tested-by: TYPO3com <noreply@typo3.com> Tested-by: Georg Ringer <georg.ringer@gmail.com> Tested-by: Frank Nägler <frank.naegler@typo3.org> Tested-by: Benni Mack <benni@typo3.org> Reviewed-by: Georg Ringer <georg.ringer@gmail.com> Reviewed-by: Frank Nägler <frank.naegler@typo3.org> Reviewed-by: Benni Mack <benni@typo3.org> --- .../Page/LocalizationController.php | 8 +- .../Controller/PageLayoutController.php | 25 +--- .../View/BackendLayout/BackendLayout.php | 94 ++++---------- .../View/BackendLayout/Grid/GridColumn.php | 7 +- .../Classes/View/BackendLayoutView.php | 122 ++++++++++++++---- 5 files changed, 137 insertions(+), 119 deletions(-) diff --git a/typo3/sysext/backend/Classes/Controller/Page/LocalizationController.php b/typo3/sysext/backend/Classes/Controller/Page/LocalizationController.php index 574384b1d4d8..9b91cf1bc0dc 100644 --- a/typo3/sysext/backend/Classes/Controller/Page/LocalizationController.php +++ b/typo3/sysext/backend/Classes/Controller/Page/LocalizationController.php @@ -260,15 +260,15 @@ class LocalizationController { $columns = []; $backendLayoutView = GeneralUtility::makeInstance(BackendLayoutView::class); - $backendLayouts = $backendLayoutView->getSelectedBackendLayout($pageId); + $backendLayout = $backendLayoutView->getBackendLayoutForPage($pageId); - foreach ($backendLayouts['__items'] as $backendLayout) { - $columns[(int)$backendLayout[1]] = $backendLayout[0]; + foreach ($backendLayout->getUsedColumns() as $columnPos => $columnLabel) { + $columns[$columnPos] = $GLOBALS['LANG']->sL($columnLabel); } return [ 'columns' => $columns, - 'columnList' => array_values($backendLayouts['__colPosList']), + 'columnList' => array_values($backendLayout->getColumnPositionNumbers()), ]; } } diff --git a/typo3/sysext/backend/Classes/Controller/PageLayoutController.php b/typo3/sysext/backend/Classes/Controller/PageLayoutController.php index 8049f783625b..62f1d6ba6bdb 100644 --- a/typo3/sysext/backend/Classes/Controller/PageLayoutController.php +++ b/typo3/sysext/backend/Classes/Controller/PageLayoutController.php @@ -546,9 +546,9 @@ class PageLayoutController '); // Find backend layout / columns - $backendLayout = $this->backendLayouts->getSelectedBackendLayout($this->id); - if (!empty($backendLayout['__colPosList'])) { - $this->colPosList = implode(',', $backendLayout['__colPosList']); + $backendLayout = $this->backendLayouts->getBackendLayoutForPage($this->id); + if (!empty($backendLayout->getColumnPositionNumbers())) { + $this->colPosList = implode(',', $backendLayout->getColumnPositionNumbers()); } // Removing duplicates, if any $this->colPosList = array_unique(GeneralUtility::intExplode(',', $this->colPosList)); @@ -612,24 +612,7 @@ class PageLayoutController $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu'); if (GeneralUtility::makeInstance(Features::class)->isFeatureEnabled('fluidBasedPageModule')) { - $selectedCombinedIdentifier = $this->backendLayouts->getSelectedCombinedIdentifier($this->id); - // If no backend layout is selected, use default - if (empty($selectedCombinedIdentifier)) { - $selectedCombinedIdentifier = 'default'; - } - - $backendLayout = $this->backendLayouts->getDataProviderCollection()->getBackendLayout( - $selectedCombinedIdentifier, - $this->id - ); - - // If backend layout is not found available anymore, use default - if ($backendLayout === null) { - $backendLayout = $this->backendLayouts->getDataProviderCollection()->getBackendLayout( - 'default', - $this->id - ); - } + $backendLayout = $this->backendLayouts->getBackendLayoutForPage((int)$this->id); $configuration = $backendLayout->getDrawingConfiguration(); $configuration->setPageId($this->id); diff --git a/typo3/sysext/backend/Classes/View/BackendLayout/BackendLayout.php b/typo3/sysext/backend/Classes/View/BackendLayout/BackendLayout.php index e0c467149685..383264246c23 100644 --- a/typo3/sysext/backend/Classes/View/BackendLayout/BackendLayout.php +++ b/typo3/sysext/backend/Classes/View/BackendLayout/BackendLayout.php @@ -19,10 +19,9 @@ use TYPO3\CMS\Backend\View\BackendLayout\Grid\Grid; use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumn; use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridRow; use TYPO3\CMS\Backend\View\BackendLayout\Grid\LanguageColumn; +use TYPO3\CMS\Backend\View\BackendLayoutView; use TYPO3\CMS\Backend\View\Drawing\BackendLayoutRenderer; use TYPO3\CMS\Backend\View\Drawing\DrawingConfiguration; -use TYPO3\CMS\Core\Localization\LanguageService; -use TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -56,9 +55,11 @@ class BackendLayout protected $configuration; /** + * The structured data of the configuration represented as array. + * * @var array */ - protected $configurationArray; + protected $structure = []; /** * @var array @@ -114,7 +115,8 @@ class BackendLayout $this->setIdentifier($identifier); $this->setTitle($title); if (is_array($configuration)) { - $this->setConfigurationArray($configuration); + $this->structure = $configuration; + $this->configuration = $configuration['config'] ?? ''; } else { $this->setConfiguration($configuration); } @@ -201,61 +203,24 @@ class BackendLayout } /** - * @param array $configurationArray + * @param string $configuration */ - public function setConfigurationArray(array $configurationArray): void + public function setConfiguration($configuration) { - if (!isset($configurationArray['__colPosList'], $configurationArray['__items'])) { - // Backend layout configuration is unprocessed, process it now to extract counts and column item lists - $colPosList = []; - $items = []; - $rowIndex = 0; - foreach ($configurationArray['backend_layout.']['rows.'] as $row) { - $index = 0; - $colCount = 0; - $columns = []; - foreach ($row['columns.'] as $column) { - if (!isset($column['colPos'])) { - continue; - } - $colPos = $column['colPos']; - $colPos = (int)$colPos; - $colPosList[$colPos] = $colPos; - $key = ($index + 1) . '.'; - $columns[$key] = $column; - $items[$colPos] = [ - (string)$this->getLanguageService()->sL($column['name']), - $colPos, - $column['icon'] - ]; - $colCount += $column['colspan'] ? $column['colspan'] : 1; - ++ $index; - } - ++ $rowIndex; - } - - $configurationArray['__config'] = $configurationArray; - $configurationArray['__colPosList'] = $colPosList; - $configurationArray['__items'] = $items; - } - $this->configurationArray = $configurationArray; + $this->configuration = $configuration; + GeneralUtility::makeInstance(BackendLayoutView::class)->parseStructure($this); } /** + * Returns the columns registered for this layout as $key => $value pair where the key is the colPos + * and the value is the title. + * "1" => "Left" etc. + * Please note that the title can contain LLL references ready for translation. * @return array */ - public function getConfigurationArray(): array + public function getUsedColumns(): array { - return $this->configurationArray; - } - - /** - * @param string $configuration - */ - public function setConfiguration($configuration) - { - $this->configuration = $configuration; - $this->parseConfigurationStringAndSetConfigurationArray($configuration); + return $this->structure['usedColumns'] ?? []; } /** @@ -274,6 +239,16 @@ class BackendLayout $this->data = $data; } + public function setStructure(array $structure) + { + $this->structure = $structure; + } + + public function getStructure(): array + { + return $this->structure; + } + /** * @return LanguageColumn[] */ @@ -295,7 +270,7 @@ class BackendLayout public function getGrid(): Grid { $grid = GeneralUtility::makeInstance(Grid::class, $this); - foreach ($this->getConfigurationArray()['__config']['backend_layout.']['rows.'] as $row) { + foreach ($this->structure['__config']['backend_layout.']['rows.'] ?? [] as $row) { $rowObject = GeneralUtility::makeInstance(GridRow::class, $this); foreach ($row['columns.'] as $column) { $columnObject = GeneralUtility::makeInstance(GridColumn::class, $this, $column); @@ -313,7 +288,7 @@ class BackendLayout public function getColumnPositionNumbers(): array { - return $this->getConfigurationArray()['__colPosList']; + return $this->structure['__colPosList']; } public function getContentFetcher(): ContentFetcher @@ -349,22 +324,9 @@ class BackendLayout return $translationData['mode'] ?? ''; } - protected function parseConfigurationStringAndSetConfigurationArray(string $configuration): void - { - $parser = GeneralUtility::makeInstance(TypoScriptParser::class); - $conditionMatcher = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Configuration\TypoScript\ConditionMatching\ConditionMatcher::class); - $parser->parse(TypoScriptParser::checkIncludeLines($configuration), $conditionMatcher); - $this->setConfigurationArray($parser->setup); - } - public function __clone() { $this->drawingConfiguration = clone $this->drawingConfiguration; $this->contentFetcher->setBackendLayout($this); } - - protected function getLanguageService(): LanguageService - { - return $GLOBALS['LANG']; - } } diff --git a/typo3/sysext/backend/Classes/View/BackendLayout/Grid/GridColumn.php b/typo3/sysext/backend/Classes/View/BackendLayout/Grid/GridColumn.php index f9f6687df914..374627c085c1 100644 --- a/typo3/sysext/backend/Classes/View/BackendLayout/Grid/GridColumn.php +++ b/typo3/sysext/backend/Classes/View/BackendLayout/Grid/GridColumn.php @@ -188,10 +188,9 @@ class GridColumn extends AbstractGridObject { $columnNumber = $this->getColumnNumber(); $colTitle = (string)BackendUtility::getProcessedValue('tt_content', 'colPos', $columnNumber); - $tcaItems = $this->backendLayout->getConfigurationArray()['__items']; - foreach ($tcaItems as $item) { - if ($item[1] === $columnNumber) { - $colTitle = (string)$this->getLanguageService()->sL($item[0]); + foreach ($this->backendLayout->getUsedColumns() as $colPos => $title) { + if ($colPos === $columnNumber) { + $colTitle = (string)$this->getLanguageService()->sL($title); } } return $colTitle; diff --git a/typo3/sysext/backend/Classes/View/BackendLayoutView.php b/typo3/sysext/backend/Classes/View/BackendLayoutView.php index b238d77725b0..3895d9457ab7 100644 --- a/typo3/sysext/backend/Classes/View/BackendLayoutView.php +++ b/typo3/sysext/backend/Classes/View/BackendLayoutView.php @@ -14,8 +14,15 @@ namespace TYPO3\CMS\Backend\View; * The TYPO3 project - inspiring people to share! */ +use TYPO3\CMS\Backend\Configuration\TypoScript\ConditionMatching\ConditionMatcher; use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Backend\View\BackendLayout\BackendLayout; +use TYPO3\CMS\Backend\View\BackendLayout\DataProviderCollection; +use TYPO3\CMS\Backend\View\BackendLayout\DataProviderContext; +use TYPO3\CMS\Backend\View\BackendLayout\DefaultDataProvider; use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Localization\LanguageService; +use TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser; use TYPO3\CMS\Core\Utility\ArrayUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -26,7 +33,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; class BackendLayoutView implements \TYPO3\CMS\Core\SingletonInterface { /** - * @var BackendLayout\DataProviderCollection + * @var DataProviderCollection */ protected $dataProviderCollection; @@ -53,15 +60,8 @@ class BackendLayoutView implements \TYPO3\CMS\Core\SingletonInterface */ protected function initializeDataProviderCollection() { - /** @var BackendLayout\DataProviderCollection $dataProviderCollection */ - $dataProviderCollection = GeneralUtility::makeInstance( - BackendLayout\DataProviderCollection::class - ); - - $dataProviderCollection->add( - 'default', - \TYPO3\CMS\Backend\View\BackendLayout\DefaultDataProvider::class - ); + $dataProviderCollection = GeneralUtility::makeInstance(DataProviderCollection::class); + $dataProviderCollection->add('default', DefaultDataProvider::class); if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['BackendLayoutDataProvider'])) { $dataProviders = (array)$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['BackendLayoutDataProvider']; @@ -74,15 +74,15 @@ class BackendLayoutView implements \TYPO3\CMS\Core\SingletonInterface } /** - * @param BackendLayout\DataProviderCollection $dataProviderCollection + * @param DataProviderCollection $dataProviderCollection */ - public function setDataProviderCollection(BackendLayout\DataProviderCollection $dataProviderCollection) + public function setDataProviderCollection(DataProviderCollection $dataProviderCollection) { $this->dataProviderCollection = $dataProviderCollection; } /** - * @return BackendLayout\DataProviderCollection + * @return DataProviderCollection */ public function getDataProviderCollection() { @@ -327,32 +327,89 @@ class BackendLayoutView implements \TYPO3\CMS\Core\SingletonInterface } /** - * Gets the selected backend layout + * Gets the selected backend layout structure as an array * * @param int $pageId * @return array|null $backendLayout */ - public function getSelectedBackendLayout($pageId) + public function getSelectedBackendLayout($pageId): ?array + { + $layout = $this->getBackendLayoutForPage((int)$pageId); + if ($layout instanceof BackendLayout) { + return $layout->getStructure(); + } + return null; + } + + /** + * Get the BackendLayout object and parse the structure based on the UserTSconfig + * @param int $pageId + * @return BackendLayout + */ + public function getBackendLayoutForPage(int $pageId): ?BackendLayout { if (isset($this->selectedBackendLayout[$pageId])) { return $this->selectedBackendLayout[$pageId]; } - $backendLayoutData = null; - $selectedCombinedIdentifier = $this->getSelectedCombinedIdentifier($pageId); // If no backend layout is selected, use default if (empty($selectedCombinedIdentifier)) { $selectedCombinedIdentifier = 'default'; } - $backendLayout = $this->getDataProviderCollection()->getBackendLayout($selectedCombinedIdentifier, $pageId); // If backend layout is not found available anymore, use default if ($backendLayout === null) { - $selectedCombinedIdentifier = 'default'; - $backendLayout = $this->getDataProviderCollection()->getBackendLayout($selectedCombinedIdentifier, $pageId); + $backendLayout = $this->getDataProviderCollection()->getBackendLayout('default', $pageId); + } + + $structure = null; + if ($backendLayout instanceof BackendLayout) { + $structure = $this->parseStructure($backendLayout); + // Parse the configuration and inject it back in the backend layout object + $backendLayout->setStructure($structure); + $this->selectedBackendLayout[$pageId] = $backendLayout; } + return $backendLayout; + } - return $backendLayout->getConfigurationArray(); + /** + * @param BackendLayout $backendLayout + * @return array + * @internal + */ + public function parseStructure(BackendLayout $backendLayout): array + { + $parser = GeneralUtility::makeInstance(TypoScriptParser::class); + $conditionMatcher = GeneralUtility::makeInstance(ConditionMatcher::class); + $parser->parse(TypoScriptParser::checkIncludeLines($backendLayout->getConfiguration()), $conditionMatcher); + + $backendLayoutData = []; + $backendLayoutData['config'] = $backendLayout->getConfiguration(); + $backendLayoutData['__config'] = $parser->setup; + $backendLayoutData['__items'] = []; + $backendLayoutData['__colPosList'] = []; + $backendLayoutData['usedColumns'] = []; + + // create items and colPosList + if (!empty($backendLayoutData['__config']['backend_layout.']['rows.'])) { + foreach ($backendLayoutData['__config']['backend_layout.']['rows.'] as $row) { + if (!empty($row['columns.'])) { + foreach ($row['columns.'] as $column) { + if (!isset($column['colPos'])) { + continue; + } + $backendLayoutData['__items'][] = [ + $this->getColumnName($column), + $column['colPos'], + null + ]; + $backendLayoutData['__colPosList'][] = $column['colPos']; + $backendLayoutData['usedColumns'][(int)$column['colPos']] = $column['name']; + } + } + } + } + return $backendLayoutData; } /** @@ -421,18 +478,35 @@ class BackendLayoutView implements \TYPO3\CMS\Core\SingletonInterface } /** - * @return BackendLayout\DataProviderContext + * @return DataProviderContext */ protected function createDataProviderContext() { - return GeneralUtility::makeInstance(BackendLayout\DataProviderContext::class); + return GeneralUtility::makeInstance(DataProviderContext::class); } /** - * @return \TYPO3\CMS\Core\Localization\LanguageService + * @return LanguageService */ protected function getLanguageService() { return $GLOBALS['LANG']; } + + /** + * Get column name from colPos item structure + * + * @param array $column + * @return string + */ + protected function getColumnName($column) + { + $columnName = $column['name']; + + if (GeneralUtility::isFirstPartOfStr($columnName, 'LLL:')) { + $columnName = $this->getLanguageService()->sL($columnName); + } + + return $columnName; + } } -- GitLab