From bb84452d02675cfa15afd3a0e13aca7ac7164607 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=84=B3ichiel=20=E2=84=9Boos?= <michiel@michielroos.com>
Date: Wed, 8 Nov 2017 21:09:43 +0100
Subject: [PATCH] [BUGFIX] Set correct PID for file references on new pages

Allow inline parent UID to have a "NEW..." placeholder value.

Resolves: #82931
Releases: master, 9.5, 8.7
Change-Id: I1714160d14d226a02663568947ed9525ad07d347
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/54598
Tested-by: Markus Klein <markus.klein@typo3.org>
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Steffen Frese <steffenf14@gmail.com>
Tested-by: Sascha Rademacher <sascha.rademacher+typo3@gmail.com>
Tested-by: Benni Mack <benni@typo3.org>
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Reviewed-by: Steffen Frese <steffenf14@gmail.com>
Reviewed-by: Sascha Rademacher <sascha.rademacher+typo3@gmail.com>
Reviewed-by: Benni Mack <benni@typo3.org>
---
 .../Controller/FormInlineAjaxController.php   | 14 +++++++++----
 .../backend/Classes/Form/FormDataCompiler.php | 12 +++++++----
 .../Form/FormDataProvider/SiteTcaInline.php   | 20 +++++++++++++------
 .../Form/FormDataProvider/TcaInline.php       | 20 +++++++++++++------
 .../Classes/Form/InlineStackProcessor.php     |  2 +-
 5 files changed, 47 insertions(+), 21 deletions(-)

diff --git a/typo3/sysext/backend/Classes/Controller/FormInlineAjaxController.php b/typo3/sysext/backend/Classes/Controller/FormInlineAjaxController.php
index 54959c00bf79..770b01d3c1f8 100644
--- a/typo3/sysext/backend/Classes/Controller/FormInlineAjaxController.php
+++ b/typo3/sysext/backend/Classes/Controller/FormInlineAjaxController.php
@@ -48,6 +48,12 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
 
         $domObjectId = $ajaxArguments[0];
         $inlineFirstPid = $this->getInlineFirstPidFromDomObjectId($domObjectId);
+        if (!MathUtility::canBeInterpretedAsInteger($inlineFirstPid) && strpos($inlineFirstPid, 'NEW') !== 0) {
+            throw new \RuntimeException(
+                'inlineFirstPid should either be an integer or a "NEW..." string',
+                1521220491
+            );
+        }
         $childChildUid = null;
         if (isset($ajaxArguments[1]) && MathUtility::canBeInterpretedAsInteger($ajaxArguments[1])) {
             $childChildUid = (int)$ajaxArguments[1];
@@ -70,7 +76,7 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
             $childVanillaUid = -1 * abs((int)$child['uid']);
         } else {
             // Else inline first Pid is the storage pid of new inline records
-            $childVanillaUid = (int)$inlineFirstPid;
+            $childVanillaUid = $inlineFirstPid;
         }
 
         $childTableName = $parentConfig['foreign_table'];
@@ -118,17 +124,17 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
                 $formDataCompilerInput = [
                     'command' => 'new',
                     'tableName' => $childData['processedTca']['columns'][$parentConfig['foreign_selector']]['config']['foreign_table'],
-                    'vanillaUid' => (int)$inlineFirstPid,
+                    'vanillaUid' => $inlineFirstPid,
                     'isInlineChild' => true,
                     'isInlineAjaxOpeningContext' => true,
                     'inlineStructure' => $inlineStackProcessor->getStructure(),
-                    'inlineFirstPid' => (int)$inlineFirstPid,
+                    'inlineFirstPid' => $inlineFirstPid,
                 ];
                 $childData['combinationChild'] = $formDataCompiler->compile($formDataCompilerInput);
             }
         }
 
-        $childData['inlineParentUid'] = (int)$parent['uid'];
+        $childData['inlineParentUid'] = $parent['uid'];
         $childData['renderType'] = 'inlineRecordContainer';
         $nodeFactory = GeneralUtility::makeInstance(NodeFactory::class);
         $childResult = $nodeFactory->create($childData)->render();
