Revert "[TASK] Avoid slow array functions in loops"
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Controller / Page / TreeController.php
index d0b942c..79f8c53 100644 (file)
@@ -17,22 +17,28 @@ namespace TYPO3\CMS\Backend\Controller\Page;
 
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
-use TYPO3\CMS\Backend\Controller\UserSettingsController;
+use TYPO3\CMS\Backend\Configuration\BackendUserConfiguration;
 use TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
-use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Database\Query\Restriction\DocumentTypeExclusionRestriction;
+use TYPO3\CMS\Core\Exception\Page\RootLineException;
+use TYPO3\CMS\Core\Exception\SiteNotFoundException;
 use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
+use TYPO3\CMS\Core\Localization\LanguageService;
+use TYPO3\CMS\Core\Site\SiteFinder;
 use TYPO3\CMS\Core\Type\Bitmask\JsConfirmation;
 use TYPO3\CMS\Core\Type\Bitmask\Permission;
 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\RootlineUtility;
 use TYPO3\CMS\Workspaces\Service\WorkspaceService;
 
 /**
  * Controller providing data to the page tree
+ * @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
  */
 class TreeController
 {
@@ -86,13 +92,6 @@ class TreeController
     protected $expandedState = [];
 
     /**
-     * Associative array containing all pageIds as key, and domain names as values.
-     *
-     * @var array|null
-     */
-    protected $domains = null;
-
-    /**
      * Instance of the icon factory, to be used for generating the items.
      *
      * @var IconFactory
@@ -105,7 +104,7 @@ class TreeController
     public function __construct()
     {
         $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
-        $this->useNavTitle = (bool)$this->getBackendUser()->getTSConfigVal('options.pageTree.showNavTitle');
+        $this->useNavTitle = (bool)($this->getBackendUser()->getTSConfig()['options.']['pageTree.']['showNavTitle'] ?? false);
     }
 
     /**
@@ -135,6 +134,7 @@ class TreeController
      */
     protected function getDokTypes(): array
     {
+        $backendUser = $this->getBackendUser();
         $doktypeLabelMap = [];
         foreach ($GLOBALS['TCA']['pages']['columns']['doktype']['config']['items'] as $doktypeItemConfig) {
             if ($doktypeItemConfig[1] === '--div--') {
@@ -142,10 +142,10 @@ class TreeController
             }
             $doktypeLabelMap[$doktypeItemConfig[1]] = $doktypeItemConfig[0];
         }
-        $doktypes = GeneralUtility::intExplode(',', $this->getBackendUser()->getTSConfigVal('options.pageTree.doktypesToShowInNewPageDragArea'), true);
+        $doktypes = GeneralUtility::intExplode(',', $backendUser->getTSConfig()['options.']['pageTree.']['doktypesToShowInNewPageDragArea'] ?? '', true);
         $output = [];
-        $allowedDoktypes = GeneralUtility::intExplode(',', $this->getBackendUser()->groupData['pagetypes_select'], true);
-        $isAdmin = $this->getBackendUser()->isAdmin();
+        $allowedDoktypes = GeneralUtility::intExplode(',', $backendUser->groupData['pagetypes_select'], true);
+        $isAdmin = $backendUser->isAdmin();
         // Early return if backend user may not create any doktype
         if (!$isAdmin && empty($allowedDoktypes)) {
             return $output;
@@ -154,7 +154,7 @@ class TreeController
             if (!$isAdmin && !in_array($doktype, $allowedDoktypes, true)) {
                 continue;
             }
-            $label = htmlspecialchars($GLOBALS['LANG']->sL($doktypeLabelMap[$doktype]));
+            $label = htmlspecialchars($this->getLanguageService()->sL($doktypeLabelMap[$doktype]));
             $output[] = [
                 'nodeType' => $doktype,
                 'icon' => $GLOBALS['TCA']['pages']['ctrl']['typeicon_classes'][$doktype] ?? '',
@@ -173,14 +173,15 @@ class TreeController
      */
     public function fetchDataAction(ServerRequestInterface $request): ResponseInterface
     {
-        $this->hiddenRecords = GeneralUtility::intExplode(',', $this->getBackendUser()->getTSConfigVal('options.hideRecords.pages'), true);
-        $this->backgroundColors = $this->getBackendUser()->getTSConfigProp('options.pageTree.backgroundColor');
-        $this->addIdAsPrefix = (bool)$this->getBackendUser()->getTSConfigVal('options.pageTree.showPageIdWithTitle');
-        $this->addDomainName = (bool)$this->getBackendUser()->getTSConfigVal('options.pageTree.showDomainNameWithTitle');
-        $this->showMountPathAboveMounts = (bool)$this->getBackendUser()->getTSConfigVal('options.pageTree.showPathAboveMounts');
-        $userSettingsController = GeneralUtility::makeInstance(UserSettingsController::class);
-        $this->expandedState = $userSettingsController->process('get', 'BackendComponents.States.Pagetree');
-        if (is_object($this->expandedState->stateHash)) {
+        $userTsConfig = $this->getBackendUser()->getTSConfig();
+        $this->hiddenRecords = GeneralUtility::intExplode(',', $userTsConfig['options.']['hideRecords.']['pages'] ?? '', true);
+        $this->backgroundColors = $userTsConfig['options.']['pageTree.']['backgroundColor.'] ?? [];
+        $this->addIdAsPrefix = (bool)($userTsConfig['options.']['pageTree.']['showPageIdWithTitle'] ?? false);
+        $this->addDomainName = (bool)($userTsConfig['options.']['pageTree.']['showDomainNameWithTitle'] ?? false);
+        $this->showMountPathAboveMounts = (bool)($userTsConfig['options.']['pageTree.']['showPathAboveMounts'] ?? false);
+        $backendUserConfiguration = GeneralUtility::makeInstance(BackendUserConfiguration::class);
+        $this->expandedState = $backendUserConfiguration->get('BackendComponents.States.Pagetree');
+        if (is_object($this->expandedState) && is_object($this->expandedState->stateHash)) {
             $this->expandedState = (array)$this->expandedState->stateHash;
         } else {
             $this->expandedState = $this->expandedState['stateHash'] ?: [];
@@ -242,10 +243,10 @@ class TreeController
             return [];
         }
 
-        $stopPageTree = $page['php_tree_stop'] && $depth > 0;
+        $stopPageTree = !empty($page['php_tree_stop']) && $depth > 0;
         $identifier = $entryPoint . '_' . $pageId;
-        $expanded = $page['expanded'] || (isset($this->expandedState[$identifier]) && $this->expandedState[$identifier]);
-        $backgroundColor = $this->backgroundColors[$pageId] ?: ($inheritedData['backgroundColor'] ?? '');
+        $expanded = !empty($page['expanded']) || (isset($this->expandedState[$identifier]) && $this->expandedState[$identifier]);
+        $backgroundColor = !empty($this->backgroundColors[$pageId]) ? $this->backgroundColors[$pageId] : ($inheritedData['backgroundColor'] ?? '');
 
         $suffix = '';
         $prefix = '';
@@ -263,11 +264,10 @@ class TreeController
             $visibleText = $page['nav_title'];
         }
         if (trim($visibleText) === '') {
-            $visibleText = htmlspecialchars('[' . $GLOBALS['LANG']->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.no_title') . ']');
+            $visibleText = htmlspecialchars('[' . $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.no_title') . ']');
         }
-        $visibleText = GeneralUtility::fixed_lgd_cs($visibleText, (int)$this->getBackendUser()->uc['titleLen'] ?: 40);
 
-        if ($this->addDomainName) {
+        if ($this->addDomainName && $page['is_siteroot']) {
             $domain = $this->getDomainNameForPage($pageId);
             $suffix = $domain !== '' ? ' [' . $domain . ']' : '';
         }
@@ -275,10 +275,9 @@ class TreeController
         $lockInfo = BackendUtility::isRecordLocked('pages', $pageId);
         if (is_array($lockInfo)) {
             $tooltip .= ' - ' . $lockInfo['msg'];
-            $prefix = '<span class="typo3-pagetree-status">' . $this->iconFactory->getIcon('warning-in-use', Icon::SIZE_SMALL)->render() . '</span>';
         }
         if ($this->addIdAsPrefix) {
-            $prefix .= htmlspecialchars('[' . $pageId . '] ');
+            $prefix = htmlspecialchars('[' . $pageId . '] ');
         }
 
         $items = [];
@@ -292,9 +291,9 @@ class TreeController
             'icon' => $icon->getIdentifier(),
             'name' => $visibleText,
             'nameSourceField' => $nameSourceField,
-            'alias' => htmlspecialchars($page['alias'] ?: ''),
             'prefix' => htmlspecialchars($prefix),
             'suffix' => htmlspecialchars($suffix),
+            'locked' => is_array($lockInfo),
             'overlayIcon' => $icon->getOverlayIcon() ? $icon->getOverlayIcon()->getIdentifier() : '',
             'selectable' => true,
             'expanded' => (bool)$expanded,
@@ -305,7 +304,7 @@ class TreeController
             'readableRootline' => $depth === 0 && $this->showMountPathAboveMounts ? $this->getMountPointPath($pageId) : '',
             'isMountPoint' => $depth === 0,
             'mountPoint' => $entryPoint,
-            'workspaceId' => $page['t3ver_oid'] ?: $pageId,
+            'workspaceId' => !empty($page['t3ver_oid']) ? $page['t3ver_oid'] : $pageId,
         ];
         if (!$stopPageTree) {
             foreach ($page['_children'] as $child) {
@@ -323,7 +322,19 @@ class TreeController
     protected function getAllEntryPointPageTrees(): array
     {
         $backendUser = $this->getBackendUser();
-        $repository = GeneralUtility::makeInstance(PageTreeRepository::class, (int)$backendUser->workspace);
+
+        $userTsConfig = $this->getBackendUser()->getTSConfig();
+        $excludedDocumentTypes = GeneralUtility::intExplode(',', $userTsConfig['options.']['pageTree.']['excludeDoktypes'] ?? '', true);
+
+        $additionalPageTreeQueryRestrictions = [];
+        if (!empty($excludedDocumentTypes)) {
+            foreach ($excludedDocumentTypes as $excludedDocumentType) {
+                $additionalPageTreeQueryRestrictions[] = new DocumentTypeExclusionRestriction((int)$excludedDocumentType);
+            }
+        }
+
+        $repository = GeneralUtility::makeInstance(PageTreeRepository::class, (int)$backendUser->workspace, [], $additionalPageTreeQueryRestrictions);
+
         $entryPoints = (int)($backendUser->uc['pageTree_temporaryMountPoint'] ?? 0);
         if ($entryPoints > 0) {
             $entryPoints = [$entryPoints];
@@ -340,11 +351,27 @@ class TreeController
         if (empty($entryPoints)) {
             return [];
         }
+
         foreach ($entryPoints as $k => &$entryPoint) {
             if (in_array($entryPoint, $this->hiddenRecords, true)) {
                 unset($entryPoints[$k]);
                 continue;
             }
+
+            if (!empty($this->backgroundColors) && is_array($this->backgroundColors)) {
+                try {
+                    $entryPointRootLine = GeneralUtility::makeInstance(RootlineUtility::class, $entryPoint)->get();
+                } catch (RootLineException $e) {
+                    $entryPointRootLine = [];
+                }
+                foreach ($entryPointRootLine as $rootLineEntry) {
+                    $parentUid = $rootLineEntry['uid'];
+                    if (!empty($this->backgroundColors[$parentUid]) && empty($this->backgroundColors[$entryPoint])) {
+                        $this->backgroundColors[$entryPoint] = $this->backgroundColors[$parentUid];
+                    }
+                }
+            }
+
             $entryPoint = $repository->getTree($entryPoint, function ($page) use ($backendUser) {
                 // check each page if the user has permission to access it
                 return $backendUser->doesUserHaveAccess($page, Permission::PAGE_SHOW);
@@ -365,24 +392,16 @@ class TreeController
      */
     protected function getDomainNameForPage(int $pageId): string
     {
-        if (!is_array($this->domains)) {
-            $this->domains = [];
-            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
-                ->getQueryBuilderForTable('sys_domain');
-            $result = $queryBuilder
-                ->select('domainName', 'pid')
-                ->from('sys_domain')
-                ->orderBy('sorting')
-                ->execute()
-                ->fetchAll();
-            foreach ($result as $domain) {
-                $domainPid = (int)$domain['pid'];
-                if (!isset($this->domains[$domainPid])) {
-                    $this->domains[$domainPid] = $domain['domainName'];
-                }
-            }
+        $domain = '';
+        $siteFinder = GeneralUtility::makeInstance(SiteFinder::class);
+        try {
+            $site = $siteFinder->getSiteByRootPageId($pageId);
+            $domain = (string)$site->getBase();
+        } catch (SiteNotFoundException $e) {
+            // No site found
         }
-        return $this->domains[$pageId] ?? '';
+
+        return $domain;
     }
 
     /**
@@ -420,6 +439,9 @@ class TreeController
     {
         $classes = [];
 
+        if ($page['uid'] === 0) {
+            return '';
+        }
         $workspaceId = (int)$this->getBackendUser()->workspace;
         if ($workspaceId > 0 && ExtensionManagementUtility::isLoaded('workspaces')) {
             if ($page['t3ver_oid'] > 0 && (int)$page['t3ver_wsid'] === $workspaceId) {
@@ -453,4 +475,12 @@ class TreeController
     {
         return $GLOBALS['BE_USER'];
     }
+
+    /**
+     * @return LanguageService|null
+     */
+    protected function getLanguageService(): ?LanguageService
+    {
+        return $GLOBALS['LANG'] ?? null;
+    }
 }