| Current Path : /var/www/surf/TYPO3/vendor/typo3/cms-backend/Classes/Form/ |
| Current File : /var/www/surf/TYPO3/vendor/typo3/cms-backend/Classes/Form/InlineStackProcessor.php |
<?php
/*
* 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\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
*/
class InlineStackProcessor
{
/**
* The structure/hierarchy where working in, e.g. cascading inline tables
*
* @var array
*/
protected $inlineStructure = [];
/**
* One of two possible initialize methods setting a given structure.
*/
public function initializeByGivenStructure(array $structure = [])
{
$this->inlineStructure = $structure;
}
/**
* 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'.
* This initializes $this->inlineStructure - used by AJAX entry points
* 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
*/
public function initializeByParsingDomObjectIdString(string $domObjectId)
{
$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) {
// Load the TCA configuration of the table field and store it in the stack
// @todo: This TCA loading here must fall - config sub-array shouldn't exist at all!
$unstable['config'] = $GLOBALS['TCA'][$unstable['table']]['columns'][$unstable['field']]['config'] ?? [];
// Fetch TSconfig:
// @todo: aaargs ;)
$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;
}
}
$this->inlineStructure['stable'][] = $unstable;
$unstable = [];
}
$unstable[$vector[$i % 3]] = $parts[$i];
}
if (!empty($unstable)) {
$this->inlineStructure['unstable'] = $unstable;
}
}
}
/**
* 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 injectAjaxConfiguration(array $config)
{
$level = $this->calculateStructureLevel(-1);
if (empty($config) || $level === false) {
return;
}
$current = &$this->inlineStructure['stable'][$level];
$current['config'] = $config;
}
/**
* Get current structure stack
*
* @return array Current structure stack
*/
public function getStructure()
{
return $this->inlineStructure;
}
/**
* Add a stable structure to the stack
*/
public function pushStableStructureItem(array $structureItem = [])
{
$this->inlineStructure['stable'][] = $structureItem;
}
/**
* Prefix for inline form fields
*
* @return string
*/
public function getCurrentStructureFormPrefix()
{
$current = $this->getStructureLevel(-1);
$inlineFormName = '';
// If there are still more inline levels available
if ($current !== false) {
$inlineFormName = 'data' . $this->getStructureItemName($current, 'Disposal_AttributeName');
}
return $inlineFormName;
}
/**
* DOM object-id for this inline level
*
* @param int|string $inlineFirstPid Pid of top level inline element storage or "NEW..."
* @return string
*/
public function getCurrentStructureDomObjectIdPrefix($inlineFirstPid)
{
$current = $this->getStructureLevel(-1);
$inlineDomObjectId = '';
// If there are still more inline levels available
if ($current !== false) {
$inlineDomObjectId = 'data-' . $inlineFirstPid . '-' . $this->getStructurePath();
}
return $inlineDomObjectId;
}
/**
* 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.
* Hint: If -1 is given, the "current" - most bottom "stable" item is returned
*
* @param int $level Which level to return
* @return array|false The item of the stack at the requested level, or false if not found.
*/
public function getStructureLevel($level)
{
$level = $this->calculateStructureLevel($level);
if ($level !== false) {
return $this->inlineStructure['stable'][$level];
}
return false;
}
/**
* Get the "unstable" structure item from structure stack.
* This is typically initialized by initializeByParsingDomObjectIdString()
*
* @return array Unstable structure item
* @throws \RuntimeException
*/
public function getUnstableStructure()
{
if (!isset($this->inlineStructure['unstable'])) {
throw new \RuntimeException('No unstable inline structure found', 1428582655);
}
return $this->inlineStructure['unstable'];
}
/**
* Calculates structure level.
*
* @param int $level Which level to return
* @return bool|int
*/
protected function calculateStructureLevel($level)
{
$result = false;
$structureCount = $this->getStructureDepth();
if ($level < 0) {
$level = $structureCount + $level;
}
if ($level >= 0 && $level < $structureCount) {
$result = $level;
}
return $result;
}
/**
* 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>".
*
* @param int $structureDepth How much levels to output, beginning from the top of the stack
* @return string The path of identifiers
*/
protected function getStructurePath($structureDepth = -1)
{
$structureLevels = [];
$structureCount = $this->getStructureDepth();
if ($structureDepth < 0 || $structureDepth > $structureCount) {
$structureDepth = $structureCount;
}
for ($i = 1; $i <= $structureDepth; $i++) {
array_unshift($structureLevels, ($this->getStructureItemName($this->getStructureLevel(-$i) ?: [], 'Disposal_AttributeId') ?: []));
}
return implode('-', $structureLevels);
}
/**
* Get the depth of the stable structure stack.
* (count($this->inlineStructure['stable'])
*
* @return int The depth of the structure stack
*/
public function getStructureDepth()
{
if (!isset($this->inlineStructure['stable']) || !is_array($this->inlineStructure['stable'])) {
return 0;
}
return count($this->inlineStructure['stable']);
}
/**
* 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
*/
protected function getStructureItemName($levelData, $disposal = 'Disposal_AttributeId')
{
$name = null;
if (is_array($levelData)) {
$parts = [$levelData['table'], $levelData['uid']];
if (!empty($levelData['field'])) {
$parts[] = $levelData['field'];
}
// Use in name attributes:
if ($disposal === 'Disposal_AttributeName') {
if (!empty($levelData['field']) && !empty($levelData['flexform']) && $this->getStructureLevel(-1) === $levelData) {
$parts[] = implode('][', $levelData['flexform']);
}
$name = '[' . implode('][', $parts) . ']';
// Use in object id attributes:
} else {
$name = implode('-', $parts);
if (!empty($levelData['field']) && !empty($levelData['flexform'])) {
array_unshift($levelData['flexform'], $name);
$name = implode('---', $levelData['flexform']);
}
}
}
return $name;
}
}