Commit 38a1bc5d authored by Christian Kuhn's avatar Christian Kuhn Committed by Anja Leichsenring
Browse files

[!!!][TASK] Improve flex and TCA handling in FormEngine

The patch adapts a series of nasty form engine areas to more solid
code. The evaluate condition code is rewritten and works much better
in flex form scenarios. The suggest wizard and svg tree are much
more solid in flex forms. The group element is rewritten
towards a better readable and easier to refactor code, dropping
method dbFileIcons(). A bunch of issues is resolved along the way.

* TCA "default" now works in flex form section container elements
* The "displayCond" parser is now strict and throws exceptions on
  invalid syntax and wrong referenced fields to help debugging
  faulty display conditions
* TCA displayCond on flex fields can now be prefixed with the
  sheet name and can reference field values from neighbor sheets
* TCA displayCond now works with flex section containers
* TCA flex section container now throw an exception if select or
  group fields configure a MM relation - this is not supported
* TCA ctrl requestUpdate field is dropped, onChange=reload is now allowed
  not only on flex form fields, but also on normal columns fields
* TCA tree now works as section container element and initializes
  correctly on new records and new containers
* GroupElement rewrite to drop dbFileIcons()
* config option maxitems now optional for type=group and type=select
  and defaults to "many items allowed"
* inline now works in "fancy" flex situations with "new" records
  by handing the final dataStructureIdentifier around
* FormEngine no longer loads extJS

Change-Id: Id1d081627529cc1502bb198389e5bd69372815cd
Resolves: #78899
Resolves: #72307
Resolves: #75646
Resolves: #76637
Resolves: #72106
Resolves: #78824
Resolves: #76793
Resolves: #68247
Resolves: #69715
Related: #78460
Related: #67198
Related: #72294
Releases: master
Reviewed-on: https://review.typo3.org/50879

