From 45e03bec9c2217845f9b8a42116ff64a87fa1b65 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20B=C3=BCrk?= <stefan@buerk.tech>
Date: Wed, 30 Aug 2023 15:23:27 +0200
Subject: [PATCH] [BUGFIX] Ensure correct record type for new record in
 SuggestWizard
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

FormEngine sends some information as payload with the suggest
wizard ajax request to the `SuggestWizardController` as context.
The controller loads the record based on the tableName and
the uid to determine the recordType to read the correct TCA field
configuration, respecting special overrides like `columnsOverrides`.

If a new record is created, there is no record uid available and
passed to the controller. Therefore, the recordType is not properly
determined. That leads to wrong record suggestions, if for example
the `allowed` record table is overriden for specific recordTypes.

This change adds the recordTypeValue as additional html data
attribute to the suggest search field, reads and sends it along
with the context payload in the ajax request to the suggest
wizard controller, which now uses the passed value while keeping
the record retrievement as fallback for now.

Suggest records are now directly searched correctly respecting
the full TCA configuration for the type, even for new records.

Additionally, uid is send as null instead of the string "NaN".

Used command(s):

> Build/Scripts/runTests.sh -s buildJavascript

Resolves: #101796
Releases: main, 12.4, 11.5
Change-Id: I3b814d37b7d4d3e9674ad6f2af882520c4f91413
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/80773
Tested-by: Stefan B�rk <stefan@buerk.tech>
Reviewed-by: Stefan B�rk <stefan@buerk.tech>
Tested-by: core-ci <typo3@b13.com>
---
 .../Sources/TypeScript/backend/form-engine-suggest.ts  |  4 +++-
 .../Controller/Wizard/SuggestWizardController.php      | 10 ++++++++--
 .../backend/Classes/Form/Element/GroupElement.php      |  4 ++++
 .../Resources/Public/JavaScript/form-engine-suggest.js |  2 +-
 4 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/Build/Sources/TypeScript/backend/form-engine-suggest.ts b/Build/Sources/TypeScript/backend/form-engine-suggest.ts
index 8f9d5e34828a..754a8feea19f 100644
--- a/Build/Sources/TypeScript/backend/form-engine-suggest.ts
+++ b/Build/Sources/TypeScript/backend/form-engine-suggest.ts
@@ -81,18 +81,20 @@ class FormEngineSuggest {
         return;
       }
 
