Skip to content
Snippets Groups Projects
Commit f8fd9ecd authored by Dimitri König's avatar Dimitri König Committed by Stefan Bürk
Browse files

[BUGFIX] Reverse rootline for PageLayoutResolver calls

There have been quite a lot of patches around the pagelayout
condition, which was introduced in early TYPO3 v11. It was
broken in the beginning and got fixed with #98044.
Soon after, a major refactoring of the ConditionMatcher
has been applied with #100047, which re-introduced bugs
regarding the pagelayout TypoScript condition. These have
partially fixed with #100764 and #100921.

The affected code is a bit messy due to a weird sorted
array returned by the 'rootline' methods and classes,
which we can't resolve right now.

The fix itself revolves a last issue by reversing the
rootline again. This was already done in the old backend
ConditionMatcher. The frontend ConditionMatcher received
the fullRootline, which is already reversed (deepest first).

With the introduction of IncludeTreeConditionMatcherVisitor
this has been unified, but the array_reverse was forgotten.

In addition, the fullRootline from the frontend controller
and BackendUtility::getPagesTSconfig() calls are sorted with
`ksort` before passed over, meaning we always receive a
top-down (root-first) rootline. Hence, the array_reverse is
a viable fix for both backend and frontend.

The patch covers BE pageTS, FE TypoScript and FE getData
with tests to cover the situation once and for all.

Resolves: #102268
Related: #100047
Related: #100764
Related: #100921
Related: #98044
Related: #97816
Releases: main, 12.4
Change-Id: Ibec77f3cf63073e40e4a711d69f584b9265b1ad6
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/82560