Tested-by: default avatarTYPO3com <no-reply@typo3.com>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Tested-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Reviewed-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
parent 82cc3e9a
<?php
declare(strict_types=1);
namespace TYPO3\CMS\Backend\Controller;
/*
* 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 Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Form\FormDataCompiler;
use TYPO3\CMS\Backend\Form\FormDataGroup\TcaDatabaseRecord;
use TYPO3\CMS\Backend\Form\NodeFactory;
use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\StringUtility;
/**
* Handle FormEngine flex field ajax calls
*/
class FormFlexAjaxController
{
/**
* Render a single flex form section container to add it to the DOM
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @return ResponseInterface
*/
public function containerAdd(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$queryParameters = $request->getParsedBody();
$vanillaUid = (int)$queryParameters['vanillaUid'];
$databaseRowUid = $queryParameters['databaseRowUid'];
$command = $queryParameters['command'];
$tableName = $queryParameters['tableName'];
$fieldName = $queryParameters['fieldName'];
$recordTypeValue = $queryParameters['recordTypeValue'];
$dataStructureIdentifier = json_encode($queryParameters['dataStructureIdentifier']);
$flexFormSheetName = $queryParameters['flexFormSheetName'];
$flexFormFieldName = $queryParameters['flexFormFieldName'];
$flexFormContainerName = $queryParameters['flexFormContainerName'];
// Prepare TCA and data values for a new section container using data providers
$processedTca = $GLOBALS['TCA'][$tableName];
$flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
$dataStructure = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
$processedTca['columns'][$fieldName]['config']['ds'] = $dataStructure;
$processedTca['columns'][$fieldName]['config']['dataStructureIdentifier'] = $dataStructureIdentifier;
// Get a new unique id for this container.
$flexFormContainerIdentifier = StringUtility::getUniqueId();
$flexSectionContainerPreparation = [
'flexFormSheetName' => $flexFormSheetName,
'flexFormFieldName' => $flexFormFieldName,
'flexFormContainerName' => $flexFormContainerName,
'flexFormContainerIdentifier' => $flexFormContainerIdentifier,
];
$formDataGroup = GeneralUtility::makeInstance(TcaDatabaseRecord::class);
$formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
$formDataCompilerInput = [
'tableName' => $tableName,
'vanillaUid' => (int)$vanillaUid,
'databaseRow' => [
'uid' => $databaseRowUid,
],
'command' => $command,
'recordTypeValue' => $recordTypeValue,
'processedTca' => $processedTca,
'flexSectionContainerPreparation' => $flexSectionContainerPreparation,
];
$formData = $formDataCompiler->compile($formDataCompilerInput);
$dataStructure = $formData['processedTca']['columns'][$fieldName]['config']['ds'];
$formData['fieldName'] = $fieldName;
$formData['flexFormDataStructureArray'] = $dataStructure['sheets'][$flexFormSheetName]['ROOT']['el'][$flexFormFieldName]['children'][$flexFormContainerIdentifier];
$formData['flexFormDataStructureIdentifier'] = $dataStructureIdentifier;
$formData['flexFormFieldName'] = $flexFormFieldName;
$formData['flexFormSheetName'] = $flexFormSheetName;
$formData['flexFormContainerName'] = $flexFormContainerName;
$formData['flexFormContainerIdentifier'] = $flexFormContainerIdentifier;
$formData['flexFormContainerElementCollapsed'] = false;
$formData['flexFormFormPrefix'] = '[data][' . $flexFormSheetName . '][lDEF]' . '[' . $flexFormFieldName . ']' . '[el]';
$formData['parameterArray']['itemFormElName'] = 'data[' . $tableName . '][' . $formData['databaseRow']['uid'] . '][' . $fieldName . ']';
// JavaScript code for event handlers:
// @todo: see if we can get rid of this - used in group elements, and also for the "reload" on type field changes
$formData['parameterArray']['fieldChangeFunc'] = [];
$formData['parameterArray']['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] = 'TBE_EDITOR.fieldChanged('
. GeneralUtility::quoteJSvalue($tableName)
. ',' . GeneralUtility::quoteJSvalue($formData['databaseRow']['uid'])
. ',' . GeneralUtility::quoteJSvalue($fieldName)
. ',' . GeneralUtility::quoteJSvalue($formData['parameterArray']['itemFormElName'])
. ');';
// @todo: check GroupElement for usage of elementBaseName ... maybe kick that thing?
// Feed resulting form data to container structure to render HTML and other result data
$nodeFactory = GeneralUtility::makeInstance(NodeFactory::class);
$formData['renderType'] = 'flexFormContainerContainer';
$newContainerResult = $nodeFactory->create($formData)->render();
$jsonResult = [
'html' => $newContainerResult['html'],
'scriptCall' => [],
];
if (!empty($newContainerResult['additionalJavaScriptSubmit'])) {
$additionalJavaScriptSubmit = implode('', $newContainerResult['additionalJavaScriptSubmit']);
$additionalJavaScriptSubmit = str_replace([CR, LF], '', $additionalJavaScriptSubmit);
$jsonResult['scriptCall'][] = 'TBE_EDITOR.addActionChecks("submit", "' . addslashes($additionalJavaScriptSubmit) . '");';
}
foreach ($newContainerResult['additionalJavaScriptPost'] as $singleAdditionalJavaScriptPost) {
$jsonResult['scriptCall'][] = $singleAdditionalJavaScriptPost;
}
// @todo: handle stylesheetFiles, additionalInlineLanguageLabelFiles
// @todo: copied from inline ajax handler - maybe extract to some abstract?
if (!empty($newContainerResult['requireJsModules'])) {
foreach ($newContainerResult['requireJsModules'] as $module) {
$moduleName = null;
$callback = null;
if (is_string($module)) {
// if $module is a string, no callback
$moduleName = $module;
$callback = null;
} elseif (is_array($module)) {
// if $module is an array, callback is possible
foreach ($module as $key => $value) {
$moduleName = $key;
$callback = $value;
break;
}
}
if ($moduleName !== null) {
$inlineCodeKey = $moduleName;
$javaScriptCode = 'require(["' . $moduleName . '"]';
if ($callback !== null) {
$inlineCodeKey .= sha1($callback);
$javaScriptCode .= ', ' . $callback;
}
$javaScriptCode .= ');';
$jsonResult['scriptCall'][] = '/*RequireJS-Module-' . $inlineCodeKey . '*/' . LF . $javaScriptCode;
}
}
}
$response->getBody()->write(json_encode($jsonResult));
return $response;
}
}
......@@ -23,6 +23,7 @@ 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;
......@@ -75,18 +76,27 @@ class FormInlineAjaxController
$databaseRow = [];
$vanillaUid = (int)$inlineFirstPid;
}
$databaseRow = $this->addFlexFormDataStructurePointersFromAjaxContext($ajaxArguments, $databaseRow);
$flexDataStructureIdentifier = $this->getFlexFormDataStructureIdentifierFromAjaxContext($ajaxArguments);
$processedTca = [];
if ($flexDataStructureIdentifier) {
$processedTca = $GLOBALS['TCA'][$parent['table']];
$flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
$dataStructure = $flexFormTools->parseDataStructureByIdentifier($flexDataStructureIdentifier);
$processedTca['columns'][$parentFieldName]['config']['dataStructureIdentifier'] = $flexDataStructureIdentifier;
$processedTca['columns'][$parentFieldName]['config']['ds'] = $dataStructure;
}
$formDataCompilerInputForParent = [
'vanillaUid' => $vanillaUid,
'command' => $command,
'tableName' => $parent['table'],
'databaseRow' => $databaseRow,
'processedTca' => $processedTca,
'inlineFirstPid' => $inlineFirstPid,
'columnsToProcess' => array_merge(
[$parentFieldName],
array_keys($databaseRow)
),
'columnsToProcess' => [
$parentFieldName,
],
// Do not resolve existing children, we don't need them now
'inlineResolveExistingChildren' => false,
];
......@@ -248,18 +258,26 @@ class FormInlineAjaxController
'uid' => (int)$parent['uid'],
];
$databaseRow = $this->addFlexFormDataStructurePointersFromAjaxContext($ajaxArguments, $databaseRow);
$flexDataStructureIdentifier = $this->getFlexFormDataStructureIdentifierFromAjaxContext($ajaxArguments);
$processedTca = [];
if ($flexDataStructureIdentifier) {
$processedTca = $GLOBALS['TCA'][$parent['table']];
$flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
$dataStructure = $flexFormTools->parseDataStructureByIdentifier($flexDataStructureIdentifier);
$processedTca['columns'][$parentFieldName]['config']['dataStructureIdentifier'] = $flexDataStructureIdentifier;
$processedTca['columns'][$parentFieldName]['config']['ds'] = $dataStructure;
}
$formDataCompilerInputForParent = [
'vanillaUid' => (int)$parent['uid'],
'command' => 'edit',
'tableName' => $parent['table'],
'databaseRow' => $databaseRow,
'processedTca' => $processedTca,
'inlineFirstPid' => $inlineFirstPid,
'columnsToProcess' => array_merge(
[$parentFieldName],
array_keys($databaseRow)
),
'columnsToProcess' => [
$parentFieldName
],
// @todo: still needed?
'inlineStructure' => $inlineStackProcessor->getStructure(),
// Do not resolve existing children, we don't need them now
......@@ -639,7 +657,6 @@ class FormInlineAjaxController
foreach ($childResult['additionalJavaScriptPost'] as $singleAdditionalJavaScriptPost) {
$jsonResult['scriptCall'][] = $singleAdditionalJavaScriptPost;
}
$jsonResult['scriptCall'][] = $childResult['extJSCODE'];
if (!empty($childResult['additionalInlineLanguageLabelFiles'])) {
$labels = [];
foreach ($childResult['additionalInlineLanguageLabelFiles'] as $additionalInlineLanguageLabelFile) {
......@@ -927,33 +944,25 @@ class FormInlineAjaxController
}
/**
* Flexforms require additional database columns to be processed to determine the correct
* data structure to be used from a flexform. The required columns and their values are
* transmitted in the AJAX context of the request and need to be added to the fake database
* row for the inline parent.
* Inline fields within a flex form need the data structure identifier that
* specifies the specific flex form this inline element is in. Retrieve it from
* the context array.
*
* @param array $ajaxArguments The AJAX request arguments
* @param array $databaseRow The fake database row
* @return array The database row with the flexform data structure pointer columns added
* @return string Data structure identifier as json string
*/
protected function addFlexFormDataStructurePointersFromAjaxContext(array $ajaxArguments, array $databaseRow)
protected function getFlexFormDataStructureIdentifierFromAjaxContext(array $ajaxArguments)
{
if (!isset($ajaxArguments['context'])) {
return $databaseRow;
return '';
}
$context = json_decode($ajaxArguments['context'], true);
if (GeneralUtility::hmac(serialize($context['config'])) !== $context['hmac']) {
return $databaseRow;
}
if (isset($context['config']['flexDataStructurePointers'])
&& is_array($context['config']['flexDataStructurePointers'])
) {
$databaseRow = array_merge($context['config']['flexDataStructurePointers'], $databaseRow);
return '';
}
return $databaseRow;
return $context['config']['flexDataStructureIdentifier'] ?? '';
}
/**
......
......@@ -24,19 +24,20 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Backend controller for selectTree ajax operations
*/
class SelectTreeController
class FormSelectTreeAjaxController
{
/**
* Returns json representing category tree
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @throws \RuntimeException
* @return ResponseInterface
*/
public function fetchDataAction(ServerRequestInterface $request, ResponseInterface $response)
{
$tableName = $request->getQueryParams()['table'];
$fieldName = $request->getQueryParams()['field'];
$tableName = $request->getQueryParams()['tableName'];
$fieldName = $request->getQueryParams()['fieldName'];
// Prepare processedTca: Remove all column definitions except the one that contains
// our tree definition. This way only this field is calculated, everything else is ignored.
......@@ -55,37 +56,96 @@ class SelectTreeController
}
// Force given record type and set showitem to our field only
$recordTypeValue = $request->getQueryParams()['record_type_value'];
$recordTypeValue = $request->getQueryParams()['recordTypeValue'];
$processedTca['types'][$recordTypeValue]['showitem'] = $fieldName;
// Unset all columns except our field
$processedTca['columns'] = [
$fieldName => $processedTca['columns'][$fieldName],
];
$flexFormPath = [];
$dataStructureIdentifier = '';
$flexFormSheetName = '';
$flexFormFieldName = '';
$flexFormContainerIdentifier = '';
$flexFormContainerFieldName = '';
$flexSectionContainerPreparation = [];
if ($processedTca['columns'][$fieldName]['config']['type'] === 'flex') {
if (!empty($request->getQueryParams()['dataStructureIdentifier'])) {
$dataStructureIdentifier = json_encode($request->getQueryParams()['dataStructureIdentifier']);
}
$flexFormSheetName = $request->getQueryParams()['flexFormSheetName'];
$flexFormFieldName = $request->getQueryParams()['flexFormFieldName'];
$flexFormContainerName = $request->getQueryParams()['flexFormContainerName'];
$flexFormContainerIdentifier = $request->getQueryParams()['flexFormContainerIdentifier'];
$flexFormContainerFieldName = $request->getQueryParams()['flexFormContainerFieldName'];
$flexFormSectionContainerIsNew = (bool)$request->getQueryParams()['flexFormSectionContainerIsNew'];
$flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
$dataStructureIdentifier = json_encode($request->getQueryParams()['flex_form_datastructure_identifier']);
$dataStructure = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
// Try to reduce given data structure down to the relevant element only
$flexFormPath = $request->getQueryParams()['flex_form_path'];
$fieldPattern = 'data[' . $tableName . '][';
$flexFormPath = str_replace($fieldPattern, '', $flexFormPath);
$flexFormPath = substr($flexFormPath, 0, -1);
$flexFormPath = explode('][', $flexFormPath);
if (isset($dataStructure['sheets'][$flexFormPath[3]]['ROOT']['el'][$flexFormPath[5]])) {
$dataStructure = [
'sheets' => [
$flexFormPath[3] => [
'ROOT' => [
'type' => 'array',
'el' => [
$flexFormPath[5] => $dataStructure['sheets'][$flexFormPath[3]]['ROOT']['el'][$flexFormPath[5]],
// Reduce given data structure down to the relevant element only
if (empty($flexFormContainerFieldName)) {
if (isset($dataStructure['sheets'][$flexFormSheetName]['ROOT']
['el'][$flexFormFieldName])
) {
$dataStructure = [
'sheets' => [
$flexFormSheetName => [
'ROOT' => [
'type' => 'array',
'el' => [
$flexFormFieldName => $dataStructure['sheets'][$flexFormSheetName]['ROOT']
['el'][$flexFormFieldName],
],
],
],
],
],
];
];
}
} else {
if (isset($dataStructure['sheets'][$flexFormSheetName]['ROOT']
['el'][$flexFormFieldName]
['el'][$flexFormContainerName]
['el'][$flexFormContainerFieldName])
) {
// If this is a tree in a section container that has just been added by the FlexFormAjaxController
// "new container" action, then this container is not yet persisted, so we need to trigger the
// TcaFlexProcess data provider again to prepare the DS and databaseRow of that container.
if ($flexFormSectionContainerIsNew) {
$flexSectionContainerPreparation = [
'flexFormSheetName' => $flexFormSheetName,
'flexFormFieldName' => $flexFormFieldName,
'flexFormContainerName' => $flexFormContainerName,
'flexFormContainerIdentifier' => $flexFormContainerIdentifier,
];
}
// Now restrict the data structure to our tree element only
$dataStructure = [
'sheets' => [
$flexFormSheetName => [
'ROOT' => [
'type' => 'array',
'el' => [
$flexFormFieldName => [
'section' => 1,
'type' => 'array',
'el' => [
$flexFormContainerName => [
'el' => [
$flexFormContainerFieldName => $dataStructure['sheets'][$flexFormSheetName]['ROOT']
['el'][$flexFormFieldName]
['el'][$flexFormContainerName]
['el'][$flexFormContainerFieldName]
],
],
],
],
],
],
],
],
];
}
}
$processedTca['columns'][$fieldName]['config']['ds'] = $dataStructure;
$processedTca['columns'][$fieldName]['config']['dataStructureIdentifier'] = $dataStructureIdentifier;
......@@ -94,24 +154,33 @@ class SelectTreeController
$formDataGroup = GeneralUtility::makeInstance(TcaDatabaseRecord::class);
$formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
$formDataCompilerInput = [
'tableName' => $request->getQueryParams()['table'],
'tableName' => $tableName,
'vanillaUid' => (int)$request->getQueryParams()['uid'],
'command' => $request->getQueryParams()['command'],
'processedTca' => $processedTca,
'recordTypeValue' => $recordTypeValue,
'selectTreeCompileItems' => true,
'flexSectionContainerPreparation' => $flexSectionContainerPreparation,
];
$formData = $formDataCompiler->compile($formDataCompilerInput);
if ($formData['processedTca']['columns'][$fieldName]['config']['type'] === 'flex') {
$treeData = $formData['processedTca']['columns'][$fieldName]['config']['ds']
['sheets'][$flexFormPath[3]]['ROOT']['el'][$flexFormPath[5]]['config']['items'];
if (empty($flexFormContainerFieldName)) {
$treeData = $formData['processedTca']['columns'][$fieldName]['config']['ds']
['sheets'][$flexFormSheetName]['ROOT']
['el'][$flexFormFieldName]['config']['items'];
} else {
$treeData = $formData['processedTca']['columns'][$fieldName]['config']['ds']
['sheets'][$flexFormSheetName]['ROOT']
['el'][$flexFormFieldName]
['children'][$flexFormContainerIdentifier]
['el'][$flexFormContainerFieldName]['config']['items'];
}
} else {
$treeData = $formData['processedTca']['columns'][$fieldName]['config']['items'];
}
$json = json_encode($treeData);
$response->getBody()->write($json);
$response->getBody()->write(json_encode($treeData));
return $response;
}
}
......@@ -22,7 +22,6 @@ use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
/**
* Receives ajax request from FormEngine suggest wizard and creates suggest answer as json result
......@@ -41,75 +40,63 @@ class SuggestWizardController
{
$parsedBody = $request->getParsedBody();
if (!isset($parsedBody['value'])
|| !isset($parsedBody['table'])
|| !isset($parsedBody['field'])
|| !isset($parsedBody['uid'])
|| !isset($parsedBody['dataStructureIdentifier'])
|| !isset($parsedBody['hmac'])
) {
throw new \RuntimeException(
'Missing at least one of the required arguments "value", "table", "field", "uid"'
. ', "dataStructureIdentifier" or "hmac"',
1478607036
);
}
$search = $parsedBody['value'];
$table = $parsedBody['table'];
$field = $parsedBody['field'];
$tableName = $parsedBody['tableName'];
$fieldName = $parsedBody['fieldName'];
$uid = $parsedBody['uid'];
$pid = (int)$parsedBody['pid'];
// flex form section container identifiers are created on js side dynamically "onClick". Those are
// not within the generated hmac ... the js side adds "idx{dateInMilliseconds}-", so this is removed here again.
// example outgoing in renderSuggestSelector():
// flex_1|data|sSuggestCheckCombination|lDEF|settings.subelements|el|ID-356586b0d3-form|item|el|content|vDEF
// incoming here:
// flex_1|data|sSuggestCheckCombination|lDEF|settings.subelements|el|ID-356586b0d3-idx1478611729574-form|item|el|content|vDEF
// Note: For existing containers, these parts are numeric, so "ID-356586b0d3-idx1478611729574-form" becomes 1 or 2, etc.
// @todo: This could be kicked is the flex form section containers are moved to an ajax call on creation
$fieldForHmac = preg_replace('/idx\d{13}-/', '', $field);
$dataStructureIdentifierString = '';
$dataStructureIdentifier = '';
if (!empty($parsedBody['dataStructureIdentifier'])) {
$dataStructureIdentifierString = json_encode($parsedBody['dataStructureIdentifier']);
}
$incomingHmac = $parsedBody['hmac'];
$calculatedHmac = GeneralUtility::hmac(
$table . $fieldForHmac . $uid . $pid . $dataStructureIdentifierString,
'formEngineSuggest'
);
if ($incomingHmac !== $calculatedHmac) {
throw new \RuntimeException(
'Incoming and calculated hmac do not match',
1478608245
);
}
// If the $uid is numeric (existing page) and a suggest wizard in pages is handled, the effective
// pid is the uid of that page - important for page ts config configuration.
if (MathUtility::canBeInterpretedAsInteger($uid) && $table === 'pages') {
$pid = $uid;
$dataStructureIdentifier = json_encode($parsedBody['dataStructureIdentifier']);
}
$TSconfig = BackendUtility::getPagesTSconfig($pid);
$flexFormSheetName = $parsedBody['flexFormSheetName'];
$flexFormFieldName = $parsedBody['flexFormFieldName'];
$flexFormContainerName = $parsedBody['flexFormContainerName'];
$flexFormContainerFieldName = $parsedBody['flexFormContainerFieldName'];
// Determine TCA config of field
if (empty($dataStructureIdentifierString)) {
if (empty($dataStructureIdentifier)) {
// Normal columns field
$fieldConfig = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
$fieldConfig = $GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config'];
$fieldNameInPageTsConfig = $fieldName;
} else {
// A flex flex form field
$flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
$dataStructureArray = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifierString);
$parts = explode('|', $field);
$fieldConfig = $this->getFlexFieldConfiguration($parts, $dataStructureArray);
// Flexform field name levels are separated with | instead of encapsulation in [];
// reverse this here to be compatible with regular field names.
$field = str_replace('|', '][', $field);
$dataStructure = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
if (empty($flexFormContainerFieldName)) {
// @todo: See if a path in pageTsConfig like "TCEForm.tableName.theContainerFieldName =" is useful and works with other pageTs, too.
$fieldNameInPageTsConfig = $flexFormFieldName;
if (!isset($dataStructure['sheets'][$flexFormSheetName]['ROOT']
['el'][$flexFormFieldName]['TCEforms']['config'])
) {
throw new \RuntimeException(
'Specified path ' . $flexFormFieldName . ' not found in flex form data structure',
1480609491
);
}
$fieldConfig = $dataStructure['sheets'][$flexFormSheetName]['ROOT']
['el'][$flexFormFieldName]['TCEforms']['config'];
} else {
$fieldNameInPageTsConfig = $flexFormContainerFieldName;
if (!isset($dataStructure['sheets'][$flexFormSheetName]['ROOT']
['el'][$flexFormFieldName]