Files
cms-backend/Classes/Controller/FormSelectTreeAjaxController.php
T

229 lines
10 KiB
PHP

<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Backend\Controller;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Attribute\AsController;
use TYPO3\CMS\Backend\Dto\Tree\SelectTreeItem;
use TYPO3\CMS\Backend\Dto\Tree\TreeItem;
use TYPO3\CMS\Backend\Form\FormDataCompiler;
use TYPO3\CMS\Backend\Form\FormDataGroup\TcaSelectTreeAjaxFieldData;
use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
use TYPO3\CMS\Core\Http\JsonResponse;
use TYPO3\CMS\Core\Schema\TcaSchemaFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Backend controller for selectTree ajax operations
*/
#[AsController]
readonly class FormSelectTreeAjaxController
{
public function __construct(
private FormDataCompiler $formDataCompiler,
private FlexFormTools $flexFormTools,
private TcaSchemaFactory $schemaFactory,
) {}
/**
* Returns json representing category tree
*
* @throws \RuntimeException
*/
public function fetchDataAction(ServerRequestInterface $request): ResponseInterface
{
$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.
if (!$this->schemaFactory->has($tableName)) {
throw new \RuntimeException(
'TCA for table ' . $tableName . ' not found',
1479386729
);
}
$schema = $this->schemaFactory->get($tableName);
if (!$schema->hasField($fieldName)) {
throw new \RuntimeException(
'TCA for table ' . $tableName . ' and field ' . $fieldName . ' not found',
1479386990
);
}
// @todo Replace with a mutable schema
$processedTca = $GLOBALS['TCA'][$tableName];
// Force given record type and set showitem to our field only
$recordTypeValue = $request->getQueryParams()['recordTypeValue'];
$processedTca['types'][$recordTypeValue]['showitem'] = $fieldName;
// Unset all columns except our field
$processedTca['columns'] = [
$fieldName => $processedTca['columns'][$fieldName],
];
$dataStructureIdentifier = '';
$flexFormSheetName = '';
$flexFormFieldName = '';
$flexFormContainerIdentifier = '';
$flexFormContainerFieldName = '';
$flexSectionContainerPreparation = [];
if ($processedTca['columns'][$fieldName]['config']['type'] === 'flex') {
if (!empty($request->getQueryParams()['dataStructureIdentifier'])) {
$dataStructureIdentifier = $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'];
$dataStructure = $this->flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier, $schema);
// 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],
],
],
],
],
];
}
} elseif (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 => [
'type' => 'array',
'el' => [
$flexFormContainerFieldName => $dataStructure['sheets'][$flexFormSheetName]['ROOT']
['el'][$flexFormFieldName]
['el'][$flexFormContainerName]
['el'][$flexFormContainerFieldName],
],
],
],
],
],
],
],
],
];
}
$processedTca['columns'][$fieldName]['config']['ds'] = $dataStructure;
$processedTca['columns'][$fieldName]['config']['dataStructureIdentifier'] = $dataStructureIdentifier;
}
$formDataCompilerInput = [
'request' => $request,
'tableName' => $tableName,
'vanillaUid' => (int)$request->getQueryParams()['uid'],
'command' => $request->getQueryParams()['command'],
'processedTca' => $processedTca,
'recordTypeValue' => $recordTypeValue,
'selectTreeCompileItems' => true,
'flexSectionContainerPreparation' => $flexSectionContainerPreparation,
];
if (!empty($request->getQueryParams()['overrideValues'])) {
$formDataCompilerInput['overrideValues'] = json_decode($request->getQueryParams()['overrideValues'], true);
}
if (!empty($request->getQueryParams()['defaultValues'])) {
$formDataCompilerInput['defaultValues'] = json_decode($request->getQueryParams()['defaultValues'], true);
}
$formData = $this->formDataCompiler->compile($formDataCompilerInput, GeneralUtility::makeInstance(TcaSelectTreeAjaxFieldData::class));
if ($formData['processedTca']['columns'][$fieldName]['config']['type'] === 'flex') {
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'];
}
$data = [];
foreach ($treeData ?? [] as $item) {
$treeItem = new SelectTreeItem(
item: new TreeItem(
identifier: (string)$item['identifier'],
parentIdentifier: (string)($item['parentIdentifier'] ?? ''),
recordType: (string)($item['recordType'] ?? ''),
name: (string)($item['name'] ?? ''),
prefix: (string)($item['prefix'] ?? ''),
suffix: (string)($item['suffix'] ?? ''),
tooltip: (string)($item['tooltip'] ?? ''),
depth: (int)($item['depth'] ?? 0),
hasChildren: (bool)($item['hasChildren'] ?? false),
loaded: true,
icon: (string)($item['icon'] ?? ''),
overlayIcon: (string)($item['overlayIcon'] ?? ''),
statusInformation: (array)($item['statusInformation'] ?? []),
labels: (array)($item['labels'] ?? []),
),
checked: (bool)($item['checked'] ?? false),
selectable: (bool)($item['selectable'] ?? false),
);
$data[] = $treeItem;
}
return new JsonResponse($data);
}
}