677 lines
35 KiB
PHP
677 lines
35 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\Form\Domain\Configuration;
|
|
|
|
use Psr\EventDispatcher\EventDispatcherInterface;
|
|
use Psr\Http\Message\ServerRequestInterface;
|
|
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
|
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
|
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
|
|
use TYPO3\CMS\Core\Http\ApplicationType;
|
|
use TYPO3\CMS\Core\Utility\ArrayUtility;
|
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
|
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface as ExtbaseConfigurationManagerInterface;
|
|
use TYPO3\CMS\Form\Domain\Configuration\ArrayProcessing\ArrayProcessing;
|
|
use TYPO3\CMS\Form\Domain\Configuration\ArrayProcessing\ArrayProcessor;
|
|
use TYPO3\CMS\Form\Domain\Configuration\Exception\PropertyException;
|
|
use TYPO3\CMS\Form\Domain\Configuration\Exception\PrototypeNotFoundException;
|
|
use TYPO3\CMS\Form\Domain\Configuration\FormDefinition\Validators\ValidationDto;
|
|
use TYPO3\CMS\Form\Domain\Configuration\FrameworkConfiguration\Extractors\AdditionalElementPropertyPathsExtractor;
|
|
use TYPO3\CMS\Form\Domain\Configuration\FrameworkConfiguration\Extractors\ExtractorDto;
|
|
use TYPO3\CMS\Form\Domain\Configuration\FrameworkConfiguration\Extractors\FormElement\IsCreatableFormElementExtractor;
|
|
use TYPO3\CMS\Form\Domain\Configuration\FrameworkConfiguration\Extractors\FormElement\MultiValuePropertiesExtractor;
|
|
use TYPO3\CMS\Form\Domain\Configuration\FrameworkConfiguration\Extractors\FormElement\PredefinedDefaultsExtractor;
|
|
use TYPO3\CMS\Form\Domain\Configuration\FrameworkConfiguration\Extractors\FormElement\PropertyPathsExtractor;
|
|
use TYPO3\CMS\Form\Domain\Configuration\FrameworkConfiguration\Extractors\FormElement\SelectOptionsExtractor;
|
|
use TYPO3\CMS\Form\Domain\Configuration\FrameworkConfiguration\Extractors\PropertyCollectionElement\IsCreatablePropertyCollectionElementExtractor;
|
|
use TYPO3\CMS\Form\Domain\Configuration\FrameworkConfiguration\Extractors\PropertyCollectionElement\MultiValuePropertiesExtractor as CollectionMultiValuePropertiesExtractor;
|
|
use TYPO3\CMS\Form\Domain\Configuration\FrameworkConfiguration\Extractors\PropertyCollectionElement\PredefinedDefaultsExtractor as CollectionPredefinedDefaultsExtractor;
|
|
use TYPO3\CMS\Form\Domain\Configuration\FrameworkConfiguration\Extractors\PropertyCollectionElement\PropertyPathsExtractor as CollectionPropertyPathsExtractor;
|
|
use TYPO3\CMS\Form\Domain\Configuration\FrameworkConfiguration\Extractors\PropertyCollectionElement\SelectOptionsExtractor as CollectionSelectOptionsExtractor;
|
|
use TYPO3\CMS\Form\Event\AfterFormDefinitionValidationConfigurationIsBuiltEvent;
|
|
use TYPO3\CMS\Form\Mvc\Configuration\ConfigurationManagerInterface as ExtFormConfigurationManagerInterface;
|
|
use TYPO3\CMS\Form\Service\TranslationService;
|
|
|
|
/**
|
|
* Helper for configuration settings
|
|
* Scope: frontend / backend
|
|
*
|
|
* @todo: Get rid of ConfigurationManagerInterface by handing over $formSettings to
|
|
* methods instead to make the indirect dependency to Request explicit in consuming classes.
|
|
* @todo: Declare readonly when ConfigurationService is no longer injected lazy, or wait
|
|
* for PHP 8.3 minimum for symfony to allow both lazy and readonly dependencies.
|
|
*/
|
|
#[Autoconfigure(public: true)]
|
|
class ConfigurationService
|
|
{
|
|
public function __construct(
|
|
protected ExtbaseConfigurationManagerInterface $extbaseConfigurationManager,
|
|
protected ExtFormConfigurationManagerInterface $extFormConfigurationManager,
|
|
protected TranslationService $translationService,
|
|
#[Autowire(service: 'cache.assets')]
|
|
protected FrontendInterface $assetsCache,
|
|
#[Autowire(service: 'cache.runtime')]
|
|
protected FrontendInterface $runtimeCache,
|
|
protected EventDispatcherInterface $eventDispatcher,
|
|
) {}
|
|
|
|
/**
|
|
* Get the prototype configuration
|
|
*
|
|
* @param string $prototypeName name of the prototype to get the configuration for
|
|
* @return array the prototype configuration
|
|
* @throws PrototypeNotFoundException if prototype with the name $prototypeName was not found
|
|
*/
|
|
public function getPrototypeConfiguration(string $prototypeName): array
|
|
{
|
|
$formSettings = $this->getFormSettings();
|
|
if (!isset($formSettings['prototypes'][$prototypeName])) {
|
|
throw new PrototypeNotFoundException(sprintf('The Prototype "%s" was not found.', $prototypeName), 1475924277);
|
|
}
|
|
return $formSettings['prototypes'][$prototypeName];
|
|
}
|
|
|
|
/**
|
|
* Return all prototype names which are defined within "formManager.selectablePrototypesConfiguration.*.identifier"
|
|
*
|
|
* @internal
|
|
*/
|
|
public function getSelectablePrototypeNamesDefinedInFormEditorSetup(): array
|
|
{
|
|
$formSettings = $this->getFormSettings();
|
|
$returnValue = GeneralUtility::makeInstance(
|
|
ArrayProcessor::class,
|
|
$formSettings['formManager']['selectablePrototypesConfiguration'] ?? []
|
|
)->forEach(
|
|
GeneralUtility::makeInstance(
|
|
ArrayProcessing::class,
|
|
'selectablePrototypeNames',
|
|
'^([\d]+)\.identifier$',
|
|
static function ($_, $value) {
|
|
return $value;
|
|
}
|
|
)
|
|
);
|
|
return array_values($returnValue['selectablePrototypeNames'] ?? []);
|
|
}
|
|
|
|
/**
|
|
* Check if a form element property is defined in the form setup.
|
|
* If a form element property is defined in the form setup then it
|
|
* means that the form element property can be written by the form editor.
|
|
* A form element property can be written if the property path is defined within
|
|
* the following form editor properties:
|
|
* * formElementsDefinition.<formElementType>.formEditor.editors.<index>.propertyPath
|
|
* * formElementsDefinition.<formElementType>.formEditor.editors.<index>.*.propertyPath
|
|
* * formElementsDefinition.<formElementType>.formEditor.editors.<index>.additionalElementPropertyPaths
|
|
* * formElementsDefinition.<formElementType>.formEditor.propertyCollections.<finishers|validators>.<index>.editors.<index>.additionalElementPropertyPaths
|
|
* If a form editor property "templateName" is
|
|
* "Inspector-PropertyGridEditor" or "Inspector-MultiSelectEditor" or "Inspector-ValidationErrorMessageEditor"
|
|
* it means that the form editor property "propertyPath" is interpreted as a so called "multiValueProperty".
|
|
* A "multiValueProperty" can contain any subproperties relative to the value from "propertyPath" which are valid.
|
|
* If "formElementsDefinition.<formElementType>.formEditor.editors.<index>.templateName = Inspector-PropertyGridEditor"
|
|
* and
|
|
* "formElementsDefinition.<formElementType>.formEditor.editors.<index>.propertyPath = options.xxx"
|
|
* then (for example) "options.xxx.yyy" is a valid property path to write.
|
|
* If you use a custom form editor "inspector editor" implementation which does not define the writable
|
|
* property paths by one of the above described inspector editor properties (e.g "propertyPath") within
|
|
* the form setup, you must provide the writable property paths via the
|
|
* AfterFormDefinitionValidationConfigurationIsBuiltEvent PSR-14 event.
|
|
*
|
|
* @internal
|
|
*/
|
|
public function isFormElementPropertyDefinedInFormEditorSetup(ValidationDto $dto): bool
|
|
{
|
|
$formDefinitionValidationConfiguration = $this->buildFormDefinitionValidationConfigurationFromFormEditorSetup(
|
|
$dto->getPrototypeName()
|
|
);
|
|
$subConfig = $formDefinitionValidationConfiguration['formElements'][$dto->getFormElementType()] ?? [];
|
|
return $this->isPropertyDefinedInFormEditorSetup($dto->getPropertyPath(), $subConfig);
|
|
}
|
|
|
|
/**
|
|
* Check if a form elements finisher|validator property is defined in the form setup.
|
|
* If a form elements finisher|validator property is defined in the form setup then it
|
|
* means that the form elements finisher|validator property can be written by the form editor.
|
|
* A form elements finisher|validator property can be written if the property path is defined within
|
|
* the following form editor properties:
|
|
* * formElementsDefinition.<formElementType>.formEditor.propertyCollections.<finishers|validators>.<index>.editors.<index>.propertyPath
|
|
* * formElementsDefinition.<formElementType>.formEditor.propertyCollections.<finishers|validators>.<index>.editors.<index>.*.propertyPath
|
|
* If a form elements finisher|validator property "templateName" is
|
|
* "Inspector-PropertyGridEditor" or "Inspector-MultiSelectEditor" or "Inspector-ValidationErrorMessageEditor"
|
|
* it means that the form editor property "propertyPath" is interpreted as a so called "multiValueProperty".
|
|
* A "multiValueProperty" can contain any subproperties relative to the value from "propertyPath" which are valid.
|
|
* If "formElementsDefinition.<formElementType>.formEditor.propertyCollections.<finishers|validators>.<index>.editors.<index>.templateName = Inspector-PropertyGridEditor"
|
|
* and
|
|
* "formElementsDefinition.<formElementType>.formEditor.propertyCollections.<finishers|validators>.<index>.editors.<index>.propertyPath = options.xxx"
|
|
* that (for example) "options.xxx.yyy" is a valid property path to write.
|
|
* If you use a custom form elements finisher|validator editor implementation which does not define the writable
|
|
* property paths by one of the above described inspector editor properties (e.g "propertyPath") within
|
|
* the form setup, you must provide the writable property paths via the
|
|
* AfterFormDefinitionValidationConfigurationIsBuiltEvent PSR-14 event.
|
|
*
|
|
* @internal
|
|
*/
|
|
public function isPropertyCollectionPropertyDefinedInFormEditorSetup(ValidationDto $dto): bool
|
|
{
|
|
$formDefinitionValidationConfiguration = $this->buildFormDefinitionValidationConfigurationFromFormEditorSetup(
|
|
$dto->getPrototypeName()
|
|
);
|
|
$subConfig = $formDefinitionValidationConfiguration['formElements'][$dto->getFormElementType()]['collections'][$dto->getPropertyCollectionName()][$dto->getPropertyCollectionElementIdentifier()] ?? [];
|
|
return $this->isPropertyDefinedInFormEditorSetup($dto->getPropertyPath(), $subConfig);
|
|
}
|
|
|
|
/**
|
|
* If a form element editor has a property called "selectOptions"
|
|
* (e.g. editors with templateName "Inspector-SingleSelectEditor" or "Inspector-MultiSelectEditor")
|
|
* then only the defined values within the selectOptions are allowed to be written
|
|
* by the form editor.
|
|
*
|
|
* @internal
|
|
*/
|
|
public function formElementPropertyHasLimitedAllowedValuesDefinedWithinFormEditorSetup(
|
|
ValidationDto $dto
|
|
): bool {
|
|
$formDefinitionValidationConfiguration = $this->buildFormDefinitionValidationConfigurationFromFormEditorSetup(
|
|
$dto->getPrototypeName()
|
|
);
|
|
$propertyPath = $this->getBasePropertyPathFromMultiValueFormElementProperty($dto);
|
|
return isset(
|
|
$formDefinitionValidationConfiguration['formElements'][$dto->getFormElementType()]['selectOptions'][$propertyPath]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the "selectOptions" value for a form element property from the form setup.
|
|
*
|
|
* @throws PropertyException
|
|
* @internal
|
|
*/
|
|
public function getAllowedValuesForFormElementPropertyFromFormEditorSetup(
|
|
ValidationDto $dto,
|
|
bool $translated = true
|
|
): array {
|
|
if (!$this->formElementPropertyHasLimitedAllowedValuesDefinedWithinFormEditorSetup($dto)) {
|
|
throw new PropertyException(
|
|
sprintf(
|
|
'No selectOptions found for form element type "%s" and property path "%s"',
|
|
$dto->getFormElementType(),
|
|
$dto->getPropertyPath()
|
|
),
|
|
1614264312
|
|
);
|
|
}
|
|
$formDefinitionValidationConfiguration = $this->buildFormDefinitionValidationConfigurationFromFormEditorSetup(
|
|
$dto->getPrototypeName()
|
|
);
|
|
$property = $translated ? 'selectOptions' : 'untranslatedSelectOptions';
|
|
$propertyPath = $this->getBasePropertyPathFromMultiValueFormElementProperty($dto);
|
|
return $formDefinitionValidationConfiguration['formElements'][$dto->getFormElementType()][$property][$propertyPath];
|
|
}
|
|
|
|
/**
|
|
* If a form elements finisher|validator editor has a property called "selectOptions"
|
|
* (e.g. editors with templateName "Inspector-SingleSelectEditor" or "Inspector-MultiSelectEditor")
|
|
* then only the defined values within the selectOptions are allowed to be written
|
|
* by the form editor.
|
|
*
|
|
* @internal
|
|
*/
|
|
public function propertyCollectionPropertyHasLimitedAllowedValuesDefinedWithinFormEditorSetup(
|
|
ValidationDto $dto
|
|
): bool {
|
|
$formDefinitionValidationConfiguration = $this->buildFormDefinitionValidationConfigurationFromFormEditorSetup(
|
|
$dto->getPrototypeName()
|
|
);
|
|
$propertyPath = $this->getBasePropertyPathFromMultiValuePropertyCollectionElement($dto);
|
|
return isset(
|
|
$formDefinitionValidationConfiguration['collections'][$dto->getPropertyCollectionName()][$dto->getPropertyCollectionElementIdentifier()]['selectOptions'][$propertyPath]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the "selectOptions" value for a form elements finisher|validator property from the form setup.
|
|
*
|
|
* @throws PropertyException
|
|
* @internal
|
|
*/
|
|
public function getAllowedValuesForPropertyCollectionPropertyFromFormEditorSetup(
|
|
ValidationDto $dto,
|
|
bool $translated = true
|
|
): array {
|
|
if (!$this->propertyCollectionPropertyHasLimitedAllowedValuesDefinedWithinFormEditorSetup($dto)) {
|
|
throw new PropertyException(
|
|
sprintf(
|
|
'No selectOptions found for property collection "%s" and identifier "%s" and property path "%s"',
|
|
$dto->getPropertyCollectionName(),
|
|
$dto->getPropertyCollectionElementIdentifier(),
|
|
$dto->getPropertyPath()
|
|
),
|
|
1614264313
|
|
);
|
|
}
|
|
$formDefinitionValidationConfiguration = $this->buildFormDefinitionValidationConfigurationFromFormEditorSetup(
|
|
$dto->getPrototypeName()
|
|
);
|
|
$property = $translated ? 'selectOptions' : 'untranslatedSelectOptions';
|
|
$propertyPath = $this->getBasePropertyPathFromMultiValuePropertyCollectionElement($dto);
|
|
return $formDefinitionValidationConfiguration['collections'][$dto->getPropertyCollectionName()][$dto->getPropertyCollectionElementIdentifier()][$property][$propertyPath];
|
|
}
|
|
|
|
protected function getBasePropertyPathFromMultiValueFormElementProperty(ValidationDto $dto): string
|
|
{
|
|
$formDefinitionValidationConfiguration = $this->buildFormDefinitionValidationConfigurationFromFormEditorSetup(
|
|
$dto->getPrototypeName()
|
|
);
|
|
$propertyPath = $dto->getPropertyPath();
|
|
$multiValueProperties = $formDefinitionValidationConfiguration['formElements'][$dto->getFormElementType()]['multiValueProperties'] ?? [];
|
|
foreach ($multiValueProperties as $multiValueProperty) {
|
|
if (str_starts_with($propertyPath, $multiValueProperty)) {
|
|
$propertyPath = $multiValueProperty;
|
|
}
|
|
}
|
|
return $propertyPath;
|
|
}
|
|
|
|
protected function getBasePropertyPathFromMultiValuePropertyCollectionElement(ValidationDto $dto): string
|
|
{
|
|
$formDefinitionValidationConfiguration = $this->buildFormDefinitionValidationConfigurationFromFormEditorSetup(
|
|
$dto->getPrototypeName()
|
|
);
|
|
$propertyPath = $dto->getPropertyPath();
|
|
$multiValueProperties = $formDefinitionValidationConfiguration['collections'][$dto->getPropertyCollectionName()][$dto->getPropertyCollectionElementIdentifier()]['multiValueProperties'] ?? [];
|
|
foreach ($multiValueProperties as $multiValueProperty) {
|
|
if (str_starts_with($propertyPath, $multiValueProperty)) {
|
|
$propertyPath = $multiValueProperty;
|
|
}
|
|
}
|
|
return $propertyPath;
|
|
}
|
|
|
|
/**
|
|
* Check if a form element property is defined in "predefinedDefaults" in the form setup.
|
|
* If a form element property is defined in the "predefinedDefaults" in the form setup then it
|
|
* means that the form element property can be written by the form editor.
|
|
* A form element default property is defined within the following form editor properties:
|
|
* * formElementsDefinition.<formElementType>.formEditor.predefinedDefaults.<propertyPath> = "default value"
|
|
*
|
|
* @internal
|
|
*/
|
|
public function isFormElementPropertyDefinedInPredefinedDefaultsInFormEditorSetup(ValidationDto $dto): bool
|
|
{
|
|
$formDefinitionValidationConfiguration = $this->buildFormDefinitionValidationConfigurationFromFormEditorSetup(
|
|
$dto->getPrototypeName()
|
|
);
|
|
return isset(
|
|
$formDefinitionValidationConfiguration['formElements'][$dto->getFormElementType()]['predefinedDefaults'][$dto->getPropertyPath()]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the "predefinedDefaults" value for a form element property from the form setup.
|
|
* A form element default property is defined within the following form editor properties:
|
|
* * formElementsDefinition.<formElementType>.formEditor.predefinedDefaults.<propertyPath> = "default value"
|
|
*
|
|
* @throws PropertyException
|
|
* @internal
|
|
*/
|
|
public function getFormElementPredefinedDefaultValueFromFormEditorSetup(ValidationDto $dto, bool $translated = true): mixed
|
|
{
|
|
if (!$this->isFormElementPropertyDefinedInPredefinedDefaultsInFormEditorSetup($dto)) {
|
|
throw new PropertyException(
|
|
sprintf(
|
|
'No predefinedDefaults found for form element type "%s" and property path "%s"',
|
|
$dto->getFormElementType(),
|
|
$dto->getPropertyPath()
|
|
),
|
|
1528578401
|
|
);
|
|
}
|
|
$formDefinitionValidationConfiguration = $this->buildFormDefinitionValidationConfigurationFromFormEditorSetup(
|
|
$dto->getPrototypeName()
|
|
);
|
|
$property = $translated ? 'predefinedDefaults' : 'untranslatedPredefinedDefaults';
|
|
return $formDefinitionValidationConfiguration['formElements'][$dto->getFormElementType()][$property][$dto->getPropertyPath()];
|
|
}
|
|
|
|
/**
|
|
* Check if a form elements finisher|validator property is defined in "predefinedDefaults" in the form setup.
|
|
* If a form elements finisher|validator property is defined in "predefinedDefaults" in the form setup then it
|
|
* means that the form elements finisher|validator property can be written by the form editor.
|
|
* A form elements finisher|validator default property is defined within the following form editor properties:
|
|
* * <validatorsDefinition|finishersDefinition>.<index>.formEditor.predefinedDefaults.<propertyPath> = "default value"
|
|
*
|
|
* @internal
|
|
*/
|
|
public function isPropertyCollectionPropertyDefinedInPredefinedDefaultsInFormEditorSetup(ValidationDto $dto): bool
|
|
{
|
|
$formDefinitionValidationConfiguration = $this->buildFormDefinitionValidationConfigurationFromFormEditorSetup(
|
|
$dto->getPrototypeName()
|
|
);
|
|
return isset(
|
|
$formDefinitionValidationConfiguration['collections'][$dto->getPropertyCollectionName()][$dto->getPropertyCollectionElementIdentifier()]['predefinedDefaults'][$dto->getPropertyPath()]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the "predefinedDefaults" value for a form elements finisher|validator property from the form setup.
|
|
* A form elements finisher|validator default property is defined within the following form editor properties:
|
|
* * <validatorsDefinition|finishersDefinition>.<index>.formEditor.predefinedDefaults.<propertyPath> = "default value"
|
|
*
|
|
* @throws PropertyException
|
|
* @internal
|
|
*/
|
|
public function getPropertyCollectionPredefinedDefaultValueFromFormEditorSetup(ValidationDto $dto, bool $translated = true): mixed
|
|
{
|
|
if (!$this->isPropertyCollectionPropertyDefinedInPredefinedDefaultsInFormEditorSetup($dto)) {
|
|
throw new PropertyException(
|
|
sprintf(
|
|
'No predefinedDefaults found for property collection "%s" and identifier "%s" and property path "%s"',
|
|
$dto->getPropertyCollectionName(),
|
|
$dto->getPropertyCollectionElementIdentifier(),
|
|
$dto->getPropertyPath()
|
|
),
|
|
1528578402
|
|
);
|
|
}
|
|
$formDefinitionValidationConfiguration = $this->buildFormDefinitionValidationConfigurationFromFormEditorSetup(
|
|
$dto->getPrototypeName()
|
|
);
|
|
$property = $translated ? 'predefinedDefaults' : 'untranslatedPredefinedDefaults';
|
|
return $formDefinitionValidationConfiguration['collections'][$dto->getPropertyCollectionName()][$dto->getPropertyCollectionElementIdentifier()][$property][$dto->getPropertyPath()];
|
|
}
|
|
|
|
/**
|
|
* Check if the form element is creatable through the form editor.
|
|
* A form element is creatable if the following properties are set:
|
|
* * formElementsDefinition.<formElementType>.formEditor.group
|
|
* * formElementsDefinition.<formElementType>.formEditor.groupSorting
|
|
* And the value from "formElementsDefinition.<formElementType>.formEditor.group" is
|
|
* one of the keys within "formEditor.formElementGroups"
|
|
*
|
|
* @internal
|
|
*/
|
|
public function isFormElementTypeCreatableByFormEditor(ValidationDto $dto): bool
|
|
{
|
|
if ($dto->getFormElementType() === 'Form') {
|
|
return true;
|
|
}
|
|
$formDefinitionValidationConfiguration = $this->buildFormDefinitionValidationConfigurationFromFormEditorSetup(
|
|
$dto->getPrototypeName()
|
|
);
|
|
return $formDefinitionValidationConfiguration['formElements'][$dto->getFormElementType()]['creatable'] ?? false;
|
|
}
|
|
|
|
/**
|
|
* Check if the form elements finisher|validator is creatable through the form editor.
|
|
* A form elements finisher|validator is creatable if the following conditions are true:
|
|
* "formElementsDefinition.<formElementType>.formEditor.editors.<index>.templateName = Inspector-FinishersEditor"
|
|
* or
|
|
* "formElementsDefinition.<formElementType>.formEditor.editors.<index>.templateName = Inspector-ValidatorsEditor"
|
|
* and
|
|
* "formElementsDefinition.<formElementType>.formEditor.editors.<index>.selectOptions.<index>.value = <finisherIdentifier|validatorIdentifier>"
|
|
*
|
|
* @internal
|
|
*/
|
|
public function isPropertyCollectionElementIdentifierCreatableByFormEditor(ValidationDto $dto): bool
|
|
{
|
|
$formDefinitionValidationConfiguration = $this->buildFormDefinitionValidationConfigurationFromFormEditorSetup(
|
|
$dto->getPrototypeName()
|
|
);
|
|
return $formDefinitionValidationConfiguration['formElements'][$dto->getFormElementType()]['collections'][$dto->getPropertyCollectionName()][$dto->getPropertyCollectionElementIdentifier()]['creatable'] ?? false;
|
|
}
|
|
|
|
/**
|
|
* Check if the form elements type is defined within the form setup.
|
|
*
|
|
* @internal
|
|
*/
|
|
public function isFormElementTypeDefinedInFormSetup(ValidationDto $dto): bool
|
|
{
|
|
$prototypeConfiguration = $this->getPrototypeConfiguration($dto->getPrototypeName());
|
|
return ArrayUtility::isValidPath(
|
|
$prototypeConfiguration,
|
|
'formElementsDefinition.' . $dto->getFormElementType(),
|
|
'.'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
public function getAllBackendTranslationsForTranslationKeys(array $keys, string $prototypeName): array
|
|
{
|
|
$translations = [];
|
|
foreach ($keys as $key) {
|
|
if (!is_string($key)) {
|
|
continue;
|
|
}
|
|
$translations[$key] = $this->getAllBackendTranslationsForTranslationKey($key, $prototypeName);
|
|
}
|
|
return $translations;
|
|
}
|
|
|
|
public function getAllBackendTranslationsForTranslationKey(string $key, string $prototypeName): array
|
|
{
|
|
$prototypeConfiguration = $this->getPrototypeConfiguration($prototypeName);
|
|
return $this->translationService->translateToAllBackendLanguages(
|
|
$key,
|
|
[],
|
|
$prototypeConfiguration['formEditor']['translationFiles'] ?? []
|
|
);
|
|
}
|
|
|
|
protected function getFormSettings(): array
|
|
{
|
|
// @todo: This is needed for extFormConfigurationManager to apply stdWrap on TS configuration.
|
|
// Find a way to get rid of this.
|
|
$isFrontend = false;
|
|
$request = $GLOBALS['TYPO3_REQUEST'] ?? null;
|
|
if ($request instanceof ServerRequestInterface) {
|
|
$isFrontend = ApplicationType::fromRequest($request)->isFrontend();
|
|
}
|
|
// @todo: Note this code relies on the fact that the request has been set to ExtbaseConfigurationManagerInterface already.
|
|
$typoScriptSettings = $this->extbaseConfigurationManager->getConfiguration(ExtbaseConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS, 'form');
|
|
return $this->extFormConfigurationManager->getYamlConfiguration($typoScriptSettings, $isFrontend, $isFrontend ? $request : null);
|
|
}
|
|
|
|
/**
|
|
* Collect all the form editor configurations which are needed to check if a
|
|
* form definition property can be written or not.
|
|
*/
|
|
protected function buildFormDefinitionValidationConfigurationFromFormEditorSetup(string $prototypeName): array
|
|
{
|
|
$cacheKey = implode('_', ['buildFormDefinitionValidationConfigurationFromFormEditorSetup', $prototypeName]);
|
|
$configuration = $this->getCacheEntry($cacheKey);
|
|
if ($configuration === null) {
|
|
$prototypeConfiguration = $this->getPrototypeConfiguration($prototypeName);
|
|
$extractorDto = GeneralUtility::makeInstance(ExtractorDto::class, $prototypeConfiguration);
|
|
GeneralUtility::makeInstance(ArrayProcessor::class, $prototypeConfiguration)->forEach(
|
|
GeneralUtility::makeInstance(
|
|
ArrayProcessing::class,
|
|
'formElementPropertyPaths',
|
|
'^formElementsDefinition\.(.*)\.formEditor\.editors\.([\d]+)\.(propertyPath|.*\.propertyPath)$',
|
|
GeneralUtility::makeInstance(PropertyPathsExtractor::class, $extractorDto)
|
|
),
|
|
GeneralUtility::makeInstance(
|
|
ArrayProcessing::class,
|
|
'formElementAdditionalElementPropertyPaths',
|
|
'^formElementsDefinition\.(.*)\.formEditor\.editors\.([\d]+)\.additionalElementPropertyPaths\.([\d]+)',
|
|
GeneralUtility::makeInstance(AdditionalElementPropertyPathsExtractor::class, $extractorDto)
|
|
),
|
|
GeneralUtility::makeInstance(
|
|
ArrayProcessing::class,
|
|
'formElementRelativeMultiValueProperties',
|
|
'^formElementsDefinition\.(.*)\.formEditor\.editors\.([\d]+)\.templateName$',
|
|
GeneralUtility::makeInstance(MultiValuePropertiesExtractor::class, $extractorDto)
|
|
),
|
|
GeneralUtility::makeInstance(
|
|
ArrayProcessing::class,
|
|
'formElementSelectOptions',
|
|
'^formElementsDefinition\.(.*)\.formEditor\.editors\.([\d]+)\.selectOptions\.([\d]+)\.value$',
|
|
GeneralUtility::makeInstance(SelectOptionsExtractor::class, $extractorDto)
|
|
),
|
|
GeneralUtility::makeInstance(
|
|
ArrayProcessing::class,
|
|
'formElementPredefinedDefaults',
|
|
'^formElementsDefinition\.(.*)\.formEditor\.predefinedDefaults\.(.+)$',
|
|
GeneralUtility::makeInstance(PredefinedDefaultsExtractor::class, $extractorDto)
|
|
),
|
|
GeneralUtility::makeInstance(
|
|
ArrayProcessing::class,
|
|
'formElementCreatable',
|
|
'^formElementsDefinition\.(.*)\.formEditor.group$',
|
|
GeneralUtility::makeInstance(IsCreatableFormElementExtractor::class, $extractorDto)
|
|
),
|
|
GeneralUtility::makeInstance(
|
|
ArrayProcessing::class,
|
|
'propertyCollectionCreatable',
|
|
'^formElementsDefinition\.(.*)\.formEditor\.editors\.([\d]+)\.templateName$',
|
|
GeneralUtility::makeInstance(IsCreatablePropertyCollectionElementExtractor::class, $extractorDto)
|
|
),
|
|
GeneralUtility::makeInstance(
|
|
ArrayProcessing::class,
|
|
'propertyCollectionPropertyPaths',
|
|
'^formElementsDefinition\.(.*)\.formEditor\.propertyCollections\.(finishers|validators)\.([\d]+)\.editors\.([\d]+)\.(propertyPath|.*\.propertyPath)$',
|
|
GeneralUtility::makeInstance(CollectionPropertyPathsExtractor::class, $extractorDto)
|
|
),
|
|
GeneralUtility::makeInstance(
|
|
ArrayProcessing::class,
|
|
'propertyCollectionAdditionalElementPropertyPaths',
|
|
'^formElementsDefinition\.(.*)\.formEditor\.propertyCollections\.(finishers|validators)\.([\d]+)\.editors\.([\d]+)\.additionalElementPropertyPaths\.([\d]+)',
|
|
GeneralUtility::makeInstance(AdditionalElementPropertyPathsExtractor::class, $extractorDto)
|
|
),
|
|
GeneralUtility::makeInstance(
|
|
ArrayProcessing::class,
|
|
'propertyCollectionRelativeMultiValueProperties',
|
|
'^formElementsDefinition\.(.*)\.formEditor\.propertyCollections\.(finishers|validators)\.([\d]+)\.editors\.([\d]+)\.templateName$',
|
|
GeneralUtility::makeInstance(CollectionMultiValuePropertiesExtractor::class, $extractorDto)
|
|
),
|
|
GeneralUtility::makeInstance(
|
|
ArrayProcessing::class,
|
|
'propertyCollectionSelectOptions',
|
|
'^formElementsDefinition\.(.*)\.formEditor\.propertyCollections\.(finishers|validators)\.([\d]+)\.editors\.([\d]+)\.selectOptions\.([\d]+)\.value$',
|
|
GeneralUtility::makeInstance(CollectionSelectOptionsExtractor::class, $extractorDto)
|
|
),
|
|
GeneralUtility::makeInstance(
|
|
ArrayProcessing::class,
|
|
'propertyCollectionPredefinedDefaults',
|
|
'^(validatorsDefinition|finishersDefinition)\.(.*)\.formEditor\.predefinedDefaults\.(.+)$',
|
|
GeneralUtility::makeInstance(CollectionPredefinedDefaultsExtractor::class, $extractorDto)
|
|
)
|
|
);
|
|
$configuration = $extractorDto->getResult();
|
|
$configuration = $this->translateValues($prototypeConfiguration, $configuration);
|
|
$configuration = $this->eventDispatcher
|
|
->dispatch(new AfterFormDefinitionValidationConfigurationIsBuiltEvent($prototypeName, $configuration))
|
|
->getConfiguration();
|
|
$this->setCacheEntry($cacheKey, $configuration);
|
|
}
|
|
return $configuration;
|
|
}
|
|
|
|
protected function isPropertyDefinedInFormEditorSetup(string $propertyPath, array $subConfig): bool
|
|
{
|
|
if (empty($subConfig)) {
|
|
return false;
|
|
}
|
|
if (in_array($propertyPath, $subConfig['propertyPaths'] ?? [], true)
|
|
|| in_array($propertyPath, $subConfig['additionalElementPropertyPaths'] ?? [], true)
|
|
|| in_array($propertyPath, $subConfig['additionalPropertyPaths'] ?? [], true)
|
|
) {
|
|
return true;
|
|
}
|
|
foreach ($subConfig['multiValueProperties'] ?? [] as $relativeMultiValueProperty) {
|
|
if (str_starts_with($propertyPath, $relativeMultiValueProperty)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected function translateValues(array $prototypeConfiguration, array $configuration): array
|
|
{
|
|
if (isset($configuration['formElements'])) {
|
|
$configuration['formElements'] = $this->translatePredefinedDefaults(
|
|
$prototypeConfiguration,
|
|
$configuration['formElements']
|
|
);
|
|
$configuration['formElements'] = $this->translateSelectOptions(
|
|
$prototypeConfiguration,
|
|
$configuration['formElements']
|
|
);
|
|
}
|
|
foreach ($configuration['collections'] ?? [] as $name => $collections) {
|
|
$configuration['collections'][$name] = $this->translatePredefinedDefaults($prototypeConfiguration, $collections);
|
|
$configuration['collections'][$name] = $this->translateSelectOptions($prototypeConfiguration, $configuration['collections'][$name]);
|
|
}
|
|
return $configuration;
|
|
}
|
|
|
|
protected function translatePredefinedDefaults(array $prototypeConfiguration, array $formElements): array
|
|
{
|
|
foreach ($formElements as $name => $formElement) {
|
|
if (!isset($formElement['predefinedDefaults'])) {
|
|
continue;
|
|
}
|
|
$formElement['untranslatedPredefinedDefaults'] = $formElement['predefinedDefaults'];
|
|
$formElement['predefinedDefaults'] = $this->translationService->translateValuesRecursive(
|
|
$formElement['predefinedDefaults'],
|
|
$prototypeConfiguration['formEditor']['translationFiles'] ?? []
|
|
);
|
|
$formElements[$name] = $formElement;
|
|
}
|
|
return $formElements;
|
|
}
|
|
|
|
protected function translateSelectOptions(array $prototypeConfiguration, array $formElements): array
|
|
{
|
|
foreach ($formElements as $name => $formElement) {
|
|
if (empty($formElement['selectOptions']) || !is_array($formElement['selectOptions'])) {
|
|
continue;
|
|
}
|
|
$formElement['untranslatedSelectOptions'] = $formElement['selectOptions'];
|
|
$formElement['selectOptions'] = $this->translationService->translateValuesRecursive(
|
|
$formElement['selectOptions'],
|
|
$prototypeConfiguration['formEditor']['translationFiles'] ?? []
|
|
);
|
|
$formElements[$name] = $formElement;
|
|
}
|
|
return $formElements;
|
|
}
|
|
|
|
protected function getCacheEntry(string $cacheKey): mixed
|
|
{
|
|
$cacheKey = 'form_' . $cacheKey;
|
|
if ($this->runtimeCache->has($cacheKey)) {
|
|
return $this->runtimeCache->get($cacheKey);
|
|
}
|
|
if ($this->assetsCache->has($cacheKey)) {
|
|
return $this->assetsCache->get($cacheKey);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
protected function setCacheEntry(string $cacheKey, mixed $value): void
|
|
{
|
|
$cacheKey = 'form_' . $cacheKey;
|
|
$this->runtimeCache->set($cacheKey, $value);
|
|
$this->assetsCache->set($cacheKey, $value);
|
|
}
|
|
}
|