From 7ee93637146d119eabd7797e9acfbc85e910c3cf Mon Sep 17 00:00:00 2001
From: Alexander Schnitzler <git@alexanderschnitzler.de>
Date: Thu, 29 Dec 2016 15:24:15 +0100
Subject: [PATCH] [BUGFIX] Properly handle flexform related exceptions
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The FlexFormTools class tries to resolve the datastructure
of flex fields by the given TCA configuration. The flexform
definition can either be set directly in the TCA or it is
fetched from another record.

Example:
TemplaVoilà fetches the data structure from the table
tx_templavoila_datastructure.

When trying to resolve the data structure from another
table and the identifier is invalid (e.g. empty or does
not point to a valid record), several exceptions are
thrown that need to be caught at several points to keep
the user interface accessible.

Other than these mentioned exceptions there are ones
that indicate that the TCA configuration is simply wrong.
These ones are not caught and will still bubble up.

Releases: master, 8.7
Fixes: #79101
Change-Id: I9be921e1425076897a86ebb0b997a998fda7f373
Reviewed-on: https://review.typo3.org/51061
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
---
 .../Form/FormDataProvider/TcaFlexPrepare.php  | 35 +++++++++++++-----
 .../Configuration/FlexForm/FlexFormTools.php  | 23 ++++++++++--
 .../core/Classes/DataHandling/DataHandler.php | 36 ++++++++++++++-----
 3 files changed, 74 insertions(+), 20 deletions(-)

diff --git a/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaFlexPrepare.php b/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaFlexPrepare.php
index c3eb139b5a16..ebce04a7aeb6 100644
--- a/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaFlexPrepare.php
+++ b/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaFlexPrepare.php
@@ -15,6 +15,11 @@ namespace TYPO3\CMS\Backend\Form\FormDataProvider;
  */
 
 use TYPO3\CMS\Backend\Form\FormDataProviderInterface;
+use TYPO3\CMS\Core\Configuration\FlexForm\Exception\InvalidIdentifierException;
+use TYPO3\CMS\Core\Configuration\FlexForm\Exception\InvalidParentRowException;
+use TYPO3\CMS\Core\Configuration\FlexForm\Exception\InvalidParentRowLoopException;
+use TYPO3\CMS\Core\Configuration\FlexForm\Exception\InvalidParentRowRootException;
+use TYPO3\CMS\Core\Configuration\FlexForm\Exception\InvalidPointerFieldValueException;
 use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
 use TYPO3\CMS\Core\Migrations\TcaMigration;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -65,15 +70,27 @@ class TcaFlexPrepare implements FormDataProviderInterface
     {
         if (!isset($result['processedTca']['columns'][$fieldName]['config']['dataStructureIdentifier'])) {
             $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
-            $dataStructureIdentifier = $flexFormTools->getDataStructureIdentifier(
-                $result['processedTca']['columns'][$fieldName],
-                $result['tableName'],
-                $fieldName,
-                $result['databaseRow']
-            );
-            // Add the identifier to TCA to use it later during rendering
-            $result['processedTca']['columns'][$fieldName]['config']['dataStructureIdentifier'] = $dataStructureIdentifier;
-            $dataStructureArray = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
+
+            $dataStructureIdentifier = '';
+            $dataStructureArray = [ 'sheets' => [ 'sDEF' => [] ] ];
+
+            try {
+                $dataStructureIdentifier = $flexFormTools->getDataStructureIdentifier(
+                    $result['processedTca']['columns'][$fieldName],
+                    $result['tableName'],
+                    $fieldName,
+                    $result['databaseRow']
+                );
+                $dataStructureArray = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
+            } catch (InvalidParentRowException $e) {
+            } catch (InvalidParentRowLoopException $e) {
+            } catch (InvalidParentRowRootException $e) {
+            } catch (InvalidPointerFieldValueException $e) {
+            } catch (InvalidIdentifierException $e) {
+            } finally {
+                // Add the identifier to TCA to use it later during rendering
+                $result['processedTca']['columns'][$fieldName]['config']['dataStructureIdentifier'] = $dataStructureIdentifier;
+            }
         } else {
             // Assume the data structure has been given from outside if the data structure identifier is already set.
             $dataStructureArray = $result['processedTca']['columns'][$fieldName]['config']['ds'];
diff --git a/typo3/sysext/core/Classes/Configuration/FlexForm/FlexFormTools.php b/typo3/sysext/core/Classes/Configuration/FlexForm/FlexFormTools.php
index 4cc26c9ac42d..ef6a71dd47d6 100644
--- a/typo3/sysext/core/Classes/Configuration/FlexForm/FlexFormTools.php
+++ b/typo3/sysext/core/Classes/Configuration/FlexForm/FlexFormTools.php
@@ -107,6 +107,11 @@ class FlexFormTools
      * @param array $row The data row
      * @return string Identifier string
      * @throws \RuntimeException If TCA is misconfigured
+     * @throws InvalidParentRowException in getDataStructureIdentifierFromRecord
+     * @throws InvalidParentRowLoopException in getDataStructureIdentifierFromRecord
+     * @throws InvalidParentRowRootException in getDataStructureIdentifierFromRecord
+     * @throws InvalidPointerFieldValueException in getDataStructureIdentifierFromRecord
+     * @throws InvalidTcaException in getDataStructureIdentifierFromRecord
      */
     public function getDataStructureIdentifier(array $fieldTca, string $tableName, string $fieldName, array $row): string
     {
@@ -761,9 +766,21 @@ class FlexFormTools
             return 'TCA table/field was not defined.';
         }
         $this->callBackObj = $callBackObj;
-        // Get Data Structure:
-        $dataStructureIdentifier = $this->getDataStructureIdentifier($GLOBALS['TCA'][$table]['columns'][$field], $table, $field, $row);
-        $dataStructureArray = $this->parseDataStructureByIdentifier($dataStructureIdentifier);
+
+        // Get data structure. The methods may throw various exceptions, with some of them being
+        // ok in certain scenarios, for instance on new record rows. Those are ok to "eat" here
+        // and substitute with a dummy DS.
+        $dataStructureArray = [ 'sheets' => [ 'sDEF' => [] ] ];
+        try {
+            $dataStructureIdentifier = $this->getDataStructureIdentifier($GLOBALS['TCA'][$table]['columns'][$field], $table, $field, $row);
+            $dataStructureArray = $this->parseDataStructureByIdentifier($dataStructureIdentifier);
+        } catch (InvalidParentRowException $e) {
+        } catch (InvalidParentRowLoopException $e) {
+        } catch (InvalidParentRowRootException $e) {
+        } catch (InvalidPointerFieldValueException $e) {
+        } catch (InvalidIdentifierException $e) {
+        }
+
         // Get flexform XML data
         $editData = GeneralUtility::xml2array($row[$field]);
         if (!is_array($editData)) {
diff --git a/typo3/sysext/core/Classes/DataHandling/DataHandler.php b/typo3/sysext/core/Classes/DataHandling/DataHandler.php
index d636a9228f0e..a29eb1c87327 100644
--- a/typo3/sysext/core/Classes/DataHandling/DataHandler.php
+++ b/typo3/sysext/core/Classes/DataHandling/DataHandler.php
@@ -25,6 +25,11 @@ use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Cache\CacheManager;
 use TYPO3\CMS\Core\Cache\Frontend\VariableFrontend;
+use TYPO3\CMS\Core\Configuration\FlexForm\Exception\InvalidIdentifierException;
+use TYPO3\CMS\Core\Configuration\FlexForm\Exception\InvalidParentRowException;
+use TYPO3\CMS\Core\Configuration\FlexForm\Exception\InvalidParentRowLoopException;
+use TYPO3\CMS\Core\Configuration\FlexForm\Exception\InvalidParentRowRootException;
+use TYPO3\CMS\Core\Configuration\FlexForm\Exception\InvalidPointerFieldValueException;
 use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
 use TYPO3\CMS\Core\Configuration\Richtext;
 use TYPO3\CMS\Core\Database\Connection;
@@ -2440,15 +2445,30 @@ class DataHandler implements LoggerAwareInterface
             if ($status === 'new') {
                 $row['pid'] = $realPid;
             }
-            // Get current value array:
+
             $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
-            $dataStructureIdentifier = $flexFormTools->getDataStructureIdentifier(
-                [ 'config' => $tcaFieldConf ],
-                $table,
-                $field,
-                $row
-            );
-            $dataStructureArray = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
+
+            // Get data structure. The methods may throw various exceptions, with some of them being
+            // ok in certain scenarios, for instance on new record rows. Those are ok to "eat" here
+            // and substitute with a dummy DS.
+            $dataStructureArray = [ 'sheets' => [ 'sDEF' => [] ] ];
+            try {
+                $dataStructureIdentifier = $flexFormTools->getDataStructureIdentifier(
+                    [ 'config' => $tcaFieldConf ],
+                    $table,
+                    $field,
+                    $row
+                );
+
+                $dataStructureArray = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
+            } catch (InvalidParentRowException $e) {
+            } catch (InvalidParentRowLoopException $e) {
+            } catch (InvalidParentRowRootException $e) {
+            } catch (InvalidPointerFieldValueException $e) {
+            } catch (InvalidIdentifierException $e) {
+            }
+
+            // Get current value array:
             $currentValueArray = (string)$curValue !== '' ? GeneralUtility::xml2array($curValue) : [];
             if (!is_array($currentValueArray)) {
                 $currentValueArray = [];
-- 
GitLab