[BUGFIX] Do not compile parent config in inline ajax controller 77/52077/4
authorChristian Kuhn <lolli@schwarzbu.ch>
Fri, 17 Mar 2017 11:14:29 +0000 (12:14 +0100)
committerChristian Kuhn <lolli@schwarzbu.ch>
Mon, 20 Mar 2017 13:24:01 +0000 (14:24 +0100)
Using the signed parent tca config incoming via ajax request in the
inline ajax controller solves a series of issues in 'new' and other
scopes.
The expensive calculation of inline parent TCA config within the
controller can be dropped.

Change-Id: I4ff31d0398ebfa1bb311bbe6ea97c839aa2df1ac
Resolves: #80325
Resolves: #76671
Releases: master
Reviewed-on: https://review.typo3.org/52077
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Tobi Kretschmann <tobi@tobishome.de>
Tested-by: Tobi Kretschmann <tobi@tobishome.de>
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/FormDataGroup/InlineParentRecord.php [deleted file]
typo3/sysext/backend/Tests/Unit/Controller/FormInlineAjaxControllerTest.php
typo3/sysext/backend/Tests/Unit/Form/FormDataGroup/InlineParentRecordTest.php [deleted file]
typo3/sysext/core/Configuration/DefaultConfiguration.php

index 9c584c0..9e273e2 100644 (file)
@@ -18,12 +18,10 @@ namespace TYPO3\CMS\Backend\Controller;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Form\FormDataCompiler;
-use TYPO3\CMS\Backend\Form\FormDataGroup\InlineParentRecord;
 use TYPO3\CMS\Backend\Form\FormDataGroup\TcaDatabaseRecord;
 use TYPO3\CMS\Backend\Form\InlineStackProcessor;
 use TYPO3\CMS\Backend\Form\NodeFactory;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
-use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
 use TYPO3\CMS\Core\DataHandling\DataHandler;
 use TYPO3\CMS\Core\Localization\LocalizationFactory;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
@@ -63,47 +61,6 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
 
         // Parent, this table embeds the child table
         $parent = $inlineStackProcessor->getStructureLevel(-1);
-        $parentFieldName = $parent['field'];
-
-        if (MathUtility::canBeInterpretedAsInteger($parent['uid'])) {
-            $command = 'edit';
-            $vanillaUid = (int)$parent['uid'];
-            $databaseRow = [
-                // TcaInlineExpandCollapseState needs the record uid
-                'uid' => (int)$parent['uid'],
-            ];
-        } else {
-            $command = 'new';
-            $databaseRow = [];
-            $vanillaUid = (int)$inlineFirstPid;
-        }
-
-        $processedTca = $GLOBALS['TCA'][$parent['table']];
-        $processedTca['columns'][$parentFieldName]['config'] = $parentConfig;
-        if (!empty($parentConfig['dataStructureIdentifier'])) {
-            $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
-            $processedTca['columns'][$parentFieldName]['config']['ds'] = $flexFormTools->parseDataStructureByIdentifier($parentConfig['dataStructureIdentifier']);
-        }
-
-        $formDataCompilerInputForParent = [
-            'vanillaUid' => $vanillaUid,
-            'command' => $command,
-            'tableName' => $parent['table'],
-            'databaseRow' => $databaseRow,
-            'processedTca' => $processedTca,
-            'inlineFirstPid' => $inlineFirstPid,
-            'columnsToProcess' => [
-                $parentFieldName,
-            ],
-            // Do not resolve existing children, we don't need them now
-            'inlineResolveExistingChildren' => false,
-        ];
-        /** @var TcaDatabaseRecord $formDataGroup */
-        $formDataGroup = GeneralUtility::makeInstance(InlineParentRecord::class);
-        /** @var FormDataCompiler $formDataCompiler */
-        $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
-        $parentData = $formDataCompiler->compile($formDataCompilerInputForParent);
-        $parentConfig = $parentData['processedTca']['columns'][$parentFieldName]['config'];
 
         // Child, a record from this table should be rendered
         $child = $inlineStackProcessor->getUnstableStructure();
@@ -115,9 +72,6 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
             $childVanillaUid = (int)$inlineFirstPid;
         }
 
-        if ($parentConfig['type'] === 'flex') {
-            $parentConfig = $this->getParentConfigFromFlexForm($parentConfig, $domObjectId);
-        }
         $childTableName = $parentConfig['foreign_table'];
 
         /** @var TcaDatabaseRecord $formDataGroup */
