[BUGFIX] Set language uid for inline children by default 61/52061/11
authorNicole Cordes <typo3@cordes.co>
Wed, 15 Mar 2017 16:11:27 +0000 (17:11 +0100)
committerNicole Cordes <typo3@cordes.co>
Sat, 25 Mar 2017 13:43:27 +0000 (14:43 +0100)
On creating child records for an inline field, currently no language
is set and the children are stored with sys_language_uid 0. This patch
adds the proper configuration to ensure a default children language
according to its parent language.

Resolves: #48883
Resolves: #76048
Resolves: #73609
Releases: master, 7.6
Change-Id: Id9e449dbb06fed11670eedec45a92529fb4acb6f
Reviewed-on: https://review.typo3.org/52061
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Tobi Kretschmann <tobi@tobishome.de>
Reviewed-by: Frank N├Ągler <frank.naegler@typo3.org>
Reviewed-by: Nicole Cordes <typo3@cordes.co>
Tested-by: Nicole Cordes <typo3@cordes.co>
typo3/sysext/backend/Classes/Form/Container/InlineControlContainer.php
typo3/sysext/backend/Classes/Form/FormDataProvider/DatabaseRowInitializeNew.php
typo3/sysext/backend/Classes/Form/FormDataProvider/TcaInlineConfiguration.php
typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/DatabaseRowInitializeNewTest.php

index 5149db7..0afe9c7 100644 (file)
@@ -109,8 +109,11 @@ class InlineControlContainer extends AbstractContainer
         $foreign_table = $config['foreign_table'];
 
         $language = 0;
-        if (BackendUtility::isTableLocalizable($table)) {
-            $language = (int)$row[$GLOBALS['TCA'][$table]['ctrl']['languageField']];
+        $languageFieldName = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
+        if (BackendUtility::isTableLocalizable($table) && isset($row[$languageFieldName][0])) {
+            $language = (int)$row[$languageFieldName][0];
+        } elseif (BackendUtility::isTableLocalizable($table)) {
+            $language = (int)$row[$languageFieldName];
         }
 
         // Add the current inline job to the structure stack
index 6e8c7e2..32511ce 100644 (file)
@@ -48,6 +48,7 @@ class DatabaseRowInitializeNew implements FormDataProviderInterface
         $result = $this->setDefaultsFromNeighborRow($result);
         $result = $this->setDefaultsFromDevVals($result);
         $result = $this->setDefaultsFromInlineRelations($result);
+        $result = $this->setDefaultsFromInlineParentLanguage($result);
         $result = $this->setPid($result);
 
         return $result;
@@ -193,6 +194,39 @@ class DatabaseRowInitializeNew implements FormDataProviderInterface
     }
 
     /**
+     * If a new child is created in an inline relation via ajax, and if the parent is a localized record,
+     * the child should have the same sys_language_uid set in the field declared in ['ctrl']['languageField']
+     * if the child is localizable itself.
+     * A localized parent transfers its sys_language_uid via inlineParentConfig['inline']['parentSysLanguageUid'],
+     * use that value as default for the child record languageField.
+     *
+     * @param array $result Result array
+     * @return array Modified result array
+     * @throws \UnexpectedValueException
+     */
+    protected function setDefaultsFromInlineParentLanguage(array $result): array
+    {
+        if (!isset($result['inlineParentConfig']['inline']['parentSysLanguageUid'])
+            || empty($result['processedTca']['ctrl']['languageField'])
+            || empty($result['processedTca']['ctrl']['transOrigPointerField'])
+        ) {
+            return $result;
+        }
+
+        if (!MathUtility::canBeInterpretedAsInteger($result['inlineParentConfig']['inline']['parentSysLanguageUid'])) {
+            throw new \UnexpectedValueException(
+                'A sys_language_uid is set from inline parent config but the value is no integer',
+                1490360772
+            );
+        }
+        $parentSysLanguageUid = (int)$result['inlineParentConfig']['inline']['parentSysLanguageUid'];
+        $languageFieldName = $result['processedTca']['ctrl']['languageField'];
+        $result['databaseRow'][$languageFieldName] = $parentSysLanguageUid;
+
+        return $result;
+    }
+
+    /**
      * Set the pid. This is either the vanillaUid (see description in FormDataCompiler),
      * or a pid given by pageTsConfig for inline children.
      *
index 02b41c8..091c163 100644 (file)
@@ -46,9 +46,11 @@ class TcaInlineConfiguration implements FormDataProviderInterface
 
             $result = $this->initializeMinMaxItems($result, $fieldName);
             $result = $this->initializeLocalizationMode($result, $fieldName);
+            $result = $this->initializeChildrenLanguage($result, $fieldName);
             $result = $this->initializeAppearance($result, $fieldName);
             $result = $this->addInlineSelectorAndUniqueConfiguration($result, $fieldName);
         }
+
         return $result;
     }
 
@@ -116,7 +118,7 @@ class TcaInlineConfiguration implements FormDataProviderInterface
             'sort' => true,
             'hide' => true,
             'delete' => true,
-            'localize' => true
+            'localize' => true,
         ];
         if (isset($config['appearance']['enabledControls']) && is_array($config['appearance']['enabledControls'])) {
             $config['appearance']['enabledControls'] = array_merge($enabledControls, $config['appearance']['enabledControls']);
@@ -146,6 +148,7 @@ class TcaInlineConfiguration implements FormDataProviderInterface
             // If handled record is not localized, set localizationMode to 'none' and return
             // @deprecated: IRRE 'localizationMode' is deprecated and will be removed in TYPO3 CMS 9
             $result['processedTca']['columns'][$fieldName]['config']['behaviour']['localizationMode'] = 'none';
+
             return $result;
         }
 
@@ -192,6 +195,46 @@ class TcaInlineConfiguration implements FormDataProviderInterface
 
         // @deprecated: IRRE 'localizationMode' is deprecated and will be removed in TYPO3 CMS 9
         $result['processedTca']['columns'][$fieldName]['config']['behaviour']['localizationMode'] = $mode;
+
+        return $result;
+    }
+
+    /**
+     * Set default value for child records 'sys_language_uid' field. This is relevant if a localized
+     * parent is edited and a child is added via the ajax call. The child should then have the same
+     * sys_language_uid as the parent.
+     * The method verifies if the parent is a localized parent, and writes the current languageField
+     * value into TCA ['config']['inline']['parentSysLanguageUid'] of the parent inline TCA field. The whole
+     * ['config'] section is transferred to the 'create new child' ajax controller, the value is then used within
+     * 'DatabaseRowInitializeNew' data provider to initialize the child languageField value with that value.
+     *
+     * @param array $result Result array
+     * @param string $fieldName Current handle field name
+     * @return array Modified item array
+     */
+    protected function initializeChildrenLanguage(array $result, $fieldName)
+    {
+        $childTableName = $result['processedTca']['columns'][$fieldName]['config']['foreign_table'];
+
+        if (empty($result['processedTca']['ctrl']['languageField'])
+            || empty($GLOBALS['TCA'][$childTableName]['ctrl']['languageField'])
+        ) {
+            return $result;
+        }
+
+        $parentConfig = $result['processedTca']['columns'][$fieldName]['config'];
+        if ($parentConfig['behaviour']['localizationMode'] === 'keep') {
+            return $result;
+        }
+
+        $parentLanguageField = $result['processedTca']['ctrl']['languageField'];
+        if (!isset($parentConfig['inline']['parentSysLanguageUid'])
+            && isset($result['databaseRow'][$parentLanguageField][0])
+        ) {
+            $result['processedTca']['columns'][$fieldName]['config']['inline']['parentSysLanguageUid']
+                = (int)$result['databaseRow'][$parentLanguageField][0];
+        }
+
         return $result;
     }
 
@@ -254,7 +297,8 @@ class TcaInlineConfiguration implements FormDataProviderInterface
 
         // Throw if field is type group, but not internal_type db
         if ($selectorOrUniqueConfiguration['config']['type'] === 'group'
-            && (!isset($selectorOrUniqueConfiguration['config']['internal_type']) ||  $selectorOrUniqueConfiguration['config']['internal_type'] !== 'db')) {
+            && (!isset($selectorOrUniqueConfiguration['config']['internal_type']) || $selectorOrUniqueConfiguration['config']['internal_type'] !== 'db')
+        ) {
             throw new \UnexpectedValueException(
                 'Table ' . $result['tableName'] . ' field ' . $fieldName . ' points in foreign_selector or foreign_unique'
                 . ' to field ' . $fieldNameInChildConfiguration . ' of table ' . $config['foreign_table'] . '. This field'
index c46ba87..8916bda 100644 (file)
@@ -599,6 +599,65 @@ class DatabaseRowInitializeNewTest extends \TYPO3\TestingFramework\Core\Unit\Uni
     /**
      * @test
      */
+    public function addDataThrowsExceptionIfInlineParentLanguageIsNoInteger()
+    {
+        $input = [
+            'command' => 'new',
+            'tableName' => 'aTable',
+            'databaseRow' => [],
+            'inlineParentConfig' => [
+                'inline' => [
+                    'parentSysLanguageUid' => 'not-an-integer',
+                ],
+            ],
+            'processedTca' => [
+                'ctrl' => [
+                    'languageField' => 'sys_language_uid',
+                    'transOrigPointerField' => 'l10n_parent',
+                ],
+                'columns' => [],
+            ],
+        ];
+        $this->expectException(\UnexpectedValueException::class);
+        $this->expectExceptionCode(1490360772);
+        $this->subject->addData($input);
+    }
+
+    /**
+     * @test
+     */
+    public function addDataSetsSysLanguageUidFromParent()
+    {
+        $input = [
+            'command' => 'new',
+            'tableName' => 'aTable',
+            'vanillaUid' => 1,
+            'databaseRow' => [],
+            'inlineParentConfig' => [
+                'inline' => [
+                    'parentSysLanguageUid' => '42',
+                ],
+            ],
+            'processedTca' => [
+                'ctrl' => [
+                    'languageField' => 'sys_language_uid',
+                    'transOrigPointerField' => 'l10n_parent',
+                ],
+                'columns' => [],
+            ],
+        ];
+        $expected = $input;
+        $expected['databaseRow'] = [
+            'sys_language_uid' => 42,
+            'pid' => 1,
+        ];
+        $result = $this->subject->addData($input);
+        $this->assertSame($expected, $result);
+    }
+
+    /**
+     * @test
+     */
     public function addDataSetsPidToVanillaUid()
     {
         $input = [