+      const uid = parseInt(target.dataset.uid, 10);
       this.currentRequest = new AjaxRequest(TYPO3.settings.ajaxUrls.record_suggest);
       this.currentRequest.post({
         value: target.value,
         tableName: target.dataset.tablename,
         fieldName: target.dataset.fieldname,
-        uid: parseInt(target.dataset.uid, 10),
+        uid: (isNaN(uid) ? null : uid),
         pid: parseInt(target.dataset.pid, 10),
         dataStructureIdentifier: target.dataset.datastructureidentifier,
         flexFormSheetName: target.dataset.flexformsheetname,
         flexFormFieldName: target.dataset.flexformfieldname,
         flexFormContainerName: target.dataset.flexformcontainername,
         flexFormContainerFieldName: target.dataset.flexformcontainerfieldname,
+        recordTypeValue: target.dataset.recordtypevalue,
       }).then(async (response: AjaxResponse): Promise<void> => {
         const resultSet = await response.raw().text();
         this.resultContainer.setAttribute('results', resultSet);
diff --git a/typo3/sysext/backend/Classes/Controller/Wizard/SuggestWizardController.php b/typo3/sysext/backend/Classes/Controller/Wizard/SuggestWizardController.php
index 8443402af54e..d464b37c8d40 100644
--- a/typo3/sysext/backend/Classes/Controller/Wizard/SuggestWizardController.php
+++ b/typo3/sysext/backend/Classes/Controller/Wizard/SuggestWizardController.php
@@ -54,6 +54,7 @@ class SuggestWizardController
         $flexFormFieldName = $parsedBody['flexFormFieldName'] ?? null;
         $flexFormContainerName = $parsedBody['flexFormContainerName'] ?? null;
         $flexFormContainerFieldName = $parsedBody['flexFormContainerFieldName'] ?? null;
+        $recordType = (string)($parsedBody['recordTypeValue'] ?? '');
 
         // Determine TCA config of field
         if (empty($dataStructureIdentifier)) {
@@ -62,8 +63,13 @@ class SuggestWizardController
             $fieldNameInPageTsConfig = $fieldName;
 
             // With possible columnsOverrides
-            $row = BackendUtility::getRecord($tableName, $uid) ?? [];
-            $recordType = BackendUtility::getTCAtypeValue($tableName, $row);
+            // @todo Validate if we can move this fallback recordType determination, should be do-able in v13?!
+            if ($recordType === '') {
+                $recordType = BackendUtility::getTCAtypeValue(
+                    $tableName,
+                    BackendUtility::getRecord($tableName, $uid) ?? []
+                );
+            }
             $columnsOverridesConfigOfField = $GLOBALS['TCA'][$tableName]['types'][$recordType]['columnsOverrides'][$fieldName]['config'] ?? null;
             if ($columnsOverridesConfigOfField) {
                 ArrayUtility::mergeRecursiveWithOverrule($fieldConfig, $columnsOverridesConfigOfField);
diff --git a/typo3/sysext/backend/Classes/Form/Element/GroupElement.php b/typo3/sysext/backend/Classes/Form/Element/GroupElement.php
index d9751e978c8d..87bd519b798c 100644
--- a/typo3/sysext/backend/Classes/Form/Element/GroupElement.php
+++ b/typo3/sysext/backend/Classes/Form/Element/GroupElement.php
@@ -116,6 +116,7 @@ class GroupElement extends AbstractFormElement
         $parameterArray = $this->data['parameterArray'];
         $config = $parameterArray['fieldConf']['config'];
         $elementName = $parameterArray['itemFormElName'];
+        $recordTypeValue = $this->data['recordTypeValue'] ?? null;
 
         $selectedItems = $parameterArray['itemFormElValue'];
         $maxItems = $config['maxitems'];
@@ -269,6 +270,9 @@ class GroupElement extends AbstractFormElement
             $html[] =                   ' data-flexformfieldname="' . htmlspecialchars($flexFormFieldName) . '"';
             $html[] =                   ' data-flexformcontainername="' . htmlspecialchars($flexFormContainerName) . '"';
             $html[] =                   ' data-flexformcontainerfieldname="' . htmlspecialchars($flexFormContainerFieldName) . '"';
+            if ($recordTypeValue !== null && $recordTypeValue !== '') {
+                $html[] =                   ' data-recordtypevalue="' . htmlspecialchars($recordTypeValue) . '"';
+            }
             $html[] =               '/>';
             $html[] =           '</div>';
             $html[] =       '</div>';
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-suggest.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-suggest.js
index 2d4532c702e5..7ae93231dc9d 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-suggest.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-suggest.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import"@typo3/backend/form-engine/element/suggest/result-container.js";import DocumentService from"@typo3/core/document-service.js";import FormEngine from"@typo3/backend/form-engine.js";import RegularEvent from"@typo3/core/event/regular-event.js";import DebounceEvent from"@typo3/core/event/debounce-event.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";class FormEngineSuggest{constructor(e){this.currentRequest=null,this.handleKeyDown=e=>{if("ArrowDown"!==e.key)"Escape"===e.key&&(e.preventDefault(),this.resultContainer.hidden=!0);else{e.preventDefault();const t=JSON.parse(this.resultContainer.getAttribute("results"));t?.length>0&&(this.resultContainer.hidden=!1);const n=this.resultContainer.querySelector("typo3-backend-formengine-suggest-result-item");n?.focus()}},this.element=e,DocumentService.ready().then((()=>{this.initialize(e),this.registerEvents()}))}initialize(e){const t=e.closest(".t3-form-suggest-container");this.resultContainer=document.createElement("typo3-backend-formengine-suggest-result-container"),this.resultContainer.hidden=!0,t.append(this.resultContainer)}registerEvents(){new RegularEvent("typo3:formengine:suggest-item-chosen",(e=>{let t="";t="select"===this.element.dataset.fieldtype?e.detail.element.uid:e.detail.element.table+"_"+e.detail.element.uid,FormEngine.setSelectOptionFromExternalSource(this.element.dataset.field,t,e.detail.element.label,e.detail.element.label),FormEngine.Validation.markFieldAsChanged(document.querySelector('input[name="'+this.element.dataset.field+'"]')),this.resultContainer.hidden=!0})).bindTo(this.resultContainer),new RegularEvent("focus",(()=>{const e=JSON.parse(this.resultContainer.getAttribute("results"));e?.length>0&&(this.resultContainer.hidden=!1)})).bindTo(this.element),new RegularEvent("blur",(e=>{"typo3-backend-formengine-suggest-result-item"!==e.relatedTarget?.tagName.toLowerCase()&&(this.resultContainer.hidden=!0)})).bindTo(this.element),new DebounceEvent("input",(e=>{this.currentRequest instanceof AjaxRequest&&this.currentRequest.abort();const t=e.target;t.value.length<parseInt(t.dataset.minchars,10)||(this.currentRequest=new AjaxRequest(TYPO3.settings.ajaxUrls.record_suggest),this.currentRequest.post({value:t.value,tableName:t.dataset.tablename,fieldName:t.dataset.fieldname,uid:parseInt(t.dataset.uid,10),pid:parseInt(t.dataset.pid,10),dataStructureIdentifier:t.dataset.datastructureidentifier,flexFormSheetName:t.dataset.flexformsheetname,flexFormFieldName:t.dataset.flexformfieldname,flexFormContainerName:t.dataset.flexformcontainername,flexFormContainerFieldName:t.dataset.flexformcontainerfieldname}).then((async e=>{const t=await e.raw().text();this.resultContainer.setAttribute("results",t),this.resultContainer.hidden=!1})))})).bindTo(this.element),new RegularEvent("keydown",this.handleKeyDown).bindTo(this.element)}}export default FormEngineSuggest;
\ No newline at end of file
+import"@typo3/backend/form-engine/element/suggest/result-container.js";import DocumentService from"@typo3/core/document-service.js";import FormEngine from"@typo3/backend/form-engine.js";import RegularEvent from"@typo3/core/event/regular-event.js";import DebounceEvent from"@typo3/core/event/debounce-event.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";class FormEngineSuggest{constructor(e){this.currentRequest=null,this.handleKeyDown=e=>{if("ArrowDown"!==e.key)"Escape"===e.key&&(e.preventDefault(),this.resultContainer.hidden=!0);else{e.preventDefault();const t=JSON.parse(this.resultContainer.getAttribute("results"));t?.length>0&&(this.resultContainer.hidden=!1);const n=this.resultContainer.querySelector("typo3-backend-formengine-suggest-result-item");n?.focus()}},this.element=e,DocumentService.ready().then((()=>{this.initialize(e),this.registerEvents()}))}initialize(e){const t=e.closest(".t3-form-suggest-container");this.resultContainer=document.createElement("typo3-backend-formengine-suggest-result-container"),this.resultContainer.hidden=!0,t.append(this.resultContainer)}registerEvents(){new RegularEvent("typo3:formengine:suggest-item-chosen",(e=>{let t="";t="select"===this.element.dataset.fieldtype?e.detail.element.uid:e.detail.element.table+"_"+e.detail.element.uid,FormEngine.setSelectOptionFromExternalSource(this.element.dataset.field,t,e.detail.element.label,e.detail.element.label),FormEngine.Validation.markFieldAsChanged(document.querySelector('input[name="'+this.element.dataset.field+'"]')),this.resultContainer.hidden=!0})).bindTo(this.resultContainer),new RegularEvent("focus",(()=>{const e=JSON.parse(this.resultContainer.getAttribute("results"));e?.length>0&&(this.resultContainer.hidden=!1)})).bindTo(this.element),new RegularEvent("blur",(e=>{"typo3-backend-formengine-suggest-result-item"!==e.relatedTarget?.tagName.toLowerCase()&&(this.resultContainer.hidden=!0)})).bindTo(this.element),new DebounceEvent("input",(e=>{this.currentRequest instanceof AjaxRequest&&this.currentRequest.abort();const t=e.target;if(t.value.length<parseInt(t.dataset.minchars,10))return;const n=parseInt(t.dataset.uid,10);this.currentRequest=new AjaxRequest(TYPO3.settings.ajaxUrls.record_suggest),this.currentRequest.post({value:t.value,tableName:t.dataset.tablename,fieldName:t.dataset.fieldname,uid:isNaN(n)?null:n,pid:parseInt(t.dataset.pid,10),dataStructureIdentifier:t.dataset.datastructureidentifier,flexFormSheetName:t.dataset.flexformsheetname,flexFormFieldName:t.dataset.flexformfieldname,flexFormContainerName:t.dataset.flexformcontainername,flexFormContainerFieldName:t.dataset.flexformcontainerfieldname,recordTypeValue:t.dataset.recordtypevalue}).then((async e=>{const t=await e.raw().text();this.resultContainer.setAttribute("results",t),this.resultContainer.hidden=!1}))})).bindTo(this.element),new RegularEvent("keydown",this.handleKeyDown).bindTo(this.element)}}export default FormEngineSuggest;
\ No newline at end of file
-- 
GitLab