From 88290dd4e9bdce329a93119d3ff8b39a5314f763 Mon Sep 17 00:00:00 2001 From: Oliver Hader <oliver@typo3.org> Date: Wed, 15 Nov 2017 16:07:57 +0100 Subject: [PATCH] [BUGFIX] Avoid invalid references in DataMapProcessor If DataMapProcessor is called with a non-reference id, e.g. zero (0), this submission is considered as a reference. Since there is no database record having UID 0, the synchronization process fails with the following exeception: #1486233164: Child record was not processed To solve this behavior, invalid references (empty/zero) are not considered anymore to compare references. Besides that, values for localized records that are configured to be synchronized are sanitized correctly now. Resolves: #83009 Releases: master, 8.7 Change-Id: Ie370007521c45dac8bca03978a387b4662952b1d Reviewed-on: https://review.typo3.org/54655 Tested-by: TYPO3com <no-reply@typo3.com> Reviewed-by: Susanne Moog <susanne.moog@typo3.org> Tested-by: Susanne Moog <susanne.moog@typo3.org> Reviewed-by: Oliver Hader <oliver.hader@typo3.org> Tested-by: Oliver Hader <oliver.hader@typo3.org> --- .../Localization/DataMapProcessor.php | 30 +++++++++++-- .../ForeignField/AbstractActionTestCase.php | 45 ++++++++++++++++++- .../IRRE/ForeignField/Modify/ActionTest.php | 32 +++++++++++++ ...SynchronizationAndCustomLocalizedHotel.csv | 2 +- 4 files changed, 103 insertions(+), 6 deletions(-) diff --git a/typo3/sysext/core/Classes/DataHandling/Localization/DataMapProcessor.php b/typo3/sysext/core/Classes/DataHandling/Localization/DataMapProcessor.php index 33784b29b745..ba5a597a230b 100644 --- a/typo3/sysext/core/Classes/DataHandling/Localization/DataMapProcessor.php +++ b/typo3/sysext/core/Classes/DataHandling/Localization/DataMapProcessor.php @@ -130,9 +130,31 @@ class DataMapProcessor $this->enrich($this->nextItems); } + $this->allDataMap = $this->purgeDataMap($this->allDataMap); return $this->allDataMap; } + /** + * Purges superfluous empty data-map sections. + * + * @param array $dataMap + * @return array + */ + protected function purgeDataMap(array $dataMap): array + { + foreach ($dataMap as $tableName => $idValues) { + foreach ($idValues as $id => $values) { + if (empty($values)) { + unset($dataMap[$tableName][$id]); + } + } + if (empty($dataMap[$tableName])) { + unset($dataMap[$tableName]); + } + } + return $dataMap; + } + /** * Create data map items of all affected rows * @@ -203,7 +225,7 @@ class DataMapProcessor } /** - * Sanitizes the submitted data-map and removes fields which are not + * Sanitizes the submitted data-map items and removes fields which are not * defined as custom and thus rely on either parent or source values. * * @param DataMapItem[] $items @@ -253,7 +275,7 @@ class DataMapProcessor foreach ($item->getApplicableScopes() as $scope) { $fieldNames = array_merge( $fieldNames, - $this->getFieldNamesForItemScope($item, $scope, !$item->isNew()) + $this->getFieldNamesForItemScope($item, $scope, false) ); } @@ -669,7 +691,7 @@ class DataMapProcessor $suggestedAncestorIds = $this->mapRelationItemId($relationHandler->itemArray); } - return $suggestedAncestorIds; + return array_filter($suggestedAncestorIds); } /** @@ -701,7 +723,7 @@ class DataMapProcessor $persistedIds = $this->mapRelationItemId($relationHandler->itemArray); } - return $persistedIds; + return array_filter($persistedIds); } /** diff --git a/typo3/sysext/core/Tests/Functional/DataHandling/IRRE/ForeignField/AbstractActionTestCase.php b/typo3/sysext/core/Tests/Functional/DataHandling/IRRE/ForeignField/AbstractActionTestCase.php index c4599ca0bea1..9c26360ffc9e 100644 --- a/typo3/sysext/core/Tests/Functional/DataHandling/IRRE/ForeignField/AbstractActionTestCase.php +++ b/typo3/sysext/core/Tests/Functional/DataHandling/IRRE/ForeignField/AbstractActionTestCase.php @@ -29,6 +29,7 @@ abstract class AbstractActionTestCase extends \TYPO3\CMS\Core\Tests\Functional\D const VALUE_HotelIdFirst = 3; const VALUE_HotelIdSecond = 4; const VALUE_HotelIdThird = 5; + const VALUE_OfferIdLast = 8; const VALUE_LanguageId = 1; const VALUE_LanguageIdSecond = 2; @@ -200,6 +201,48 @@ abstract class AbstractActionTestCase extends \TYPO3\CMS\Core\Tests\Functional\D $this->recordIds['newPriceId'] = $this->actionService->getDataHandler()->substNEWwithIDs[$newPriceId]; } + /** + * @see DataSet/localizeParentContentSynchronization.csv + */ + public function localizeParentContentAndSetInvalidChildReferenceWithLanguageSynchronization() + { + $GLOBALS['TCA'][self::TABLE_Content]['columns'][self::FIELD_ContentHotel]['config']['behaviour']['allowLanguageSynchronization'] = true; + $GLOBALS['TCA'][self::TABLE_Hotel]['columns'][self::FIELD_HotelOffer]['config']['behaviour']['allowLanguageSynchronization'] = true; + $GLOBALS['TCA'][self::TABLE_Offer]['columns'][self::FIELD_OfferPrice]['config']['behaviour']['allowLanguageSynchronization'] = true; + $newTableIds = $this->actionService->localizeRecord(self::TABLE_Content, self::VALUE_ContentIdLast, self::VALUE_LanguageId); + $this->recordIds['localizedContentId'] = $newTableIds[self::TABLE_Content][self::VALUE_ContentIdLast]; + // modify IRRE relation value of localized record (which should be sanitized and filtered) + $this->actionService->modifyRecord(self::TABLE_Content, $this->recordIds['localizedContentId'], [self::FIELD_ContentHotel => '0']); + } + + /** + * @see DataSet/localizeParentContentSynchronization.csv + */ + public function localizeParentContentAndSetInvalidChildReferenceWithLateLanguageSynchronization() + { + // disable language synchronization + $GLOBALS['TCA'][self::TABLE_Content]['columns'][self::FIELD_ContentHotel]['config']['behaviour']['allowLanguageSynchronization'] = false; + $GLOBALS['TCA'][self::TABLE_Hotel]['columns'][self::FIELD_HotelOffer]['config']['behaviour']['allowLanguageSynchronization'] = false; + $GLOBALS['TCA'][self::TABLE_Offer]['columns'][self::FIELD_OfferPrice]['config']['behaviour']['allowLanguageSynchronization'] = false; + $newTableIds = $this->actionService->localizeRecord(self::TABLE_Content, self::VALUE_ContentIdLast, self::VALUE_LanguageId); + $this->recordIds['localizedContentId'] = $newTableIds[self::TABLE_Content][self::VALUE_ContentIdLast]; + $this->recordIds['localizedHotelId'] = $newTableIds[self::TABLE_Hotel][self::VALUE_HotelIdThird]; + $this->recordIds['localizedOfferId'] = $newTableIds[self::TABLE_Offer][self::VALUE_OfferIdLast]; + // now enable language synchronization + $GLOBALS['TCA'][self::TABLE_Content]['columns'][self::FIELD_ContentHotel]['config']['behaviour']['allowLanguageSynchronization'] = true; + $GLOBALS['TCA'][self::TABLE_Hotel]['columns'][self::FIELD_HotelOffer]['config']['behaviour']['allowLanguageSynchronization'] = true; + $GLOBALS['TCA'][self::TABLE_Offer]['columns'][self::FIELD_OfferPrice]['config']['behaviour']['allowLanguageSynchronization'] = true; + // modify IRRE relation values of localized records (which should be sanitized and filtered) + $this->actionService->modifyRecords( + self::VALUE_PageId, + [ + self::TABLE_Content => ['uid' => $this->recordIds['localizedContentId'], self::FIELD_ContentHotel => '0'], + self::TABLE_Hotel => ['uid' => $this->recordIds['localizedHotelId'], self::FIELD_ContentHotel => '0'], + self::TABLE_Offer => ['uid' => $this->recordIds['localizedOfferId'], self::FIELD_ContentHotel => '0'], + ] + ); + } + /** * @see DataSet/changeParentContentRecordSorting.csv */ @@ -506,7 +549,7 @@ abstract class AbstractActionTestCase extends \TYPO3\CMS\Core\Tests\Functional\D $this->actionService->modifyRecords( $this->recordIds['localizedPageId'], [ - self::TABLE_Page => ['uid' => $this->recordIds['localizedPageId'], self::FIELD_PageHotel => '6,__nextUid'], + self::TABLE_Page => ['uid' => $this->recordIds['localizedPageId'], self::FIELD_PageHotel => '6,__nextUid', 'l10n_state' => [self::FIELD_PageHotel => 'custom']], self::TABLE_Hotel => ['uid' => '__NEW', 'sys_language_uid' => self::VALUE_LanguageId, 'title' => 'Hotel in dansk page only'], ] ); diff --git a/typo3/sysext/core/Tests/Functional/DataHandling/IRRE/ForeignField/Modify/ActionTest.php b/typo3/sysext/core/Tests/Functional/DataHandling/IRRE/ForeignField/Modify/ActionTest.php index 626e87e2d7d5..8e1ab4e53d71 100644 --- a/typo3/sysext/core/Tests/Functional/DataHandling/IRRE/ForeignField/Modify/ActionTest.php +++ b/typo3/sysext/core/Tests/Functional/DataHandling/IRRE/ForeignField/Modify/ActionTest.php @@ -183,6 +183,38 @@ class ActionTest extends \TYPO3\CMS\Core\Tests\Functional\DataHandling\IRRE\Fore ->setTable(self::TABLE_Hotel)->setField('title')->setValues('[Translate to Dansk:] Hotel #1', '[Translate to Dansk:] New Hotel #1')); } + /** + * @test + * @see DataSet/localizeParentContentSynchronization.csv + */ + public function localizeParentContentAndSetInvalidChildReferenceWithLanguageSynchronization() + { + parent::localizeParentContentAndSetInvalidChildReferenceWithLanguageSynchronization(); + // the assertion is the same as for localizeParentContentWithLanguageSynchronization() + $this->assertAssertionDataSet('localizeParentContentSynchronization'); + + $responseSections = $this->getFrontendResponse(self::VALUE_PageId, self::VALUE_LanguageId)->getResponseSections('Default', 'Extbase:list()'); + $this->assertThat($responseSections, $this->getRequestSectionStructureHasRecordConstraint() + ->setRecordIdentifier(self::TABLE_Content . ':' . self::VALUE_ContentIdLast)->setRecordField(self::FIELD_ContentHotel) + ->setTable(self::TABLE_Hotel)->setField('title')->setValues('[Translate to Dansk:] Hotel #1')); + } + + /** + * @test + * @see DataSet/localizeParentContentSynchronization.csv + */ + public function localizeParentContentAndSetInvalidChildReferenceWithLateLanguageSynchronization() + { + parent::localizeParentContentAndSetInvalidChildReferenceWithLateLanguageSynchronization(); + // the assertion is the same as for localizeParentContentWithLanguageSynchronization() + $this->assertAssertionDataSet('localizeParentContentSynchronization'); + + $responseSections = $this->getFrontendResponse(self::VALUE_PageId, self::VALUE_LanguageId)->getResponseSections('Default', 'Extbase:list()'); + $this->assertThat($responseSections, $this->getRequestSectionStructureHasRecordConstraint() + ->setRecordIdentifier(self::TABLE_Content . ':' . self::VALUE_ContentIdLast)->setRecordField(self::FIELD_ContentHotel) + ->setTable(self::TABLE_Hotel)->setField('title')->setValues('[Translate to Dansk:] Hotel #1')); + } + /** * @test * @see DataSet/changeParentContentRecordSorting.csv diff --git a/typo3/sysext/core/Tests/Functional/DataHandling/IRRE/ForeignField/Modify/DataSet/localizePageWithSynchronizationAndCustomLocalizedHotel.csv b/typo3/sysext/core/Tests/Functional/DataHandling/IRRE/ForeignField/Modify/DataSet/localizePageWithSynchronizationAndCustomLocalizedHotel.csv index 57676d2e07d3..f8e6da3c42a3 100644 --- a/typo3/sysext/core/Tests/Functional/DataHandling/IRRE/ForeignField/Modify/DataSet/localizePageWithSynchronizationAndCustomLocalizedHotel.csv +++ b/typo3/sysext/core/Tests/Functional/DataHandling/IRRE/ForeignField/Modify/DataSet/localizePageWithSynchronizationAndCustomLocalizedHotel.csv @@ -4,7 +4,7 @@ pages ,88,1,256,0,0,0,0,0,0,0,0,0,DataHandlerTest,0, ,89,88,256,0,0,0,0,0,0,0,0,0,Relations,1, ,90,88,512,0,0,0,0,0,0,0,0,0,Target,0, -,91,88,256,0,1,89,0,0,0,0,0,0,"[Translate to Dansk:] Relations",2,"{""url"":""parent"",""lastUpdated"":""parent"",""newUntil"":""parent"",""no_search"":""parent"",""shortcut"":""parent"",""shortcut_mode"":""parent"",""author"":""parent"",""author_email"":""parent"",""media"":""parent"",""tx_irretutorial_hotels"":""parent""}" +,91,88,256,0,1,89,0,0,0,0,0,0,"[Translate to Dansk:] Relations",2,"{""url"":""parent"",""lastUpdated"":""parent"",""newUntil"":""parent"",""no_search"":""parent"",""shortcut"":""parent"",""shortcut_mode"":""parent"",""author"":""parent"",""author_email"":""parent"",""media"":""parent"",""tx_irretutorial_hotels"":""custom""}" tx_irretutorial_1nff_hotel ,uid,pid,sorting,deleted,sys_language_uid,l18n_parent,t3_origuid,t3ver_wsid,t3ver_state,t3ver_stage,t3ver_oid,t3ver_move_id,title,parentid,parenttable,parentidentifier,offers ,2,89,512,0,0,0,0,0,0,0,0,0,"Hotel #0",89,pages,,0 -- GitLab