From 7d3fedff3f91fe90881f2cd5ccee84192ac98f25 Mon Sep 17 00:00:00 2001 From: Oliver Hader <oliver@typo3.org> Date: Sat, 18 Mar 2017 23:38:26 +0100 Subject: [PATCH] [BUGFIX] AllowLanguageSynchronization processes null values twice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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: TYPO3com <no-reply@typo3.com> Reviewed-by: Frank Nägler <frank.naegler@typo3.org> Tested-by: Frank Nägler <frank.naegler@typo3.org> Reviewed-by: Georg Ringer <georg.ringer@gmail.com> Tested-by: Georg Ringer <georg.ringer@gmail.com> --- .../Localization/DataMapProcessor.php | 42 ++++++++++++++++--- .../Regular/AbstractActionTestCase.php | 8 ++++ .../Regular/DataSet/LiveDefaultElements.csv | 26 ++++++------ .../Regular/Modify/ActionTest.php | 14 +++++++ .../localizeContentWSynchronizationHNull.csv | 9 ++++ 5 files changed, 80 insertions(+), 19 deletions(-) create mode 100644 typo3/sysext/core/Tests/Functional/DataHandling/Regular/Modify/DataSet/localizeContentWSynchronizationHNull.csv diff --git a/typo3/sysext/core/Classes/DataHandling/Localization/DataMapProcessor.php b/typo3/sysext/core/Classes/DataHandling/Localization/DataMapProcessor.php index 727b842558c4..684185dcf84e 100644 --- a/typo3/sysext/core/Classes/DataHandling/Localization/DataMapProcessor.php +++ b/typo3/sysext/core/Classes/DataHandling/Localization/DataMapProcessor.php @@ -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 diff --git a/typo3/sysext/core/Tests/Functional/DataHandling/Regular/AbstractActionTestCase.php b/typo3/sysext/core/Tests/Functional/DataHandling/Regular/AbstractActionTestCase.php index f0d32585b00e..caf3e2ca0bd1 100644 --- a/typo3/sysext/core/Tests/Functional/DataHandling/Regular/AbstractActionTestCase.php +++ b/typo3/sysext/core/Tests/Functional/DataHandling/Regular/AbstractActionTestCase.php @@ -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 */ diff --git a/typo3/sysext/core/Tests/Functional/DataHandling/Regular/DataSet/LiveDefaultElements.csv b/typo3/sysext/core/Tests/Functional/DataHandling/Regular/DataSet/LiveDefaultElements.csv index 93875a7a38c2..1bc27deaf409 100644 --- a/typo3/sysext/core/Tests/Functional/DataHandling/Regular/DataSet/LiveDefaultElements.csv +++ b/typo3/sysext/core/Tests/Functional/DataHandling/Regular/DataSet/LiveDefaultElements.csv @@ -1,18 +1,18 @@ -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, diff --git a/typo3/sysext/core/Tests/Functional/DataHandling/Regular/Modify/ActionTest.php b/typo3/sysext/core/Tests/Functional/DataHandling/Regular/Modify/ActionTest.php index 3e041b463e9e..1afcd9a7d143 100644 --- a/typo3/sysext/core/Tests/Functional/DataHandling/Regular/Modify/ActionTest.php +++ b/typo3/sysext/core/Tests/Functional/DataHandling/Regular/Modify/ActionTest.php @@ -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 diff --git a/typo3/sysext/core/Tests/Functional/DataHandling/Regular/Modify/DataSet/localizeContentWSynchronizationHNull.csv b/typo3/sysext/core/Tests/Functional/DataHandling/Regular/Modify/DataSet/localizeContentWSynchronizationHNull.csv new file mode 100644 index 000000000000..899fb88f8e65 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/DataHandling/Regular/Modify/DataSet/localizeContentWSynchronizationHNull.csv @@ -0,0 +1,9 @@ +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""}" -- GitLab