diff --git a/typo3/sysext/backend/Classes/View/BackendLayoutView.php b/typo3/sysext/backend/Classes/View/BackendLayoutView.php index c65a8d47057286a54f770f4e97274e3dc7b1f323..3ede3dfb0f30da4f83f5f908226a19ea899533a0 100644 --- a/typo3/sysext/backend/Classes/View/BackendLayoutView.php +++ b/typo3/sysext/backend/Classes/View/BackendLayoutView.php @@ -24,6 +24,7 @@ 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\Page\PageLayoutResolver; use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\TypoScript\TypoScriptStringFactory; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -43,6 +44,7 @@ class BackendLayoutView implements SingletonInterface public function __construct( private readonly DataProviderCollection $dataProviderCollection, private readonly TypoScriptStringFactory $typoScriptStringFactory, + private readonly PageLayoutResolver $pageLayoutResolver, ) { $this->dataProviderCollection->add('default', DefaultDataProvider::class); foreach ((array)($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['BackendLayoutDataProvider'] ?? []) as $identifier => $className) { @@ -146,35 +148,26 @@ class BackendLayoutView implements SingletonInterface * Returns the backend layout which should be used for this page. * * @return false|string Identifier of the backend layout to be used, or FALSE if none + * @internal only public for testing purposes */ - protected function getSelectedCombinedIdentifier(int $pageId): string|false + public function getSelectedCombinedIdentifier(int $pageId): string|false { if (!isset($this->selectedCombinedIdentifier[$pageId])) { - $page = $this->getPage($pageId); - $this->selectedCombinedIdentifier[$pageId] = (string)($page['backend_layout'] ?? null); - if ($this->selectedCombinedIdentifier[$pageId] === '-1') { + // If it not set check the root-line for a layout on next level and use this + // (root-line starts with current page and has page "0" at the end) + $rootLine = BackendUtility::BEgetRootLine($pageId, '', true); + // Use first element as current page, + $page = reset($rootLine); + // and remove last element (root page / pid=0) + array_pop($rootLine); + $selectedLayout = $this->pageLayoutResolver->getLayoutIdentifierForPage($page, $rootLine); + if ($selectedLayout === 'none') { // If it is set to "none" - don't use any - $this->selectedCombinedIdentifier[$pageId] = false; - } elseif ($this->selectedCombinedIdentifier[$pageId] === '' || $this->selectedCombinedIdentifier[$pageId] === '0') { - // If it not set check the root-line for a layout on next level and use this - // (root-line starts with current page and has page "0" at the end) - $rootLine = BackendUtility::BEgetRootLine($pageId, '', true); - // Remove first and last element (current and root page) - array_shift($rootLine); - array_pop($rootLine); - foreach ($rootLine as $rootLinePage) { - $this->selectedCombinedIdentifier[$pageId] = (string)$rootLinePage['backend_layout_next_level']; - if ($this->selectedCombinedIdentifier[$pageId] === '-1') { - // If layout for "next level" is set to "none" - don't use any and stop searching - $this->selectedCombinedIdentifier[$pageId] = false; - break; - } - if ($this->selectedCombinedIdentifier[$pageId] !== '' && $this->selectedCombinedIdentifier[$pageId] !== '0') { - // Stop searching if a layout for "next level" is set - break; - } - } + $selectedLayout = false; + } elseif ($selectedLayout === 'default') { + $selectedLayout = '0'; } + $this->selectedCombinedIdentifier[$pageId] = $selectedLayout; } // If it is set to a positive value use this return $this->selectedCombinedIdentifier[$pageId]; @@ -316,31 +309,4 @@ class BackendLayoutView implements SingletonInterface } '; } - - /** - * Gets a page record. - */ - protected function getPage(int $pageId): ?array - { - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) - ->getQueryBuilderForTable('pages'); - $queryBuilder->getRestrictions() - ->removeAll(); - $page = $queryBuilder - ->select('uid', 'pid', 'backend_layout') - ->from('pages') - ->where( - $queryBuilder->expr()->eq( - 'uid', - $queryBuilder->createNamedParameter($pageId, Connection::PARAM_INT) - ) - ) - ->executeQuery() - ->fetchAssociative(); - if (is_array($page)) { - BackendUtility::workspaceOL('pages', $page); - } - - return is_array($page) ? $page : null; - } } diff --git a/typo3/sysext/backend/Tests/Functional/View/BackendLayoutViewTest.php b/typo3/sysext/backend/Tests/Functional/View/BackendLayoutViewTest.php index a5904c93772488b196d33beb72534a86b3dfb001..55b00a3b02a64d74e829c073f738bce1d8007747 100644 --- a/typo3/sysext/backend/Tests/Functional/View/BackendLayoutViewTest.php +++ b/typo3/sysext/backend/Tests/Functional/View/BackendLayoutViewTest.php @@ -19,11 +19,9 @@ namespace TYPO3\CMS\Backend\Tests\Functional\View; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; -use PHPUnit\Framework\MockObject\MockObject; use TYPO3\CMS\Backend\View\BackendLayoutView; use TYPO3\CMS\Core\Cache\CacheManager; use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface; -use TYPO3\TestingFramework\Core\AccessibleObjectInterface; use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; final class BackendLayoutViewTest extends FunctionalTestCase @@ -31,19 +29,13 @@ final class BackendLayoutViewTest extends FunctionalTestCase private const RUNTIME_CACHE_ENTRY = 'backendUtilityBeGetRootLine'; private FrontendInterface $runtimeCache; - private BackendLayoutView&MockObject&AccessibleObjectInterface $backendLayoutView; + private BackendLayoutView $subject; protected function setUp(): void { parent::setUp(); $this->runtimeCache = $this->get(CacheManager::class)->getCache('runtime'); - $this->backendLayoutView = $this->getAccessibleMock( - BackendLayoutView::class, - ['getPage'], - [], - '', - false - ); + $this->subject = $this->get(BackendLayoutView::class); } protected function tearDown(): void @@ -61,11 +53,7 @@ final class BackendLayoutViewTest extends FunctionalTestCase $this->mockRootLine((int)$pageId, $rootLine); } - $this->backendLayoutView->expects(self::once()) - ->method('getPage')->with(self::equalTo($pageId)) - ->willReturn($page); - - $selectedCombinedIdentifier = $this->backendLayoutView->_call('getSelectedCombinedIdentifier', $pageId); + $selectedCombinedIdentifier = $this->subject->getSelectedCombinedIdentifier($pageId); self::assertEquals($expected, $selectedCombinedIdentifier); } diff --git a/typo3/sysext/core/Classes/Page/PageLayout.php b/typo3/sysext/core/Classes/Page/PageLayout.php new file mode 100644 index 0000000000000000000000000000000000000000..61b49254a034cd7a23f24db98001f1a91f527f03 --- /dev/null +++ b/typo3/sysext/core/Classes/Page/PageLayout.php @@ -0,0 +1,49 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3\CMS\Core\Page; + +/** + * Contains information about the layout of a page, + * mainly which content areas (colPos=0, colPos=1, ...) are used and filled. + * + * @internal This is not part of TYPO3 Core API. + */ +class PageLayout +{ + public function __construct( + protected string $identifier, + protected string $title, + protected array $contentAreas, + protected array $fullConfiguration + ) {} + + public function getIdentifier(): string + { + return $this->identifier; + } + + public function getTitle(): string + { + return $this->title; + } + + public function getContentAreas(): array + { + return $this->contentAreas; + } +} diff --git a/typo3/sysext/core/Classes/Page/PageLayoutResolver.php b/typo3/sysext/core/Classes/Page/PageLayoutResolver.php new file mode 100644 index 0000000000000000000000000000000000000000..df7c330eb4a395256f46fa0ec56c13e8ebf3d8a0 --- /dev/null +++ b/typo3/sysext/core/Classes/Page/PageLayoutResolver.php @@ -0,0 +1,134 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3\CMS\Core\Page; + +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\Site\SiteFinder; +use TYPO3\CMS\Core\TypoScript\PageTsConfigFactory; +use TYPO3\CMS\Core\Utility\GeneralUtility; + +/** + * Finds the proper layout for a page, using the database fields "backend_layout" + * and "backend_layout_next_level". + * + * The most crucial part is that "backend_layout" is only applied for the CURRENT level, + * whereas backend_layout_next_level. + * + * Used in TypoScript as "getData:pagelayout". + * + * Currently, there is a hard dependency on EXT:backend however, all DataProvider logic should be migrated + * towards EXT:core. + * + * @internal This is not part of TYPO3 Core API. + */ +class PageLayoutResolver +{ + public function __construct( + protected readonly DataProviderCollection $dataProviderCollection, + protected readonly SiteFinder $siteFinder, + protected readonly PageTsConfigFactory $pageTsConfigFactory + ) { + $this->dataProviderCollection->add('default', DefaultDataProvider::class); + foreach ((array)($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['BackendLayoutDataProvider'] ?? []) as $identifier => $className) { + $this->dataProviderCollection->add($identifier, $className); + } + } + + public function getLayoutForPage(array $pageRecord, array $rootLine): ?PageLayout + { + $pageId = (int)$pageRecord['uid']; + $site = $this->siteFinder->getSiteByPageId($pageId, $rootLine); + $pageTsConfig = $this->pageTsConfigFactory->create($rootLine, $site); + + $dataProviderContext = GeneralUtility::makeInstance(DataProviderContext::class); + $dataProviderContext + ->setPageId($pageId) + ->setData($pageRecord) + ->setTableName('pages') + ->setFieldName('backend_layout') + ->setPageTsConfig($pageTsConfig->getPageTsConfigArray()); + + $selectedPageLayout = $this->getLayoutIdentifierForPage($pageRecord, $rootLine); + $layout = $this->dataProviderCollection->getBackendLayout($selectedPageLayout, $pageId); + + if ($layout === null) { + return null; + } + + $fullStructure = $layout->getStructure()['__config']; + $contentAreas = []; + // find all arrays recursively from , where one of the columns within the array is called "colPos" + $findColPos = function (array $structure) use (&$findColPos, &$contentAreas) { + if (isset($structure['colPos'])) { + unset($structure['colspan'], $structure['rowspan']); + $contentAreas[] = $structure; + } + foreach ($structure as $value) { + if (is_array($value)) { + $findColPos($value); + } + } + }; + $findColPos($fullStructure); + + return new PageLayout($layout->getIdentifier(), $layout->getTitle(), $contentAreas, $layout->getStructure()); + } + + /** + * Check if the current page has a value in the DB field "backend_layout" + * if empty, check the root line for "backend_layout_next_level" + * Same as TypoScript: + * field = backend_layout + * ifEmpty.data = levelfield:-2, backend_layout_next_level, slide + * ifEmpty.ifEmpty = default + */ + public function getLayoutIdentifierForPage(array $page, array $rootLine): string + { + $selectedLayout = $page['backend_layout'] ?? ''; + + // If it is set to "none" - don't use any + if ($selectedLayout === '-1') { + return 'none'; + } + + if ($selectedLayout === '' || $selectedLayout === '0') { + // If it not set check the root-line for a layout on next level and use this + // Remove first element, which is the current page + // See also \TYPO3\CMS\Backend\View\BackendLayoutView::getSelectedCombinedIdentifier() + array_shift($rootLine); + foreach ($rootLine as $rootLinePage) { + $selectedLayout = (string)($rootLinePage['backend_layout_next_level'] ?? ''); + // If layout for "next level" is set to "none" - don't use any and stop searching + if ($selectedLayout === '-1') { + $selectedLayout = 'none'; + break; + } + if ($selectedLayout !== '' && $selectedLayout !== '0') { + // Stop searching if a layout for "next level" is set + break; + } + } + } + if ($selectedLayout === '0' || $selectedLayout === '') { + $selectedLayout = 'default'; + } + return $selectedLayout; + } +} diff --git a/typo3/sysext/core/Classes/TypoScript/IncludeTree/Visitor/IncludeTreeConditionMatcherVisitor.php b/typo3/sysext/core/Classes/TypoScript/IncludeTree/Visitor/IncludeTreeConditionMatcherVisitor.php index ebf959c667b4e46eef71f6efa8fbc44d17c9adad..f41520121c7a95ac8ce260d7cfd56c388724458c 100644 --- a/typo3/sysext/core/Classes/TypoScript/IncludeTree/Visitor/IncludeTreeConditionMatcherVisitor.php +++ b/typo3/sysext/core/Classes/TypoScript/IncludeTree/Visitor/IncludeTreeConditionMatcherVisitor.php @@ -26,9 +26,9 @@ use TYPO3\CMS\Core\Context\UserAspect; use TYPO3\CMS\Core\Context\WorkspaceAspect; use TYPO3\CMS\Core\ExpressionLanguage\RequestWrapper; use TYPO3\CMS\Core\ExpressionLanguage\Resolver; +use TYPO3\CMS\Core\Page\PageLayoutResolver; use TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode\IncludeConditionInterface; use TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode\IncludeInterface; -use TYPO3\CMS\Frontend\Page\PageLayoutResolver; /** * A visitor that looks at IncludeConditionInterface nodes and @@ -122,7 +122,7 @@ final class IncludeTreeConditionMatcherVisitor implements IncludeTreeVisitorInte // the 'nearest' parent. However, here it is always passed sorted, so it is a top-down rootLine. Hence, this needs to be once // again reversed at this point. $bottomUpFullRootLine = array_reverse($fullRootLine); - $tree->pagelayout = $this->pageLayoutResolver->getLayoutForPage($variables['page'], $bottomUpFullRootLine); + $tree->pagelayout = $this->pageLayoutResolver->getLayoutIdentifierForPage($variables['page'], $bottomUpFullRootLine); $enrichedVariables['tree'] = $tree; } diff --git a/typo3/sysext/core/Configuration/Services.yaml b/typo3/sysext/core/Configuration/Services.yaml index 7d38fbae51747057424f3249e9c005148fe19175..138be5a334626967dfe5720e17f0c2bc06e71f76 100644 --- a/typo3/sysext/core/Configuration/Services.yaml +++ b/typo3/sysext/core/Configuration/Services.yaml @@ -213,6 +213,9 @@ services: TYPO3\CMS\Core\Locking\ResourceMutex: public: true + TYPO3\CMS\Core\Page\PageLayoutResolver: + public: true + TYPO3\CMS\Core\Page\PageRenderer: arguments: $assetsCache: '@cache.assets' diff --git a/typo3/sysext/core/Tests/Functional/Page/PageLayoutResolverTest.php b/typo3/sysext/core/Tests/Functional/Page/PageLayoutResolverTest.php new file mode 100644 index 0000000000000000000000000000000000000000..11021c298aa9943cf8fd8058d4cc553a7c081cea --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Page/PageLayoutResolverTest.php @@ -0,0 +1,87 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3\CMS\Core\Tests\Functional\Page; + +use PHPUnit\Framework\Attributes\Test; +use TYPO3\CMS\Core\Page\PageLayoutResolver; +use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; + +final class PageLayoutResolverTest extends FunctionalTestCase +{ + #[Test] + public function getLayoutIdentifierForPageFetchesSelectedPageDirectly(): void + { + $subject = $this->get(PageLayoutResolver::class); + $result = $subject->getLayoutIdentifierForPage(['backend_layout' => '1'], ['does-not-matter']); + self::assertEquals('1', $result); + } + + #[Test] + public function getLayoutIdentifierForPageTreatsSpecialMinusOneValueAsNone(): void + { + $subject = $this->get(PageLayoutResolver::class); + $result = $subject->getLayoutIdentifierForPage(['backend_layout' => '-1'], ['does-not-matter']); + self::assertEquals('none', $result); + } + + #[Test] + public function getLayoutIdentifierForPageTreatsSpecialValueZeroOrEmptyAsDefaultWithEmptyRootLine(): void + { + $subject = $this->get(PageLayoutResolver::class); + $parentPages = [['backend_layout' => '']]; + $page = ['backend_layout' => '0', 'uid' => 123]; + $result = $subject->getLayoutIdentifierForPage($page, array_merge([$page], $parentPages)); + self::assertEquals('default', $result); + $page = ['backend_layout' => '', 'uid' => 123]; + $result = $subject->getLayoutIdentifierForPage($page, array_merge([$page], $parentPages)); + self::assertEquals('default', $result); + } + + #[Test] + public function getLayoutIdentifierForPageTreatsSpecialValueZeroOrEmptyAsDefaultWhenNothingGivenInRootLine(): void + { + $subject = $this->get(PageLayoutResolver::class); + // No layout specified for current page + $page = ['backend_layout' => '', 'uid' => 123]; + $parentPages = [['uid' => 13, 'backend_layout' => 'does-not-matter'], ['uid' => 1, 'backend_layout_next_level' => '0']]; + $result = $subject->getLayoutIdentifierForPage($page, array_merge([$page], $parentPages)); + self::assertEquals('default', $result); + } + + #[Test] + public function getLayoutIdentifierForPageFetchesRootLinePagesUpUntilSomethingWasFound(): void + { + $subject = $this->get(PageLayoutResolver::class); + // No layout specified for current page + $page = ['backend_layout' => '', 'uid' => 123]; + $parentPages = [['uid' => 13, 'backend_layout' => 'does-not-matter', 'backend_layout_next_level' => ''], ['uid' => 1, 'backend_layout_next_level' => 'regular']]; + $result = $subject->getLayoutIdentifierForPage($page, array_merge([$page], $parentPages)); + self::assertEquals('regular', $result); + } + + #[Test] + public function getLayoutIdentifierForPageFetchesRootLinePagesUpWhenNoneWasSelectedExplicitly(): void + { + $subject = $this->get(PageLayoutResolver::class); + // No layout specified for current page + $page = ['backend_layout' => '', 'uid' => 123]; + $parentPages = [['uid' => 13, 'backend_layout' => 'does-not-matter'], ['uid' => 15, 'backend_layout_next_level' => '-1'], ['uid' => 1, 'backend_layout_next_level' => 'regular']]; + $result = $subject->getLayoutIdentifierForPage($page, array_merge([$page], $parentPages)); + self::assertEquals('none', $result); + } +} diff --git a/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php b/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php index 19b4dd829a1a3602c80fa7cb98b18e59d27d8e90..a854c8de238bb6712582faffdd2f85b0f0662d76 100644 --- a/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php +++ b/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php @@ -47,6 +47,7 @@ use TYPO3\CMS\Core\Localization\LanguageServiceFactory; use TYPO3\CMS\Core\Localization\Locales; use TYPO3\CMS\Core\Log\LogManager; use TYPO3\CMS\Core\Page\DefaultJavaScriptAssetTrait; +use TYPO3\CMS\Core\Page\PageLayoutResolver; use TYPO3\CMS\Core\Page\PageRenderer; use TYPO3\CMS\Core\Resource\Exception; use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException; @@ -82,7 +83,6 @@ use TYPO3\CMS\Frontend\ContentObject\Exception\ExceptionHandlerInterface; use TYPO3\CMS\Frontend\ContentObject\Exception\ProductionExceptionHandler; use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; use TYPO3\CMS\Frontend\Imaging\GifBuilder; -use TYPO3\CMS\Frontend\Page\PageLayoutResolver; use TYPO3\CMS\Frontend\Resource\FilePathSanitizer; use TYPO3\CMS\Frontend\Typolink\LinkFactory; use TYPO3\CMS\Frontend\Typolink\LinkResult; @@ -3901,7 +3901,7 @@ class ContentObjectRenderer implements LoggerAwareInterface case 'pagelayout': $pageInformation = $this->getRequest()->getAttribute('frontend.page.information'); $pageLayoutResolver = GeneralUtility::makeInstance(PageLayoutResolver::class); - $retVal = $pageLayoutResolver->getLayoutForPage($pageInformation->getPageRecord(), $pageInformation->getRootLine()); + $retVal = $pageLayoutResolver->getLayoutIdentifierForPage($pageInformation->getPageRecord(), $pageInformation->getRootLine()); break; case 'current': $retVal = $this->data[$this->currentValKey] ?? null; diff --git a/typo3/sysext/frontend/Classes/Page/PageLayoutResolver.php b/typo3/sysext/frontend/Classes/Page/PageLayoutResolver.php deleted file mode 100644 index 2d11653001655dadb4ba06d20d2d135b5387c49d..0000000000000000000000000000000000000000 --- a/typo3/sysext/frontend/Classes/Page/PageLayoutResolver.php +++ /dev/null @@ -1,73 +0,0 @@ -<?php - -declare(strict_types=1); - -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - -namespace TYPO3\CMS\Frontend\Page; - -/** - * Finds the proper layout for a page, using the database fields "backend_layout" - * and "backend_layout_next_level". - * - * The most crucial part is that "backend_layout" is only applied for the CURRENT level, - * whereas backend_layout_next_level. - * - * Used in TypoScript as "getData: pagelayout". - * - * @internal as this might get moved to EXT:core if usages in TYPO3 Backend are helpful as well. - */ -class PageLayoutResolver -{ - /** - * Check if the current page has a value in the DB field "backend_layout" - * if empty, check the root line for "backend_layout_next_level" - * Same as TypoScript: - * field = backend_layout - * ifEmpty.data = levelfield:-2, backend_layout_next_level, slide - * ifEmpty.ifEmpty = default - */ - public function getLayoutForPage(array $page, array $rootLine): string - { - $selectedLayout = $page['backend_layout'] ?? ''; - - // If it is set to "none" - don't use any - if ($selectedLayout === '-1') { - return 'none'; - } - - if ($selectedLayout === '' || $selectedLayout === '0') { - // If it not set check the root-line for a layout on next level and use this - // Remove first element, which is the current page - // See also \TYPO3\CMS\Backend\View\BackendLayoutView::getSelectedCombinedIdentifier() - array_shift($rootLine); - foreach ($rootLine as $rootLinePage) { - $selectedLayout = (string)($rootLinePage['backend_layout_next_level'] ?? ''); - // If layout for "next level" is set to "none" - don't use any and stop searching - if ($selectedLayout === '-1') { - $selectedLayout = 'none'; - break; - } - if ($selectedLayout !== '' && $selectedLayout !== '0') { - // Stop searching if a layout for "next level" is set - break; - } - } - } - if ($selectedLayout === '0' || $selectedLayout === '') { - $selectedLayout = 'default'; - } - return $selectedLayout; - } -} diff --git a/typo3/sysext/frontend/Tests/Unit/Page/PageLayoutResolverTest.php b/typo3/sysext/frontend/Tests/Unit/Page/PageLayoutResolverTest.php deleted file mode 100644 index b34d24079d8588e8939c34d0a78a1052c26c4a83..0000000000000000000000000000000000000000 --- a/typo3/sysext/frontend/Tests/Unit/Page/PageLayoutResolverTest.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php - -declare(strict_types=1); - -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - -namespace TYPO3\CMS\Frontend\Tests\Unit\Page; - -use PHPUnit\Framework\Attributes\Test; -use TYPO3\CMS\Frontend\Page\PageLayoutResolver; -use TYPO3\TestingFramework\Core\Unit\UnitTestCase; - -final class PageLayoutResolverTest extends UnitTestCase -{ - #[Test] - public function getLayoutForPageFetchesSelectedPageDirectly(): void - { - $subject = new PageLayoutResolver(); - $result = $subject->getLayoutForPage(['backend_layout' => '1'], ['does-not-matter']); - self::assertEquals('1', $result); - } - - #[Test] - public function getLayoutForPageTreatsSpecialMinusOneValueAsNone(): void - { - $subject = new PageLayoutResolver(); - $result = $subject->getLayoutForPage(['backend_layout' => '-1'], ['does-not-matter']); - self::assertEquals('none', $result); - } - - #[Test] - public function getLayoutForPageTreatsSpecialValueZeroOrEmptyAsDefaultWithEmptyRootLine(): void - { - $subject = new PageLayoutResolver(); - $parentPages = [['backend_layout' => '']]; - $page = ['backend_layout' => '0']; - $result = $subject->getLayoutForPage($page, array_merge([$page], $parentPages)); - self::assertEquals('default', $result); - $page = ['backend_layout' => '']; - $result = $subject->getLayoutForPage($page, array_merge([$page], $parentPages)); - self::assertEquals('default', $result); - } - - #[Test] - public function getLayoutForPageTreatsSpecialValueZeroOrEmptyAsDefaultWhenNothingGivenInRootLine(): void - { - $subject = new PageLayoutResolver(); - // No layout specified for current page - $page = ['backend_layout' => '']; - $parentPages = [['uid' => 13, 'backend_layout' => 'does-not-matter'], ['uid' => 1, 'backend_layout_next_level' => '0']]; - $result = $subject->getLayoutForPage($page, array_merge([$page], $parentPages)); - self::assertEquals('default', $result); - } - - #[Test] - public function getLayoutForPageFetchesRootLinePagesUpUntilSomethingWasFound(): void - { - $subject = new PageLayoutResolver(); - // No layout specified for current page - $page = ['backend_layout' => '']; - $parentPages = [['uid' => 13, 'backend_layout' => 'does-not-matter', 'backend_layout_next_level' => ''], ['uid' => 1, 'backend_layout_next_level' => 'regular']]; - $result = $subject->getLayoutForPage($page, array_merge([$page], $parentPages)); - self::assertEquals('regular', $result); - } - - #[Test] - public function getLayoutForPageFetchesRootLinePagesUpWhenNoneWasSelectedExplicitly(): void - { - $subject = new PageLayoutResolver(); - // No layout specified for current page - $page = ['backend_layout' => '']; - $parentPages = [['uid' => 13, 'backend_layout' => 'does-not-matter'], ['uid' => 15, 'backend_layout_next_level' => '-1'], ['uid' => 1, 'backend_layout_next_level' => 'regular']]; - $result = $subject->getLayoutForPage($page, array_merge([$page], $parentPages)); - self::assertEquals('none', $result); - } -} diff --git a/typo3/sysext/webhooks/Tests/Functional/WebhookExecutionTest.php b/typo3/sysext/webhooks/Tests/Functional/WebhookExecutionTest.php index 15adafdcad983118c078ccafeb62820e3a7a4d03..4ec5841cd36fc20824e5d9b2d60d7838a4db620a 100644 --- a/typo3/sysext/webhooks/Tests/Functional/WebhookExecutionTest.php +++ b/typo3/sysext/webhooks/Tests/Functional/WebhookExecutionTest.php @@ -57,9 +57,6 @@ final class WebhookExecutionTest extends FunctionalTestCase $this->importCSVDataSet(__DIR__ . '/Fixtures/pages.csv'); $this->importCSVDataSet(__DIR__ . '/Fixtures/sys_webhooks.csv'); - $backendUser = $this->setUpBackendUser(1); - $GLOBALS['LANG'] = $this->get(LanguageServiceFactory::class)->createFromUserPreferences($backendUser); - $this->writeSiteConfiguration( 'testing', $this->buildSiteConfiguration(1, '/'), @@ -67,6 +64,8 @@ final class WebhookExecutionTest extends FunctionalTestCase $this->buildDefaultLanguageConfiguration('EN', '/'), ] ); + $backendUser = $this->setUpBackendUser(1); + $GLOBALS['LANG'] = $this->get(LanguageServiceFactory::class)->createFromUserPreferences($backendUser); } private function registerRequestInspector(callable $inspector): void