TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
<?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;
|
||||
|
||||
use TYPO3\CMS\Backend\Form\Utility\FormEngineUtility;
|
||||
use TYPO3\CMS\Core\Schema\SchemaCollection;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Handle inline stack.
|
||||
*
|
||||
* Code related to inline elements need to know their nesting level. This class takes
|
||||
* care of the according handling and can return field prefixes to be used in DOM.
|
||||
*
|
||||
* @internal: This class may change any time or vanish altogether
|
||||
*/
|
||||
readonly class InlineStackProcessor
|
||||
{
|
||||
/**
|
||||
* Get a level from the stack and return the data.
|
||||
* If the $level value is negative, this function works top-down,
|
||||
* if the $level value is positive, this function works bottom-up.
|
||||
* If -1 is given, the "current" - most bottom "stable" item is returned
|
||||
*
|
||||
* @param int $level Which level to return
|
||||
* @return array|null The item of the stack at the requested level, or null if not found
|
||||
*/
|
||||
public function getStructureLevelFromStructure(array $structure, int $level): ?array
|
||||
{
|
||||
$structureCount = count($structure['stable'] ?? []);
|
||||
if ($level < 0) {
|
||||
// A level like -1 requests "the level before last" / "direct parent level"
|
||||
return $structure['stable'][$structureCount + $level] ?? null;
|
||||
}
|
||||
return $structure['stable'][$level] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "unstable" structure item from structure stack.
|
||||
*/
|
||||
public function getUnstableStructureFromStructure(array $structure): array
|
||||
{
|
||||
if (!is_array($structure['unstable'] ?? false)) {
|
||||
throw new \RuntimeException('No unstable inline structure found', 1428582655);
|
||||
}
|
||||
return $structure['unstable'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the DOM object-id of an inline container to an array.
|
||||
* The object-id could look like 'data-bs-parentPageId-tx_mmftest_company-1-employees'.
|
||||
* There are two keys:
|
||||
* - 'stable': Containing full qualified identifiers (table, uid and field)
|
||||
* - 'unstable': Containing partly filled data (e.g. only table and possibly field)
|
||||
*
|
||||
* @param string $domObjectId The DOM object-id
|
||||
* @param SchemaCollection $schemaCollection The TCA schemata collection
|
||||
*/
|
||||
public function getStructureFromString(string $domObjectId, SchemaCollection $schemaCollection): array
|
||||
{
|
||||
$structure = [];
|
||||
$unstable = [];
|
||||
$vector = ['table', 'uid', 'field'];
|
||||
|
||||
// Substitute FlexForm addition and make parsing a bit easier
|
||||
$domObjectId = str_replace('---', ':', $domObjectId);
|
||||
// The starting pattern of an object identifier (e.g. "data-<firstPidValue>-<anything>)
|
||||
$pattern = '/^data-(.+?)-(.+)$/';
|
||||
|
||||
if (preg_match($pattern, $domObjectId, $match)) {
|
||||
$inlineFirstPid = $match[1];
|
||||
$parts = explode('-', $match[2]);
|
||||
$partsCnt = count($parts);
|
||||
for ($i = 0; $i < $partsCnt; $i++) {
|
||||
if ($i > 0 && $i % 3 == 0) {
|
||||
// Store the TCA configuration of the table field in the stack
|
||||
$unstable['config'] = [];
|
||||
if ($schemaCollection->has($unstable['table'])) {
|
||||
$tableSchema = $schemaCollection->get($unstable['table']);
|
||||
if ($tableSchema->hasField($unstable['field'])) {
|
||||
$unstable['config'] = $tableSchema->getField($unstable['field'])->getConfiguration();
|
||||
}
|
||||
}
|
||||
// Fetch TSconfig:
|
||||
$TSconfig = FormEngineUtility::getTSconfigForTableRow($unstable['table'], ['uid' => $unstable['uid'], 'pid' => $inlineFirstPid], $unstable['field']);
|
||||
// Override TCA field config by TSconfig:
|
||||
if (!isset($TSconfig['disabled']) || !$TSconfig['disabled']) {
|
||||
$unstable['config'] = FormEngineUtility::overrideFieldConf($unstable['config'], $TSconfig);
|
||||
}
|
||||
|
||||
// Extract FlexForm from field part (if any)
|
||||
if (str_contains($unstable['field'], ':')) {
|
||||
$fieldParts = GeneralUtility::trimExplode(':', $unstable['field']);
|
||||
$unstable['field'] = array_shift($fieldParts);
|
||||
// FlexForm parts start with data:
|
||||
if (!empty($fieldParts) && $fieldParts[0] === 'data') {
|
||||
$unstable['flexform'] = $fieldParts;
|
||||
}
|
||||
}
|
||||
|
||||
$structure['stable'][] = $unstable;
|
||||
$unstable = [];
|
||||
}
|
||||
$unstable[$vector[$i % 3]] = $parts[$i];
|
||||
}
|
||||
$structure['unstable'] = $unstable;
|
||||
}
|
||||
return $structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects configuration via AJAX calls.
|
||||
* This is used by inline ajax calls that transfer configuration options back to the stack for initialization.
|
||||
*
|
||||
* @param array $config Given config extracted from ajax call
|
||||
* @todo: Review this construct - Why can't the ajax call fetch these data on its own and transfers it to client instead?
|
||||
*/
|
||||
public function addAjaxConfigurationToStructure(array $structure, array $config): array
|
||||
{
|
||||
$lastStableStructureKey = count($structure['stable'] ?? []) - 1;
|
||||
if (empty($config) || $lastStableStructureKey < 0) {
|
||||
return $structure;
|
||||
}
|
||||
$structure['stable'][$lastStableStructureKey]['config'] = $config;
|
||||
return $structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefix for inline form fields
|
||||
*/
|
||||
public function getFormPrefixFromStructure(array $structure): string
|
||||
{
|
||||
$current = $this->getStructureLevelFromStructure($structure, -1);
|
||||
if ($current) {
|
||||
return 'data' . $this->getStructureItemNameFromStructure($structure, $current, 'Disposal_AttributeName');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* DOM object-id for this inline level
|
||||
*
|
||||
* @param int|string $inlineFirstPid Pid of top level inline element storage or "NEW..."
|
||||
*/
|
||||
public function getDomObjectIdPrefixFromStructure(array $structure, int|string $inlineFirstPid): string
|
||||
{
|
||||
$current = $this->getStructureLevelFromStructure($structure, -1);
|
||||
// If there are still more inline levels available
|
||||
if ($current) {
|
||||
return 'data-' . $inlineFirstPid . '-' . $this->getStructurePathFromStructure($structure);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the identifiers of a given depth of level, from the top of the stack to the bottom.
|
||||
* An identifier looks like "<table>-<uid>-<field>".
|
||||
*
|
||||
* @return string The path of identifiers
|
||||
*/
|
||||
private function getStructurePathFromStructure(array $structure): string
|
||||
{
|
||||
$structureLevels = [];
|
||||
$structureCount = count($structure['stable'] ?? []);
|
||||
for ($i = 1; $i <= $structureCount; $i++) {
|
||||
$currentStructure = $this->getStructureLevelFromStructure($structure, -$i) ?: [];
|
||||
$currentItem = $this->getStructureItemNameFromStructure($structure, $currentStructure, 'Disposal_AttributeId');
|
||||
array_unshift($structureLevels, $currentItem);
|
||||
}
|
||||
return implode('-', $structureLevels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a name/id for usage in HTML output of a level of the structure stack to be used in form names.
|
||||
*
|
||||
* @param array $levelData Array of a level of the structure stack (containing the keys table, uid and field)
|
||||
* @param string $disposal How the structure name is used (e.g. as <div id="..."> or <input name="..." />)
|
||||
* @return string The name/id of that level, to be used for HTML output
|
||||
*/
|
||||
private function getStructureItemNameFromStructure(array $structure, array $levelData, string $disposal): string
|
||||
{
|
||||
$parts = [$levelData['table'], $levelData['uid']];
|
||||
if (!empty($levelData['field'])) {
|
||||
$parts[] = $levelData['field'];
|
||||
}
|
||||
if ($disposal === 'Disposal_AttributeName') {
|
||||
// Use in name attributes
|
||||
$parent = $this->getStructureLevelFromStructure($structure, -1);
|
||||
if (!empty($levelData['field']) && !empty($levelData['flexform']) && $parent === $levelData) {
|
||||
$parts[] = implode('][', $levelData['flexform']);
|
||||
}
|
||||
$name = '[' . implode('][', $parts) . ']';
|
||||
} else {
|
||||
// Use in object id attributes
|
||||
$name = implode('-', $parts);
|
||||
if (!empty($levelData['field']) && !empty($levelData['flexform'])) {
|
||||
array_unshift($levelData['flexform'], $name);
|
||||
$name = implode('---', $levelData['flexform']);
|
||||
}
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user