Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Build/phpstan-baseline-13.neon
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,7 @@ parameters:
message: "#^Unreachable statement \\- code above always terminates\\.$#"
count: 1
path: ../Classes/Backend/Grid/ContainerGridColumnItem.php
-
message: "#^Call to an undefined method TYPO3\\\\CMS\\\\Backend\\\\Domain\\\\Repository\\\\Localization\\\\LocalizationRepository\\:\\:getRecordTranslation\\(\\)\\.$#"
count: 1
path: ../Classes/Listener/ContentUsedOnPage.php
43 changes: 41 additions & 2 deletions Classes/Listener/ContentUsedOnPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,20 @@
use B13\Container\Domain\Factory\Exception;
use B13\Container\Domain\Factory\PageView\Backend\ContainerFactory;
use B13\Container\Tca\Registry;
use TYPO3\CMS\Backend\Domain\Repository\Localization\LocalizationRepository;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Backend\View\Event\IsContentUsedOnPageLayoutEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Core\Information\Typo3Version;

#[AsEventListener(identifier: 'tx-container-content-used-on-page')]
class ContentUsedOnPage
{
public function __construct(protected ContainerFactory $containerFactory, protected Registry $tcaRegistry)
{
public function __construct(
protected ContainerFactory $containerFactory,
protected Registry $tcaRegistry,
protected LocalizationRepository $localizationRepository
) {
}

public function __invoke(IsContentUsedOnPageLayoutEvent $event): void
Expand All @@ -31,6 +37,14 @@ public function __invoke(IsContentUsedOnPageLayoutEvent $event): void
if ($record['tx_container_parent'] > 0) {
try {
$container = $this->containerFactory->buildContainer((int)$record['tx_container_parent']);
if (($record['sys_language_uid'] ?? 0) > 0 && ($record['l18n_parent'] ?? 0) > 0) {
// child is connected, assure container is connected too
if ($this->assureContainerIsConnected($record) === false) {
$event->setUsed(false);
return;
}
}

$columns = $this->tcaRegistry->getAvailableColumns($container->getCType());
foreach ($columns as $column) {
if ($column['colPos'] === (int)$record['colPos']) {
Expand All @@ -48,4 +62,29 @@ public function __invoke(IsContentUsedOnPageLayoutEvent $event): void
}
}
}

protected function assureContainerIsConnected(array $record): bool
{
if ((new Typo3Version())->getMajorVersion() < 14) {
$translations = BackendUtility::getRecordLocalization(
'tt_content',
(int)$record['tx_container_parent'],
(int)$record['sys_language_uid']
);

if ($translations === false || empty($translations[0]) || (int)($translations[0]['l18n_parent'] ?? 0) !== (int)$record['tx_container_parent']) {
return false;
}
} else {
$translation = $this->localizationRepository->getRecordTranslation(
'tt_content',
(int)$record['tx_container_parent'],
(int)$record['sys_language_uid']
);
if ($translation === null || (int)$translation->getRawRecord()->get('l18n_parent') !== (int)$record['tx_container_parent']) {
return false;
}
}
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"tt_content"
,"uid","pid","CType","header","sys_language_uid","l18n_parent","l10n_source","tx_container_parent"
,"1","1","b13-2cols-with-header-container","My Container","0","0","0","0"
,"2","1","textmedia","Child Element","0","0","0","1"
,"5","1","b13-2cols-with-header-container","[Translate to de:] My Container","1","0","1","0"
,"6","1","textmedia","[Translate to de:] Child Element","1","2","2","1"
"pages"
,"uid","pid","sys_language_uid","l10n_parent"
,"1","0","0","0"
,"2","0","1","1"

19 changes: 19 additions & 0 deletions Tests/Functional/Listener/ContentUsedOnPageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ class ContentUsedOnPageTest extends FunctionalTestCase
'typo3conf/ext/container_example',
];

protected function setUp(): void
{
parent::setUp();
$this->importCSVDataSet(__DIR__ . '/../Fixtures/be_users.csv');
$GLOBALS['BE_USER'] = $GLOBALS['BE_USER'] = $this->setUpBackendUser(1);
}

#[Test]
public function addContainerChildrenReturnsTrueIfChildrenInContainer(): void
{
Expand Down Expand Up @@ -88,6 +95,18 @@ public function addContainerChildrenReturnsTrueForLocalizedContent(): void
self::assertTrue($event->isRecordUsed());
}

#[Test]
public function childrenOfDetachedContainerTranslationsReturnsTrue(): void
{
$this->importCSVDataSet(__DIR__ . '/../Hooks/Fixtures/UsedRecords/children_of_detachted_contaienr_translations.csv');
$pageLayoutContext = $this->getMockBuilder(PageLayoutContext::class)->disableOriginalConstructor()->getMock();
$record = $this->fetchOneRecordByUid(6);
$event = new IsContentUsedOnPageLayoutEvent($record, false, $pageLayoutContext);
$listener = GeneralUtility::makeInstance(ContentUsedOnPage::class);
$listener($event);
self::assertFalse($event->isRecordUsed());
}

protected function fetchOneRecordByUid(int $uid): array
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
Expand Down
13 changes: 9 additions & 4 deletions Tests/Unit/Listener/ContentUsedOnPageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use B13\Container\Listener\ContentUsedOnPage;
use B13\Container\Tca\Registry;
use PHPUnit\Framework\Attributes\Test;
use TYPO3\CMS\Backend\Domain\Repository\Localization\LocalizationRepository;
use TYPO3\CMS\Backend\View\Event\IsContentUsedOnPageLayoutEvent;
use TYPO3\CMS\Backend\View\PageLayoutContext;
use TYPO3\CMS\Core\Utility\GeneralUtility;
Expand All @@ -32,8 +33,9 @@ public function addContainerChildrenReturnsUsedOfParamsIfTxContainerParentIsZero
$containerFactory = $this->getMockBuilder(ContainerFactory::class)->disableOriginalConstructor()->getMock();
$registry = $this->getMockBuilder(Registry::class)->disableOriginalConstructor()->getMock();
$pageLayoutContext = $this->getMockBuilder(PageLayoutContext::class)->disableOriginalConstructor()->getMock();
$localizationRepository = $this->getMockBuilder(LocalizationRepository::class)->disableOriginalConstructor()->getMock();
$event = new IsContentUsedOnPageLayoutEvent(['tx_container_parent' => 0], true, $pageLayoutContext);
$listener = GeneralUtility::makeInstance(ContentUsedOnPage::class, $containerFactory, $registry);
$listener = GeneralUtility::makeInstance(ContentUsedOnPage::class, $containerFactory, $registry, $localizationRepository);
$listener($event);
self::assertTrue($event->isRecordUsed());
$event = new IsContentUsedOnPageLayoutEvent(['tx_container_parent' => 0], false, $pageLayoutContext);
Expand All @@ -45,6 +47,7 @@ public function addContainerChildrenReturnsUsedOfParamsIfTxContainerParentIsZero
public function addContainerChildrenReturnsTrueIfChildrenInContainerColPos(): void
{
$pageLayoutContext = $this->getMockBuilder(PageLayoutContext::class)->disableOriginalConstructor()->getMock();
$localizationRepository = $this->getMockBuilder(LocalizationRepository::class)->disableOriginalConstructor()->getMock();
$containerFactory = $this->getMockBuilder(ContainerFactory::class)
->disableOriginalConstructor()
->onlyMethods(['buildContainer'])
Expand All @@ -60,7 +63,7 @@ public function addContainerChildrenReturnsTrueIfChildrenInContainerColPos(): vo
$tcaRegistry->expects(self::once())->method('getAvailableColumns')->with('myCType')->willReturn([['colPos' => 2]]);

$event = new IsContentUsedOnPageLayoutEvent(['tx_container_parent' => 1, 'colPos' => 2, 'uid' => 3, 'sys_language_uid' => 0], false, $pageLayoutContext);
$listener = GeneralUtility::makeInstance(ContentUsedOnPage::class, $containerFactory, $tcaRegistry);
$listener = GeneralUtility::makeInstance(ContentUsedOnPage::class, $containerFactory, $tcaRegistry, $localizationRepository);
$listener($event);
self::assertTrue($event->isRecordUsed());
}
Expand All @@ -69,6 +72,7 @@ public function addContainerChildrenReturnsTrueIfChildrenInContainerColPos(): vo
public function addContainerChildrenReturnsFalseIfChildrenIsNotInContainerColPos(): void
{
$pageLayoutContext = $this->getMockBuilder(PageLayoutContext::class)->disableOriginalConstructor()->getMock();
$localizationRepository = $this->getMockBuilder(LocalizationRepository::class)->disableOriginalConstructor()->getMock();
$containerFactory = $this->getMockBuilder(ContainerFactory::class)
->disableOriginalConstructor()
->onlyMethods(['buildContainer'])
Expand All @@ -84,7 +88,7 @@ public function addContainerChildrenReturnsFalseIfChildrenIsNotInContainerColPos
$tcaRegistry->expects(self::once())->method('getAvailableColumns')->with('myCType')->willReturn([['colPos' => 2]]);

$event = new IsContentUsedOnPageLayoutEvent(['tx_container_parent' => 1, 'colPos' => 2, 'uid' => 3, 'sys_language_uid' => 0], false, $pageLayoutContext);
$listener = GeneralUtility::makeInstance(ContentUsedOnPage::class, $containerFactory, $tcaRegistry);
$listener = GeneralUtility::makeInstance(ContentUsedOnPage::class, $containerFactory, $tcaRegistry, $localizationRepository);
$listener($event);
self::assertFalse($event->isRecordUsed());
}
Expand All @@ -93,6 +97,7 @@ public function addContainerChildrenReturnsFalseIfChildrenIsNotInContainerColPos
public function addContainerChildrenReturnsFalseIfChildrenIsNotInRegisterdGrid(): void
{
$pageLayoutContext = $this->getMockBuilder(PageLayoutContext::class)->disableOriginalConstructor()->getMock();
$localizationRepository = $this->getMockBuilder(LocalizationRepository::class)->disableOriginalConstructor()->getMock();
$containerFactory = $this->getMockBuilder(ContainerFactory::class)
->disableOriginalConstructor()
->onlyMethods(['buildContainer'])
Expand All @@ -107,7 +112,7 @@ public function addContainerChildrenReturnsFalseIfChildrenIsNotInRegisterdGrid()
$tcaRegistry->expects(self::once())->method('getAvailableColumns')->with('myCType')->willReturn([['colPos' => 3]]);

$event = new IsContentUsedOnPageLayoutEvent(['tx_container_parent' => 1, 'colPos' => 2, 'uid' => 3], false, $pageLayoutContext);
$listener = GeneralUtility::makeInstance(ContentUsedOnPage::class, $containerFactory, $tcaRegistry);
$listener = GeneralUtility::makeInstance(ContentUsedOnPage::class, $containerFactory, $tcaRegistry, $localizationRepository);
$listener($event);
self::assertFalse($event->isRecordUsed());
}
Expand Down