From 3c5e86915ef6747de085cb71b224c10e8e6b514a Mon Sep 17 00:00:00 2001
From: Benni Mack <benni@typo3.org>
Date: Thu, 21 Mar 2024 23:25:35 +0100
Subject: [PATCH] [TASK] Reduce overhead in BackendLayoutView
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

BackendLayoutView is used to build the BackendLayout
structure, and does a few things too much:

a) it uses ArrayUtility over a simple isset() check for pageTsconfig
b) it resolves LLL labels directly, even if MoveContent wizard and Page module handle that as well
c) it re-builds logic only used in Move Content which is not needed due to a properly resolved colPos list already

All of this internal code can be removed and thus reduced to
a minimum, with Page module, FormEngine (pages+tt_content) and Move
Content Wizard continue to work, because FormEngine and all
other modules already use the proper API for rendering already.

Resolves: #103463
Releases: main
Change-Id: Ib3878d08936e60a9cbd2f405260f3badcefcf8ae
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/83565
Reviewed-by: Andreas Kienast <a.fernandez@scripting-base.de>
Tested-by: Andreas Kienast <a.fernandez@scripting-base.de>
Reviewed-by: Stefan Bürk <stefan@buerk.tech>
Tested-by: Oliver Bartsch <bo@cedev.de>
Tested-by: core-ci <typo3@b13.com>
Reviewed-by: Oliver Bartsch <bo@cedev.de>
---
 Build/phpstan/phpstan-baseline.neon           |  5 --
 .../View/AbstractContentPagePositionMap.php   | 24 +++---
 .../Classes/View/BackendLayoutView.php        | 86 ++-----------------
 3 files changed, 18 insertions(+), 97 deletions(-)

diff --git a/Build/phpstan/phpstan-baseline.neon b/Build/phpstan/phpstan-baseline.neon
index 7b95c57d83b5..29c38a368f28 100644
--- a/Build/phpstan/phpstan-baseline.neon
+++ b/Build/phpstan/phpstan-baseline.neon
@@ -155,11 +155,6 @@ parameters:
 			count: 1
 			path: ../../typo3/sysext/backend/Classes/Tree/Renderer/UnorderedListTreeRenderer.php
 
-		-
-			message: "#^Variable \\$columnsConfiguration in empty\\(\\) always exists and is not falsy\\.$#"
-			count: 1
-			path: ../../typo3/sysext/backend/Classes/Tree/View/AbstractContentPagePositionMap.php
-
 		-
 			message: "#^Else branch is unreachable because previous condition is always true\\.$#"
 			count: 2
diff --git a/typo3/sysext/backend/Classes/Tree/View/AbstractContentPagePositionMap.php b/typo3/sysext/backend/Classes/Tree/View/AbstractContentPagePositionMap.php
index df4867389259..cc303c938aa2 100644
--- a/typo3/sysext/backend/Classes/Tree/View/AbstractContentPagePositionMap.php
+++ b/typo3/sysext/backend/Classes/Tree/View/AbstractContentPagePositionMap.php
@@ -283,28 +283,28 @@ abstract class AbstractContentPagePositionMap
      * Fetch TCA colPos list from BackendLayoutView and prepare for map generation.
      * This also takes the "colPos_list" TSconfig into account.
      */
-    protected function getColumnsConfiguration(int $id): array
+    protected function getColumnsConfiguration(int $pageId): array
     {
-        $columnsConfiguration = $this->backendLayoutView->getColPosListItemsParsed($id);
-        if ($columnsConfiguration === []) {
+        $backendLayout = $this->backendLayoutView->getBackendLayoutForPage($pageId);
+        if (!$backendLayout) {
             return [];
         }
 
+        $items = [];
         // Prepare the columns configuration (using named keys, etc.)
-        foreach ($columnsConfiguration as &$item) {
-            $item = [
-                'title' => $item['label'] ?? '',
-                'colPos' => (int)($item['value'] ?? 0),
+        foreach ($backendLayout->getUsedColumns() as $colPos => $label) {
+            $items[] = [
+                'title' => $label,
+                'colPos' => (int)$colPos,
                 'isRestricted' => false,
             ];
         }
-        unset($item);
 
-        $sharedColPosList = trim(BackendUtility::getPagesTSconfig($id)['mod.']['SHARED.']['colPos_list'] ?? '');
+        $sharedColPosList = trim(BackendUtility::getPagesTSconfig($pageId)['mod.']['SHARED.']['colPos_list'] ?? '');
         if ($sharedColPosList !== '') {
             $activeColPosArray = array_unique(GeneralUtility::intExplode(',', $sharedColPosList));
-            if (!empty($columnsConfiguration) && !empty($activeColPosArray)) {
-                foreach ($columnsConfiguration as &$item) {
+            if (!empty($items) && !empty($activeColPosArray)) {
+                foreach ($items as &$item) {
                     if (!in_array((int)$item['colPos'], $activeColPosArray, true)) {
                         $item['isRestricted'] = true;
                     }
@@ -313,7 +313,7 @@ abstract class AbstractContentPagePositionMap
             }
         }
 
-        return $columnsConfiguration;
+        return $items;
     }
 
     protected function getBackendUser(): BackendUserAuthentication
diff --git a/typo3/sysext/backend/Classes/View/BackendLayoutView.php b/typo3/sysext/backend/Classes/View/BackendLayoutView.php
index 23a8759376aa..c65a8d470572 100644
--- a/typo3/sysext/backend/Classes/View/BackendLayoutView.php
+++ b/typo3/sysext/backend/Classes/View/BackendLayoutView.php
@@ -24,10 +24,8 @@ use TYPO3\CMS\Backend\View\BackendLayout\DataProviderContext;
 use TYPO3\CMS\Backend\View\BackendLayout\DefaultDataProvider;
 use TYPO3\CMS\Core\Database\Connection;
 use TYPO3\CMS\Core\Database\ConnectionPool;
-use TYPO3\CMS\Core\Localization\LanguageService;
 use TYPO3\CMS\Core\SingletonInterface;
 use TYPO3\CMS\Core\TypoScript\TypoScriptStringFactory;
-use TYPO3\CMS\Core\Utility\ArrayUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -93,7 +91,7 @@ class BackendLayoutView implements SingletonInterface
                 }
 
                 $parameters['items'][] = [
-                    'label' => $this->getLanguageService()->sL($backendLayout->getTitle()),
+                    'label' => $backendLayout->getTitle(),
                     'value' => $combinedIdentifier,
                     'icon' => $backendLayout->getIconPath(),
                 ];
@@ -187,17 +185,14 @@ class BackendLayoutView implements SingletonInterface
      */
     protected function getIdentifiersToBeExcluded(array $pageTSconfig): array
     {
-        $identifiersToBeExcluded = [];
-
-        if (ArrayUtility::isValidPath($pageTSconfig, 'options./backendLayout./exclude')) {
-            $identifiersToBeExcluded = GeneralUtility::trimExplode(
+        if (isset($pageTSconfig['options.']['backendLayout.']['exclude'])) {
+            return GeneralUtility::trimExplode(
                 ',',
-                ArrayUtility::getValueByPath($pageTSconfig, 'options./backendLayout./exclude'),
+                $pageTSconfig['options.']['backendLayout.']['exclude'],
                 true
             );
         }
-
-        return $identifiersToBeExcluded;
+        return [];
     }
 
     /**
@@ -226,62 +221,6 @@ class BackendLayoutView implements SingletonInterface
         return $items;
     }
 
-    /**
-     * Gets the list of available columns for a given page id
-     * @todo: will be removed once Page Position Map for content is removed.
-     */
-    public function getColPosListItemsParsed(int $id): array
-    {
-        $tsConfig = BackendUtility::getPagesTSconfig($id)['TCEFORM.']['tt_content.']['colPos.'] ?? [];
-        $tcaConfig = $GLOBALS['TCA']['tt_content']['columns']['colPos']['config'] ?? [];
-        $tcaItems = $tcaConfig['items'];
-        $tcaItems = $this->addItems($tcaItems, $tsConfig['addItems.'] ?? []);
-        if (isset($tcaConfig['itemsProcFunc']) && $tcaConfig['itemsProcFunc']) {
-            $tcaItems = $this->addColPosListLayoutItems($id, $tcaItems);
-        }
-        if (!empty($tsConfig['removeItems'])) {
-            foreach (GeneralUtility::trimExplode(',', $tsConfig['removeItems'], true) as $removeId) {
-                foreach ($tcaItems as $key => $item) {
-                    if ($item[1] == $removeId) {
-                        unset($tcaItems[$key]);
-                    }
-                }
-            }
-        }
-        return $tcaItems;
-    }
-
-    /**
-     * Merges items into an item-array, optionally with an icon
-     * example:
-     * TCEFORM.pages.doktype.addItems.13 = My Label
-     * TCEFORM.pages.doktype.addItems.13.icon = EXT:t3skin/icons/gfx/i/pages.gif
-     *
-     * @param array $items The existing item array
-     * @param array $iArray An array of items to add. NOTICE: The keys are mapped to values, and the values and mapped to be labels. No possibility of adding an icon.
-     * @return array The updated $item array
-     * @internal
-     */
-    protected function addItems(array $items, array $iArray): array
-    {
-        $languageService = $this->getLanguageService();
-        foreach ($iArray as $value => $label) {
-            // if the label is an array (that means it is a subelement
-            // like "34.icon = mylabel.png", skip it (see its usage below)
-            if (is_array($label)) {
-                continue;
-            }
-            // check if the value "34 = mylabel" also has a "34.icon = myimage.png"
-            if (isset($iArray[$value . '.']) && $iArray[$value . '.']['icon']) {
-                $icon = $iArray[$value . '.']['icon'];
-            } else {
-                $icon = '';
-            }
-            $items[] = [$languageService->sL($label), $value, $icon];
-        }
-        return $items;
-    }
-
     /**
      * Gets the selected backend layout structure as an array
      */
@@ -342,7 +281,7 @@ class BackendLayoutView implements SingletonInterface
                             continue;
                         }
                         $backendLayoutData['__items'][] = [
-                            'label' => $this->getColumnName($column),
+                            'label' => $column['name'],
                             'value' => $column['colPos'],
                             'icon' => null,
                         ];
@@ -404,17 +343,4 @@ class BackendLayoutView implements SingletonInterface
 
         return is_array($page) ? $page : null;
     }
-
-    protected function getLanguageService(): LanguageService
-    {
-        return $GLOBALS['LANG'];
-    }
-
-    /**
-     * Get column name from colPos item structure
-     */
-    protected function getColumnName(array $column): string
-    {
-        return $this->getLanguageService()->sL($column['name']);
-    }
 }
-- 
GitLab