Commit 88290dd4 authored by Oliver Hader's avatar Oliver Hader Committed by Oliver Hader
Browse files

[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: default avatarTYPO3com <no-reply@typo3.com>
Reviewed-by: Susanne Moog's avatarSusanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog's avatarSusanne Moog <susanne.moog@typo3.org>
Reviewed-by: Oliver Hader's avatarOliver Hader <oliver.hader@typo3.org>
Tested-by: Oliver Hader's avatarOliver Hader <oliver.hader@typo3.org>
parent d4f055b1
......@@ -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);
}
/**
......
......@@ -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'],
]
);
......
......@@ -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
......
......@@ -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
......
Supports Markdown
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