Skip to content

Commit 3b374d5

Browse files
committed
[BUGFIX] record summay for localization
before v14 it was possible to list records in any colPos in LocalizationWizard. We have the RecordSummaryForLocalization Listener to modify colums and records to be displayd in the LocalizationWizard. With v14 backend layout is used to render translateable records and so only columns with known colPos in backend layout are rendered. s. https://review.typo3.org/c/Packages/TYPO3.CMS/+/90489 this PR changed the colPos of container children and move them into the outer container colPos which exists in the backend layout. e.g. a children in a container has colPos 200, and container has colPos 0 in the backend layout and the container is translated but the children not we changed the children colPos to the container colPos so it is displayed in the Localazation Wizard in colPos 0. if the container is also in translateable records, the childrens are not listed, as before. They will automatically localized in DataHandler Hook.
1 parent a50aaff commit 3b374d5

File tree

7 files changed

+254
-10
lines changed

7 files changed

+254
-10
lines changed

Classes/Listener/RecordSummaryForLocalization.php

Lines changed: 106 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414

1515
use B13\Container\Service\RecordLocalizeSummaryModifier;
1616
use TYPO3\CMS\Backend\Controller\Event\AfterRecordSummaryForLocalizationEvent;
17+
use TYPO3\CMS\Core\Database\Connection;
18+
use TYPO3\CMS\Core\Database\ConnectionPool;
19+
use TYPO3\CMS\Core\Information\Typo3Version;
1720

