TYPO3 v15 dev-main snapshot ()

This commit is contained in:
2026-08-10 22:31:24 +02:00
commit aad9daaefd
1506 changed files with 94005 additions and 0 deletions
@@ -0,0 +1,40 @@
<?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\Form\Domain\Configuration\FormDefinition\Converters;
/**
* @internal
*/
abstract class AbstractConverter implements ConverterInterface
{
/**
* @var string
*/
protected $sessionToken;
/**
* @var ConverterDto
*/
protected $converterDto;
public function __construct(ConverterDto $converterDto, string $sessionToken = '')
{
$this->converterDto = $converterDto;
$this->sessionToken = $sessionToken;
}
}
@@ -0,0 +1,94 @@
<?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\Form\Domain\Configuration\FormDefinition\Converters;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Form\Domain\Configuration\ArrayProcessing\ArrayProcessing;
use TYPO3\CMS\Form\Domain\Configuration\ArrayProcessing\ArrayProcessor;
/**
* @internal
*/
class AddHmacDataConverter extends AbstractConverter
{
/**
* Add a new value "_orig_<propertyName>" as a sibling of the property key.
* "_orig_<propertyName>" is an array which contains the property value
* and a hmac hash for the property value.
* "_orig_<propertyName>" will be used to validate the form definition on saving.
* @see \TYPO3\CMS\Form\Domain\Configuration\FormDefinitionValidationService::validateFormDefinitionProperties()
*
* @param mixed $value
*/
public function __invoke(string $key, $value): void
{
$formDefinition = $this->converterDto->getFormDefinition();
$renderablePathParts = explode('.', $key);
array_pop($renderablePathParts);
if (count($renderablePathParts) > 1) {
$renderablePath = implode('.', $renderablePathParts);
$currentFormElement = ArrayUtility::getValueByPath($formDefinition, $renderablePath, '.');
} else {
$currentFormElement = $formDefinition;
}
$propertyCollectionElements = $currentFormElement['finishers'] ?? $currentFormElement['validators'] ?? [];
$propertyCollectionName = $currentFormElement['type'] === 'Form' ? 'finishers' : 'validators';
unset($currentFormElement['renderables'], $currentFormElement['finishers'], $currentFormElement['validators']);
$this->converterDto
->setRenderablePathParts($renderablePathParts)
->setFormElementIdentifier($value);
GeneralUtility::makeInstance(ArrayProcessor::class, $currentFormElement)->forEach(
GeneralUtility::makeInstance(
ArrayProcessing::class,
'addHmacData',
'^.*',
GeneralUtility::makeInstance(
AddHmacDataToFormElementPropertyConverter::class,
$this->converterDto,
$this->sessionToken
)
)
);
$this->converterDto->setPropertyCollectionName($propertyCollectionName);
foreach ($propertyCollectionElements as $propertyCollectionIndex => $propertyCollectionElement) {
$this->converterDto
->setPropertyCollectionIndex((int)$propertyCollectionIndex)
->setPropertyCollectionElementIdentifier($propertyCollectionElement['identifier']);
GeneralUtility::makeInstance(ArrayProcessor::class, $propertyCollectionElement)->forEach(
GeneralUtility::makeInstance(
ArrayProcessing::class,
'addHmacData',
'^(?!(.*\._label|.*\._value)$).*',
GeneralUtility::makeInstance(
AddHmacDataToPropertyCollectionElementConverter::class,
$this->converterDto,
$this->sessionToken
)
)
);
}
}
}
@@ -0,0 +1,51 @@
<?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\Form\Domain\Configuration\FormDefinition\Converters;
use TYPO3\CMS\Core\Crypto\HashService;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* @internal
*/
class AddHmacDataToFormElementPropertyConverter extends AbstractConverter
{
/**
* @param mixed $value
*/
public function __invoke(string $key, $value): void
{
$formDefinition = $this->converterDto->getFormDefinition();
$propertyPathParts = explode('.', $key);
$lastKeySegment = array_pop($propertyPathParts);
$propertyPathParts[] = '_orig_' . $lastKeySegment;
$hashService = GeneralUtility::makeInstance(HashService::class);
$hmacValuePath = implode('.', array_merge($this->converterDto->getRenderablePathParts(), $propertyPathParts));
$hmacValue = [
'value' => $value,
'hmac' => $hashService->hmac(serialize([$this->converterDto->getFormElementIdentifier(), $key, $value]), $this->sessionToken),
];
$formDefinition = ArrayUtility::setValueByPath($formDefinition, $hmacValuePath, $hmacValue, '.');
$this->converterDto->setFormDefinition($formDefinition);
}
}
@@ -0,0 +1,65 @@
<?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\Form\Domain\Configuration\FormDefinition\Converters;
use TYPO3\CMS\Core\Crypto\HashService;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* @internal
*/
class AddHmacDataToPropertyCollectionElementConverter extends AbstractConverter
{
/**
* @param mixed $value
*/
public function __invoke(string $key, $value): void
{
$formDefinition = $this->converterDto->getFormDefinition();
$propertyPathParts = explode('.', $key);
$lastKeySegment = array_pop($propertyPathParts);
$propertyPathParts[] = '_orig_' . $lastKeySegment;
$hmacValuePath = implode('.', array_merge(
$this->converterDto->getRenderablePathParts(),
[$this->converterDto->getPropertyCollectionName(), $this->converterDto->getPropertyCollectionIndex()],
$propertyPathParts
));
$hashService = GeneralUtility::makeInstance(HashService::class);
$hmacValue = [
'value' => $value,
'hmac' => $hashService->hmac(
serialize([
$this->converterDto->getFormElementIdentifier(),
$this->converterDto->getPropertyCollectionName(),
$this->converterDto->getPropertyCollectionElementIdentifier(),
$key,
$value,
]),
$this->sessionToken
),
];
$formDefinition = ArrayUtility::setValueByPath($formDefinition, $hmacValuePath, $hmacValue, '.');
$this->converterDto->setFormDefinition($formDefinition);
}
}
@@ -0,0 +1,125 @@
<?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\Form\Domain\Configuration\FormDefinition\Converters;
/**
* @internal
*/
class ConverterDto
{
/**
* @var array
*/
protected $formDefinition = [];
/**
* @var array
*/
protected $renderablePathParts = [];
/**
* @var string
*/
protected $formElementIdentifier = '';
/**
* @var int
*/
protected $propertyCollectionIndex = 0;
/**
* @var string
*/
protected $propertyCollectionName = '';
/**
* @var string
*/
protected $propertyCollectionElementIdentifier = '';
public function __construct(array $formDefinition)
{
$this->formDefinition = $formDefinition;
}
public function getFormDefinition(): array
{
return $this->formDefinition;
}
public function setFormDefinition(array $formDefinition): ConverterDto
{
$this->formDefinition = $formDefinition;
return $this;
}
public function getRenderablePathParts(): array
{
return $this->renderablePathParts;
}
public function setRenderablePathParts(array $renderablePathParts): ConverterDto
{
$this->renderablePathParts = $renderablePathParts;
return $this;
}
public function getFormElementIdentifier(): string
{
return $this->formElementIdentifier;
}
public function setFormElementIdentifier(string $formElementIdentifier): ConverterDto
{
$this->formElementIdentifier = $formElementIdentifier;
return $this;
}
public function getPropertyCollectionIndex(): int
{
return $this->propertyCollectionIndex;
}
public function setPropertyCollectionIndex(int $propertyCollectionIndex): ConverterDto
{
$this->propertyCollectionIndex = $propertyCollectionIndex;
return $this;
}
public function getPropertyCollectionName(): string
{
return $this->propertyCollectionName;
}
public function setPropertyCollectionName(string $propertyCollectionName): ConverterDto
{
$this->propertyCollectionName = $propertyCollectionName;
return $this;
}
public function getPropertyCollectionElementIdentifier(): string
{
return $this->propertyCollectionElementIdentifier;
}
public function setPropertyCollectionElementIdentifier(string $propertyCollectionElementIdentifier): ConverterDto
{
$this->propertyCollectionElementIdentifier = $propertyCollectionElementIdentifier;
return $this;
}
}
@@ -0,0 +1,31 @@
<?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\Form\Domain\Configuration\FormDefinition\Converters;
/**
* @internal
*/
interface ConverterInterface
{
public function __construct(ConverterDto $converterDto, string $sessionToken = '');
/**
* @param mixed $value
*/
public function __invoke(string $key, $value);
}
@@ -0,0 +1,117 @@
<?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\Form\Domain\Configuration\FormDefinition\Converters;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
/**
* Apply FlexForm finisher option overrides
*
* @internal
*/
class FinisherOptionsFlexFormOverridesConverter
{
/**
* @var FlexFormFinisherOverridesConverterDto
*/
protected $converterDto;
public function __construct(FlexFormFinisherOverridesConverterDto $converterDto)
{
$this->converterDto = $converterDto;
}
/**
* Used for overriding finisher options with flexform settings
* Flexform settings "win": When a setting is set in the form
* definition and in flexform the one in flexform will overwrite the
* one defined in the form definition.
*
* Here we adjust the parsed configuration and apply the overrides.
*
* @param string $_ unused in this context
* @param mixed $__ unused in this context
* @param array $matches the expression matches from the ArrayProcessor - for example matches of ^(.*)\.config\.type$
*/
public function __invoke(string $_, $__, array $matches): void
{
[, $optionKey] = $matches;
$prototypeFinisherDefinition = $this->converterDto->getPrototypeFinisherDefinition();
$finisherDefinition = $this->converterDto->getFinisherDefinition();
$finisherIdentifier = $this->converterDto->getFinisherIdentifier();
$flexFormSheetSettings = $this->converterDto->getFlexFormSheetSettings();
try {
$value = ArrayUtility::getValueByPath(
$flexFormSheetSettings['finishers'][$finisherIdentifier],
$optionKey,
'.'
);
} catch (MissingArrayPathException $exception) {
return;
}
$fieldConfiguration = $prototypeFinisherDefinition['FormEngine']['elements'][$optionKey] ?? [];
if ($fieldConfiguration['section'] ?? false) {
if (!is_array($value) || $value === []) {
// Do not process empty values for sections
return;
}
$processedOptionValue = [];
foreach ($value as $optionListValue) {
$key = $optionListValue[$fieldConfiguration['sectionItemKey']];
$value = $optionListValue[$fieldConfiguration['sectionItemValue']];
$processedOptionValue[$key] = $value;
}
$value = $processedOptionValue;
}
$optionPath = 'options.' . $optionKey;
// Skip additional translation for finisher options that were changed via flexform
if ($this->optionValueHasChanged($finisherDefinition, $optionPath, $value)) {
$finisherDefinition['options']['translation']['propertiesExcludedFromTranslation'][] = $optionKey;
}
$finisherDefinition = ArrayUtility::setValueByPath($finisherDefinition, $optionPath, $value, '.');
$this->converterDto->setFinisherDefinition($finisherDefinition);
}
/**
* Test if finisher option value differs from finisher definition.
*
* Compares the given finisher option value with the corresponding value in the
* finisher definition. Returns `true` if both values are equal, `false` otherwise.
*
* @param array<string, mixed> $finisherDefinition
*/
protected function optionValueHasChanged(array $finisherDefinition, string $optionPath, mixed $value): bool
{
try {
return $value !== ArrayUtility::getValueByPath($finisherDefinition, $optionPath, '.');
} catch (MissingArrayPathException) {
return true;
}
}
}
@@ -0,0 +1,54 @@
<?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\Form\Domain\Configuration\FormDefinition\Converters;
use TYPO3\CMS\Core\Utility\ArrayUtility;
/**
* @internal
*/
class FinisherTranslationLanguageConverter extends AbstractConverter
{
/**
* If "finishers.x.options.translation.language" is empty then set the value to "default" and remove
* the hmac.
*
* @param mixed $value
*/
public function __invoke(string $key, $value): void
{
if (!empty($value)) {
return;
}
$formDefinition = $this->converterDto->getFormDefinition();
$formDefinition = ArrayUtility::setValueByPath($formDefinition, $key, 'default', '.');
$hmacPropertyPathParts = explode('.', $key);
$lastKeySegment = array_pop($hmacPropertyPathParts);
$hmacPropertyPathParts[] = '_orig_' . $lastKeySegment;
$hmacValuePath = implode('.', $hmacPropertyPathParts);
if (ArrayUtility::isValidPath($formDefinition, $hmacValuePath, '.')) {
$formDefinition = ArrayUtility::removeByPath($formDefinition, $hmacValuePath, '.');
}
$this->converterDto->setFormDefinition($formDefinition);
}
}
@@ -0,0 +1,83 @@
<?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\Form\Domain\Configuration\FormDefinition\Converters;
/**
* @internal
*/
class FlexFormFinisherOverridesConverterDto
{
/**
* @var array
*/
protected $prototypeFinisherDefinition = [];
/**
* @var array
*/
protected $finisherDefinition = [];
/**
* @var string
*/
protected $finisherIdentifier = '';
/**
* @var array
*/
protected $flexFormSheetSettings = [];
public function __construct(
array $prototypeFinisherDefinition,
array $finisherDefinition,
string $finisherIdentifier,
array $flexFormSheetSettings
) {
$this->prototypeFinisherDefinition = $prototypeFinisherDefinition;
$this->finisherDefinition = $finisherDefinition;
$this->finisherIdentifier = $finisherIdentifier;
$this->flexFormSheetSettings = $flexFormSheetSettings;
}
public function getPrototypeFinisherDefinition(): array
{
return $this->prototypeFinisherDefinition;
}
public function getFinisherDefinition(): array
{
return $this->finisherDefinition;
}
public function setFinisherDefinition(array $finisherDefinition): FlexFormFinisherOverridesConverterDto
{
$this->finisherDefinition = $finisherDefinition;
return $this;
}
public function getFinisherIdentifier(): string
{
return $this->finisherIdentifier;
}
public function getFlexFormSheetSettings(): array
{
return $this->flexFormSheetSettings;
}
}
@@ -0,0 +1,43 @@
<?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\Form\Domain\Configuration\FormDefinition\Converters;
use TYPO3\CMS\Core\Utility\ArrayUtility;
/**
* @internal
*/
class RemoveHmacDataConverter extends AbstractConverter
{
/**
* Remove the hmac data ("_orig_<propertyName>") for the corresponding property.
*
* @param mixed $value
*/
public function __invoke(string $key, $value): void
{
$formDefinition = $this->converterDto->getFormDefinition();
$propertyPathParts = explode('.', $key);
array_pop($propertyPathParts);
$propertyPath = implode('.', $propertyPathParts);
$formDefinition = ArrayUtility::removeByPath($formDefinition, $propertyPath, '.');
$this->converterDto->setFormDefinition($formDefinition);
}
}