first commit

This commit is contained in:
salam committed 2026-10-19 08:18:39 +02:00
commit 1218dd1517
12716 files changed
+989233

No files matched your search

@@ -0,0 +1,71 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Form\ViewHelpers\Be;
/*
* 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!
*/
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Backend\View\PageLayoutView;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Used by the form editor.
* Render a content element preview like the page module
*
* Scope: backend
* @internal
*/
class RenderContentElementPreviewViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* As this ViewHelper renders HTML, the output must not be escaped.
*
* @var bool
*/
protected $escapeOutput = false;
/**
* Initialize arguments.
*
* @internal
*/
public function initializeArguments()
{
$this->registerArgument('contentElementUid', 'int', 'The uid of a content element');
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
* @internal
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$content = '';
$contentElementUid = $arguments['contentElementUid'];
$contentRecord = BackendUtility::getRecord('tt_content', $contentElementUid);
if (!empty($contentRecord)) {
$pageLayoutView = GeneralUtility::makeInstance(PageLayoutView::class);
$content = $pageLayoutView->tt_content_drawItem($contentRecord);
}
return $content;
}
}
@@ -0,0 +1,203 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Form\ViewHelpers\Form;
/*
* This file is part of the TYPO3 CMS project.
*
* It originated from the Neos.Form package (www.neos.io)
*
* 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!
*/
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper;
use TYPO3\CMS\Form\ViewHelpers\RenderRenderableViewHelper;
/**
* Display a jQuery date picker.
*
* Note: Requires jQuery UI to be included on the page.
*
* Scope: frontend
*/
class DatePickerViewHelper extends AbstractFormFieldViewHelper
{
/**
* @var string
*/
protected $tagName = 'input';
/**
* @var \TYPO3\CMS\Extbase\Property\PropertyMapper
*/
protected $propertyMapper;
/**
* @param \TYPO3\CMS\Extbase\Property\PropertyMapper $propertyMapper
* @internal
*/
public function injectPropertyMapper(\TYPO3\CMS\Extbase\Property\PropertyMapper $propertyMapper)
{
$this->propertyMapper = $propertyMapper;
}
/**
* Initialize the arguments.
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerTagAttribute('size', 'int', 'The size of the input field');
$this->registerTagAttribute('placeholder', 'string', 'Specifies a short hint that describes the expected value of an input element');
$this->registerArgument('errorClass', 'string', 'CSS class to set if there are errors for this ViewHelper', false, 'f3-form-error');
$this->registerArgument('initialDate', 'string', 'Initial date (@see http://www.php.net/manual/en/datetime.formats.php for supported formats)');
$this->registerArgument('enableDatePicker', 'bool', 'Enable the Datepicker', false, true);
$this->registerArgument('previewMode', 'bool', 'Preview mde flag', true, false);
$this->registerArgument('dateFormat', 'string', 'The date format', false, 'Y-m-d');
$this->registerUniversalTagAttributes();
}
/**
* Renders the text field, hidden field and required javascript
*
* @return string
*/
public function render()
{
$enableDatePicker = $this->arguments['enableDatePicker'];
$dateFormat = $this->arguments['dateFormat'];
$previewMode = (bool)$this->arguments['previewMode'];
$placeholder = $this->arguments['additionalAttributes']['placeholder'] ?? $this->arguments['placeholder'];
$name = $this->getName();
$this->registerFieldNameForFormTokenGeneration($name);
$this->tag->addAttribute('type', 'text');
$this->tag->addAttribute('name', $name . '[date]');
if ($this->hasArgument('id')) {
$id = $this->arguments['id'];
} else {
$id = 'field' . md5(uniqid());
}
if (empty($placeholder)) {
$this->tag->addAttribute('placeholder', $dateFormat);
}
if ($enableDatePicker) {
$this->tag->addAttribute('readonly', 'readonly');
if (!$previewMode) {
$datePickerDateFormat = $this->convertDateFormatToDatePickerFormat($dateFormat);
$this->renderInlineJavascript($id, $datePickerDateFormat);
}
}
$date = $this->getSelectedDate();
if ($date !== null) {
$this->tag->addAttribute('value', $date->format($dateFormat));
}
$this->tag->addAttribute('id', $id);
$this->setErrorClassAttribute();
$content = '';
$content .= $this->tag->render();
$content .= '<input type="hidden" name="' . htmlspecialchars($name) . '[dateFormat]" value="' . htmlspecialchars($dateFormat) . '" />';
return $content;
}
/**
* @return \DateTime|null
*/
protected function getSelectedDate()
{
/** @var FormRuntime $formRuntime */
$formRuntime = $this->renderingContext
->getViewHelperVariableContainer()
->get(RenderRenderableViewHelper::class, 'formRuntime');
$formState = $formRuntime->getFormState();
$date = $formRuntime[$this->arguments['property']];
if ($date instanceof \DateTime) {
return $date;
}
if ($date !== null) {
$date = $this->propertyMapper->convert($date, 'DateTime');
if (!$date instanceof \DateTime) {
return null;
}
return $date;
}
if ($this->hasArgument('initialDate')) {
return new \DateTime($this->arguments['initialDate']);
}
}
/**
* @param string $dateFormat
* @return string
*/
protected function convertDateFormatToDatePickerFormat(string $dateFormat): string
{
$replacements = [
'd' => 'dd',
'D' => 'D',
'j' => 'o',
'l' => 'DD',
'F' => 'MM',
'm' => 'mm',
'M' => 'M',
'n' => 'm',
'Y' => 'yy',
'y' => 'y'
];
return strtr($dateFormat, $replacements);
}
/**
* @param string $uniqueIdentifier
* @param string $datePickerDateFormat
*/
protected function renderInlineJavascript(string $uniqueIdentifier, string $datePickerDateFormat)
{
$this->getPageRenderer()->addJsFooterInlineCode(
'ext_form_datepicker-' . $uniqueIdentifier,
'if ("undefined" !== typeof $) {
$(function() {
$("#' . $uniqueIdentifier . '").datepicker({
dateFormat: "' . $datePickerDateFormat . '"
}).on("keydown", function(e) {
// By using "backspace" or "delete", you can clear the datepicker again.
if(e.keyCode == 8 || e.keyCode == 46) {
e.preventDefault();
$.datepicker._clearDate(this);
}
});
});
}
'
);
}
/**
* @return PageRenderer
*/
protected function getPageRenderer(): PageRenderer
{
return GeneralUtility::makeInstance(PageRenderer::class);
}
}
@@ -0,0 +1,159 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Form\ViewHelpers\Form;
/*
* This file is part of the TYPO3 CMS project.
*
* It originated from the Neos.Form package (www.neos.io)
*
* 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!
*/
use TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper;
use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
use TYPO3\CMS\Form\ViewHelpers\RenderRenderableViewHelper;
/**
* Displays two select-boxes for hour and minute selection.
*
* Scope: frontend
*/
class TimePickerViewHelper extends AbstractFormFieldViewHelper
{
/**
* @var string
*/
protected $tagName = 'select';
/**
* @var \TYPO3\CMS\Extbase\Property\PropertyMapper
*/
protected $propertyMapper;
/**
* @param \TYPO3\CMS\Extbase\Property\PropertyMapper $propertyMapper
* @internal
*/
public function injectPropertyMapper(\TYPO3\CMS\Extbase\Property\PropertyMapper $propertyMapper)
{
$this->propertyMapper = $propertyMapper;
}
/**
* Initialize the arguments.
*
* @internal
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerTagAttribute('size', 'int', 'The size of the select field');
$this->registerTagAttribute('placeholder', 'string', 'Specifies a short hint that describes the expected value of an input element');
$this->registerTagAttribute('disabled', 'string', 'Specifies that the select element should be disabled when the page loads');
$this->registerArgument('errorClass', 'string', 'CSS class to set if there are errors for this ViewHelper', false, 'f3-form-error');
$this->registerArgument('initialDate', 'string', 'Initial time (@see http://www.php.net/manual/en/datetime.formats.php for supported formats)');
$this->registerArgument('timeType', 'string', '"hour" or "minute"');
$this->registerUniversalTagAttributes();
}
/**
* Renders the select fields for hour & minute
*
* @return string
*/
public function render()
{
$name = $this->getName();
$this->registerFieldNameForFormTokenGeneration($name);
$this->tag->addAttribute('name', $name . '[hour]');
$date = $this->getSelectedDate();
$this->setErrorClassAttribute();
$content = '';
if ($this->arguments['timeType'] === 'hour') {
$content .= $this->buildHourSelector($date);
} else {
$content .= $this->buildMinuteSelector($date);
}
return $content;
}
/**
* @return \DateTime|null
*/
protected function getSelectedDate()
{
/** @var FormRuntime $formRuntime */
$formRuntime = $this->renderingContext
->getViewHelperVariableContainer()
->get(RenderRenderableViewHelper::class, 'formRuntime');
$date = $formRuntime[$this->arguments['property']];
if ($date instanceof \DateTime) {
return $date;
}
if ($date !== null) {
$date = $this->propertyMapper->convert($date, 'DateTime');
if (!$date instanceof \DateTime) {
return null;
}
return $date;
}
if ($this->hasArgument('initialDate')) {
return new \DateTime($this->arguments['initialDate']);
}
}
/**
* @param \DateTime $date
* @return string
*/
protected function buildHourSelector(\DateTime $date = null): string
{
$value = $date !== null ? $date->format('H') : null;
$hourSelector = clone $this->tag;
$hourSelector->addAttribute('name', sprintf('%s[hour]', $this->getName()));
$options = '';
foreach (range(0, 23) as $hour) {
$hour = str_pad((string)$hour, 2, '0', STR_PAD_LEFT);
$selected = $hour === $value ? ' selected="selected"' : '';
$options .= '<option value="' . htmlspecialchars($hour) . '" ' . $selected . '>' . htmlspecialchars($hour) . '</option>';
}
$hourSelector->setContent($options);
return $hourSelector->render();
}
/**
* @param \DateTime $date
* @return string
*/
protected function buildMinuteSelector(\DateTime $date = null): string
{
$value = $date !== null ? $date->format('i') : null;
$minuteSelector = clone $this->tag;
if ($this->hasArgument('id')) {
$minuteSelector->addAttribute('id', $this->arguments['id'] . '-minute');
}
$minuteSelector->addAttribute('name', sprintf('%s[minute]', $this->getName()));
$options = '';
foreach (range(0, 59) as $minute) {
$minute = str_pad((string)$minute, 2, '0', STR_PAD_LEFT);
$selected = $minute === $value ? ' selected="selected"' : '';
$options .= '<option value="' . htmlspecialchars($minute) . '"' . $selected . '>' . htmlspecialchars($minute) . '</option>';
}
$minuteSelector->setContent($options);
return $minuteSelector->render();
}
}
@@ -0,0 +1,127 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Form\ViewHelpers\Form;
/*
* 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!
*/
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
use TYPO3\CMS\Extbase\Security\Cryptography\HashService;
use TYPO3\CMS\Fluid\ViewHelpers\Form\UploadViewHelper;
/**
* 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
*/
class UploadedResourceViewHelper extends UploadViewHelper
{
/**
* @var HashService
*/
protected $hashService;
/**
* @var \TYPO3\CMS\Extbase\Property\PropertyMapper
*/
protected $propertyMapper;
/**
* @param HashService $hashService
* @internal
*/
public function injectHashService(\TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService)
{
$this->hashService = $hashService;
}
/**
* @param \TYPO3\CMS\Extbase\Property\PropertyMapper $propertyMapper
* @internal
*/
public function injectPropertyMapper(\TYPO3\CMS\Extbase\Property\PropertyMapper $propertyMapper)
{
$this->propertyMapper = $propertyMapper;
}
/**
* Initialize the arguments.
*
* @internal
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('as', 'string', '');
$this->registerArgument('accept', 'array', 'Values for the accept attribute', false, []);
}
/**
* @return string
*/
public function render()
{
$output = '';
$as = $this->arguments['as'];
$accept = $this->arguments['accept'];
$resource = $this->getUploadedResource();
if (!empty($accept)) {
$this->tag->addAttribute('accept', implode(',', $accept));
}
if ($resource !== null) {
$resourcePointerIdAttribute = '';
if ($this->hasArgument('id')) {
$resourcePointerIdAttribute = ' id="' . htmlspecialchars($this->arguments['id']) . '-file-reference"';
}
$resourcePointerValue = $resource->getUid();
if ($resourcePointerValue === null) {
// Newly created file reference which is not persisted yet.
// Use the file UID instead, but prefix it with "file:" to communicate this to the type converter
$resourcePointerValue = 'file:' . $resource->getOriginalResource()->getOriginalFile()->getUid();
}
$output .= '<input type="hidden" name="' . htmlspecialchars($this->getName()) . '[submittedFile][resourcePointer]" value="' . htmlspecialchars($this->hashService->appendHmac((string)$resourcePointerValue)) . '"' . $resourcePointerIdAttribute . ' />';
$this->templateVariableContainer->add($as, $resource);
$output .= $this->renderChildren();
$this->templateVariableContainer->remove($as);
}
$output .= parent::render();
return $output;
}
/**
* Return a previously uploaded resource.
* Return NULL if errors occurred during property mapping for this property.
*
* @return FileReference|null
*/
protected function getUploadedResource()
{
if ($this->getMappingResultsForProperty()->hasErrors()) {
return null;
}
$resource = $this->getValueAttribute();
if ($resource instanceof FileReference) {
return $resource;
}
return $this->propertyMapper->convert($resource, FileReference::class);
}
}
@@ -0,0 +1,90 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Form\ViewHelpers;
/*
* This file is part of the TYPO3 CMS project.
*
* It originated from the Neos.Form package (www.neos.io)
*
* 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!
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper as FluidFormViewHelper;
use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
use TYPO3Fluid\Fluid\Core\ViewHelper\TagBuilder;
/**
* Custom form ViewHelper that renders the form state instead of referrer fields
*
* Scope: frontend
*/
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()
{
$formRuntime = $this->getFormRuntime();
$prefix = $this->prefixFieldName($this->getFormObjectName());
$markup = $this->createHiddenInputElement(
$prefix . '[__state]',
$this->hashService->appendHmac(
base64_encode(serialize($formRuntime->getFormState()))
)
);
// ONLY assign `__session` if form is performing (uncached)
if ($formRuntime->canProcessFormSubmission() && $formRuntime->getFormSession() !== null) {
$markup .= $this->createHiddenInputElement(
$prefix . '[__session]',
$formRuntime->getFormSession()->getAuthenticatedIdentifier()
);
}
return $markup;
}
/**
* @param string $name
* @param string $value
* @return string
*/
protected 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.
*
* @return string
*/
protected function getFormObjectName()
{
return $this->getFormRuntime()->getFormDefinition()->getIdentifier();
}
protected function getFormRuntime(): FormRuntime
{
return $this->arguments['object'];
}
}
@@ -0,0 +1,129 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Form\ViewHelpers;
/*
* 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!
*/
use TYPO3\CMS\Form\Domain\Model\FormElements\GridContainerInterface;
use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Scope: frontend
*/
class GridColumnClassAutoConfigurationViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* @var bool
*/
protected $escapeOutput = false;
/**
* Initialize the arguments.
*
* @internal
*/
public function initializeArguments()
{
$this->registerArgument('element', RootRenderableInterface::class, 'A RootRenderableInterface instance', true);
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
$formElement = $arguments['element'];
$gridRowElement = $formElement->getParentRenderable();
$gridContainerElement = $gridRowElement->getParentRenderable();
$gridRowEChildElements = $gridRowElement->getElements();
if ($gridContainerElement instanceof GridContainerInterface) {
$gridViewPortConfiguration = $gridContainerElement->getProperties()['gridColumnClassAutoConfiguration'];
} else {
$gridViewPortConfiguration = $gridRowElement->getProperties()['gridColumnClassAutoConfiguration'];
}
if (empty($gridViewPortConfiguration)) {
return '';
}
$gridSize = (int)$gridViewPortConfiguration['gridSize'];
$columnsToCalculate = [];
$usedColumns = [];
foreach ($gridRowEChildElements as $childElement) {
if (empty($childElement->getProperties()['gridColumnClassAutoConfiguration'])) {
foreach ($gridViewPortConfiguration['viewPorts'] as $viewPortName => $configuration) {
$columnsToCalculate[$viewPortName]['elements']++;
}
} 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'] += (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']++;
}
}
}
}
$classes = [];
foreach ($gridViewPortConfiguration['viewPorts'] as $viewPortName => $configuration) {
if (isset($usedColumns[$viewPortName]['concreteNumbersOfColumnsToUse'])) {
$numbersOfColumnsToUse = $usedColumns[$viewPortName]['concreteNumbersOfColumnsToUse'];
} else {
$restColumnsToDivide = $gridSize - $usedColumns[$viewPortName]['sum'];
$restElements = (int)$columnsToCalculate[$viewPortName]['elements'];
if ($restColumnsToDivide < 1) {
$restColumnsToDivide = $gridSize;
}
if ($restElements < 1) {
$restElements = 1;
}
$numbersOfColumnsToUse = floor($restColumnsToDivide / $restElements);
}
$classes[] = str_replace(
'{@numbersOfColumnsToUse}',
$numbersOfColumnsToUse,
$configuration['classPattern']
);
}
return implode(' ', $classes);
}
}
@@ -0,0 +1,248 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Form\ViewHelpers;
/*
* This file is part of the TYPO3 CMS project.
*
* It originated from the Neos.Form package (www.neos.io)
*
* 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!
*/
use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface;
use TYPO3\CMS\Form\Domain\Model\Renderable\CompositeRenderableInterface;
use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface;
use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Renders the values of a form
*
* Scope: frontend
*/
class RenderAllFormValuesViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* @var bool
*/
protected $escapeOutput = false;
/**
* Initialize the arguments.
*
* @internal
*/
public function initializeArguments()
{
$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.
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string the rendered form values
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$renderable = $arguments['renderable'];
$as = $arguments['as'];
if (!$renderable->isEnabled()) {
return '';
}
if ($renderable instanceof CompositeRenderableInterface) {
$elements = $renderable->getRenderablesRecursively();
} else {
$elements = [$renderable];
}
$formRuntime = $renderingContext
->getViewHelperVariableContainer()
->get(RenderRenderableViewHelper::class, 'formRuntime');
$output = '';
foreach ($elements as $element) {
$renderingOptions = $element->getRenderingOptions();
if (
!$element instanceof FormElementInterface
|| (isset($renderingOptions['_isCompositeFormElement']) && (bool)$renderingOptions['_isCompositeFormElement'] === true)
|| !$element->isEnabled()
|| self::hasDisabledParent($element)
) {
continue;
}
if (
(isset($renderingOptions['_isHiddenFormElement']) && (bool)$renderingOptions['_isHiddenFormElement'] === true)
|| (isset($renderingOptions['_isReadOnlyFormElement']) && (bool)$renderingOptions['_isReadOnlyFormElement'] === true)
) {
trigger_error(
'Using the properties "renderingOptions._isHiddenFormElement" and "renderingOptions._isReadOnlyFormElement" will be removed in TYPO3 v10.0. Use variants instead.',
E_USER_DEPRECATED
);
continue;
}
$value = $formRuntime[$element->getIdentifier()];
$formValue = [
'element' => $element,
'value' => $value,
'processedValue' => self::processElementValue($element, $value, $renderChildrenClosure, $renderingContext),
'isMultiValue' => is_array($value) || $value instanceof \Iterator
];
$renderingContext->getVariableProvider()->add($as, $formValue);
$output .= $renderChildrenClosure();
$renderingContext->getVariableProvider()->remove($as);
}
return $output;
}
/**
* Converts the given value to a simple type (string or array) considering the underlying FormElement definition
*
* @param FormElementInterface $element
* @param mixed $value
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return mixed
* @internal
*/
public static function processElementValue(
FormElementInterface $element,
$value,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
$properties = $element->getProperties();
if (isset($properties['options']) && is_array($properties['options'])) {
$properties['options'] = TranslateElementPropertyViewHelper::renderStatic(
['element' => $element, 'property' => 'options'],
$renderChildrenClosure,
$renderingContext
);
if (is_array($value)) {
return self::mapValuesToOptions($value, $properties['options']);
}
return self::mapValueToOption($value, $properties['options']);
}
if (is_object($value)) {
return self::processObject($element, $value);
}
return $value;
}
/**
* Replaces the given values (=keys) with the corresponding elements in $options
* @see mapValueToOption()
*
* @param array $value
* @param array $options
* @return array
* @internal
*/
public 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
* @param array $options
* @return mixed
* @internal
*/
public static function mapValueToOption($value, array $options)
{
return $options[$value] ?? $value;
}
/**
* Converts the given $object to a string representation considering the $element FormElement definition
*
* @param FormElementInterface $element
* @param object $object
* @return string
* @internal
*/
public static function processObject(FormElementInterface $element, $object): string
{
$properties = $element->getProperties();
if ($object instanceof \DateTime) {
if (
$element->getType() === 'DatePicker'
&& isset($properties['dateFormat'])
) {
$dateFormat = $properties['dateFormat'];
if (isset($properties['displayTimeSelector']) && $properties['displayTimeSelector'] === true) {
$dateFormat .= ' H:i';
}
} elseif ($element->getType() === 'Date') {
if (isset($properties['displayFormat'])) {
$dateFormat = $properties['displayFormat'];
} else {
$dateFormat = 'Y-m-d';
}
} else {
$dateFormat = \DateTime::W3C;
}
return $object->format($dateFormat);
}
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) . ']';
}
/**
* @param RenderableInterface $renderable
* @return bool
* @internal
*/
public static function hasDisabledParent(RenderableInterface $renderable): bool
{
while ($renderable = $renderable->getParentRenderable()) {
if ($renderable instanceof RenderableInterface && !$renderable->isEnabled()) {
return true;
}
}
return false;
}
}
@@ -0,0 +1,103 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Form\ViewHelpers;
/*
* This file is part of the TYPO3 CMS project.
*
* It originated from the Neos.Form package (www.neos.io)
*
* 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!
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface;
use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface;
use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Render a renderable.
*
* Set the renderable into the \TYPO3\CMS\Form\Mvc\View\FormView
* and return the rendered content.
*
* Scope: frontend
*/
class RenderRenderableViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* @var bool
*/
protected $escapeOutput = false;
/**
* Initialize the arguments.
*
* @internal
*/
public function initializeArguments()
{
$this->registerArgument('renderable', RootRenderableInterface::class, 'A RenderableInterface instance', true);
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
* @internal
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
/** @var FormRuntime $formRuntime */
$formRuntime = $renderingContext
->getViewHelperVariableContainer()
->get(self::class, 'formRuntime');
$renderable = $arguments['renderable'];
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeRendering'] ?? [] as $className) {
$hookObj = GeneralUtility::makeInstance($className);
if (method_exists($hookObj, 'beforeRendering')) {
$hookObj->beforeRendering(
$formRuntime,
$renderable
);
}
}
$content = '';
if ($renderable instanceof FormRuntime || $renderable instanceof RenderableInterface && $renderable->isEnabled()) {
$content = $renderChildrenClosure();
}
// Wrap every renderable with a span with a 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,114 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Form\ViewHelpers;
/*
* This file is part of the TYPO3 CMS project.
*
* It originated from the Neos.Form package (www.neos.io)
*
* 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!
*/
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Web\Response;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Form\Domain\Factory\ArrayFormFactory;
use TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* 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
*/
class RenderViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* @var bool
*/
protected $escapeOutput = false;
/**
* Initialize the arguments.
*
* @internal
*/
public function initializeArguments()
{
$this->registerArgument('persistenceIdentifier', 'string', 'The persistence identifier for the form.', false, null);
$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', false, null);
$this->registerArgument('overrideConfiguration', 'array', 'factory specific configuration', false, []);
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$persistenceIdentifier = $arguments['persistenceIdentifier'];
$factoryClass = $arguments['factoryClass'];
$prototypeName = $arguments['prototypeName'];
$overrideConfiguration = $arguments['overrideConfiguration'];
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
if (!empty($persistenceIdentifier)) {
$formPersistenceManager = $objectManager->get(FormPersistenceManagerInterface::class);
$formConfiguration = $formPersistenceManager->load($persistenceIdentifier);
ArrayUtility::mergeRecursiveWithOverrule(
$formConfiguration,
$overrideConfiguration
);
$overrideConfiguration = $formConfiguration;
$overrideConfiguration['persistenceIdentifier'] = $persistenceIdentifier;
}
if (empty($prototypeName)) {
$prototypeName = $overrideConfiguration['prototypeName'] ?? 'standard';
}
$factory = $objectManager->get($factoryClass);
$formDefinition = $factory->build($overrideConfiguration, $prototypeName);
$response = $renderingContext->getControllerContext()->getResponse() ?? $objectManager->get(Response::class);
$form = $formDefinition->bind($renderingContext->getControllerContext()->getRequest(), $response);
// If the controller context does not contain a response object, this viewhelper is used in a
// fluid template rendered by the FluidTemplateContentObject. Handle the StopActionException
// as there is no extbase dispatcher involved that catches that. */
if ($renderingContext->getControllerContext()->getResponse() === null) {
try {
return $form->render();
} catch (\TYPO3\CMS\Extbase\Mvc\Exception\StopActionException $exception) {
return $response->shutdown();
}
}
return $form->render();
}
}
@@ -0,0 +1,90 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Form\ViewHelpers;
/*
* 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!
*/
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\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Translate form element properites.
*
* Scope: frontend / backend
*/
class TranslateElementErrorViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Initialize arguments.
*
* @internal
*/
public function initializeArguments()
{
$this->registerArgument('element', RootRenderableInterface::class, 'Form Element to translate', true);
$this->registerArgument('error', Error::class, '', false, '');
$this->registerArgument('code', 'integer', 'Error code - deprecated', false, '');
$this->registerArgument('arguments', 'array', 'Error arguments - deprecated', false, null);
$this->registerArgument('defaultValue', 'string', 'The default value - deprecated', false, '');
}
/**
* Return array element by key.
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$element = $arguments['element'];
$error = $arguments['error'];
$code = $arguments['code'];
$errorArguments = $arguments['arguments'];
$defaultValue = $arguments['defaultValue'];
if ($error instanceof Error) {
$code = $error->getCode();
$errorArguments = $error->getArguments();
$defaultValue = $error->__toString();
} else {
trigger_error(
'TranslateElementErrorViewHelper arguments "code", "arguments" and "defaultValue" will be removed in TYPO3 v10.0. Use "error" instead.',
E_USER_DEPRECATED
);
}
/** @var FormRuntime $formRuntime */
$formRuntime = $renderingContext
->getViewHelperVariableContainer()
->get(RenderRenderableViewHelper::class, 'formRuntime');
return TranslationService::getInstance()->translateFormElementError(
$element,
$code,
$errorArguments,
$defaultValue,
$formRuntime
);
}
}
@@ -0,0 +1,106 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Form\ViewHelpers;
/*
* 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!
*/
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\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Exception;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Translate form element properties.
*
* Scope: frontend / backend
*/
class TranslateElementPropertyViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Initialize arguments.
*
* @internal
*/
public function initializeArguments()
{
$this->registerArgument('element', RootRenderableInterface::class, 'Form Element to translate', true);
$this->registerArgument('property', 'mixed', 'Property to translate', false);
$this->registerArgument('renderingOptionProperty', 'mixed', 'Property to translate', false);
}
/**
* Return array element by key.
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string|array
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
static::assertArgumentTypes($arguments);
$element = $arguments['element'];
$property = null;
if (!empty($arguments['property'])) {
$property = $arguments['property'];
} elseif (!empty($arguments['renderingOptionProperty'])) {
$property = $arguments['renderingOptionProperty'];
}
if (empty($property)) {
$propertyParts = [];
} elseif (is_array($property)) {
$propertyParts = $property;
} else {
$propertyParts = [$property];
}
/** @var FormRuntime $formRuntime */
$formRuntime = $renderingContext
->getViewHelperVariableContainer()
->get(RenderRenderableViewHelper::class, 'formRuntime');
return TranslationService::getInstance()->translateFormElementValue($element, $propertyParts, $formRuntime);
}
/**
* @param array $arguments
*/
protected static function assertArgumentTypes(array $arguments)
{
foreach (['property', 'renderingOptionProperty'] as $argumentName) {
if (
!isset($arguments[$argumentName])
|| is_string($arguments[$argumentName])
|| is_array($arguments[$argumentName])
) {
continue;
}
throw new Exception(
sprintf(
'Arguments "%s" either must be string or array',
$argumentName
),
1504871830
);
}
}
}