1821
class RecordSummaryForLocalization
1922
{
@@ -22,18 +25,115 @@ class RecordSummaryForLocalization
2225
*/
2326
protected $recordLocalizeSummaryModifier;
2427

25-
public function __construct(RecordLocalizeSummaryModifier $recordLocalizeSummaryModifier)
26-
{
28+
protected ConnectionPool $connectionPool;
29+
30+
public function __construct(
31+
RecordLocalizeSummaryModifier $recordLocalizeSummaryModifier,
32+
ConnectionPool $connectionPool
33+
) {
2734
$this->recordLocalizeSummaryModifier = $recordLocalizeSummaryModifier;
35+
$this->connectionPool = $connectionPool;
2836
}
2937

3038
public function __invoke(AfterRecordSummaryForLocalizationEvent $event): void
3139
{
3240
$records = $event->getRecords();
33-
$columns = $event->getColumns();
34-
$records = $this->recordLocalizeSummaryModifier->filterRecords($records);
35-
$columns = $this->recordLocalizeSummaryModifier->rebuildColumns($columns);
36-
$event->setColumns($columns);
41+
if ((new Typo3Version())->getMajorVersion() < 14) {
42+
$columns = $event->getColumns();
43+
$records = $this->recordLocalizeSummaryModifier->filterRecords($records);
44+
$columns = $this->recordLocalizeSummaryModifier->rebuildColumns($columns);
45+
$event->setColumns($columns);
46+
$event->setRecords($records);
47+
return;
48+
}
49+
$localizeRecords = [];
50+
foreach ($records as $colPos => $recordsPerColPos) {
51+
foreach ($recordsPerColPos as $record) {
52+
$localizeRecords[$record['uid']] = $record;
53+
}
54+
}
55+
$fullRecords = $this->fetchAllRecords(array_keys($localizeRecords));
56+
$records = $this->moveContainerColPosIntoPageColPos($fullRecords, $localizeRecords);
3757
$event->setRecords($records);
3858
}
59+
60+
protected function moveContainerColPosIntoPageColPos(array $records, array $localizeRecords): array
61+
{
62+
$recordsPerColPos = [];
63+
foreach ($records as $record) {
64+
$colPos = $this->resolveRecordColPos($record, $localizeRecords);
65+
if (!isset($recordsPerColPos[$colPos])) {
66+
$recordsPerColPos[$colPos] = [];
67+
}
68+
if (!isset($localizeRecords[$record['uid']])) {
69+
throw new RecordSummaryForLocalizationException('localizeRecord not set ' . $record['uid'], 1769247959);
70+
}
71+
$recordsPerColPos[$colPos][] = $localizeRecords[$record['uid']];
72+
}
73+
return $recordsPerColPos;
74+
}
75+
76+
protected function resolveRecordColPos(array $record, $localizeRecords): int
77+
{
78+
if (($record['tx_container_parent'] ?? 0) === 0) {
79+
return $record['colPos'];
80+
}
81+
$loopCnt = 0;
82+
$maxDepth = 20;
83+
$containerUid = $record['tx_container_parent'];
84+
while (true) {
85+
if (in_array($containerUid, array_keys($localizeRecords))) {
86+
return $record['colPos'];
87+
}
88+
if ($loopCnt > $maxDepth) {
89+
throw new RecordSummaryForLocalizationException('maxDepth has reached ' . $maxDepth, 1769247958);
90+
}
91+
$containerRecord = $this->fetchOneRecord($containerUid);
92+
if ($containerRecord === null) {
93+
throw new RecordSummaryForLocalizationException('cannot fetch record for uid ' . $containerUid, 1769247957);
94+
}
95+
if (($containerRecord['tx_container_parent'] ?? 0) === 0) {
96+
return $containerRecord['colPos'];
97+
}
98+
$containerUid = $containerRecord['tx_container_parent'];
99+
$loopCnt++;
100+
}
101+
}
102+
103+
protected function fetchOneRecord(int $uid): ?array
104+
{
105+
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('tt_content');
106+
$row = $queryBuilder->select('*')
107+
->from('tt_content')
108+
->where(
109+
$queryBuilder->expr()->eq(
110+
'uid',
111+
$queryBuilder->createNamedParameter($uid, Connection::PARAM_INT)
112+
)
113+
)
114+
->executeQuery()
115+
->fetchAssociative();
116+
return $row ?: null;
117+
}
118+
119+
protected function fetchAllRecords(array $uids): array
120+
{
121+
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('tt_content');
122+
$rows = $queryBuilder->select('*')
123+
->from('tt_content')
124+
->where(
125+
$queryBuilder->expr()->in(
126+
'uid',
127+
$queryBuilder->createNamedParameter($uids, Connection::PARAM_INT_ARRAY)
128+
)
129+
)
130+
->orderBy('sorting')
131+
->executeQuery()
132+
->fetchAllAssociative();
133+
$rowsPerUid = [];
134+
foreach ($rows as $row) {
135+
$rowsPerUid[$row['uid']] = $row;
136+
}
137+
return $rowsPerUid;
138+
}
39139
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace B13\Container\Listener;
6+
7+
/*
8+
* This file is part of TYPO3 CMS-based extension "container" by b13.
9+
*
10+
* It is free software; you can redistribute it and/or modify it under
11+
* the terms of the GNU General Public License, either version 2
12+
* of the License, or any later version.
13+
*/
14+
15+
use B13\Container\Exception;
16+
17+
class RecordSummaryForLocalizationException extends Exception
18+
{
19+
}

Tests/Acceptance/Backend/LayoutCest.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -395,17 +395,19 @@ public function canTranslateChildWithTranslationModule(BackendTester $I, PageTre
395395
$I->click('a.t3js-localize');
396396
} else {
397397
$I->waitForText('Translate');
398-
$I->executeJS("document.querySelector('typo3-backend-localization-button').click()");
399-
// AfterRecordSummaryForLocalizationEvent
400-
$scenario->skip('need more work, AfterRecordSummaryForLocalizationEvent needs refactoring');
398+
$I->executeJS("document.querySelector('#PageLayoutController typo3-backend-localization-button').click()");
401399
}
402400

403401
$I->switchToIFrame();
404402
if ($I->getTypo3MajorVersion() < 13) {
405403
$I->waitForElement('.t3js-localization-option');
406404
$I->waitForElement('div[data-bs-slide="localize-summary"]');
407405
}
408-
$I->waitForText('(212) headerOfChild');
406+
if ($I->getTypo3MajorVersion() < 14) {
407+
$I->waitForText('(212) headerOfChild');
408+
} else {
409+
$I->waitForText('headerOfChild');
410+
}
409411
}
410412

411413
/**
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"tt_content"
2+
,"uid","pid","CType","header","sorting","sys_language_uid","colPos","tx_container_parent","l18n_parent"
3+
,"4","1","header","ce 2","288","0","200","5","0"
4+
,"5","1","b13-2cols-with-header-container","container","256","0","0","0","0"
5+
"pages"
6+
,"uid","pid","sys_language_uid","l10n_parent"
7+
,"1","0","0","0"
8+
,"2","0","1","1"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
"tt_content"
2+
,"uid","pid","CType","header","sorting","sys_language_uid","colPos","tx_container_parent","l18n_parent"
3+
,"4","1","header","ce 2","288","0","200","5","0"
4+
,"5","1","b13-2cols-with-header-container","container","256","0","0","0","0"
5+
,"38","1","b13-2cols-with-header-container","translated container","272","1","0","0","5"
6+
,"40","1","header","last element","300","0","0","0","0"
7+
,"41","1","header","first element","128","0","0","0","0"
8+
"pages"
9+
,"uid","pid","sys_language_uid","l10n_parent"
10+
,"1","0","0","0"
11+
,"2","0","1","1"
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace B13\Container\Tests\Functional\Listener;
6+
7+
/*
8+
* This file is part of TYPO3 CMS-based extension "container" by b13.
9+
*
10+
* It is free software; you can redistribute it and/or modify it under
11+
* the terms of the GNU General Public License, either version 2
12+
* of the License, or any later version.
13+
*/
14+
15+
use TYPO3\CMS\Backend\Controller\Event\AfterRecordSummaryForLocalizationEvent;
16+
use TYPO3\CMS\Core\Information\Typo3Version;
17+
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
18+
19+
class RecordSummaryForLocalization extends FunctionalTestCase
20+
{
21+
/**
22+
* @var non-empty-string[]
23+
*/
24+
protected array $testExtensionsToLoad = [
25+
'typo3conf/ext/container',
26+
'typo3conf/ext/container_example',
27+
];
28+
29+
/**
30+
* @test
31+
*/
32+
public function childrenIsMovedIntoBackendLayoutColPosIfContainerIsAlreadyTranslated(): void
33+
{
34+
if ((new Typo3Version())->getMajorVersion() < 14) {
35+
self::markTestSkipped('tested by RecordLocalizeSummaryModifierTest Unit Test');
36+
}
37+
$records = [
38+
0 => [
39+
0 => ['uid' => 41, 'title' => 'first element'],
40+
1 => ['uid' => 40, 'title' => 'last element'],
41+
],
42+
200 => [
43+
0 => ['uid' => 4, 'title' => 'ce 2'],
44+
],
45+
];
46+
$columns = [0 => 'Normal'];
47+
$event = new AfterRecordSummaryForLocalizationEvent($records, $columns);
48+
$this->importCSVDataSet(__DIR__ . '/Fixtures/localize_container_child.csv');
49+
$listener = $this->getContainer()->get(\B13\Container\Listener\RecordSummaryForLocalization::class);
50+
$listener($event);
51+
$records = $event->getRecords();
52+
$expected = [
53+
0 => [
54+
0 => ['uid' => 41, 'title' => 'first element'],
55+
1 => ['uid' => 4, 'title' => 'ce 2'],
56+
2 => ['uid' => 40, 'title' => 'last element'],
57+
],
58+
];
59+
self::assertSame($expected, $records);
60+
}
61+
62+
/**
63+
* @test
64+
*/
65+
public function childrenIsNotMovedIntoBackendLayoutColPosIfContainerShouldBeTranslated(): void
66+
{
67+
if ((new Typo3Version())->getMajorVersion() < 14) {
68+
self::markTestSkipped('tested by RecordLocalizeSummaryModifierTest Unit Test');
69+
}
70+
$records = [
71+
0 => [
72+
0 => ['uid' => 5, 'title' => 'container'],
73+
],
74+
200 => [
75+
0 => ['uid' => 4, 'title' => 'ce 2'],
76+
],
77+
];
78+
$columns = [0 => 'Normal'];
79+
$event = new AfterRecordSummaryForLocalizationEvent($records, $columns);
80+
$this->importCSVDataSet(__DIR__ . '/Fixtures/localize_container.csv');
81+
$listener = $this->getContainer()->get(\B13\Container\Listener\RecordSummaryForLocalization::class);
82+
$listener($event);
83+
$records = $event->getRecords();
84+
$expected = [
85+
0 => [
86+
0 => ['uid' => 5, 'title' => 'container'],
87+
],
88+
200 => [
89+
0 => ['uid' => 4, 'title' => 'ce 2'],
90+
],
91+
];
92+
self::assertSame($expected, $records);
93+
}
94+
}

Tests/Unit/Service/RecordLocalizeSummaryModifierTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
use B13\Container\Service\RecordLocalizeSummaryModifier;
1616
use B13\Container\Tca\Registry;
17+
use TYPO3\CMS\Core\Information\Typo3Version;
1718
use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
1819

1920
class RecordLocalizeSummaryModifierTest extends UnitTestCase
@@ -25,6 +26,9 @@ class RecordLocalizeSummaryModifierTest extends UnitTestCase
2526
*/
2627
public function filterRecordsRemovesContainerChildrenIfParentContainerIsTranslatedAsWell(): void
2728
{
29+
if ((new Typo3Version())->getMajorVersion() >= 14) {
30+
self::markTestSkipped('not used in v14');
31+
}
2832
$recordLocalizeSummeryModifier = $this->getAccessibleMock(
2933
RecordLocalizeSummaryModifier::class,
3034
['getContainerUids', 'getContainerChildren'],
@@ -48,6 +52,9 @@ public function filterRecordsRemovesContainerChildrenIfParentContainerIsTranslat
4852
*/
4953
public function filterRecordsKeepsContainerChildrenIfParentContainerIsNotTranslated(): void
5054
{
55+
if ((new Typo3Version())->getMajorVersion() >= 14) {
56+
self::markTestSkipped('not used in v14');
57+
}
5158
$recordLocalizeSummeryModifier = $this->getAccessibleMock(
5259
RecordLocalizeSummaryModifier::class,
5360
['getContainerUids', 'getContainerChildren'],
@@ -71,6 +78,9 @@ public function filterRecordsKeepsContainerChildrenIfParentContainerIsNotTransla
7178
*/
7279
public function rebuildColumnsReturnsColumnListWithConsecutiveArrayKeysAlsoWhenRegistryReturnsRepeatingColumns(): void
7380
{
81+
if ((new Typo3Version())->getMajorVersion() >= 14) {
82+
self::markTestSkipped('not used in v14');
83+
}
7484
$tcaRegistry = $this->getMockBuilder(Registry::class)
7585
->disableOriginalConstructor()
7686
->onlyMethods(['getAllAvailableColumns'])

0 commit comments

Comments
 (0)