TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
<?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!
|
||||
*/
|
||||
|
||||
/*
|
||||
* Inspired by and partially taken from the Neos.Form package (www.neos.io)
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Form\Domain\Factory;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use TYPO3\CMS\Form\Domain\Configuration\FormDefinitionConversionService;
|
||||
use TYPO3\CMS\Form\Domain\Model\FormDefinition;
|
||||
use TYPO3\CMS\Form\Event\AfterFormIsBuiltEvent;
|
||||
|
||||
/**
|
||||
* Base class for custom *Form Factories*. A Form Factory is responsible for building
|
||||
* a {@link TYPO3\CMS\Form\Domain\Model\FormDefinition}.
|
||||
*
|
||||
* Example
|
||||
* =======
|
||||
*
|
||||
* Generally, you should use this class as follows:
|
||||
*
|
||||
* <pre>
|
||||
* class MyFooBarFactory extends AbstractFormFactory {
|
||||
* public function build(array $configuration, $prototypeName) {
|
||||
* $configurationService = GeneralUtility::makeInstance(ConfigurationService::class);
|
||||
* $prototypeConfiguration = $configurationService->getPrototypeConfiguration($prototypeName);
|
||||
* $formDefinition = GeneralUtility::makeInstance(FormDefinition::class, 'nameOfMyForm', $prototypeConfiguration);
|
||||
*
|
||||
* // now, you should call methods on $formDefinition to add pages and form elements
|
||||
*
|
||||
* return $formDefinition;
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Scope: frontend / backend
|
||||
* **This class is meant to be sub classed by developers.**
|
||||
*/
|
||||
abstract class AbstractFormFactory implements FormFactoryInterface
|
||||
{
|
||||
protected ?EventDispatcherInterface $eventDispatcher = null;
|
||||
protected ?FormDefinitionConversionService $formDefinitionConversionService = null;
|
||||
|
||||
public function injectEventDispatcher(EventDispatcherInterface $eventDispatcher): void
|
||||
{
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
public function injectFormDefinitionConversionService(FormDefinitionConversionService $formDefinitionConversionService): void
|
||||
{
|
||||
$this->formDefinitionConversionService = $formDefinitionConversionService;
|
||||
}
|
||||
|
||||
protected function triggerFormBuildingFinished(FormDefinition $form): FormDefinition
|
||||
{
|
||||
return $this->eventDispatcher->dispatch(new AfterFormIsBuiltEvent($form))->form;
|
||||
}
|
||||
|
||||
protected function getFormDefinitionConversionService(): FormDefinitionConversionService
|
||||
{
|
||||
return $this->formDefinitionConversionService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<?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!
|
||||
*/
|
||||
|
||||
/*
|
||||
* Inspired by and partially taken from the Neos.Form package (www.neos.io)
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Form\Domain\Factory;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Form\Domain\Configuration\ConfigurationService;
|
||||
use TYPO3\CMS\Form\Domain\Exception\IdentifierNotValidException;
|
||||
use TYPO3\CMS\Form\Domain\Exception\RenderingException;
|
||||
use TYPO3\CMS\Form\Domain\Exception\UnknownCompositRenderableException;
|
||||
use TYPO3\CMS\Form\Domain\Model\FormDefinition;
|
||||
use TYPO3\CMS\Form\Domain\Model\FormElements\AbstractSection;
|
||||
use TYPO3\CMS\Form\Domain\Model\Renderable\CompositeRenderableInterface;
|
||||
use TYPO3\CMS\Form\Event\BeforeRenderableIsAddedToFormEvent;
|
||||
|
||||
/**
|
||||
* A factory that creates a FormDefinition from an array
|
||||
*
|
||||
* Scope: frontend / backend
|
||||
*/
|
||||
#[Autoconfigure(public: true, shared: false)]
|
||||
class ArrayFormFactory extends AbstractFormFactory
|
||||
{
|
||||
/**
|
||||
* Build a form definition, depending on some configuration.
|
||||
*
|
||||
* @throws RenderingException
|
||||
* @internal
|
||||
*/
|
||||
public function build(
|
||||
array $configuration,
|
||||
?string $prototypeName = null,
|
||||
?ServerRequestInterface $request = null
|
||||
): FormDefinition {
|
||||
if (empty($prototypeName)) {
|
||||
$prototypeName = $configuration['prototypeName'] ?? 'standard';
|
||||
}
|
||||
$persistenceIdentifier = $configuration['persistenceIdentifier'] ?? null;
|
||||
|
||||
// Get prototype configuration once and reuse it
|
||||
$prototypeConfiguration = GeneralUtility::makeInstance(ConfigurationService::class)
|
||||
->getPrototypeConfiguration($prototypeName);
|
||||
|
||||
// Get RTE property paths for proper sanitization
|
||||
$rtePropertyPaths = $this->getFormDefinitionConversionService()->extractRtePropertyPaths($prototypeConfiguration);
|
||||
|
||||
$configuration = $this->getFormDefinitionConversionService()->sanitizeHtml($configuration, $rtePropertyPaths);
|
||||
|
||||
if ($configuration['invalid'] ?? false) {
|
||||
throw new RenderingException($configuration['label'], 1529710560);
|
||||
}
|
||||
|
||||
$form = GeneralUtility::makeInstance(
|
||||
FormDefinition::class,
|
||||
$configuration['identifier'],
|
||||
$prototypeConfiguration,
|
||||
'Form',
|
||||
$persistenceIdentifier
|
||||
);
|
||||
// Set renderingOptions before processing renderables, so that options
|
||||
// like 'previewMode' are available during initializeFormElement().
|
||||
if (isset($configuration['renderingOptions'])) {
|
||||
foreach ($configuration['renderingOptions'] as $key => $value) {
|
||||
$form->setRenderingOption($key, $value);
|
||||
}
|
||||
}
|
||||
if (isset($configuration['renderables'])) {
|
||||
foreach ($configuration['renderables'] as $pageConfiguration) {
|
||||
$this->addNestedRenderable($pageConfiguration, $form, $request);
|
||||
}
|
||||
}
|
||||
|
||||
unset($configuration['persistenceIdentifier']);
|
||||
unset($configuration['prototypeName']);
|
||||
unset($configuration['renderables']);
|
||||
unset($configuration['type']);
|
||||
unset($configuration['identifier']);
|
||||
$form->setOptions($configuration);
|
||||
$form->setRequest($request);
|
||||
|
||||
return $this->triggerFormBuildingFinished($form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add form elements to the $parentRenderable
|
||||
*
|
||||
* @return mixed
|
||||
* @throws IdentifierNotValidException
|
||||
* @throws UnknownCompositRenderableException
|
||||
*/
|
||||
protected function addNestedRenderable(
|
||||
array $nestedRenderableConfiguration,
|
||||
CompositeRenderableInterface $parentRenderable,
|
||||
?ServerRequestInterface $request = null
|
||||
) {
|
||||
if (!isset($nestedRenderableConfiguration['identifier'])) {
|
||||
throw new IdentifierNotValidException('Identifier not set.', 1329289436);
|
||||
}
|
||||
if ($parentRenderable instanceof FormDefinition) {
|
||||
$renderable = $parentRenderable->createPage($nestedRenderableConfiguration['identifier'], $nestedRenderableConfiguration['type']);
|
||||
} elseif ($parentRenderable instanceof AbstractSection) {
|
||||
$renderable = $parentRenderable->createElement($nestedRenderableConfiguration['identifier'], $nestedRenderableConfiguration['type']);
|
||||
if ($request !== null && method_exists($renderable, 'setRequest')) {
|
||||
$renderable->setRequest($request);
|
||||
}
|
||||
} else {
|
||||
throw new UnknownCompositRenderableException('Unknown composit renderable "' . get_class($parentRenderable) . '"', 1479593622);
|
||||
}
|
||||
|
||||
$childRenderables = is_array($nestedRenderableConfiguration['renderables'] ?? null)
|
||||
? $nestedRenderableConfiguration['renderables']
|
||||
: [];
|
||||
|
||||
unset($nestedRenderableConfiguration['type']);
|
||||
unset($nestedRenderableConfiguration['identifier']);
|
||||
unset($nestedRenderableConfiguration['renderables']);
|
||||
|
||||
$renderable->setOptions($nestedRenderableConfiguration);
|
||||
|
||||
if ($renderable instanceof CompositeRenderableInterface) {
|
||||
foreach ($childRenderables as $elementConfiguration) {
|
||||
$this->addNestedRenderable($elementConfiguration, $renderable, $request);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->eventDispatcher->dispatch(new BeforeRenderableIsAddedToFormEvent($renderable))->renderable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?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!
|
||||
*/
|
||||
|
||||
/*
|
||||
* Inspired by and partially taken from the Neos.Form package (www.neos.io)
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Form\Domain\Factory;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Form\Domain\Model\FormDefinition;
|
||||
|
||||
/**
|
||||
* A Form Factory is responsible for building a {@link TYPO3\CMS\Form\Domain\Model\FormDefinition}.
|
||||
* **Instead of implementing this interface, subclassing {@link AbstractFormFactory} is more appropriate
|
||||
* in most cases**.
|
||||
*
|
||||
* A Form Factory can be called anytime a FormDefinition should be built; in most cases
|
||||
* it is done through an invocation of a Form Rendering ViewHelper.
|
||||
*
|
||||
* Scope: frontend / backend
|
||||
*/
|
||||
interface FormFactoryInterface
|
||||
{
|
||||
/**
|
||||
* Build a form definition, depending on some configuration.
|
||||
*
|
||||
* The configuration array is factory-specific; for example a YAML or JSON factory
|
||||
* could retrieve the path to the YAML / JSON file via the configuration array.
|
||||
*
|
||||
* @param array $configuration factory-specific configuration array
|
||||
* @param string $prototypeName The name of the "PrototypeName" to use; it is factory-specific to implement this.
|
||||
* @param ServerRequestInterface $request The PSR-7 request object
|
||||
* @return FormDefinition a newly built form definition
|
||||
*/
|
||||
public function build(
|
||||
array $configuration,
|
||||
?string $prototypeName = null,
|
||||
?ServerRequestInterface $request = null
|
||||
): FormDefinition;
|
||||
}
|
||||
Reference in New Issue
Block a user