diff --git a/typo3/sysext/backend/Classes/Form/FormDataCompiler.php b/typo3/sysext/backend/Classes/Form/FormDataCompiler.php
index 1fdd51a6a49f..7d4532dd893e 100644
--- a/typo3/sysext/backend/Classes/Form/FormDataCompiler.php
+++ b/typo3/sysext/backend/Classes/Form/FormDataCompiler.php
@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Backend\Form;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Core\Utility\MathUtility;
+
 /**
  * Create and return a defined array of data ready to be used by the
  * container / element render part of FormEngine
@@ -82,8 +84,8 @@ class FormDataCompiler
                 }
             }
             if ($dataKey === 'vanillaUid') {
-                if (!is_int($dataValue)) {
-                    throw new \InvalidArgumentException('$vanillaUid is not an integer', 1437654247);
+                if (!MathUtility::canBeInterpretedAsInteger($dataValue) && strpos($dataValue, 'NEW') !== 0) {
+                    throw new \InvalidArgumentException('$vanillaUid is not an integer or "NEW..." string ID', 1437654247);
                 }
                 if (isset($initialData['command']) && $initialData['command'] === 'edit' && $dataValue < 0) {
                     throw new \InvalidArgumentException('Negative $vanillaUid is not supported with $command="edit', 1437654332);
@@ -140,13 +142,14 @@ class FormDataCompiler
             'command' => '',
             // Table name of the handled row
             'tableName' => '',
-            // Forced integer of otherwise not changed uid of the record, meaning of value depends on context (new / edit)
+            // Not changed uid of the record, meaning of value depends on context (new / edit)
             // * If $command is "edit"
             // ** $vanillaUid is a positive integer > 0 pointing to the record in the table
             // * If $command is "new":
             // ** If $vanillaUid > 0, it is the uid of a page the record should be added at
             // ** If $vanillaUid < 0, it is the uid of a record in the same table after which the new record should be added
             // ** If $vanillaUid = 0, a new record is added on page 0
+            // ** If $vanillaUid = "NEW..." Id of a parent page record if an inline child is added to a not yet persisted page
             'vanillaUid' => 0,
             // Url to return to
             'returnUrl' => null,
@@ -238,7 +241,8 @@ class FormDataCompiler
             'inlineExpandCollapseStateArray' => [],
             // The "entry" pid for inline records. Nested inline records can potentially hang around on different
             // pid's, but the entry pid is needed for AJAX calls, so that they would know where the action takes
-            // place on the page structure.
+            // place on the page structure. This is usually an int, but can be a "NEW..." string if an inline relation
+            // is added to a currently being created page.
             'inlineFirstPid' => null,
             // The "config" section of an inline parent, prepared and sanitized by TcaInlineConfiguration provider
             'inlineParentConfig' => [],
diff --git a/typo3/sysext/backend/Classes/Form/FormDataProvider/SiteTcaInline.php b/typo3/sysext/backend/Classes/Form/FormDataProvider/SiteTcaInline.php
index 1a808f6ddd5c..9b2af4a76469 100644
--- a/typo3/sysext/backend/Classes/Form/FormDataProvider/SiteTcaInline.php
+++ b/typo3/sysext/backend/Classes/Form/FormDataProvider/SiteTcaInline.php
@@ -26,6 +26,7 @@ use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Exception\SiteNotFoundException;
 use TYPO3\CMS\Core\Site\SiteFinder;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\MathUtility;
 
 /**
  * Special data provider for the sites configuration module.
@@ -87,17 +88,24 @@ class SiteTcaInline extends AbstractDatabaseRecordProvider implements FormDataPr
             if ($table === 'pages') {
                 $liveVersionId = BackendUtility::getLiveVersionIdOfRecord('pages', $row['uid']);
                 $pid = $liveVersionId ?? $row['uid'];
-            } elseif ($row['pid'] < 0) {
+            } elseif (($row['pid'] ?? 0) < 0) {
                 $prevRec = BackendUtility::getRecord($table, abs($row['pid']));
                 $pid = $prevRec['pid'];
             } else {
-                $pid = $row['pid'];
+                $pid = $row['pid'] ?? 0;
             }
-            $pageRecord = BackendUtility::getRecord('pages', $pid);
-            if ((int)$pageRecord[$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField']] > 0) {
-                $pid = (int)$pageRecord[$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField']];
+            if (MathUtility::canBeInterpretedAsInteger($pid)) {
+                $pageRecord = BackendUtility::getRecord('pages', (int)$pid);
+                if ((int)$pageRecord[$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField']] > 0) {
+                    $pid = (int)$pageRecord[$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField']];
+                }
+            } elseif (strpos($pid, 'NEW') !== 0) {
+                throw new \RuntimeException(
+                    'inlineFirstPid should either be an integer or a "NEW..." string',
+                    1521220141
+                );
             }
-            $result['inlineFirstPid'] = (int)$pid;
+            $result['inlineFirstPid'] = $pid;
         }
         return $result;
     }
diff --git a/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaInline.php b/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaInline.php
index 14a57ce70f60..ce96bee919a9 100644
--- a/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaInline.php
+++ b/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaInline.php
@@ -27,6 +27,7 @@ use TYPO3\CMS\Core\Localization\LanguageService;
 use TYPO3\CMS\Core\Messaging\FlashMessage;
 use TYPO3\CMS\Core\Messaging\FlashMessageService;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\MathUtility;
 use TYPO3\CMS\Core\Versioning\VersionState;
 
 /**
@@ -100,17 +101,24 @@ class TcaInline extends AbstractDatabaseRecordProvider implements FormDataProvid
             if ($table === 'pages') {
                 $liveVersionId = BackendUtility::getLiveVersionIdOfRecord('pages', $row['uid']);
                 $pid = $liveVersionId ?? $row['uid'];
-            } elseif ($row['pid'] < 0) {
+            } elseif (($row['pid'] ?? 0) < 0) {
                 $prevRec = BackendUtility::getRecord($table, abs($row['pid']));
                 $pid = $prevRec['pid'];
             } else {
-                $pid = $row['pid'];
+                $pid = $row['pid'] ?? 0;
             }
-            $pageRecord = BackendUtility::getRecord('pages', $pid);
-            if ((int)$pageRecord[$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField']] > 0) {
-                $pid = (int)$pageRecord[$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField']];
+            if (MathUtility::canBeInterpretedAsInteger($pid)) {
+                $pageRecord = BackendUtility::getRecord('pages', (int)$pid);
+                if ((int)$pageRecord[$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField']] > 0) {
+                    $pid = (int)$pageRecord[$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField']];
+                }
+            } elseif (strpos($pid, 'NEW') !== 0) {
+                throw new \RuntimeException(
+                    'inlineFirstPid should either be an integer or a "NEW..." string',
+                    1521220142
+                );
             }
-            $result['inlineFirstPid'] = (int)$pid;
+            $result['inlineFirstPid'] = $pid;
         }
         return $result;
     }
diff --git a/typo3/sysext/backend/Classes/Form/InlineStackProcessor.php b/typo3/sysext/backend/Classes/Form/InlineStackProcessor.php
index 315ae2f1d47e..2f2dbf8c7480 100644
--- a/typo3/sysext/backend/Classes/Form/InlineStackProcessor.php
+++ b/typo3/sysext/backend/Classes/Form/InlineStackProcessor.php
@@ -158,7 +158,7 @@ class InlineStackProcessor
     /**
      * DOM object-id for this inline level
      *
-     * @param int $inlineFirstPid Pid of top level inline element storage
+     * @param int|string $inlineFirstPid Pid of top level inline element storage or "NEW..."
      * @return string
      */
     public function getCurrentStructureDomObjectIdPrefix($inlineFirstPid)
-- 
GitLab