Tested-by: default avatarStefan Bürk <stefan@buerk.tech>
Reviewed-by: default avatarChristian Kuhn <lolli@schwarzbu.ch>
Tested-by: default avatarNikita Hovratov <nikita.h@live.de>
Reviewed-by: default avatarStefan Bürk <stefan@buerk.tech>
Tested-by: default avatarOliver Klee <typo3-coding@oliverklee.de>
Tested-by: default avatarcore-ci <typo3@b13.com>
Reviewed-by: default avatarNikita Hovratov <nikita.h@live.de>
Tested-by: default avatarChristian Kuhn <lolli@schwarzbu.ch>
parent 1817a247
No related merge requests found
Showing
with 253 additions and 3 deletions
......@@ -118,9 +118,11 @@ final class IncludeTreeConditionMatcherVisitor implements IncludeTreeVisitorInte
$tree->rootLineParentIds = array_slice(array_column($localRootLine, 'pid'), 1);
// We're feeding the "full" RootLine here, not the "local" one that stops at sys_template record having 'root' set.
// This is to be in-line with backend here: A 'backend_layout_next_level' on a page above sys_template 'root' page should
// still be considered. Additionally, $fullRootLine is "deepest page first, then up" for getLayoutForPage() to find
// the 'nearest' parent.
$tree->pagelayout = $this->pageLayoutResolver->getLayoutForPage($variables['page'], $fullRootLine);
// still be considered. Normally, $fullRootLine is "deepest page first, then up". This is needed for getLayoutForPage() to find
// 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);
$enrichedVariables['tree'] = $tree;
}
......
"pages"
,"uid","pid","title","backend_layout","backend_layout_next_level"
,1,0,"Homepage","pagets__default","pagets__inherit"
,2,1,"Subpage","",""
page = PAGE
[tree.pagelayout == 'pagets__inherit']
page.10 = TEXT
page.10.value = Inherited Layout
[end]
"pages"
,"uid","pid","title","backend_layout","backend_layout_next_level"
,1,0,"Homepage","pagets__default","pagets__inherit"
,2,1,"Subpage","pagets__default",""
,3,2,"Subpage 2","","pagets__default"
page = PAGE
[tree.pagelayout == 'pagets__inherit']
page.10 = TEXT
page.10.value = Inherited Layout
[end]
"pages"
,"uid","pid","title","backend_layout","backend_layout_next_level"
,1,0,"Homepage","pagets__default","pagets__inherit"
,2,1,"Subpage","","pagets__foo"
,3,2,"Subpage 2","","pagets__extra"
,4,3,"Subpage 3","pagets__extra",""
page = PAGE
[tree.pagelayout == 'pagets__extra']
page.10 = TEXT
page.10.value = Extra Layout
[end]
"pages"
,"uid","pid","title","backend_layout","backend_layout_next_level"
,1,0,"Homepage","pagets__default","pagets__inherit"
,2,1,"Subpage","","pagets__foo"
,3,2,"Subpage 2","","pagets__extra"
,4,3,"Subpage 3","",""
page = PAGE
[tree.pagelayout == 'pagets__extra']
page.10 = TEXT
page.10.value = Extra Layout
[end]
"pages"
,"uid","pid","title","backend_layout","backend_layout_next_level"
,1,0,"Homepage","","pagets__default"
page = PAGE
[tree.pagelayout == 'pagets__default']
page.10 = TEXT
page.10.value = Default Layout
[end]
"pages"
,"uid","pid","title","backend_layout","backend_layout_next_level"
,1,0,"Homepage","pagets__default",""
page = PAGE
[tree.pagelayout == 'pagets__default']
page.10 = TEXT
page.10.value = Default Layout
[end]
"pages"
,"uid","pid","title","backend_layout","backend_layout_next_level","TSconfig"
,1,0,"Homepage","pagets__default","pagets__inherit","
[tree.pagelayout == 'pagets__inherit']
layout = Inherited Layout
[end]"
,2,1,"Subpage","","",""
"pages"
,"uid","pid","title","backend_layout","backend_layout_next_level","TSconfig"
,1,0,"Homepage","pagets__default","pagets__inherit","
[tree.pagelayout == 'pagets__inherit']
layout = Inherited Layout
[end]"
,2,1,"Subpage","pagets__default","",""
,3,2,"Subpage 2","","pagets__default",""
"pages"
,"uid","pid","title","backend_layout","backend_layout_next_level","TSconfig"
,1,0,"Homepage","pagets__default","pagets__inherit","
[tree.pagelayout == 'pagets__extra']
layout = Extra Layout
[end]"
,2,1,"Subpage","","pagets__foo",""
,3,2,"Subpage 2","","pagets__extra",""
,4,3,"Subpage 3","pagets__extra","",""
"pages"
,"uid","pid","title","backend_layout","backend_layout_next_level","TSconfig"
,1,0,"Homepage","pagets__default","pagets__inherit","
[tree.pagelayout == 'pagets__extra']
layout = Extra Layout
[end]"
,2,1,"Subpage","","pagets__foo",""
,3,2,"Subpage 2","","pagets__extra",""
,4,3,"Subpage 3","","",""
"pages"
,"uid","pid","title","backend_layout","backend_layout_next_level","TSconfig"
,1,0,"Homepage","","pagets__default","
[tree.pagelayout == 'pagets__default']
layout = Default Layout
[end]"
"pages"
,"uid","pid","title","backend_layout","backend_layout_next_level","TSconfig"
,1,0,"Homepage","pagets__default","","
[tree.pagelayout == 'pagets__default']
layout = Default Layout
[end]"
<?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\TypoScript;
use TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait;
use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequest;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
/**
* Test Frontend TypoScript 'tree.pagelayout' related condition matching.
*/
final class FrontendTypoScriptFactoryPageLayoutConditionTest extends FunctionalTestCase
{
use SiteBasedTestTrait;
protected const LANGUAGE_PRESETS = [
'EN' => ['id' => 0, 'title' => 'English', 'locale' => 'en_US.UTF8'],
];
private const ROOT_PAGE_ID = 1;
public function setUp(): void
{
parent::setUp();
$this->writeSiteConfiguration(
'tree_page_layout_test',
$this->buildSiteConfiguration(self::ROOT_PAGE_ID, '/'),
);
}
/**
* @test
*/
public function treePageLayoutConditionMetForBackendLayoutOnRootPage(): void
{
$this->importCSVDataSet(__DIR__ . '/Fixtures/FrontendTypoScriptFactoryPageLayoutCondition/backendLayoutOnRoot.csv');
$this->setUpFrontendRootPage(
self::ROOT_PAGE_ID,
[
'EXT:core/Tests/Functional/TypoScript/Fixtures/FrontendTypoScriptFactoryPageLayoutCondition/backendLayoutOnRootSetup.typoscript',
]
);
$response = $this->executeFrontendSubRequest((new InternalRequest())->withPageId(self::ROOT_PAGE_ID));
self::assertStringContainsString('Default Layout', (string)$response->getBody());
}
/**
* @test
*/
public function treePageLayoutConditionNotMetForBackendLayoutNextLevelOnRootPage(): void
{
$this->importCSVDataSet(__DIR__ . '/Fixtures/FrontendTypoScriptFactoryPageLayoutCondition/backendLayoutNextLevelOnRoot.csv');
$this->setUpFrontendRootPage(
self::ROOT_PAGE_ID,
[
'EXT:core/Tests/Functional/TypoScript/Fixtures/FrontendTypoScriptFactoryPageLayoutCondition/backendLayoutNextLevelOnRootSetup.typoscript',
]
);
$response = $this->executeFrontendSubRequest((new InternalRequest())->withPageId(self::ROOT_PAGE_ID));
self::assertStringNotContainsString('Default Layout', (string)$response->getBody());
}
/**
* @test
*/
public function treePageLayoutConditionMetForBackendLayoutNextLevelInheritedOnSubpageLevel1(): void
{
$this->importCSVDataSet(__DIR__ . '/Fixtures/FrontendTypoScriptFactoryPageLayoutCondition/backendLayoutAndNextLevelOnRoot.csv');
$this->setUpFrontendRootPage(
self::ROOT_PAGE_ID,
[
'EXT:core/Tests/Functional/TypoScript/Fixtures/FrontendTypoScriptFactoryPageLayoutCondition/backendLayoutAndNextLevelOnRootSetup.typoscript',
]
);
$response = $this->executeFrontendSubRequest((new InternalRequest())->withPageId(2));
self::assertStringContainsString('Inherited Layout', (string)$response->getBody());
}
/**
* @test
*/
public function treePageLayoutConditionMetForBackendLayoutNextLevelInheritedOnSubpageLevel2(): void
{
$this->importCSVDataSet(__DIR__ . '/Fixtures/FrontendTypoScriptFactoryPageLayoutCondition/backendLayoutAndNextLevelOnRootSubOverride1.csv');
$this->setUpFrontendRootPage(
self::ROOT_PAGE_ID,
[
'EXT:core/Tests/Functional/TypoScript/Fixtures/FrontendTypoScriptFactoryPageLayoutCondition/backendLayoutAndNextLevelOnRootSubOverride1Setup.typoscript',
]
);
$response = $this->executeFrontendSubRequest((new InternalRequest())->withPageId(3));
self::assertStringContainsString('Inherited Layout', (string)$response->getBody());
}
/**
* @test
*/
public function treePageLayoutConditionMetForBackendLayoutOnSubpageLevel3(): void
{
$this->importCSVDataSet(__DIR__ . '/Fixtures/FrontendTypoScriptFactoryPageLayoutCondition/backendLayoutAndNextLevelOnRootSubOverride2.csv');
$this->setUpFrontendRootPage(
self::ROOT_PAGE_ID,
[
'EXT:core/Tests/Functional/TypoScript/Fixtures/FrontendTypoScriptFactoryPageLayoutCondition/backendLayoutAndNextLevelOnRootSubOverride2Setup.typoscript',
]
);
$response = $this->executeFrontendSubRequest((new InternalRequest())->withPageId(4));
self::assertStringContainsString('Extra Layout', (string)$response->getBody());
}
/**
* @test
*/
public function treePageLayoutConditionMetForBackendLayoutNextLevelOverrideOnSubpageLevel3(): void
{
$this->importCSVDataSet(__DIR__ . '/Fixtures/FrontendTypoScriptFactoryPageLayoutCondition/backendLayoutAndNextLevelOnRootSubOverride3.csv');
$this->setUpFrontendRootPage(
self::ROOT_PAGE_ID,
[
'EXT:core/Tests/Functional/TypoScript/Fixtures/FrontendTypoScriptFactoryPageLayoutCondition/backendLayoutAndNextLevelOnRootSubOverride3Setup.typoscript',
]
);
$response = $this->executeFrontendSubRequest((new InternalRequest())->withPageId(4));
self::assertStringContainsString('Extra Layout', (string)$response->getBody());
}
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment