[BUGFIX] FormEngine: Fix IRRE handling in flexforms 79/44579/10
authorMorton Jonuschat <m.jonuschat@mojocode.de>
Fri, 6 Nov 2015 14:16:03 +0000 (15:16 +0100)
committerChristian Kuhn <lolli@schwarzbu.ch>
Mon, 9 Nov 2015 14:03:25 +0000 (15:03 +0100)
This patchset solves multiple problems with handling IRRE within a
FlexForm:

 * creating new records no longer triggers an internal server error
   due to missing parent data
 * IRRE children are passed to the renderer so that they get displayed
   in the backend
 * child table is determined correctly within flexforms when toggling
   expand/collapse state.

Resolves: #70918
Resolves: #70859
Releases: master
Change-Id: I93abeab696fff356453f0a1c305a8cfeede7158c
Reviewed-on: https://review.typo3.org/44579
Reviewed-by: Frank Nägler <frank.naegler@typo3.org>
Tested-by: Frank Nägler <frank.naegler@typo3.org>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
typo3/sysext/backend/Classes/Controller/FormInlineAjaxController.php
typo3/sysext/backend/Classes/Form/Container/FlexFormElementContainer.php
typo3/sysext/core/Configuration/DefaultConfiguration.php

index c31d769..9187133 100644 (file)
@@ -101,7 +101,11 @@ class FormInlineAjaxController
             $childVanillaUid = (int)$inlineFirstPid;
         }
 
+        if ($parentConfig['type'] === 'flex') {
+            $parentConfig = $this->getParentConfigFromFlexForm($parentConfig, $domObjectId);
+        }
         $childTableName = $parentConfig['foreign_table'];
+
         /** @var TcaDatabaseRecord $formDataGroup */
         $formDataGroup = GeneralUtility::makeInstance(TcaDatabaseRecord::class);
         /** @var FormDataCompiler $formDataCompiler */
@@ -113,6 +117,9 @@ class FormInlineAjaxController
             'isInlineChild' => true,
             'inlineStructure' => $inlineStackProcessor->getStructure(),
             'inlineFirstPid' => $inlineFirstPid,
