TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
<?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\Form\FormDataProvider;
|
||||
|
||||
use TYPO3\CMS\Backend\Form\FormDataCompiler;
|
||||
use TYPO3\CMS\Backend\Form\FormDataGroup\SiteConfigurationDataGroup;
|
||||
use TYPO3\CMS\Backend\Form\FormDataProviderInterface;
|
||||
use TYPO3\CMS\Backend\Form\InlineStackProcessor;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Exception\SiteNotFoundException;
|
||||
use TYPO3\CMS\Core\Schema\Capability\TcaSchemaCapability;
|
||||
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.
|
||||
*
|
||||
* Handle inline children of 'site'
|
||||
*/
|
||||
class SiteTcaInline extends AbstractDatabaseRecordProvider implements FormDataProviderInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly SiteFinder $siteFinder,
|
||||
private readonly InlineStackProcessor $inlineStackProcessor,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Resolve inline fields
|
||||
*/
|
||||
public function addData(array $result): array
|
||||
{
|
||||
$result = $this->addInlineFirstPid($result);
|
||||
foreach ($result['processedTca']['columns'] as $fieldName => $fieldConfig) {
|
||||
if (!$this->isInlineField($fieldConfig)) {
|
||||
continue;
|
||||
}
|
||||
$childTableName = $fieldConfig['config']['foreign_table'] ?? '';
|
||||
if (!in_array($childTableName, ['site_errorhandling', 'site_route', 'site_base_variant'], true)) {
|
||||
throw new \RuntimeException('Inline relation to other tables not implemented', 1522494737);
|
||||
}
|
||||
$result['processedTca']['columns'][$fieldName]['children'] = [];
|
||||
$result = $this->resolveSiteRelatedChildren($result, $fieldName);
|
||||
if (!empty($result['processedTca']['columns'][$fieldName]['config']['selectorOrUniqueConfiguration'])) {
|
||||
throw new \RuntimeException('selectorOrUniqueConfiguration not implemented in sites module', 1624313533);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is column of type "inline"
|
||||
*/
|
||||
protected function isInlineField(array $fieldConfig): bool
|
||||
{
|
||||
return !empty($fieldConfig['config']['type']) && $fieldConfig['config']['type'] === 'inline';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param array $result Incoming result
|
||||
* @return array Modified result
|
||||
* @todo: Find out when and if this is different from 'effectivePid'
|
||||
*/
|
||||
protected function addInlineFirstPid(array $result): array
|
||||
{
|
||||
if ($result['inlineFirstPid'] === null) {
|
||||
$table = $result['tableName'];
|
||||
$row = $result['databaseRow'];
|
||||
// If the parent is a page, use the uid(!) of the (new?) page as pid for the child records:
|
||||
if ($table === 'pages') {
|
||||
$liveVersionId = BackendUtility::getLiveVersionIdOfRecord('pages', $row['uid']);
|
||||
$pid = $liveVersionId ?? $row['uid'];
|
||||
} elseif (($row['pid'] ?? 0) < 0) {
|
||||
$prevRec = BackendUtility::getRecord($table, (int)abs($row['pid']));
|
||||
$pid = $prevRec['pid'];
|
||||
} else {
|
||||
$pid = $row['pid'] ?? 0;
|
||||
}
|
||||
if (MathUtility::canBeInterpretedAsInteger($pid)) {
|
||||
$pageRecord = BackendUtility::getRecord('pages', (int)$pid);
|
||||
$pageSchema = $result['tcaSchemata']->has('pages') ? $result['tcaSchemata']->get('pages') : null;
|
||||
if ($pageSchema !== null
|
||||
&& $pageSchema->hasCapability(TcaSchemaCapability::Language)
|
||||
&& ($pageRecord[$pageSchema->getCapability(TcaSchemaCapability::Language)->getTranslationOriginPointerField()->getName()] ?? 0) > 0) {
|
||||
$pid = (int)$pageRecord[$pageSchema->getCapability(TcaSchemaCapability::Language)->getTranslationOriginPointerField()->getName()];
|
||||
}
|
||||
} elseif (!str_starts_with($pid, 'NEW')) {
|
||||
throw new \RuntimeException(
|
||||
'inlineFirstPid should either be an integer or a "NEW..." string',
|
||||
1521220141
|
||||
);
|
||||
}
|
||||
$result['inlineFirstPid'] = $pid;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitute the value in databaseRow of this inline field with an array
|
||||
* that contains the databaseRows of currently connected records and some meta information.
|
||||
*
|
||||
* @param array $result Result array
|
||||
* @param string $fieldName Current handle field name
|
||||
* @return array Modified item array
|
||||
*/
|
||||
protected function resolveSiteRelatedChildren(array $result, string $fieldName): array
|
||||
{
|
||||
$connectedUids = [];
|
||||
if ($result['command'] === 'edit') {
|
||||
$siteConfigurationForPageUid = (int)$result['databaseRow']['rootPageId'][0];
|
||||
try {
|
||||
$site = $this->siteFinder->getSiteByRootPageId($siteConfigurationForPageUid);
|
||||
} catch (SiteNotFoundException $e) {
|
||||
$site = null;
|
||||
}
|
||||
$siteConfiguration = $site ? $site->getConfiguration() : [];
|
||||
if (is_array($siteConfiguration[$fieldName] ?? false)) {
|
||||
$connectedUids = array_keys($siteConfiguration[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
$result['databaseRow'][$fieldName] = implode(',', $connectedUids);
|
||||
if ($result['inlineCompileExistingChildren']) {
|
||||
foreach ($connectedUids as $uid) {
|
||||
if (!str_starts_with((string)$uid, 'NEW')) {
|
||||
$compiledChild = $this->compileChild($result, $fieldName, (int)$uid);
|
||||
$result['processedTca']['columns'][$fieldName]['children'][] = $compiledChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a full child record
|
||||
*
|
||||
* @param array $result Result array of parent
|
||||
* @param string $parentFieldName Name of parent field
|
||||
* @param int $childUid Uid of child to compile
|
||||
* @return array Full result array
|
||||
*/
|
||||
protected function compileChild(array $result, string $parentFieldName, int $childUid): array
|
||||
{
|
||||
$parentConfig = $result['processedTca']['columns'][$parentFieldName]['config'];
|
||||
$childTableName = $parentConfig['foreign_table'];
|
||||
|
||||
$inlineTopMostParent = $this->inlineStackProcessor->getStructureLevelFromStructure($result['inlineStructure'], 0);
|
||||
|
||||
$formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class);
|
||||
$formDataCompilerInput = [
|
||||
'request' => $result['request'],
|
||||
'command' => 'edit',
|
||||
'tableName' => $childTableName,
|
||||
'vanillaUid' => $childUid,
|
||||
// Give incoming returnUrl down to children so they generate a returnUrl back to
|
||||
// the originally opening record, also see "originalReturnUrl" in inline container
|
||||
// and FormInlineAjaxController
|
||||
'returnUrl' => $result['returnUrl'],
|
||||
'isInlineChild' => true,
|
||||
'inlineStructure' => $result['inlineStructure'],
|
||||
'inlineExpandCollapseStateArray' => $result['inlineExpandCollapseStateArray'],
|
||||
'inlineFirstPid' => $result['inlineFirstPid'],
|
||||
'inlineParentConfig' => $parentConfig,
|
||||
|
||||
// values of the current parent element
|
||||
// it is always a string either an id or new...
|
||||
'inlineParentUid' => $result['databaseRow']['uid'],
|
||||
'inlineParentTableName' => $result['tableName'],
|
||||
'inlineParentFieldName' => $parentFieldName,
|
||||
|
||||
// values of the top most parent element set on first level and not overridden on following levels
|
||||
'inlineTopMostParentUid' => $result['inlineTopMostParentUid'] ?: ($inlineTopMostParent['uid'] ?? null),
|
||||
'inlineTopMostParentTableName' => $result['inlineTopMostParentTableName'] ?: ($inlineTopMostParent['table'] ?? ''),
|
||||
'inlineTopMostParentFieldName' => $result['inlineTopMostParentFieldName'] ?: ($inlineTopMostParent['field'] ?? ''),
|
||||
|
||||
// pass through schemata as they are immutable once they are set
|
||||
'tcaSchemata' => $result['tcaSchemata'],
|
||||
// pass through fullTca as it is immutable once set
|
||||
'fullTca' => $result['fullTca'],
|
||||
];
|
||||
|
||||
if (($parentConfig['foreign_selector'] ?? false) && ($parentConfig['appearance']['useCombination'] ?? false)) {
|
||||
throw new \RuntimeException('useCombination not implemented in sites module', 1522493097);
|
||||
}
|
||||
return $formDataCompiler->compile($formDataCompilerInput, GeneralUtility::makeInstance(SiteConfigurationDataGroup::class));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user