@@ -135,10 +89,9 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
             'inlineParentTableName' => $parent['table'],
             'inlineParentFieldName' => $parent['field'],
             'inlineParentConfig' => $parentConfig,
-            // Fallback to $parentData is probably not needed here.
-            'inlineTopMostParentUid' => $parentData['inlineTopMostParentUid'] ?: $inlineTopMostParent['uid'],
-            'inlineTopMostParentTableName' => $parentData['inlineTopMostParentTableName'] ?: $inlineTopMostParent['table'],
-            'inlineTopMostParentFieldName' => $parentData['inlineTopMostParentFieldName'] ?: $inlineTopMostParent['field'],
+            'inlineTopMostParentUid' => $inlineTopMostParent['uid'],
+            'inlineTopMostParentTableName' => $inlineTopMostParent['table'],
+            'inlineTopMostParentFieldName' => $inlineTopMostParent['field'],
         ];
         if ($childChildUid) {
             $formDataCompilerInput['inlineChildChildUid'] = $childChildUid;
@@ -253,48 +206,24 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
         $parent = $inlineStackProcessor->getStructureLevel(-1);
         $parentFieldName = $parent['field'];
 
-        $databaseRow = [
-            // TcaInlineExpandCollapseState needs this
-            'uid' => (int)$parent['uid'],
-        ];
-
-        $processedTca = $GLOBALS['TCA'][$parent['table']];
-        $processedTca['columns'][$parentFieldName]['config'] = $parentConfig;
-        if (!empty($parentConfig['dataStructureIdentifier'])) {
-            $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
-            $processedTca['columns'][$parentFieldName]['config']['ds'] = $flexFormTools->parseDataStructureByIdentifier($parentConfig['dataStructureIdentifier']);
-        }
-
-        $formDataCompilerInputForParent = [
-            'vanillaUid' => (int)$parent['uid'],
-            'command' => 'edit',
+        // Set flag in config so that only the fields are rendered
+        // @todo: Solve differently / rename / whatever
+        $parentConfig['renderFieldsOnly'] = true;
+
+        $parentData = [
+            'processedTca' => [
+                'columns' => [
+                    $parentFieldName => [
+                        'config' => $parentConfig,
+                    ],
+                ],
+            ],
+            'databaseRow' => [
+                'uid' => (int)$parent['uid'],
+            ],
             'tableName' => $parent['table'],
-            'databaseRow' => $databaseRow,
-            'processedTca' => $processedTca,
             'inlineFirstPid' => $inlineFirstPid,
-            'columnsToProcess' => [
-                $parentFieldName
-            ],
-            // @todo: still needed?
-            'inlineStructure' => $inlineStackProcessor->getStructure(),
-            // Do not resolve existing children, we don't need them now
-            'inlineResolveExistingChildren' => false,
         ];
-        /** @var TcaDatabaseRecord $formDataGroup */
-        $formDataGroup = GeneralUtility::makeInstance(InlineParentRecord::class);
-        /** @var FormDataCompiler $formDataCompiler */
-        $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
-        $parentData = $formDataCompiler->compile($formDataCompilerInputForParent);
-        $parentConfig = $parentData['processedTca']['columns'][$parentFieldName]['config'];
-
-        if ($parentConfig['type'] === 'flex') {
-            $parentConfig = $this->getParentConfigFromFlexForm($parentConfig, $domObjectId);
-            $parentData['processedTca']['columns'][$parentFieldName]['config'] = $parentConfig;
-        }
-
-        // Set flag in config so that only the fields are rendered
-        // @todo: Solve differently / rename / whatever
-        $parentData['processedTca']['columns'][$parentFieldName]['config']['renderFieldsOnly'] = true;
 
         // Child, a record from this table should be rendered
         $child = $inlineStackProcessor->getUnstableStructure();
@@ -488,7 +417,7 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
     }
 
     /**
-     * Adds localizations or synchronizes the locations of all child records.
+     * Store status of inline children expand / collapse state in backend user uC.
      *
      * @param ServerRequestInterface $request the incoming request
      * @param ResponseInterface $response the empty response
@@ -585,9 +514,9 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
             'inlineParentFieldName' => $parentFieldName,
 
              // values of the top most parent element set on first level and not overridden on following levels
-            'inlineTopMostParentUid' => $parentData['inlineTopMostParentUid'] ?: $inlineTopMostParent['uid'],
-            'inlineTopMostParentTableName' => $parentData['inlineTopMostParentTableName'] ?: $inlineTopMostParent['table'],
-            'inlineTopMostParentFieldName' => $parentData['inlineTopMostParentFieldName'] ?: $inlineTopMostParent['field'],
+            'inlineTopMostParentUid' => $inlineTopMostParent['uid'],
+            'inlineTopMostParentTableName' => $inlineTopMostParent['table'],
+            'inlineTopMostParentFieldName' => $inlineTopMostParent['field'],
         ];
         // For foreign_selector with useCombination $mainChild is the mm record
         // and $combinationChild is the child-child. For "normal" relations, $mainChild
@@ -744,29 +673,6 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
     }
 
     /**
-     * Checks if a record selector may select a certain file type
-     *
-     * @param array $selectorConfiguration
-     * @param array $fileRecord
-     * @return bool
-     * @todo: check this ...
-     */
-    protected function checkInlineFileTypeAccessForField(array $selectorConfiguration, array $fileRecord)
-    {
-        if (!empty($selectorConfiguration['PA']['fieldConf']['config']['appearance']['elementBrowserAllowed'])) {
-            $allowedFileExtensions = GeneralUtility::trimExplode(
-                ',',
-                $selectorConfiguration['PA']['fieldConf']['config']['appearance']['elementBrowserAllowed'],
-                true
-            );
-            if (!in_array(strtolower($fileRecord['extension']), $allowedFileExtensions, true)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
      * Return expand / collapse state array for a given table / uid combination
      *
      * @param string $table Handled table
@@ -869,121 +775,33 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
     }
 
     /**
-     * @return BackendUserAuthentication
-     */
-    protected function getBackendUserAuthentication()
-    {
-        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, string $domObjectId) : array
-    {
-        list($flexFormPath, $foreignTableName) = $this->splitDomObjectId($domObjectId);
-
-        $childConfig = $parentConfig['ds']['sheets'];
-        $flexFormPath = explode(':', $flexFormPath);
-        foreach ($flexFormPath as $flexFormNode) {
-            // We are dealing with configuration information from a flexform,
-            // not value storage, identifiers that 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'
-            || $childConfig['config']['foreign_table'] !== $foreignTableName
-        ) {
-            throw new \UnexpectedValueException(
-                'Configuration retrieved from FlexForm is incomplete or not of type "inline".',
-                1446996319
-            );
-        }
-        return $childConfig['config'];
-    }
-
-    /**
      * Validates the config that is transferred over the wire to provide the
      * correct TCA config for the parent table
      *
      * @param string $contextString
+     * @throws \RuntimeException
      * @return array
-     * @todo: Review this construct - Why can't the ajax call fetch these data on its own and transfers it to client instead?
      */
     protected function extractSignedParentConfigFromRequest(string $contextString): array
     {
         if ($contextString === '') {
-            return [];
+            throw new \RuntimeException('Empty context string given', 1489751361);
         }
         $context = json_decode($contextString, true);
         if (empty($context['config'])) {
-            return [];
+            throw new \RuntimeException('Empty context config section given', 1489751362);
         }
         if (!\hash_equals(GeneralUtility::hmac(json_encode($context['config']), 'InlineContext'), $context['hmac'])) {
-            return [];
+            throw new \RuntimeException('Hash does not validate', 1489751363);
         }
         return $context['config'];
     }
 
     /**
-     * split the domObjectID and retrieve the needed parts
-     *
-     * @param string $domObjectId
-     *
-     * @return array
+     * @return BackendUserAuthentication
      */
-    protected function splitDomObjectId(string $domObjectId) : array
+    protected function getBackendUserAuthentication()
     {
-
-        // Substitute FlexForm addition and make parsing a bit easier
-        $domObjectId = str_replace('---', ':', $domObjectId);
-        $pattern = '/:data:(?<flexformPath>.*?)-(?<tableName>[^-]+)(?:-(?:NEW)?\w+)?$/';
-
-        /* EXPLANATION for the regex:
-         * according https://regex101.com/
-         *
-         * :data: matches the characters :data: literally (case sensitive)
-         * (?<flexformPath>.*?) Named capturing group flexformPath
-         * .*? matches any character (except newline)
-         * Quantifier: *? Between zero and unlimited times, as few times as possible, expanding as needed [lazy]
-         * - matches the character - literally
-         * (?<tableName>[^-]+) Named capturing group tableName
-         * [^-]+ match a single character not present in the list below
-         * Quantifier: + Between one and unlimited times, as many times as possible, giving back as needed [greedy]
-         * - the literal character -
-         * (?:-(?:NEW)?\w+)? Non-capturing group
-         * Quantifier: ? Between zero and one time, as many times as possible, giving back as needed [greedy]
-         * - matches the character - literally
-         * (?:NEW)? Non-capturing group
-         * Quantifier: ? Between zero and one time, as many times as possible, giving back as needed [greedy]
-         * NEW matches the characters NEW literally (case sensitive)
-         * \w+ match any word character [a-zA-Z0-9_]
-         * Quantifier: + Between one and unlimited times, as many times as possible, giving back as needed [greedy]
-         * $ assert position at end of a line
-         */
-
-        if (preg_match($pattern, $domObjectId, $match)) {
-            return [$match['flexformPath'], $match['tableName']];
-        }
-
-        return [];
+        return $GLOBALS['BE_USER'];
     }
 }
diff --git a/typo3/sysext/backend/Classes/Form/FormDataGroup/InlineParentRecord.php b/typo3/sysext/backend/Classes/Form/FormDataGroup/InlineParentRecord.php
deleted file mode 100644 (file)
index a175f44..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-namespace TYPO3\CMS\Backend\Form\FormDataGroup;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-use TYPO3\CMS\Backend\Form\FormDataGroupInterface;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-
-/**
- * A data provider group for inline relations. This one calculates a slim "parent"
- * record with only the inline config.
- */
-class InlineParentRecord implements FormDataGroupInterface
-{
-    /**
-     * Compile form data
-     *
-     * @param array $result Initialized result array
-     * @return array Result filled with data
-     * @throws \UnexpectedValueException
-     */
-    public function compile(array $result)
-    {
-        $orderedProviderList = GeneralUtility::makeInstance(OrderedProviderList::class);
-        $orderedProviderList->setProviderList(
-            $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['formDataGroup']['inlineParentRecord']
-        );
-
-        return $orderedProviderList->compile($result);
-    }
-}
index 47252d5..862b76c 100644 (file)
@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Backend\Tests\Unit\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Controller\FormInlineAjaxController;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 
@@ -23,147 +25,195 @@ use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 class FormInlineAjaxControllerTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
 {
     /**
-     * Checks if the given file type may be uploaded without *ANY* limit to file types being given
-     *
      * @test
      */
-    public function checkInlineFileTypeAccessForFieldForFieldNoFiletypesReturnsTrue()
+    public function createActionThrowsExceptionIfContextIsEmpty()
     {
-        $selectorData = [];
-        $fileData['extension'] = 'png';
-        $mockObject = $this->getAccessibleMock(FormInlineAjaxController::class, ['dummy'], [], '', false);
-        $mayUploadFile = $mockObject->_call('checkInlineFileTypeAccessForField', $selectorData, $fileData);
-        $this->assertTrue($mayUploadFile);
+        $responseProphecy = $this->prophesize(ResponseInterface::class);
+        $requestProphecy = $this->prophesize(ServerRequestInterface::class);
+        $requestProphecy->getParsedBody()->shouldBeCalled()->willReturn(
+            [
+                'ajax' => [
+                    'context' => '',
+                ],
+            ]
+        );
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionCode(1489751361);
+        (new FormInlineAjaxController())->createAction($requestProphecy->reveal(), $responseProphecy->reveal());
     }
 
     /**
-     * Checks if the given file type may be uploaded and the given file type is *NOT* in the list of allowed files
-     *
      * @test
      */
-    public function checkInlineFileTypeAccessForFieldFiletypesSetRecordTypeNotInListReturnsFalse()
+    public function createActionThrowsExceptionIfContextConfigSectionIsEmpty()
     {
-        $selectorData['PA']['fieldConf']['config']['appearance']['elementBrowserAllowed'] = 'doc, png, jpg, tiff';
-        $fileData['extension'] = 'php';
-        $mockObject = $this->getAccessibleMock(FormInlineAjaxController::class, ['dummy'], [], '', false);
-        $mayUploadFile = $mockObject->_call('checkInlineFileTypeAccessForField', $selectorData, $fileData);
-        $this->assertFalse($mayUploadFile);
+        $responseProphecy = $this->prophesize(ResponseInterface::class);
+        $requestProphecy = $this->prophesize(ServerRequestInterface::class);
+        $requestProphecy->getParsedBody()->shouldBeCalled()->willReturn(
+            [
+                'ajax' => [
+                    'context' => json_encode([ 'config' => '' ]),
+                ],
+            ]
+        );
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionCode(1489751362);
+        (new FormInlineAjaxController())->createAction($requestProphecy->reveal(), $responseProphecy->reveal());
     }
 
     /**
-     * Checks if the given file type may be uploaded and the given file type *is* in the list of allowed files
      * @test
      */
-    public function checkInlineFileTypeAccessForFieldFiletypesSetRecordTypeInListReturnsTrue()
+    public function createActionThrowsExceptionIfContextConfigSectionDoesNotValidate()
     {
-        $selectorData['PA']['fieldConf']['config']['appearance']['elementBrowserAllowed'] = 'doc, png, jpg, tiff';
-        $fileData['extension'] = 'png';
-        $mockObject = $this->getAccessibleMock(FormInlineAjaxController::class, ['dummy'], [], '', false);
-        $mayUploadFile = $mockObject->_call('checkInlineFileTypeAccessForField', $selectorData, $fileData);
-        $this->assertTrue($mayUploadFile);
+        $responseProphecy = $this->prophesize(ResponseInterface::class);
+        $requestProphecy = $this->prophesize(ServerRequestInterface::class);
+        $requestProphecy->getParsedBody()->shouldBeCalled()->willReturn(
+            [
+                'ajax' => [
+                    'context' => json_encode(
+                        [
+                            'config' => [
+                                'type' => 'inline',
+                            ],
+                            'hmac' => 'anInvalidHash',
+                        ]
+                    ),
+                ],
+            ]
+        );
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionCode(1489751363);
+        (new FormInlineAjaxController())->createAction($requestProphecy->reveal(), $responseProphecy->reveal());
     }
 
     /**
-     * @dataProvider splitDomObjectIdDataProviderForTableName
-     * @param string $dataStructure
-     * @param string $expectedTableName
      * @test
-     *
-     * test for the flexform domobject identifier split
      */
-    public function splitDomObjectIdResolvesTablenameCorrectly($dataStructure, $expectedTableName)
+    public function detailsActionThrowsExceptionIfContextIsEmpty()
     {
-        $mock = $this->getAccessibleMock(FormInlineAjaxController::class, ['dummy'], [], '', false);
-        $result = $mock->_call('splitDomObjectId', $dataStructure);
-        $this->assertSame($expectedTableName, $result[1]);
+        $responseProphecy = $this->prophesize(ResponseInterface::class);
+        $requestProphecy = $this->prophesize(ServerRequestInterface::class);
+        $requestProphecy->getParsedBody()->shouldBeCalled()->willReturn(
+            [
+                'ajax' => [
+                    'context' => '',
+                ],
+            ]
+        );
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionCode(1489751361);
+        (new FormInlineAjaxController())->detailsAction($requestProphecy->reveal(), $responseProphecy->reveal());
     }
 
     /**
-     * @return array
+     * @test
      */
-    public function splitDomObjectIdDataProviderForTableName()
+    public function detailsActionThrowsExceptionIfContextConfigSectionIsEmpty()
     {
-        return [
-            'news new' => [
-                'data-335-tx_news_domain_model_news-2-content_elements-tt_content-999-pi_flexform---data---sheet.tabGeneral---lDEF---settings.related_files---vDEF-tx_news_domain_model_file',
-                'tx_news_domain_model_file'
-            ],
-            'load existing child' => [
-                'data-318-tx_styleguide_flex-4-flex_3---data---sInline---lDEF---inline_1---vDEF-tx_styleguide_flex_flex_3_inline_1_child-4',
-                'tx_styleguide_flex_flex_3_inline_1_child'
-            ],
-            'create new child' => [
-                'data-318-tx_styleguide_flex-4-flex_3---data---sInline---lDEF---inline_1---vDEF-tx_styleguide_flex_flex_3_inline_1_child',
-                'tx_styleguide_flex_flex_3_inline_1_child'
-            ],
-            'insert new after' => [
-                'data-336-tt_content-1000-pi_flexform---data---sheet.tabGeneral---lDEF---settings.related_files---vDEF-tx_news_domain_model_file-6',
-                'tx_news_domain_model_file'
-            ],
-            'fal simple' => [
-                'data-336-tt_content-998-pi_flexform---data---sheet.tabGeneral---lDEF---settings.image---vDEF-sys_file_reference-837',
-                'sys_file_reference'
-            ],
-            'fal down deep' => [
-                'data-335-tx_news_domain_model_news-2-content_elements-tt_content-999-pi_flexform---data---sheet.tabGeneral---lDEF---settings.image---vDEF-sys_file_reference',
-                'sys_file_reference'
-            ],
-            'new record after others' => ['data-336-tt_content-1000-pi_flexform---data---sheet.tabGeneral---lDEF---settings.related_files---vDEF-tx_news_domain_model_file-NEW5757f36287214984252204', 'tx_news_domain_model_file'],
-        ];
+        $responseProphecy = $this->prophesize(ResponseInterface::class);
+        $requestProphecy = $this->prophesize(ServerRequestInterface::class);
+        $requestProphecy->getParsedBody()->shouldBeCalled()->willReturn(
+            [
+                'ajax' => [
+                    'context' => json_encode([ 'config' => '' ]),
+                ],
+            ]
+        );
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionCode(1489751362);
+        (new FormInlineAjaxController())->detailsAction($requestProphecy->reveal(), $responseProphecy->reveal());
     }
 
     /**
-     * @dataProvider splitDomObjectIdDataProviderForFlexFormPath
-     *
-     * @param string $dataStructure
-     * @param string $expectedFlexformPath
-     *
      * @test
-     *
-     * test for the flexform domobject identifier split
      */
-    public function splitDomObjectIdResolvesFlexformPathCorrectly($dataStructure, $expectedFlexformPath)
+    public function detailsActionThrowsExceptionIfContextConfigSectionDoesNotValidate()
     {
-        $mock = $this->getAccessibleMock(FormInlineAjaxController::class, ['dummy'], [], '', false);
-        $result = $mock->_call('splitDomObjectId', $dataStructure);
-        $this->assertSame($expectedFlexformPath, $result[0]);
+        $responseProphecy = $this->prophesize(ResponseInterface::class);
+        $requestProphecy = $this->prophesize(ServerRequestInterface::class);
+        $requestProphecy->getParsedBody()->shouldBeCalled()->willReturn(
+            [
+                'ajax' => [
+                    'context' => json_encode(
+                        [
+                            'config' => [
+                                'type' => 'inline',
+                            ],
+                            'hmac' => 'anInvalidHash',
+                        ]
+                    ),
+                ],
+            ]
+        );
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionCode(1489751363);
+        (new FormInlineAjaxController())->detailsAction($requestProphecy->reveal(), $responseProphecy->reveal());
     }
 
     /**
-     * @return array
+     * @test
      */
-    public function splitDomObjectIdDataProviderForFlexFormPath()
+    public function synchronizeLocalizeActionThrowsExceptionIfContextIsEmpty()
     {
-        return [
-            'news new' => [
-                'data-335-tx_news_domain_model_news-2-content_elements-tt_content-999-pi_flexform---data---sheet.tabGeneral---lDEF---settings.related_files---vDEF-tx_news_domain_model_file',
-                'sheet.tabGeneral:lDEF:settings.related_files:vDEF'
-            ],
-            'load existing child' => [
-                'data-318-tx_styleguide_flex-4-flex_3---data---sInline---lDEF---inline_1---vDEF-tx_styleguide_flex_flex_3_inline_1_child-4',
-                'sInline:lDEF:inline_1:vDEF'
-            ],
-            'create new child' => [
-                'data-318-tx_styleguide_flex-4-flex_3---data---sInline---lDEF---inline_1---vDEF-tx_styleguide_flex_flex_3_inline_1_child',
-                'sInline:lDEF:inline_1:vDEF'
-            ],
-            'insert new after' => [
-                'data-336-tt_content-1000-pi_flexform---data---sheet.tabGeneral---lDEF---settings.related_files---vDEF-tx_news_domain_model_file-6',
-                'sheet.tabGeneral:lDEF:settings.related_files:vDEF'
-            ],
-            'fal simple' => [
-                'data-336-tt_content-998-pi_flexform---data---sheet.tabGeneral---lDEF---settings.image---vDEF-sys_file_reference-837',
-                'sheet.tabGeneral:lDEF:settings.image:vDEF'
-            ],
-            'fal down deep' => [
-                'data-335-tx_news_domain_model_news-2-content_elements-tt_content-999-pi_flexform---data---sheet.tabGeneral---lDEF---settings.image---vDEF-sys_file_reference',
-                'sheet.tabGeneral:lDEF:settings.image:vDEF'
-            ],
-            'new record after others' => [
-                'data-336-tt_content-1000-pi_flexform---data---sheet.tabGeneral---lDEF---settings.related_files---vDEF-tx_news_domain_model_file-NEW5757f36287214984252204',
-                'sheet.tabGeneral:lDEF:settings.related_files:vDEF'
-            ],
-        ];
+        $responseProphecy = $this->prophesize(ResponseInterface::class);
+        $requestProphecy = $this->prophesize(ServerRequestInterface::class);
+        $requestProphecy->getParsedBody()->shouldBeCalled()->willReturn(
+            [
+                'ajax' => [
+                    'context' => '',
+                ],
+            ]
+        );
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionCode(1489751361);
+        (new FormInlineAjaxController())->synchronizeLocalizeAction($requestProphecy->reveal(), $responseProphecy->reveal());
+    }
+
+    /**
+     * @test
+     */
+    public function synchronizeLocalizeActionThrowsExceptionIfContextConfigSectionIsEmpty()
+    {
+        $responseProphecy = $this->prophesize(ResponseInterface::class);
+        $requestProphecy = $this->prophesize(ServerRequestInterface::class);
+        $requestProphecy->getParsedBody()->shouldBeCalled()->willReturn(
+            [
+                'ajax' => [
+                    'context' => json_encode([ 'config' => '' ]),
+                ],
+            ]
+        );
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionCode(1489751362);
+        (new FormInlineAjaxController())->synchronizeLocalizeAction($requestProphecy->reveal(), $responseProphecy->reveal());
+    }
+
+    /**
+     * @test
+     */
+    public function synchronizeLocalizeActionThrowsExceptionIfContextConfigSectionDoesNotValidate()
+    {
+        $responseProphecy = $this->prophesize(ResponseInterface::class);
+        $requestProphecy = $this->prophesize(ServerRequestInterface::class);
+        $requestProphecy->getParsedBody()->shouldBeCalled()->willReturn(
+            [
+                'ajax' => [
+                    'context' => json_encode(
+                        [
+                            'config' => [
+                                'type' => 'inline',
+                            ],
+                            'hmac' => 'anInvalidHash',
+                        ]
+                    ),
+                ],
+            ]
+        );
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionCode(1489751363);
+        (new FormInlineAjaxController())->synchronizeLocalizeAction($requestProphecy->reveal(), $responseProphecy->reveal());
     }
 
     /**
diff --git a/typo3/sysext/backend/Tests/Unit/Form/FormDataGroup/InlineParentRecordTest.php b/typo3/sysext/backend/Tests/Unit/Form/FormDataGroup/InlineParentRecordTest.php
deleted file mode 100644 (file)
index 010db48..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-<?php
-namespace TYPO3\CMS\Backend\Tests\Unit\Form\FormDataGroup;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-use Prophecy\Argument;
-use Prophecy\Prophecy\ObjectProphecy;
-use TYPO3\CMS\Backend\Form\FormDataGroup\InlineParentRecord;
-use TYPO3\CMS\Backend\Form\FormDataProviderInterface;
-use TYPO3\CMS\Core\Service\DependencyOrderingService;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-
-/**
- * Test case
- */
-class InlineParentRecordTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
-{
-    /**
-     * @var InlineParentRecord
-     */
-    protected $subject;
-
-    protected function setUp()
-    {
-        $this->subject = new InlineParentRecord();
-    }
-
-    /**
-     * @test
-     */
-    public function compileReturnsIncomingData()
-    {
-        /** @var DependencyOrderingService|ObjectProphecy $orderingServiceProphecy */
-        $orderingServiceProphecy = $this->prophesize(DependencyOrderingService::class);
-        GeneralUtility::addInstance(DependencyOrderingService::class, $orderingServiceProphecy->reveal());
-        $orderingServiceProphecy->orderByDependencies(Argument::cetera())->willReturnArgument(0);
-
-        $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['formDataGroup']['inlineParentRecord'] = [];
-
-        $input = ['foo'];
-
-        $this->assertEquals($input, $this->subject->compile($input));
-    }
-
-    /**
-     * @test
-     */
-    public function compileReturnsResultChangedByDataProvider()
-    {
-        /** @var DependencyOrderingService|ObjectProphecy $orderingServiceProphecy */
-        $orderingServiceProphecy = $this->prophesize(DependencyOrderingService::class);
-        GeneralUtility::addInstance(DependencyOrderingService::class, $orderingServiceProphecy->reveal());
-        $orderingServiceProphecy->orderByDependencies(Argument::cetera())->willReturnArgument(0);
-
-        /** @var FormDataProviderInterface|ObjectProphecy $formDataProviderProphecy */
-        $formDataProviderProphecy = $this->prophesize(FormDataProviderInterface::class);
-        $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['formDataGroup']['inlineParentRecord'] = [
-            FormDataProviderInterface::class => [],
-        ];
-        GeneralUtility::addInstance(FormDataProviderInterface::class, $formDataProviderProphecy->reveal());
-        $providerResult = ['foo'];
-        $formDataProviderProphecy->addData(Argument::cetera())->shouldBeCalled()->willReturn($providerResult);
-
-        $this->assertEquals($providerResult, $this->subject->compile([]));
-    }
-
-    /**
-     * @test
-     */
-    public function compileThrowsExceptionIfDataProviderDoesNotImplementInterface()
-    {
-        /** @var DependencyOrderingService|ObjectProphecy $orderingServiceProphecy */
-        $orderingServiceProphecy = $this->prophesize(DependencyOrderingService::class);
-        GeneralUtility::addInstance(DependencyOrderingService::class, $orderingServiceProphecy->reveal());
-        $orderingServiceProphecy->orderByDependencies(Argument::cetera())->willReturnArgument(0);
-
-        /** @var FormDataProviderInterface|ObjectProphecy $formDataProviderProphecy */
-        $formDataProviderProphecy = $this->prophesize(\stdClass::class);
-        $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['formDataGroup']['inlineParentRecord'] = [
-            \stdClass::class => [],
-        ];
-        GeneralUtility::addInstance(\stdClass::class, $formDataProviderProphecy->reveal());
-
-        $this->expectException(\UnexpectedValueException::class);
-        $this->expectExceptionCode(1485299408);
-
-        $this->subject->compile([]);
-    }
-}
index a867f0c..9f553dc 100644 (file)
@@ -810,41 +810,6 @@ return [
                         ],
                     ],
                 ],
-                'inlineParentRecord' => [
-                    \TYPO3\CMS\Backend\Form\FormDataProvider\InitializeProcessedTca::class => [],
-                    \TYPO3\CMS\Backend\Form\FormDataProvider\InlineOverrideChildTca::class => [
-                        'depends' => [
-                            \TYPO3\CMS\Backend\Form\FormDataProvider\InitializeProcessedTca::class,
-                        ]
-                    ],
-                    \TYPO3\CMS\Backend\Form\FormDataProvider\TcaColumnsRemoveUnused::class => [
-                        'depends' => [
-                            \TYPO3\CMS\Backend\Form\FormDataProvider\InlineOverrideChildTca::class,
-                        ],
-                    ],
-                    \TYPO3\CMS\Backend\Form\FormDataProvider\TcaFlexPrepare::class => [
-                        'depends' => [
-                            \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\TcaFlexProcess::class => [
-                        'depends' => [
-                            \TYPO3\CMS\Backend\Form\FormDataProvider\TcaFlexPrepare::class,
-                        ],
-                    ],
-                    \TYPO3\CMS\Backend\Form\FormDataProvider\TcaInlineExpandCollapseState::class => [
-                        'depends' => [
-                            \TYPO3\CMS\Backend\Form\FormDataProvider\TcaFlexProcess::class,
-                        ],
-                    ],
-                    \TYPO3\CMS\Backend\Form\FormDataProvider\TcaInlineConfiguration::class => [
-                        'depends' => [
-                            \TYPO3\CMS\Backend\Form\FormDataProvider\TcaInlineExpandCollapseState::class,
-                        ],
-                    ],
-                ],
             ],
         ],
     ],