From 89d076a1f839b34ffa5d86252d35b9e75f021d28 Mon Sep 17 00:00:00 2001 From: Denis Mir Date: Tue, 4 Aug 2026 13:55:49 +0200 Subject: [PATCH] [BUGFIX] keep child order on copy and move to other language copyOrMoveChildren reverses the children per colPos, which is only correct as long as every child is pasted on the same target, resolved to the top of the container column. When the language changes, the target switches to the previously handled child from the second child on, so the reversed order is no longer compensated and the children end up in reverse order. Fixes: #760 --- .../CommandMapPostProcessingHook.php | 13 +++- .../Localization/FreeMode/ContainerTest.php | 68 +++++++++++++++++++ .../CopyContainerKeepsChildOrder.csv | 15 ++++ .../CopyContainerKeepsChildOrderResult.csv | 15 ++++ ...ontainerToOtherLanguageKeepsChildOrder.csv | 15 ++++ ...erToOtherLanguageKeepsChildOrderResult.csv | 15 ++++ ...ontainerToOtherLanguageKeepsChildOrder.csv | 15 ++++ ...erToOtherLanguageKeepsChildOrderResult.csv | 9 +++ 8 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/CopyContainerKeepsChildOrder.csv create mode 100644 Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/CopyContainerKeepsChildOrderResult.csv create mode 100644 Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/CopyContainerToOtherLanguageKeepsChildOrder.csv create mode 100644 Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/CopyContainerToOtherLanguageKeepsChildOrderResult.csv create mode 100644 Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/MoveContainerToOtherLanguageKeepsChildOrder.csv create mode 100644 Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/MoveContainerToOtherLanguageKeepsChildOrderResult.csv diff --git a/Classes/Hooks/Datahandler/CommandMapPostProcessingHook.php b/Classes/Hooks/Datahandler/CommandMapPostProcessingHook.php index b4e80719..c7d220b1 100644 --- a/Classes/Hooks/Datahandler/CommandMapPostProcessingHook.php +++ b/Classes/Hooks/Datahandler/CommandMapPostProcessingHook.php @@ -119,11 +119,22 @@ protected function copyOrMoveChildren(int $origUid, int $newId, int $containerId $container = $this->containerFactory->buildContainer($origUid); (GeneralUtility::makeInstance(DatahandlerProcess::class))->startContainerProcess($origUid); (GeneralUtility::makeInstance(DatahandlerProcess::class))->lockContentElementRestrictions(); + $allChildren = $container->getChildRecords(); + $targetLanguage = $dataHandler->cmdmap['tt_content'][$origUid][$command]['update']['sys_language_uid'] ?? null; + // the loop below chains every child after the previously handled one as soon as the + // language changes, in that case the original order has to be kept + $chainAfterPrevious = $targetLanguage !== null + && isset($allChildren[0]) + && (int)$targetLanguage !== (int)$allChildren[0]['sys_language_uid']; $children = []; $colPosVals = $container->getChildrenColPos(); foreach ($colPosVals as $colPos) { $childrenByColPos = $container->getChildrenByColPos($colPos); - $childrenByColPos = array_reverse($childrenByColPos); + if (!$chainAfterPrevious) { + // every child is pasted on the same target, which is resolved to the top of + // the container column, so the reversed order restores the original order + $childrenByColPos = array_reverse($childrenByColPos); + } foreach ($childrenByColPos as $child) { $children[] = $child; } diff --git a/Tests/Functional/Datahandler/Localization/FreeMode/ContainerTest.php b/Tests/Functional/Datahandler/Localization/FreeMode/ContainerTest.php index ad64fed9..7fbef1f7 100644 --- a/Tests/Functional/Datahandler/Localization/FreeMode/ContainerTest.php +++ b/Tests/Functional/Datahandler/Localization/FreeMode/ContainerTest.php @@ -79,6 +79,74 @@ public function copyContainerCopiesChildren(): void self::assertCSVDataSet(__DIR__ . '/Fixtures/Container/CopyContainerCopiesChildrenResult.csv'); } + #[Test] + public function copyContainerKeepsChildOrder(): void + { + $this->importCSVDataSet(__DIR__ . '/Fixtures/Container/CopyContainerKeepsChildOrder.csv'); + $cmdmap = [ + 'tt_content' => [ + 51 => [ + 'copy' => [ + 'action' => 'paste', + 'target' => 3, + 'update' => [ + 'colPos' => 0, + ], + ], + ], + ], + ]; + $this->dataHandler->start([], $cmdmap, $this->backendUser); + $this->dataHandler->process_cmdmap(); + self::assertCSVDataSet(__DIR__ . '/Fixtures/Container/CopyContainerKeepsChildOrderResult.csv'); + } + + #[Test] + public function copyContainerToOtherLanguageKeepsChildOrder(): void + { + $this->importCSVDataSet(__DIR__ . '/Fixtures/Container/CopyContainerToOtherLanguageKeepsChildOrder.csv'); + $cmdmap = [ + 'tt_content' => [ + 51 => [ + 'copy' => [ + 'action' => 'paste', + 'target' => 3, + 'update' => [ + 'colPos' => 0, + 'sys_language_uid' => 1, + ], + ], + ], + ], + ]; + $this->dataHandler->start([], $cmdmap, $this->backendUser); + $this->dataHandler->process_cmdmap(); + self::assertCSVDataSet(__DIR__ . '/Fixtures/Container/CopyContainerToOtherLanguageKeepsChildOrderResult.csv'); + } + + #[Test] + public function moveContainerToOtherLanguageKeepsChildOrder(): void + { + $this->importCSVDataSet(__DIR__ . '/Fixtures/Container/MoveContainerToOtherLanguageKeepsChildOrder.csv'); + $cmdmap = [ + 'tt_content' => [ + 51 => [ + 'move' => [ + 'action' => 'paste', + 'target' => 3, + 'update' => [ + 'colPos' => 0, + 'sys_language_uid' => 1, + ], + ], + ], + ], + ]; + $this->dataHandler->start([], $cmdmap, $this->backendUser); + $this->dataHandler->process_cmdmap(); + self::assertCSVDataSet(__DIR__ . '/Fixtures/Container/MoveContainerToOtherLanguageKeepsChildOrderResult.csv'); + } + #[Test] public function copyContainerToOtherLanguageCopiesChildren(): void { diff --git a/Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/CopyContainerKeepsChildOrder.csv b/Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/CopyContainerKeepsChildOrder.csv new file mode 100644 index 00000000..28e8a930 --- /dev/null +++ b/Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/CopyContainerKeepsChildOrder.csv @@ -0,0 +1,15 @@ +"pages" +,"uid","pid","title","slug","sys_language_uid","l10n_parent","l10n_source",is_siteroot +,1,0,"page-1","/",0,0,0,1 +,2,0,"page-1-language-1","/",1,1,1,0 +,3,1,"page-2","/page-2",0,0,0,0 +,4,1,"page-2-language-1","/page-2",1,3,3,0 +"tt_content" +,"uid","pid","CType","header","sorting","sys_language_uid","colPos","tx_container_parent" +,51,1,"b13-2cols-with-header-container","container-3",1024,0,0,0 +,52,1,"header","container-3-child-header-1",1280,0,200,51 +,53,1,"header","container-3-child-header-2",1536,0,200,51 +,54,1,"header","container-3-child-header-3",1792,0,200,51 +,55,1,"header","container-3-child-left-1",2048,0,201,51 +,56,1,"header","container-3-child-left-2",2304,0,201,51 +,57,1,"header","outside",2560,0,0,0 diff --git a/Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/CopyContainerKeepsChildOrderResult.csv b/Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/CopyContainerKeepsChildOrderResult.csv new file mode 100644 index 00000000..392f1cd3 --- /dev/null +++ b/Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/CopyContainerKeepsChildOrderResult.csv @@ -0,0 +1,15 @@ +"tt_content" +,"uid","pid","CType","header","sorting","sys_language_uid","colPos","tx_container_parent" +,51,1,"b13-2cols-with-header-container","container-3",1024,0,0,0 +,52,1,"header","container-3-child-header-1",1280,0,200,51 +,53,1,"header","container-3-child-header-2",1536,0,200,51 +,54,1,"header","container-3-child-header-3",1792,0,200,51 +,55,1,"header","container-3-child-left-1",2048,0,201,51 +,56,1,"header","container-3-child-left-2",2304,0,201,51 +,57,1,"header","outside",2560,0,0,0 +,58,3,"b13-2cols-with-header-container","container-3",256,0,0,0 +,59,3,"header","container-3-child-header-3",512,0,200,58 +,60,3,"header","container-3-child-header-2",384,0,200,58 +,61,3,"header","container-3-child-header-1",320,0,200,58 +,62,3,"header","container-3-child-left-2",768,0,201,58 +,63,3,"header","container-3-child-left-1",640,0,201,58 diff --git a/Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/CopyContainerToOtherLanguageKeepsChildOrder.csv b/Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/CopyContainerToOtherLanguageKeepsChildOrder.csv new file mode 100644 index 00000000..28e8a930 --- /dev/null +++ b/Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/CopyContainerToOtherLanguageKeepsChildOrder.csv @@ -0,0 +1,15 @@ +"pages" +,"uid","pid","title","slug","sys_language_uid","l10n_parent","l10n_source",is_siteroot +,1,0,"page-1","/",0,0,0,1 +,2,0,"page-1-language-1","/",1,1,1,0 +,3,1,"page-2","/page-2",0,0,0,0 +,4,1,"page-2-language-1","/page-2",1,3,3,0 +"tt_content" +,"uid","pid","CType","header","sorting","sys_language_uid","colPos","tx_container_parent" +,51,1,"b13-2cols-with-header-container","container-3",1024,0,0,0 +,52,1,"header","container-3-child-header-1",1280,0,200,51 +,53,1,"header","container-3-child-header-2",1536,0,200,51 +,54,1,"header","container-3-child-header-3",1792,0,200,51 +,55,1,"header","container-3-child-left-1",2048,0,201,51 +,56,1,"header","container-3-child-left-2",2304,0,201,51 +,57,1,"header","outside",2560,0,0,0 diff --git a/Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/CopyContainerToOtherLanguageKeepsChildOrderResult.csv b/Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/CopyContainerToOtherLanguageKeepsChildOrderResult.csv new file mode 100644 index 00000000..3cb1357d --- /dev/null +++ b/Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/CopyContainerToOtherLanguageKeepsChildOrderResult.csv @@ -0,0 +1,15 @@ +"tt_content" +,"uid","pid","CType","header","sorting","sys_language_uid","colPos","tx_container_parent" +,51,1,"b13-2cols-with-header-container","container-3",1024,0,0,0 +,52,1,"header","container-3-child-header-1",1280,0,200,51 +,53,1,"header","container-3-child-header-2",1536,0,200,51 +,54,1,"header","container-3-child-header-3",1792,0,200,51 +,55,1,"header","container-3-child-left-1",2048,0,201,51 +,56,1,"header","container-3-child-left-2",2304,0,201,51 +,57,1,"header","outside",2560,0,0,0 +,58,3,"b13-2cols-with-header-container","container-3",256,1,0,0 +,59,3,"header","container-3-child-header-1",512,1,200,58 +,60,3,"header","container-3-child-header-2",768,1,200,58 +,61,3,"header","container-3-child-header-3",1024,1,200,58 +,62,3,"header","container-3-child-left-1",1280,1,201,58 +,63,3,"header","container-3-child-left-2",1536,1,201,58 diff --git a/Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/MoveContainerToOtherLanguageKeepsChildOrder.csv b/Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/MoveContainerToOtherLanguageKeepsChildOrder.csv new file mode 100644 index 00000000..28e8a930 --- /dev/null +++ b/Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/MoveContainerToOtherLanguageKeepsChildOrder.csv @@ -0,0 +1,15 @@ +"pages" +,"uid","pid","title","slug","sys_language_uid","l10n_parent","l10n_source",is_siteroot +,1,0,"page-1","/",0,0,0,1 +,2,0,"page-1-language-1","/",1,1,1,0 +,3,1,"page-2","/page-2",0,0,0,0 +,4,1,"page-2-language-1","/page-2",1,3,3,0 +"tt_content" +,"uid","pid","CType","header","sorting","sys_language_uid","colPos","tx_container_parent" +,51,1,"b13-2cols-with-header-container","container-3",1024,0,0,0 +,52,1,"header","container-3-child-header-1",1280,0,200,51 +,53,1,"header","container-3-child-header-2",1536,0,200,51 +,54,1,"header","container-3-child-header-3",1792,0,200,51 +,55,1,"header","container-3-child-left-1",2048,0,201,51 +,56,1,"header","container-3-child-left-2",2304,0,201,51 +,57,1,"header","outside",2560,0,0,0 diff --git a/Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/MoveContainerToOtherLanguageKeepsChildOrderResult.csv b/Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/MoveContainerToOtherLanguageKeepsChildOrderResult.csv new file mode 100644 index 00000000..3ccf4ba9 --- /dev/null +++ b/Tests/Functional/Datahandler/Localization/FreeMode/Fixtures/Container/MoveContainerToOtherLanguageKeepsChildOrderResult.csv @@ -0,0 +1,9 @@ +"tt_content" +,"uid","pid","CType","header","sorting","sys_language_uid","colPos","tx_container_parent" +,51,3,"b13-2cols-with-header-container","container-3",256,1,0,0 +,52,3,"header","container-3-child-header-1",512,1,200,51 +,53,3,"header","container-3-child-header-2",768,1,200,51 +,54,3,"header","container-3-child-header-3",1024,1,200,51 +,55,3,"header","container-3-child-left-1",1280,1,201,51 +,56,3,"header","container-3-child-left-2",1536,1,201,51 +,57,1,"header","outside",2560,0,0,0