TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
<?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\ViewHelpers\Be;
|
||||
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
|
||||
/**
|
||||
* Return the max file size for use in the form editor
|
||||
*
|
||||
* Scope: backend
|
||||
*
|
||||
* @see https://docs.typo3.org/permalink/t3viewhelper:typo3-form-be-maximumfilesize
|
||||
* @internal
|
||||
*/
|
||||
final class MaximumFileSizeViewHelper extends AbstractViewHelper
|
||||
{
|
||||
public function render(): string
|
||||
{
|
||||
$maxUploadFileSize = GeneralUtility::getMaxUploadFileSize();
|
||||
// format according to PHP formatting rules (K = kilobytes instead of kibibytes)
|
||||
$formattedSize = GeneralUtility::formatSize($maxUploadFileSize * 1024, '|k|M|G|T|P|E|Z|Y');
|
||||
// remove decimals from result to match EXT:form validator integer format
|
||||
return preg_replace('/(\d+)(.+\d{2})?([kMGTPEZY]{0,1})/', '$1$3', $formattedSize);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<?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\ViewHelpers\Be;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
||||
use TYPO3\CMS\Backend\Context\PageContext;
|
||||
use TYPO3\CMS\Backend\Context\PageContextFactory;
|
||||
use TYPO3\CMS\Backend\Routing\UriBuilder;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Backend\View\BackendLayout\BackendLayout;
|
||||
use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumn;
|
||||
use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumnItem;
|
||||
use TYPO3\CMS\Backend\View\Drawing\DrawingConfiguration;
|
||||
use TYPO3\CMS\Backend\View\PageLayoutContext;
|
||||
use TYPO3\CMS\Backend\View\PageViewMode;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Domain\RecordFactory;
|
||||
use TYPO3\CMS\Core\Http\NormalizedParams;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
|
||||
/**
|
||||
* Used by the form editor.
|
||||
* Render a content element preview like the page module
|
||||
*
|
||||
* Scope: backend
|
||||
*
|
||||
* @see https://docs.typo3.org/permalink/t3viewhelper:typo3-form-be-rendercontentelementpreview
|
||||
* @internal
|
||||
*/
|
||||
#[Autoconfigure(public: true)]
|
||||
final class RenderContentElementPreviewViewHelper extends AbstractViewHelper
|
||||
{
|
||||
/**
|
||||
* As this ViewHelper renders HTML, the output must not be escaped.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $escapeOutput = false;
|
||||
|
||||
public function __construct(
|
||||
private readonly PageContextFactory $pageContextFactory,
|
||||
private readonly UriBuilder $uriBuilder,
|
||||
private readonly RecordFactory $recordFactory,
|
||||
) {}
|
||||
|
||||
public function initializeArguments(): void
|
||||
{
|
||||
$this->registerArgument('contentElementUid', 'int', 'The uid of a content element');
|
||||
$this->registerArgument('formPersistenceIdentifier', 'string', 'The form persistence identifier for return URL', false, '');
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
$content = '';
|
||||
$contentElementUid = $this->arguments['contentElementUid'];
|
||||
$contentRecord = BackendUtility::getRecord('tt_content', $contentElementUid);
|
||||
$request = null;
|
||||
if ($this->renderingContext->hasAttribute(ServerRequestInterface::class)) {
|
||||
$request = $this->renderingContext->getAttribute(ServerRequestInterface::class);
|
||||
}
|
||||
if (!empty($contentRecord) && $request !== null) {
|
||||
$backendLayout = GeneralUtility::makeInstance(BackendLayout::class, 'dummy', 'dummy', []);
|
||||
$pageId = (int)$contentRecord['pid'];
|
||||
$pageContext = $request->getAttribute('pageContext');
|
||||
if (!$pageContext instanceof PageContext) {
|
||||
try {
|
||||
$pageContext = $this->pageContextFactory->createFromRequest($request, $pageId, $this->getBackendUser());
|
||||
} catch (\Exception $e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
$manipulatedRequest = $this->getManipulatedRequestToFormEditor($request, $contentRecord);
|
||||
|
||||
$pageLayoutContext = GeneralUtility::makeInstance(
|
||||
PageLayoutContext::class,
|
||||
$pageContext,
|
||||
$backendLayout,
|
||||
DrawingConfiguration::create($backendLayout, BackendUtility::getPagesTSconfig($pageId), PageViewMode::LayoutView),
|
||||
$manipulatedRequest
|
||||
);
|
||||
$gridColumn = GeneralUtility::makeInstance(GridColumn::class, $pageLayoutContext, []);
|
||||
$contentRecord = $this->recordFactory->createResolvedRecordFromDatabaseRow('tt_content', $contentRecord, null, $pageLayoutContext->getRecordIdentityMap());
|
||||
$columnItem = GeneralUtility::makeInstance(GridColumnItem::class, $pageLayoutContext, $gridColumn, $contentRecord);
|
||||
return $columnItem->getPreview();
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function getBackendUser(): BackendUserAuthentication
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a manipulated request with custom NormalizedParams to override the return URL.
|
||||
* This allows customizing the return URL used by PageLayoutContext->getReturnUrl()
|
||||
* without modifying the original request or the PageLayoutContext logic.
|
||||
*/
|
||||
private function getManipulatedRequestToFormEditor(ServerRequestInterface $request, array $contentRecord): ServerRequestInterface
|
||||
{
|
||||
$serverParams = $request->getServerParams();
|
||||
$serverParams['REQUEST_URI'] = $this->buildCustomReturnUrl($request, $contentRecord);
|
||||
|
||||
$customNormalizedParams = NormalizedParams::createFromServerParams($serverParams);
|
||||
|
||||
return $request->withAttribute('normalizedParams', $customNormalizedParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the custom return URL for the form editor.
|
||||
* Generates the URL to FormEditor->index action with the formPersistenceIdentifier parameter.
|
||||
*/
|
||||
private function buildCustomReturnUrl(ServerRequestInterface $request, array $contentRecord): string
|
||||
{
|
||||
$formPersistenceIdentifier = $this->arguments['formPersistenceIdentifier'] ?? '';
|
||||
|
||||
if (empty($formPersistenceIdentifier)) {
|
||||
return $request->getAttribute('normalizedParams')->getRequestUri();
|
||||
}
|
||||
|
||||
$uri = $this->uriBuilder->buildUriFromRoute(
|
||||
'form_editor',
|
||||
['formPersistenceIdentifier' => $formPersistenceIdentifier]
|
||||
);
|
||||
return (string)$uri;
|
||||
}
|
||||
}
|
||||
@@ -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\ViewHelpers\Form;
|
||||
|
||||
use TYPO3\CMS\Core\Crypto\HashService;
|
||||
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
|
||||
use TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper;
|
||||
use TYPO3\CMS\Form\Security\HashScope;
|
||||
|
||||
/**
|
||||
* ViewHelper which renders a checkbox for file deletion in EXT:form.
|
||||
*
|
||||
* This ViewHelper is similar to Extbase's UploadDeleteCheckboxViewHelper but adapted
|
||||
* for the EXT:form context. It renders a checkbox that, when checked, marks the
|
||||
* associated file for deletion on form submission.
|
||||
*
|
||||
* Example usage:
|
||||
* ```
|
||||
* <formvh:form.uploadDeleteCheckbox
|
||||
* property="{element.identifier}"
|
||||
* fileReference="{file}"
|
||||
* fileIndex="{iterator.index}"
|
||||
* />
|
||||
* ```
|
||||
*
|
||||
* Scope: frontend
|
||||
*
|
||||
* @see https://docs.typo3.org/permalink/t3viewhelper:typo3-form-form-uploaddeletecheckbox
|
||||
*/
|
||||
final class UploadDeleteCheckboxViewHelper extends AbstractFormFieldViewHelper
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $tagName = 'input';
|
||||
|
||||
public function __construct(
|
||||
private readonly HashService $hashService,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function initializeArguments(): void
|
||||
{
|
||||
parent::initializeArguments();
|
||||
$this->registerArgument('fileReference', FileReference::class, 'The file reference object', true);
|
||||
$this->registerArgument('fileIndex', 'int', 'Index of the file in multiple upload context', false, 0);
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
/** @var FileReference|null $fileReference */
|
||||
$fileReference = $this->arguments['fileReference'];
|
||||
$fileIndex = (int)$this->arguments['fileIndex'];
|
||||
|
||||
// Early return if no file reference given
|
||||
if (!$fileReference instanceof FileReference) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$this->tag->addAttribute('type', 'checkbox');
|
||||
|
||||
// Build the deletion data that will be validated on submit
|
||||
$deleteData = [
|
||||
'property' => $this->arguments['property'],
|
||||
'fileIndex' => $fileIndex,
|
||||
'fileUid' => $fileReference->getUid() ?? $fileReference->getOriginalResource()->getOriginalFile()->getUid(),
|
||||
];
|
||||
|
||||
// Create HMAC-signed value
|
||||
$valueAttribute = $this->hashService->appendHmac(
|
||||
json_encode($deleteData, JSON_THROW_ON_ERROR),
|
||||
HashScope::DeleteFile->prefix()
|
||||
);
|
||||
|
||||
// Build name attribute using the form field prefix
|
||||
$name = $this->getName();
|
||||
$nameAttribute = $name . '[__deleteFile][' . $fileIndex . ']';
|
||||
|
||||
$this->tag->addAttribute('name', $nameAttribute);
|
||||
$this->tag->addAttribute('value', $valueAttribute);
|
||||
|
||||
// Check if this checkbox was previously checked (in case of validation errors)
|
||||
if ($this->isChecked($fileIndex)) {
|
||||
$this->tag->addAttribute('checked', 'checked');
|
||||
}
|
||||
|
||||
return $this->tag->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the checkbox for the given file index was checked in the current request
|
||||
*/
|
||||
private function isChecked(int $fileIndex): bool
|
||||
{
|
||||
$value = $this->getValueAttribute();
|
||||
if (is_array($value) && isset($value['__deleteFile'][$fileIndex])) {
|
||||
return !empty($value['__deleteFile'][$fileIndex]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
<?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\ViewHelpers\Form;
|
||||
|
||||
use TYPO3\CMS\Core\Crypto\HashService;
|
||||
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
|
||||
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
|
||||
use TYPO3\CMS\Extbase\Property\PropertyMapper;
|
||||
use TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper;
|
||||
use TYPO3\CMS\Form\Security\HashScope;
|
||||
|
||||
/**
|
||||
* This ViewHelper makes the specified Image object available for its
|
||||
* childNodes.
|
||||
* In case the form is redisplayed because of validation errors, a previously
|
||||
* uploaded image will be correctly used.
|
||||
*
|
||||
* Scope: frontend
|
||||
*
|
||||
* @see https://docs.typo3.org/permalink/t3viewhelper:typo3-form-form-uploadedresource
|
||||
*/
|
||||
final class UploadedResourceViewHelper extends AbstractFormFieldViewHelper
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $tagName = 'input';
|
||||
|
||||
public function __construct(
|
||||
private readonly HashService $hashService,
|
||||
private readonly PropertyMapper $propertyMapper,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function initializeArguments(): void
|
||||
{
|
||||
parent::initializeArguments();
|
||||
$this->registerArgument('as', 'string', '');
|
||||
$this->registerArgument('accept', 'array', 'Values for the accept attribute', false, []);
|
||||
$this->registerArgument('errorClass', 'string', 'CSS class to set if there are errors for this ViewHelper', false, 'f3-form-error');
|
||||
$this->registerArgument('multiple', 'boolean', 'Defines the upload element accepting multiple files', false, false);
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
$output = '';
|
||||
|
||||
$name = $this->getName();
|
||||
$as = $this->arguments['as'];
|
||||
$accept = $this->arguments['accept'];
|
||||
$multiple = $this->arguments['multiple'];
|
||||
$resource = $this->getUploadedResource();
|
||||
|
||||
if (!empty($accept)) {
|
||||
$this->tag->addAttribute('accept', implode(',', $accept));
|
||||
}
|
||||
|
||||
if ($resource !== null) {
|
||||
if ($resource instanceof FileReference) {
|
||||
$resourcePointerValue = $resource->getUid() ?? ('file:' . $resource->getOriginalResource()->getOriginalFile()->getUid());
|
||||
$output .= $this->buildResourcePointerInput(
|
||||
0,
|
||||
(string)$resourcePointerValue,
|
||||
$this->buildResourcePointerIdAttribute(),
|
||||
);
|
||||
} elseif ($resource instanceof ObjectStorage) {
|
||||
foreach ($resource as $file) {
|
||||
$index = $resource->getPosition($file);
|
||||
$resourcePointerValue = $file->getUid() ?? ('file:' . $file->getOriginalResource()->getOriginalFile()->getUid());
|
||||
$output .= $this->buildResourcePointerInput(
|
||||
$index,
|
||||
(string)$resourcePointerValue,
|
||||
$this->buildResourcePointerIdAttribute('-' . $index),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->templateVariableContainer->add($as, $resource);
|
||||
$output .= $this->renderChildren();
|
||||
$this->templateVariableContainer->remove($as);
|
||||
}
|
||||
|
||||
foreach (['name', 'type', 'tmp_name', 'error', 'size'] as $fieldName) {
|
||||
$this->registerFieldNameForFormTokenGeneration($name . '[' . $fieldName . ']');
|
||||
}
|
||||
$this->tag->addAttribute('type', 'file');
|
||||
|
||||
if ($multiple === true) {
|
||||
$this->tag->addAttribute('name', $name . '[]');
|
||||
$this->tag->addAttribute('multiple', true);
|
||||
} else {
|
||||
$this->tag->addAttribute('name', $name);
|
||||
}
|
||||
|
||||
$this->setErrorClassAttribute();
|
||||
$output .= $this->tag->render();
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
private function buildResourcePointerInput(int $index, string $resourcePointerValue, string $idAttribute): string
|
||||
{
|
||||
$name = htmlspecialchars($this->getName());
|
||||
$hmac = htmlspecialchars($this->hashService->appendHmac($resourcePointerValue, HashScope::ResourcePointer->prefix()));
|
||||
return '<input type="hidden"'
|
||||
. ' name="' . $name . '[__submittedFiles][' . $index . '][submittedFile][resourcePointer]"'
|
||||
. ' value="' . $hmac . '"'
|
||||
. $idAttribute
|
||||
. ' />';
|
||||
}
|
||||
|
||||
private function buildResourcePointerIdAttribute(string $suffix = ''): string
|
||||
{
|
||||
if (!isset($this->additionalArguments['id'])) {
|
||||
return '';
|
||||
}
|
||||
return ' id="' . htmlspecialchars($this->additionalArguments['id']) . '-file-reference' . $suffix . '"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a previously uploaded resource.
|
||||
* Return NULL if errors occurred during property mapping for this property.
|
||||
*/
|
||||
private function getUploadedResource(): FileReference|ObjectStorage|null
|
||||
{
|
||||
if ($this->getMappingResultsForProperty()->hasErrors()) {
|
||||
return null;
|
||||
}
|
||||
$resource = $this->getValueAttribute();
|
||||
if ($resource instanceof ObjectStorage) {
|
||||
return $resource;
|
||||
}
|
||||
if ($resource instanceof FileReference) {
|
||||
// When multiple uploads are enabled but the stored value is a single
|
||||
// FileReference, wrap it in an ObjectStorage so that the Fluid template's
|
||||
// f:for ViewHelper receives an iterable instead of crashing.
|
||||
if ($this->arguments['multiple']) {
|
||||
$storage = new ObjectStorage();
|
||||
$storage->attach($resource);
|
||||
return $storage;
|
||||
}
|
||||
return $resource;
|
||||
}
|
||||
return $this->propertyMapper->convert($resource, FileReference::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?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\ViewHelpers;
|
||||
|
||||
use TYPO3\CMS\Core\Crypto\HashAlgo;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper as FluidFormViewHelper;
|
||||
use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
|
||||
use TYPO3\CMS\Form\Security\HashScope;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\TagBuilder;
|
||||
|
||||
/**
|
||||
* Custom form ViewHelper that renders the form state instead of referrer fields
|
||||
*
|
||||
* Scope: frontend
|
||||
*
|
||||
* @see https://docs.typo3.org/permalink/t3viewhelper:typo3-form-form
|
||||
*/
|
||||
final class FormViewHelper extends FluidFormViewHelper
|
||||
{
|
||||
/**
|
||||
* Renders hidden form fields for referrer information about
|
||||
* the current request.
|
||||
*
|
||||
* @return string Hidden fields with referrer information
|
||||
*/
|
||||
protected function renderHiddenReferrerFields(): string
|
||||
{
|
||||
$formRuntime = $this->getFormRuntime();
|
||||
$prefix = $this->prefixFieldName($this->getFormObjectName());
|
||||
|
||||
$markup = $this->createHiddenInputElement(
|
||||
$prefix . '[__state]',
|
||||
$this->hashService->appendHmac(
|
||||
base64_encode(serialize($formRuntime->getFormState())),
|
||||
HashScope::FormState->prefix(),
|
||||
HashAlgo::SHA3_256
|
||||
)
|
||||
);
|
||||
|
||||
// ONLY assign `__session` if form is performing (uncached)
|
||||
if ($formRuntime->canProcessFormSubmission() && $formRuntime->getFormSession() !== null) {
|
||||
$markup .= $this->createHiddenInputElement(
|
||||
$prefix . '[__session]',
|
||||
$formRuntime->getFormSession()->getAuthenticatedIdentifier()
|
||||
);
|
||||
}
|
||||
return $markup;
|
||||
}
|
||||
|
||||
private function createHiddenInputElement(string $name, string $value): string
|
||||
{
|
||||
$tagBuilder = GeneralUtility::makeInstance(TagBuilder::class, 'input');
|
||||
$tagBuilder->addAttribute('type', 'hidden');
|
||||
$tagBuilder->addAttribute('name', $name);
|
||||
$tagBuilder->addAttribute('value', $value);
|
||||
return $tagBuilder->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* We do NOT return NULL as in this case, the Form ViewHelpers do not enter $objectAccessorMode.
|
||||
* However, we return the form identifier.
|
||||
*/
|
||||
protected function getFormObjectName(): string
|
||||
{
|
||||
return $this->getFormRuntime()->getFormDefinition()->getIdentifier();
|
||||
}
|
||||
|
||||
private function getFormRuntime(): FormRuntime
|
||||
{
|
||||
return $this->arguments['object'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?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\ViewHelpers;
|
||||
|
||||
use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface;
|
||||
use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
|
||||
/**
|
||||
* Scope: frontend
|
||||
*
|
||||
* @see https://docs.typo3.org/permalink/t3viewhelper:typo3-form-gridcolumnclassautoconfiguration
|
||||
*/
|
||||
final class GridColumnClassAutoConfigurationViewHelper extends AbstractViewHelper
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $escapeOutput = false;
|
||||
|
||||
public function initializeArguments(): void
|
||||
{
|
||||
$this->registerArgument('element', RootRenderableInterface::class, 'A RootRenderableInterface instance', true);
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
$formElement = $this->arguments['element'];
|
||||
|
||||
if ($formElement instanceof RenderableInterface && !$formElement->isEnabled()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$gridRowElement = $formElement->getParentRenderable();
|
||||
$gridRowChildElements = $gridRowElement->getElements();
|
||||
$gridViewPortConfiguration = $gridRowElement->getProperties()['gridColumnClassAutoConfiguration'];
|
||||
if (empty($gridViewPortConfiguration)) {
|
||||
return '';
|
||||
}
|
||||
$gridSize = (int)$gridViewPortConfiguration['gridSize'];
|
||||
$columnsToCalculate = [];
|
||||
$usedColumns = [];
|
||||
foreach ($gridRowChildElements as $childElement) {
|
||||
if ($childElement instanceof RenderableInterface && !$childElement->isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
if (empty($childElement->getProperties()['gridColumnClassAutoConfiguration'])) {
|
||||
foreach ($gridViewPortConfiguration['viewPorts'] as $viewPortName => $configuration) {
|
||||
$columnsToCalculate[$viewPortName]['elements'] = ($columnsToCalculate[$viewPortName]['elements'] ?? 0) + 1;
|
||||
}
|
||||
} else {
|
||||
$gridColumnViewPortConfiguration = $childElement->getProperties()['gridColumnClassAutoConfiguration'];
|
||||
foreach ($gridViewPortConfiguration['viewPorts'] as $viewPortName => $configuration) {
|
||||
$configuration = $gridColumnViewPortConfiguration['viewPorts'][$viewPortName] ?? [];
|
||||
if (
|
||||
isset($configuration['numbersOfColumnsToUse'])
|
||||
&& (int)$configuration['numbersOfColumnsToUse'] > 0
|
||||
) {
|
||||
$usedColumns[$viewPortName]['sum'] = ($usedColumns[$viewPortName]['sum'] ?? 0);
|
||||
$usedColumns[$viewPortName]['sum'] += (int)$configuration['numbersOfColumnsToUse'];
|
||||
if ($childElement->getIdentifier() === $formElement->getIdentifier()) {
|
||||
$usedColumns[$viewPortName]['concreteNumbersOfColumnsToUse'] = (int)$configuration['numbersOfColumnsToUse'];
|
||||
if ($usedColumns[$viewPortName]['concreteNumbersOfColumnsToUse'] > $gridSize) {
|
||||
$usedColumns[$viewPortName]['concreteNumbersOfColumnsToUse'] = $gridSize;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$columnsToCalculate[$viewPortName]['elements'] = ($columnsToCalculate[$viewPortName]['elements'] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$classes = [];
|
||||
foreach ($gridViewPortConfiguration['viewPorts'] as $viewPortName => $configuration) {
|
||||
if (isset($usedColumns[$viewPortName]['concreteNumbersOfColumnsToUse'])) {
|
||||
$numbersOfColumnsToUse = $usedColumns[$viewPortName]['concreteNumbersOfColumnsToUse'];
|
||||
} else {
|
||||
$restColumnsToDivide = $gridSize - ($usedColumns[$viewPortName]['sum'] ?? 0);
|
||||
$restElements = (int)$columnsToCalculate[$viewPortName]['elements'];
|
||||
|
||||
if ($restColumnsToDivide < 1) {
|
||||
$restColumnsToDivide = $gridSize;
|
||||
}
|
||||
$numbersOfColumnsToUse = floor($restColumnsToDivide / $restElements);
|
||||
}
|
||||
|
||||
$classes[] = str_replace(
|
||||
'{@numbersOfColumnsToUse}',
|
||||
(string)$numbersOfColumnsToUse,
|
||||
$configuration['classPattern']
|
||||
);
|
||||
}
|
||||
return implode(' ', $classes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?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\ViewHelpers;
|
||||
|
||||
use TYPO3\CMS\Form\Domain\Model\Renderable\CompositeRenderableInterface;
|
||||
use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
|
||||
/**
|
||||
* Renders the values of a form
|
||||
*
|
||||
* Scope: frontend
|
||||
*
|
||||
* @see https://docs.typo3.org/permalink/t3viewhelper:typo3-form-renderallformvalues
|
||||
*/
|
||||
final class RenderAllFormValuesViewHelper extends AbstractViewHelper
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $escapeOutput = false;
|
||||
|
||||
public function initializeArguments(): void
|
||||
{
|
||||
$this->registerArgument('renderable', RootRenderableInterface::class, 'A RootRenderableInterface instance', true);
|
||||
$this->registerArgument('as', 'string', 'The name within the template', false, 'formValue');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array element by key.
|
||||
*/
|
||||
public function render(): string
|
||||
{
|
||||
$renderable = $this->arguments['renderable'];
|
||||
if ($renderable instanceof CompositeRenderableInterface) {
|
||||
$elements = $renderable->getRenderablesRecursively();
|
||||
} else {
|
||||
$elements = [$renderable];
|
||||
}
|
||||
$as = $this->arguments['as'];
|
||||
$output = '';
|
||||
foreach ($elements as $element) {
|
||||
$output .= $this->renderingContext->getViewHelperInvoker()->invoke(
|
||||
RenderFormValueViewHelper::class,
|
||||
[
|
||||
'renderable' => $element,
|
||||
'as' => $as,
|
||||
],
|
||||
$this->renderingContext,
|
||||
$this->renderChildren(...),
|
||||
);
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
<?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\ViewHelpers;
|
||||
|
||||
use TYPO3\CMS\Core\Country\CountryProvider;
|
||||
use TYPO3\CMS\Core\Resource\File;
|
||||
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
|
||||
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
|
||||
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
|
||||
use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface;
|
||||
use TYPO3\CMS\Form\Domain\Model\FormElements\StringableFormElementInterface;
|
||||
use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface;
|
||||
use TYPO3Fluid\Fluid\Core\Variables\ScopedVariableProvider;
|
||||
use TYPO3Fluid\Fluid\Core\Variables\StandardVariableProvider;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
|
||||
/**
|
||||
* Renders a single value of a form
|
||||
*
|
||||
* Scope: frontend
|
||||
*
|
||||
* @see https://docs.typo3.org/permalink/t3viewhelper:typo3-form-renderformvalue
|
||||
*/
|
||||
final class RenderFormValueViewHelper extends AbstractViewHelper
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $escapeOutput = false;
|
||||
|
||||
public function __construct(
|
||||
private readonly CountryProvider $countryProvider
|
||||
) {}
|
||||
|
||||
public function initializeArguments(): void
|
||||
{
|
||||
$this->registerArgument('renderable', RenderableInterface::class, 'A renderable element', true);
|
||||
$this->registerArgument('as', 'string', 'The name within the template', false, 'formValue');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array element by key
|
||||
*/
|
||||
public function render(): string
|
||||
{
|
||||
$element = $this->arguments['renderable'];
|
||||
if (!$element instanceof FormElementInterface || !self::isEnabled($element)) {
|
||||
return '';
|
||||
}
|
||||
$renderingOptions = $element->getRenderingOptions();
|
||||
if ($renderingOptions['_isSection'] ?? false) {
|
||||
$data = [
|
||||
'element' => $element,
|
||||
'isSection' => true,
|
||||
];
|
||||
} elseif ($renderingOptions['_isCompositeFormElement'] ?? false) {
|
||||
return '';
|
||||
} else {
|
||||
$formRuntime = $this->renderingContext
|
||||
->getViewHelperVariableContainer()
|
||||
->get(RenderRenderableViewHelper::class, 'formRuntime');
|
||||
$value = $formRuntime[$element->getIdentifier()];
|
||||
$data = [
|
||||
'element' => $element,
|
||||
'value' => $value,
|
||||
'processedValue' => $this->processElementValue($element, $value),
|
||||
'isMultiValue' => is_iterable($value),
|
||||
];
|
||||
}
|
||||
$variableProvider = new ScopedVariableProvider($this->renderingContext->getVariableProvider(), new StandardVariableProvider([$this->arguments['as'] => $data]));
|
||||
$this->renderingContext->setVariableProvider($variableProvider);
|
||||
$output = (string)$this->renderChildren();
|
||||
$this->renderingContext->setVariableProvider($variableProvider->getGlobalVariableProvider());
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given value to a simple type (string or array) considering the underlying FormElement definition.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
private function processElementValue(
|
||||
FormElementInterface $element,
|
||||
$value
|
||||
) {
|
||||
$properties = $element->getProperties();
|
||||
$options = $properties['options'] ?? null;
|
||||
if ($element->getType() === 'CountrySelect') {
|
||||
$country = $this->countryProvider->getByIsoCode($value ?? '');
|
||||
if ($country !== null) {
|
||||
return (string)LocalizationUtility::translate($country->getLocalizedNameLabel());
|
||||
}
|
||||
}
|
||||
if (is_array($options)) {
|
||||
$options = (array)$this->renderingContext->getViewHelperInvoker()->invoke(
|
||||
TranslateElementPropertyViewHelper::class,
|
||||
['element' => $element, 'property' => 'options'],
|
||||
$this->renderingContext,
|
||||
$this->renderChildren(...),
|
||||
);
|
||||
if (is_array($value)) {
|
||||
return self::mapValuesToOptions($value, $options);
|
||||
}
|
||||
return self::mapValueToOption($value, $options);
|
||||
}
|
||||
if ($value instanceof ObjectStorage) {
|
||||
$result = [];
|
||||
foreach ($value as $item) {
|
||||
$result[] = is_object($item) ? self::processObject($element, $item) : $item;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
if (is_object($value)) {
|
||||
return self::processObject($element, $value);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the given values (=keys) with the corresponding elements in $options.
|
||||
*
|
||||
* @see mapValueToOption()
|
||||
*/
|
||||
private static function mapValuesToOptions(array $value, array $options): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($value as $key) {
|
||||
$result[] = self::mapValueToOption($key, $options);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the given value (=key) with the corresponding element in $options
|
||||
* If the key does not exist in $options, it is returned without modification
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
private static function mapValueToOption($value, array $options)
|
||||
{
|
||||
return $options[$value] ?? $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given $object to a string representation considering the $element FormElement definition.
|
||||
*
|
||||
* @param object $object
|
||||
*/
|
||||
private static function processObject(FormElementInterface $element, $object): string
|
||||
{
|
||||
if ($element instanceof StringableFormElementInterface) {
|
||||
return $element->valueToString($object);
|
||||
}
|
||||
|
||||
if ($object instanceof \DateTime) {
|
||||
return $object->format(\DateTimeInterface::W3C);
|
||||
}
|
||||
|
||||
if ($object instanceof File || $object instanceof FileReference) {
|
||||
if ($object instanceof FileReference) {
|
||||
$object = $object->getOriginalResource();
|
||||
}
|
||||
|
||||
return $object->getName();
|
||||
}
|
||||
|
||||
if (method_exists($object, '__toString')) {
|
||||
return (string)$object;
|
||||
}
|
||||
|
||||
return 'Object [' . get_class($object) . ']';
|
||||
}
|
||||
|
||||
private static function isEnabled(RenderableInterface $renderable): bool
|
||||
{
|
||||
if (!$renderable->isEnabled()) {
|
||||
return false;
|
||||
}
|
||||
while ($renderable = $renderable->getParentRenderable()) {
|
||||
if (!$renderable->isEnabled()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?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\ViewHelpers;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface;
|
||||
use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface;
|
||||
use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
|
||||
use TYPO3\CMS\Form\Event\BeforeRenderableIsRenderedEvent;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
|
||||
/**
|
||||
* Render a renderable.
|
||||
*
|
||||
* Set the renderable into the \TYPO3\CMS\Form\Mvc\View\FormView
|
||||
* and return the rendered content.
|
||||
*
|
||||
* Scope: frontend
|
||||
*
|
||||
* @see https://docs.typo3.org/permalink/t3viewhelper:typo3-form-renderrenderable
|
||||
*/
|
||||
final class RenderRenderableViewHelper extends AbstractViewHelper
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $escapeOutput = false;
|
||||
|
||||
public function __construct(
|
||||
private readonly EventDispatcherInterface $eventDispatcher,
|
||||
) {}
|
||||
|
||||
public function initializeArguments(): void
|
||||
{
|
||||
$this->registerArgument('renderable', RootRenderableInterface::class, 'A RenderableInterface instance', true);
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
/** @var FormRuntime $formRuntime */
|
||||
$formRuntime = $this->renderingContext
|
||||
->getViewHelperVariableContainer()
|
||||
->get(self::class, 'formRuntime');
|
||||
$renderable = $this->arguments['renderable'];
|
||||
$this->eventDispatcher->dispatch(new BeforeRenderableIsRenderedEvent($renderable, $formRuntime));
|
||||
$content = '';
|
||||
if ($renderable instanceof FormRuntime || ($renderable instanceof RenderableInterface && $renderable->isEnabled())) {
|
||||
$content = $this->renderChildren();
|
||||
}
|
||||
// Wrap every renderable with a span with an identifier path data attribute if previewMode is active
|
||||
if (!empty($content)) {
|
||||
$renderingOptions = $formRuntime->getRenderingOptions();
|
||||
if (isset($renderingOptions['previewMode']) && $renderingOptions['previewMode'] === true) {
|
||||
$path = $renderable->getIdentifier();
|
||||
if ($renderable instanceof RenderableInterface) {
|
||||
while ($renderable = $renderable->getParentRenderable()) {
|
||||
$path = $renderable->getIdentifier() . '/' . $path;
|
||||
}
|
||||
}
|
||||
$content = '<span data-element-identifier-path="' . $path . '">' . $content . '</span>';
|
||||
}
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?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\ViewHelpers;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Utility\ArrayUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface as ExtbaseConfigurationManagerInterface;
|
||||
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
|
||||
use TYPO3\CMS\Form\Domain\Factory\ArrayFormFactory;
|
||||
use TYPO3\CMS\Form\Domain\Factory\FormFactoryInterface;
|
||||
use TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
|
||||
/**
|
||||
* Main Entry Point to render a Form into a Fluid Template
|
||||
*
|
||||
* Usage
|
||||
* =====
|
||||
*
|
||||
* Default::
|
||||
*
|
||||
* {namespace formvh=TYPO3\CMS\Form\ViewHelpers}
|
||||
* <formvh:render factoryClass="NameOfYourCustomFactoryClass" />
|
||||
*
|
||||
* The factory class must implement :php:`TYPO3\CMS\Form\Domain\Factory\FormFactoryInterface`.
|
||||
*
|
||||
* Scope: frontend
|
||||
*
|
||||
* @see https://docs.typo3.org/permalink/t3viewhelper:typo3-form-render
|
||||
*/
|
||||
final class RenderViewHelper extends AbstractViewHelper
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $escapeOutput = false;
|
||||
|
||||
public function __construct(
|
||||
private readonly FormPersistenceManagerInterface $formPersistenceManager,
|
||||
private readonly ExtbaseConfigurationManagerInterface $extbaseConfigurationManager,
|
||||
) {}
|
||||
|
||||
public function initializeArguments(): void
|
||||
{
|
||||
$this->registerArgument('persistenceIdentifier', 'string', 'The persistence identifier for the form.');
|
||||
$this->registerArgument('factoryClass', 'string', 'The fully qualified class name of the factory', false, ArrayFormFactory::class);
|
||||
$this->registerArgument('prototypeName', 'string', 'Name of the prototype to use');
|
||||
$this->registerArgument('overrideConfiguration', 'array', 'factory specific configuration', false, []);
|
||||
}
|
||||
|
||||
public function render(): ?string
|
||||
{
|
||||
$persistenceIdentifier = $this->arguments['persistenceIdentifier'];
|
||||
$prototypeName = $this->arguments['prototypeName'];
|
||||
$overrideConfiguration = $this->arguments['overrideConfiguration'];
|
||||
/** @var RequestInterface $request */
|
||||
$request = $this->renderingContext->getAttribute(ServerRequestInterface::class);
|
||||
// @todo: formvh:render() does not make sense without a persistenceIdentifier, does it?
|
||||
if (!empty($persistenceIdentifier)) {
|
||||
// The ConfigurationManager of ext:form needs ext:extbase ConfigurationManager to retrieve basic TS
|
||||
// settings. ConfigurationManager of extbase should *usually* only be called in extbase context and
|
||||
// needs a Request, which is usually set by extbase bootstrap.
|
||||
// We are however (most likely) not in extbase context here.
|
||||
// To prevent a fallback of extbase ConfigurationManager to $GLOBALS['TYPO3_REQUEST'], we set
|
||||
// the request explicitly here, to then fetch $formSettings from ext:form ConfigurationManager.
|
||||
// $typoScriptSettings is hand over to load() to apply TS overrides for single forms, see #92408.
|
||||
$this->extbaseConfigurationManager->setRequest($request);
|
||||
$typoScriptSettings = $this->extbaseConfigurationManager->getConfiguration(ExtbaseConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS, 'form');
|
||||
$formConfiguration = $this->formPersistenceManager->load($persistenceIdentifier, $typoScriptSettings, $request);
|
||||
ArrayUtility::mergeRecursiveWithOverrule($formConfiguration, $overrideConfiguration);
|
||||
$overrideConfiguration = $formConfiguration;
|
||||
$overrideConfiguration['persistenceIdentifier'] = $persistenceIdentifier;
|
||||
}
|
||||
if (empty($prototypeName)) {
|
||||
$prototypeName = $overrideConfiguration['prototypeName'] ?? 'standard';
|
||||
}
|
||||
// Even though getContainer() is internal, we can't get container injected here due to static scope
|
||||
/** @var FormFactoryInterface $factory */
|
||||
$factory = GeneralUtility::getContainer()->get($this->arguments['factoryClass']);
|
||||
$formDefinition = $factory->build($overrideConfiguration, $prototypeName, $request);
|
||||
$form = $formDefinition->bind($request);
|
||||
return $form->render();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?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\ViewHelpers;
|
||||
|
||||
use TYPO3\CMS\Extbase\Error\Error;
|
||||
use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface;
|
||||
use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
|
||||
use TYPO3\CMS\Form\Service\TranslationService;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
|
||||
/**
|
||||
* Translate form element properties.
|
||||
*
|
||||
* Scope: frontend / backend
|
||||
*
|
||||
* @see https://docs.typo3.org/permalink/t3viewhelper:typo3-form-translateelementerror
|
||||
*/
|
||||
final class TranslateElementErrorViewHelper extends AbstractViewHelper
|
||||
{
|
||||
public function __construct(
|
||||
private readonly TranslationService $translationService
|
||||
) {}
|
||||
|
||||
public function initializeArguments(): void
|
||||
{
|
||||
$this->registerArgument('element', RootRenderableInterface::class, 'Form Element to translate', true);
|
||||
$this->registerArgument('error', Error::class, 'Error', true);
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
$element = $this->arguments['element'];
|
||||
$error = $this->arguments['error'];
|
||||
/** @var FormRuntime $formRuntime */
|
||||
$formRuntime = $this->renderingContext
|
||||
->getViewHelperVariableContainer()
|
||||
->get(RenderRenderableViewHelper::class, 'formRuntime');
|
||||
return $this->translationService->translateFormElementError(
|
||||
$element,
|
||||
$error->getCode(),
|
||||
$error->getArguments(),
|
||||
$error->__toString(),
|
||||
$formRuntime
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?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\ViewHelpers;
|
||||
|
||||
use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface;
|
||||
use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
|
||||
use TYPO3\CMS\Form\Service\TranslationService;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\InvalidArgumentValueException;
|
||||
|
||||
/**
|
||||
* Translate form element properties.
|
||||
*
|
||||
* Scope: frontend / backend
|
||||
*
|
||||
* @see https://docs.typo3.org/permalink/t3viewhelper:typo3-form-translateelementproperty
|
||||
*/
|
||||
final class TranslateElementPropertyViewHelper extends AbstractViewHelper
|
||||
{
|
||||
public function __construct(
|
||||
private readonly TranslationService $translationService
|
||||
) {}
|
||||
|
||||
public function initializeArguments(): void
|
||||
{
|
||||
$this->registerArgument('element', RootRenderableInterface::class, 'Form Element to translate', true);
|
||||
$this->registerArgument('property', 'mixed', 'Property to translate');
|
||||
$this->registerArgument('renderingOptionProperty', 'mixed', 'Property to translate');
|
||||
$this->registerArgument('languageKey', 'string', 'Language key ("da" for example) or "default" to use. Also a Locale object is possible. If empty, use current locale from the request.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array element by key.
|
||||
*/
|
||||
public function render(): array|string|null
|
||||
{
|
||||
self::assertArgumentTypes($this->arguments);
|
||||
$element = $this->arguments['element'];
|
||||
$property = null;
|
||||
if (!empty($this->arguments['property'])) {
|
||||
$property = $this->arguments['property'];
|
||||
} elseif (!empty($this->arguments['renderingOptionProperty'])) {
|
||||
$property = $this->arguments['renderingOptionProperty'];
|
||||
}
|
||||
if (empty($property)) {
|
||||
$propertyParts = [];
|
||||
} elseif (is_array($property)) {
|
||||
$propertyParts = $property;
|
||||
} else {
|
||||
$propertyParts = [$property];
|
||||
}
|
||||
/** @var FormRuntime $formRuntime */
|
||||
$formRuntime = $this->renderingContext
|
||||
->getViewHelperVariableContainer()
|
||||
->get(RenderRenderableViewHelper::class, 'formRuntime');
|
||||
return $this->translationService->translateFormElementValue($element, $propertyParts, $formRuntime, $this->arguments['languageKey']);
|
||||
}
|
||||
|
||||
private static function assertArgumentTypes(array $arguments): void
|
||||
{
|
||||
foreach (['property', 'renderingOptionProperty'] as $argumentName) {
|
||||
if (
|
||||
!isset($arguments[$argumentName])
|
||||
|| is_string($arguments[$argumentName])
|
||||
|| is_array($arguments[$argumentName])
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
throw new InvalidArgumentValueException(
|
||||
sprintf(
|
||||
'Arguments "%s" either must be string or array',
|
||||
$argumentName
|
||||
),
|
||||
1504871830
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user