+            'inlineParentUid' => $parent['uid'],
+            'inlineParentTableName' => $parent['table'],
+            'inlineParentFieldName' => $parent['field'],
             'inlineParentConfig' => $parentConfig,
         ];
         if ($childChildUid) {
@@ -485,13 +492,16 @@ class FormInlineAjaxController
     protected function compileChild(array $parentData, $parentFieldName, $childUid, array $inlineStructure)
     {
         $parentConfig = $parentData['processedTca']['columns'][$parentFieldName]['config'];
-        $childTableName = $parentConfig['foreign_table'];
 
         /** @var InlineStackProcessor $inlineStackProcessor */
         $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
         $inlineStackProcessor->initializeByGivenStructure($inlineStructure);
         $inlineTopMostParent = $inlineStackProcessor->getStructureLevel(0);
 
+        // @todo: do not use stack processor here ...
+        $child = $inlineStackProcessor->getUnstableStructure();
+        $childTableName = $child['table'];
+
         /** @var TcaDatabaseRecord $formDataGroup */
         $formDataGroup = GeneralUtility::makeInstance(TcaDatabaseRecord::class);
         /** @var FormDataCompiler $formDataCompiler */
@@ -734,4 +744,76 @@ class FormInlineAjaxController
     {
         return $GLOBALS['BE_USER'];
     }
+
+    /**
+     * Extract the inline child table configuration from the flexform data structure
+     * using the the domObjectId to traverse the XML structure.
+     *
+     * domObjectId parsing has been copied from InlineStackProcessor::initializeByDomObjectId
+     *
+     * @param array $parentConfig
+     * @param string $domObjectId
+     * @return array
+     */
+    protected function getParentConfigFromFlexForm(array $parentConfig, $domObjectId)
+    {
+        // Substitute FlexForm addition and make parsing a bit easier
+        $domObjectId = str_replace('---', ':', $domObjectId);
+        // The starting pattern of an object identifier (e.g. "data-<firstPidValue>-<anything>)
+        $pattern = '/^data' . '-' . '(?<firstPidValue>.+?)' . '-' . '(?<anything>.+)$/';
+
+        $flexFormPath = [];
+
+        if (preg_match($pattern, $domObjectId, $match)) {
+            $parts = explode('-', $match['anything']);
+
+            if (!isset($parts[2]) || strpos($parts[2], ':') === false) {
+                throw new \UnexpectedValueException(
+                    'DOM Object ID' . $domObjectId. 'does not contain required information '
+                    . 'to extract inline field configuration.',
+                    1446996136
+                );
+            }
+
+            $fieldParts = GeneralUtility::trimExplode(':', $parts[2]);
+
+            // FlexForm parts start with data:
+            if (empty($fieldParts) || !isset($fieldParts[1]) || $fieldParts[1] !== 'data') {
+                throw new \UnexpectedValueException(
+                    'Malformed flexform identifier: ' . $parts[2],
+                    1446996254
+                );
+            }
+
+            $flexFormPath = array_slice($fieldParts, 2);
+        }
+
+        $childConfig = $parentConfig['ds']['sheets'];
+
+        foreach ($flexFormPath as $flexFormNode) {
+            // We are dealing with configuration information from a flexform,
+            // not value storage, identifiers that the reference language or
+            // value nodes must be skipped.
+            if (!isset($childConfig[$flexFormNode]) && preg_match('/^[lv][[:alpha:]]+$/', $flexFormNode)) {
+                continue;
+            }
+            $childConfig = $childConfig[$flexFormNode];
+
+            // Skip to the field configuration of a sheet
+            if (isset($childConfig['ROOT']) && $childConfig['ROOT']['type'] == 'array') {
+                $childConfig = $childConfig['ROOT']['el'];
+            }
+        }
+
+        if (!isset($childConfig['config'])
+            || !is_array($childConfig['config'])
+            || $childConfig['config']['type'] !== 'inline'
+        ) {
+            throw new \UnexpectedValueException(
+                'Configuration retrieved from FlexForm is incomplete or not of type "inline".',
+                1446996319
+            );
+        }
+        return $childConfig['config'];
+    }
 }
index fb8e0ab..0982fc3 100644 (file)
@@ -81,6 +81,7 @@ class FlexFormElementContainer extends AbstractContainer
                     'fieldConf' => [
                         'label' => $languageService->sL(trim($flexFormFieldArray['label'])),
                         'config' => $flexFormFieldArray['config'],
+                        'children' => $flexFormFieldArray['children'],
                         'defaultExtras' => $flexFormFieldArray['defaultExtras'],
                         'onChange' => $flexFormFieldArray['onChange'],
                     ],
index 441037a..5292e46 100644 (file)
@@ -658,11 +658,28 @@ return array(
                             \TYPO3\CMS\Backend\Form\FormDataProvider\InlineOverrideChildTca::class,
                         ),
                     ),
-                    \TYPO3\CMS\Backend\Form\FormDataProvider\TcaInlineExpandCollapseState::class => array(
+                    \TYPO3\CMS\Backend\Form\FormDataProvider\TcaFlexFetch::class => array(
                         'depends' => array(
+                            \TYPO3\CMS\Backend\Form\FormDataProvider\InitializeProcessedTca::class,
+                            \TYPO3\CMS\Backend\Form\FormDataProvider\InlineOverrideChildTca::class,
                             \TYPO3\CMS\Backend\Form\FormDataProvider\TcaColumnsRemoveUnused::class,
                         ),
                     ),
+                    \TYPO3\CMS\Backend\Form\FormDataProvider\TcaFlexPrepare::class => array(
+                        'depends' => array(
+                            \TYPO3\CMS\Backend\Form\FormDataProvider\TcaFlexFetch::class,
+                        ),
+                    ),
+                    \TYPO3\CMS\Backend\Form\FormDataProvider\TcaFlexProcess::class => array(
+                        'depends' => array(
+                            \TYPO3\CMS\Backend\Form\FormDataProvider\TcaFlexPrepare::class,
+                        ),
+                    ),
+                    \TYPO3\CMS\Backend\Form\FormDataProvider\TcaInlineExpandCollapseState::class => array(
+                        'depends' => array(
+                            \TYPO3\CMS\Backend\Form\FormDataProvider\TcaFlexProcess::class,
+                        ),
+                    ),
                     \TYPO3\CMS\Backend\Form\FormDataProvider\TcaInlineConfiguration::class => array(
                         'depends' => array(
                             \TYPO3\CMS\Backend\Form\FormDataProvider\TcaInlineExpandCollapseState::class,