Commit 7d3fedff authored by Oliver Hader's avatar Oliver Hader Committed by Georg Ringer
Browse files

[BUGFIX] AllowLanguageSynchronization processes null values twice

TCA columns having 'l10n_mode' defined to either 'exclude' or
'mergeIfNotBlank' (later is autotatically migrated to the according
'allowLanguageSynchronization' behavior) are processed twice on handling
modifications in DataHandler. In a result an exeception is throws which
prevents recursions on chained translations.

Checking null values with plain isset() is replaced with a new method
using array_key_exists() to consider null values as well.

Change-Id: I3f1b0cdf3f62845f3bae3632f6aaa9b2dfdc6e0b
Resolves: #80338
Releases: master
Reviewed-on: https://review.typo3.org/52091


Tested-by: default avatarTYPO3com <no-reply@typo3.com>
Reviewed-by: Frank Nägler's avatarFrank Nägler <frank.naegler@typo3.org>
Tested-by: Frank Nägler's avatarFrank Nägler <frank.naegler@typo3.org>
Reviewed-by: Georg Ringer's avatarGeorg Ringer <georg.ringer@gmail.com>
Tested-by: Georg Ringer's avatarGeorg Ringer <georg.ringer@gmail.com>
parent 4eb91cae
......@@ -382,12 +382,16 @@ class DataMapProcessor
protected function synchronizeFieldValues(DataMapItem $item, string $fieldName, array $fromRecord, array $forRecord)
{
// skip if this field has been processed already, assumed that proper sanitation happened
if (isset($this->allDataMap[$item->getTableName()][$item->getId()][$fieldName])) {
if ($this->isSetInDataMap($item->getTableName(), $item->getId(), $fieldName)) {
return;
}
$fromId = $fromRecord['uid'];
$fromValue = $this->allDataMap[$item->getFromTableName()][$fromId][$fieldName] ?? $fromRecord[$fieldName];
if ($this->isSetInDataMap($item->getFromTableName(), $fromId, $fieldName)) {
$fromValue = $this->allDataMap[$item->getFromTableName()][$fromId][$fieldName];
} else {
$fromValue = $fromRecord[$fieldName];
}
// plain values
if (!$this->isRelationField($item->getFromTableName(), $fieldName)) {
......@@ -414,16 +418,21 @@ class DataMapProcessor
*/
protected function synchronizeDirectRelations(DataMapItem $item, string $fieldName, array $fromRecord)
{
$fromId = $fromRecord['uid'];
$fromValue = $this->allDataMap[$item->getFromTableName()][$fromId][$fieldName] ?? $fromRecord[$fieldName];
$configuration = $GLOBALS['TCA'][$item->getFromTableName()]['columns'][$fieldName];
$isSpecialLanguageField = ($configuration['config']['special'] ?? null) === 'languages';
$fromId = $fromRecord['uid'];
if ($this->isSetInDataMap($item->getFromTableName(), $fromId, $fieldName)) {
$fromValue = $this->allDataMap[$item->getFromTableName()][$fromId][$fieldName];
} else {
$fromValue = $fromRecord[$fieldName];
}
// non-MM relations are stored as comma separated values, just use them
// if values are available in data-map already, just use them as well
if (
empty($configuration['config']['MM'])
|| isset($this->allDataMap[$item->getFromTableName()][$fromId][$fieldName])
|| $this->isSetInDataMap($item->getFromTableName(), $fromId, $fieldName)
|| $isSpecialLanguageField
) {
$this->modifyDataMap(
......@@ -493,7 +502,7 @@ class DataMapProcessor
// determine suggested elements of either translation parent or source record
// from data-map, in case the accordant language parent/source record was modified
if (isset($this->allDataMap[$item->getFromTableName()][$fromId][$fieldName])) {
if ($this->isSetInDataMap($item->getFromTableName(), $fromId, $fieldName)) {
$suggestedAncestorIds = GeneralUtility::trimExplode(
',',
$this->allDataMap[$item->getFromTableName()][$fromId][$fieldName],
......@@ -643,6 +652,27 @@ class DataMapProcessor
);
}
/**
* Determines whether a combination of table name, id and field name is
* set in data-map. This method considers null values as well, that would
* not be considered by a plain isset() invocation.
*
* @param string $tableName
* @param string|int $id
* @param string $fieldName
* @return bool
*/
protected function isSetInDataMap(string $tableName, $id, string $fieldName)
{
return
// directly look-up field name
isset($this->allDataMap[$tableName][$id][$fieldName])
// check existence of field name as key for null values
|| isset($this->allDataMap[$tableName][$id])
&& is_array($this->allDataMap[$tableName][$id])
&& array_key_exists($fieldName, $this->allDataMap[$tableName][$id]);
}
/**
* Applies modifications to the data-map, calling this method is essential
* to determine new data-map items to be process for synchronizing chained
......
......@@ -155,6 +155,14 @@ abstract class AbstractActionTestCase extends \TYPO3\CMS\Core\Tests\Functional\D
$this->actionService->modifyRecord(self::TABLE_Content, self::VALUE_ContentIdSecond, ['header' => 'Testing #1']);
}
public function localizeContentWithLanguageSynchronizationHavingNullValue()
{
$GLOBALS['TCA']['tt_content']['columns']['bodytext']['config']['eval'] = 'null';
$GLOBALS['TCA']['tt_content']['columns']['bodytext']['config']['behaviour']['allowLanguageSynchronization'] = true;
$this->actionService->modifyRecord(self::TABLE_Content, self::VALUE_ContentIdSecond, ['bodytext' => null]);
self::localizeContentWithLanguageSynchronization();
}
/**
* @see DataSet/localizeContentFromNonDefaultLanguage.csv
*/
......
sys_language,,,,,,,,,,,,,,,,,
,uid,pid,hidden,title,flag,,,,,,,,,,,,
,1,0,0,Dansk,dk,,,,,,,,,,,,
,2,0,0,Deutsch,de,,,,,,,,,,,,
sys_category,,,,,,,,,,,,,,,,,
sys_language
,uid,pid,hidden,title,flag
,1,0,0,Dansk,dk
,2,0,0,Deutsch,de
sys_category
,uid,pid,sorting,deleted,sys_language_uid,l10n_parent,t3_origuid,t3ver_wsid,t3ver_state,t3ver_stage,t3ver_oid,t3ver_move_id,title,parent,items,l10n_diffsource,description
,28,0,256,0,0,0,0,0,0,0,0,0,Category A,0,0,,
,29,0,512,0,0,0,0,0,0,0,0,0,Category B,0,0,,
,30,0,768,0,0,0,0,0,0,0,0,0,Category C,0,0,,
,31,0,1024,0,0,0,0,0,0,0,0,0,Category A.A,28,0,,
tt_content,,,,,,,,,,,,,,,,,
,uid,pid,sorting,deleted,sys_language_uid,l18n_parent,l10n_source,t3_origuid,t3ver_wsid,t3ver_state,t3ver_stage,t3ver_oid,t3ver_move_id,header,,,
,297,89,256,0,0,0,0,0,0,0,0,0,0,Regular Element #1,,,
,298,89,512,0,0,0,0,0,0,0,0,0,0,Regular Element #2,,,
,299,89,768,0,0,0,0,0,0,0,0,0,0,Regular Element #3,,,
,300,89,1024,0,1,299,299,299,0,0,0,0,0,[Translate to Dansk:] Regular Element #3,,,
,301,89,384,0,1,297,297,297,0,0,0,0,0,[Translate to Dansk:] Regular Element #1,,,
,302,89,448,0,2,297,301,301,0,0,0,0,0,[Translate to Deutsch:] [Translate to Dansk:] Regular Element #1,,,
tt_content
,uid,pid,sorting,deleted,sys_language_uid,l18n_parent,l10n_source,t3_origuid,t3ver_wsid,t3ver_state,t3ver_stage,t3ver_oid,t3ver_move_id,header,bodytext
,297,89,256,0,0,0,0,0,0,0,0,0,0,Regular Element #1,
,298,89,512,0,0,0,0,0,0,0,0,0,0,Regular Element #2,
,299,89,768,0,0,0,0,0,0,0,0,0,0,Regular Element #3,
,300,89,1024,0,1,299,299,299,0,0,0,0,0,[Translate to Dansk:] Regular Element #3,
,301,89,384,0,1,297,297,297,0,0,0,0,0,[Translate to Dansk:] Regular Element #1,
,302,89,448,0,2,297,301,301,0,0,0,0,0,[Translate to Deutsch:] [Translate to Dansk:] Regular Element #1,
......@@ -193,6 +193,20 @@ class ActionTest extends \TYPO3\CMS\Core\Tests\Functional\DataHandling\Regular\A
->setTable(self::TABLE_Content)->setField('header')->setValues('[Translate to Dansk:] Regular Element #1', 'Testing #1'));
}
/**
* @test
* @see DataSet/localizeContentWSynchronizationHNull.csv
*/
public function localizeContentWithLanguageSynchronizationHavingNullValue()
{
parent::localizeContentWithLanguageSynchronizationHavingNullValue();
$this->assertAssertionDataSet('localizeContentWSynchronizationHNull');
$responseSections = $this->getFrontendResponse(self::VALUE_PageId, self::VALUE_LanguageId)->getResponseSections();
$this->assertThat($responseSections, $this->getRequestSectionHasRecordConstraint()
->setTable(self::TABLE_Content)->setField('header')->setValues('[Translate to Dansk:] Regular Element #1', 'Testing #1'));
}
/**
* @test
* @see DataSet/localizeContentFromNonDefaultLanguage.csv
......
tt_content
,uid,pid,sorting,deleted,sys_language_uid,l18n_parent,l10n_source,t3_origuid,t3ver_wsid,t3ver_state,t3ver_stage,t3ver_oid,t3ver_move_id,header,bodytext,l10n_state
,297,89,256,0,0,0,0,0,0,0,0,0,0,"Regular Element #1",,
,298,89,512,0,0,0,0,0,0,0,0,0,0,"Testing #1",\NULL,\NULL
,299,89,768,0,0,0,0,0,0,0,0,0,0,"Regular Element #3",,
,300,89,1024,0,1,299,299,299,0,0,0,0,0,"[Translate to Dansk:] Regular Element #3",,
,301,89,384,0,1,297,297,297,0,0,0,0,0,"[Translate to Dansk:] Regular Element #1",,
,302,89,448,0,2,297,301,301,0,0,0,0,0,"[Translate to Deutsch:] [Translate to Dansk:] Regular Element #1",,
,303,89,416,0,1,298,298,298,0,0,0,0,0,"Testing #1",\NULL,"{""header"":""parent"",""bodytext"":""parent""}"
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