TYPO3 CMS  TYPO3_7-6
FormBuilder.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
25 
33 {
37  const COMPATIBILITY_THEME_NAME = 'Compatibility';
38 
43  public static function create(Configuration $configuration)
44  {
46  $formBuilder = \TYPO3\CMS\Form\Utility\FormUtility::getObjectManager()->get(self::class);
47  $formBuilder->setConfiguration($configuration);
48  return $formBuilder;
49  }
50 
54  protected $formUtility;
55 
59  protected $typoScriptService;
60 
65 
69  protected $validationBuilder;
70 
75 
80 
84  protected $sessionUtility;
85 
89  protected $objectManager;
90 
94  protected $elementCounter;
95 
99  protected $validationErrors = null;
100 
104  protected $configuration;
105 
110 
115  public function injectTypoScriptService(\TYPO3\CMS\Extbase\Service\TypoScriptService $typoScriptService)
116  {
117  $this->typoScriptService = $typoScriptService;
118  }
119 
124  public function injectTypoScriptRepository(\TYPO3\CMS\Form\Domain\Repository\TypoScriptRepository $typoScriptRepository)
125  {
126  $this->typoScriptRepository = $typoScriptRepository;
127  }
128 
133  public function injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher)
134  {
135  $this->signalSlotDispatcher = $signalSlotDispatcher;
136  }
137 
142  public function injectSessionUtility(\TYPO3\CMS\Form\Utility\SessionUtility $sessionUtility)
143  {
144  $this->sessionUtility = $sessionUtility;
145  }
146 
151  public function injectElementCounter(\TYPO3\CMS\Form\Utility\ElementCounter $elementCounter)
152  {
153  $this->elementCounter = $elementCounter;
154  }
155 
160  public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
161  {
162  $this->objectManager = $objectManager;
163  }
164 
168  public function __construct()
169  {
170  $this->compatibilityService = CompatibilityLayerUtility::create($this);
171  }
172 
176  public function getConfiguration()
177  {
178  return $this->configuration;
179  }
180 
185  {
186  $this->configuration = $configuration;
187  }
188 
192  public function getControllerContext()
193  {
195  }
196 
201  {
202  $this->controllerContext = $controllerContext;
203  }
204 
208  public function getCompatibilityService()
209  {
211  }
212 
217  {
218  $this->compatibilityService = $compatibilityService;
219  }
220 
224  public function getFormUtility()
225  {
226  return $this->formUtility;
227  }
228 
233  {
234  $this->formUtility = $formUtility;
235  }
236 
240  public function getValidationBuilder()
241  {
243  }
244 
249  {
250  $this->validationBuilder = $validationBuilder;
251  }
252 
259  public function buildModel()
260  {
261  $userConfiguredFormTypoScript = $this->configuration->getTypoScript();
262 
263  if ($this->configuration->getCompatibility()) {
264  $layout = [];
265  if (isset($userConfiguredFormTypoScript['layout.'])) {
266  $layout = $userConfiguredFormTypoScript['layout.'];
267  /* use the compatibility theme whenever if a layout is defined */
268  $this->configuration->setThemeName(static::COMPATIBILITY_THEME_NAME);
269  unset($userConfiguredFormTypoScript['layout.']);
270  }
271 
272  switch ($this->getControllerAction()) {
273  case 'show':
274  $actionLayoutKey = 'form.';
275  break;
276  case 'confirmation':
277  $actionLayoutKey = 'confirmationView.';
278  break;
279  case 'process':
280  $actionLayoutKey = 'postProcessor.';
281  break;
282  default:
283  $actionLayoutKey = '';
284  break;
285  }
286  if ($actionLayoutKey && isset($userConfiguredFormTypoScript[$actionLayoutKey]['layout.'])) {
287  $actionLayout = $userConfiguredFormTypoScript[$actionLayoutKey]['layout.'];
288  $this->configuration->setThemeName(static::COMPATIBILITY_THEME_NAME);
289  unset($userConfiguredFormTypoScript[$actionLayoutKey]['layout.']);
290  $layout = array_replace_recursive($layout, $actionLayout);
291  }
292 
293  if (!empty($layout)) {
294  $this->compatibilityService->setGlobalLayoutConfiguration($layout);
295  }
296  }
297 
298  $form = $this->createElementObject();
299  $this->reviveElement($form, $userConfiguredFormTypoScript, 'FORM');
300  $form->setThemeName($this->configuration->getThemeName());
301  return $form;
302  }
303 
309  protected function createElementObject()
310  {
311  $element = GeneralUtility::makeInstance(Element::class);
312  return $element;
313  }
314 
323  protected function reviveElement(Element $element, array $userConfiguredElementTypoScript, $elementType = '')
324  {
325  // @todo Check $userConfiguredElementTypoScript
326 
327  if ($elementType === 'IMAGEBUTTON') {
328  GeneralUtility::deprecationLog('EXT:form: The element IMAGEBUTTON is deprecated since TYPO3 CMS 7, will be removed with TYPO3 CMS 8.');
329  }
330 
331  $element->setElementType($elementType);
332  $element->setElementCounter($this->elementCounter->getElementId());
333 
334  $elementBuilder = ElementBuilder::create($this, $element, $userConfiguredElementTypoScript);
335  $elementBuilder->setPartialPaths();
336  $elementBuilder->setVisibility();
337 
338  if ($element->getElementType() == 'CONTENTELEMENT') {
339  $attributeValue = '';
340  if ($this->configuration->getContentElementRendering()) {
341  $attributeValue = $this->formUtility->renderItem(
342  $userConfiguredElementTypoScript['cObj.'],
343  $userConfiguredElementTypoScript['cObj']
344  );
345  }
346  $element->setAdditionalArguments([
347  'content' => $attributeValue,
348  ]);
349  /* use the compatibility theme whenever if a layout is defined */
350  if ($this->configuration->getCompatibility()) {
351  $this->compatibilityService->setElementLayouts($element, $userConfiguredElementTypoScript);
352  if (isset($userConfiguredElementTypoScript['layout'])) {
353  $this->configuration->setThemeName(static::COMPATIBILITY_THEME_NAME);
354  unset($userConfiguredElementTypoScript['layout']);
355  }
356  }
357  } else {
358  $this->setAttributes($elementBuilder, $element, $userConfiguredElementTypoScript);
359  $userConfiguredElementTypoScript = $elementBuilder->getUserConfiguredElementTypoScript();
360  $this->setValidationMessages($element);
361  /* use the compatibility theme whenever if a layout is defined */
362  if ($this->configuration->getCompatibility()) {
363  $this->compatibilityService->setElementLayouts($element, $userConfiguredElementTypoScript);
364  if (isset($userConfiguredElementTypoScript['layout'])) {
365  $this->configuration->setThemeName(static::COMPATIBILITY_THEME_NAME);
366  unset($userConfiguredElementTypoScript['layout']);
367  }
368  }
369  $this->signalSlotDispatcher->dispatch(
370  __CLASS__,
371  'txFormAfterElementCreation',
372  [$element, $this]
373  );
374  // create all child elements
375  $this->setChildElementsByIntegerKey($element, $userConfiguredElementTypoScript);
376  }
377  }
378 
388  protected function setChildElementsByIntegerKey(Element $element, array $userConfiguredElementTypoScript)
389  {
390  if (is_array($userConfiguredElementTypoScript)) {
391  $keys = TemplateService::sortedKeyList($userConfiguredElementTypoScript);
392  foreach ($keys as $key) {
393  if (
394  (int)$key
395  && strpos($key, '.') === false
396  ) {
397  $elementType = $userConfiguredElementTypoScript[$key];
398  if (isset($userConfiguredElementTypoScript[$key . '.'])) {
399  $concreteChildElementTypoScript = $userConfiguredElementTypoScript[$key . '.'];
400  } else {
401  $concreteChildElementTypoScript = [];
402  }
403  $this->distinguishElementType($element, $concreteChildElementTypoScript, $elementType);
404  }
405  }
406  } else {
407  throw new \InvalidArgumentException('Container element with id=' . $element->getElementCounter() . ' has no configuration which means no children.', 1333754854);
408  }
409  }
410 
422  protected function distinguishElementType(Element $element, array $userConfiguredElementTypoScript, $elementType = '')
423  {
424  if (in_array($elementType, $this->typoScriptRepository->getRegisteredElementTypes())) {
425  $this->addChildElement($element, $userConfiguredElementTypoScript, $elementType);
426  } elseif ($this->configuration->getContentElementRendering()) {
427  $contentObject = [
428  'cObj' => $elementType,
429  'cObj.' => $userConfiguredElementTypoScript
430  ];
431  $this->addChildElement($element, $contentObject, 'CONTENTELEMENT');
432  }
433  }
434 
443  protected function addChildElement(Element $element, array $userConfiguredElementTypoScript, $elementType = '')
444  {
445  $childElement = $this->createElementObject();
446  $childElement->setParentElement($element);
447  $element->addChildElement($childElement);
448  $this->reviveElement($childElement, $userConfiguredElementTypoScript, $elementType);
449  }
450 
459  protected function setAttributes(ElementBuilder $elementBuilder, Element $element)
460  {
461  $htmlAttributes = $this->typoScriptRepository->getModelDefinedHtmlAttributes($element->getElementType());
462  $elementBuilder->setHtmlAttributes($htmlAttributes);
463  $elementBuilder->setHtmlAttributeWildcards();
464  $elementBuilder->overlayUserdefinedHtmlAttributeValues();
465  $elementBuilder->setNameAndId();
466  $elementBuilder->overlayFixedHtmlAttributeValues();
467  // remove all NULL values
468  $htmlAttributes = array_filter($elementBuilder->getHtmlAttributes());
469 
470  $elementBuilder->setHtmlAttributes($htmlAttributes);
471  $elementBuilder->moveHtmlAttributesToAdditionalArguments();
474  $htmlAttributes = $elementBuilder->getHtmlAttributes();
475  $userConfiguredElementTypoScript = $elementBuilder->getUserConfiguredElementTypoScript();
476  $additionalArguments = $elementBuilder->getAdditionalArguments();
477  $element->setHtmlAttributes($htmlAttributes);
478  $additionalArguments = $this->typoScriptService->convertTypoScriptArrayToPlainArray($additionalArguments);
479  $additionalArguments['prefix'] = $this->configuration->getPrefix();
480  $element->setAdditionalArguments($additionalArguments);
481  $this->handleIncomingValues($element, $userConfiguredElementTypoScript);
482 
483  if (
484  $element->getElementType() === 'FORM'
485  && $this->getControllerAction() === 'show'
486  ) {
487  if (empty($element->getHtmlAttribute('action'))) {
488  if (
489  $element->getAdditionalArgument('confirmation')
490  && (int)$element->getAdditionalArgument('confirmation') === 1
491  ) {
492  $element->setAdditionalArgument('action', 'confirmation');
493  } else {
494  $element->setAdditionalArgument('action', 'process');
495  }
496  } else {
497  $element->setAdditionalArgument('pageUid', $element->getHtmlAttribute('action'));
498  $element->setAdditionalArgument('action', null);
499  }
500  }
501 
502  // needed if confirmation page is enabled
503  if (
504  $this->sessionUtility->getSessionData($element->getName())
505  && $element->getAdditionalArgument('uploadedFiles') === null
506  ) {
507  $element->setAdditionalArgument('uploadedFiles', $this->sessionUtility->getSessionData($element->getName()));
508  }
509  }
510 
518  protected function handleIncomingValues(Element $element, array $userConfiguredElementTypoScript)
519  {
520  if (!$this->getIncomingData()) {
521  return;
522  }
523  $elementName = $element->getName();
524  if ($element->getHtmlAttribute('value') !== null) {
525  $modelValue = $element->getHtmlAttribute('value');
526  } else {
527  $modelValue = $element->getAdditionalArgument('value');
528  }
529 
530  if ($this->getIncomingData()->getIncomingField($elementName) !== null) {
531  /* filter values and set it back to incoming fields */
532  /* remove xss every time */
533  $userConfiguredElementTypoScript['filters.'][-1] = 'removexss';
534  $keys = TemplateService::sortedKeyList($userConfiguredElementTypoScript['filters.']);
535  foreach ($keys as $key) {
536  $class = $userConfiguredElementTypoScript['filters.'][$key];
537  if (
538  (int)$key
539  && strpos($key, '.') === false
540  ) {
541  $filterArguments = $userConfiguredElementTypoScript['filters.'][$key . '.'];
542  $filterClassName = $this->typoScriptRepository->getRegisteredClassName((string)$class, 'registeredFilters');
543  if ($filterClassName !== null) {
544  // toDo: handel array values
545  if (is_string($this->getIncomingData()->getIncomingField($elementName))) {
546  if (is_null($filterArguments)) {
547  $filter = $this->objectManager->get($filterClassName);
548  } else {
549  $filter = $this->objectManager->get($filterClassName, $filterArguments);
550  }
551  if ($filter) {
552  $value = $filter->filter($this->getIncomingData()->getIncomingField($elementName));
553  $this->getIncomingData()->setIncomingField($elementName, $value);
554  } else {
555  throw new \RuntimeException('Class "' . $filterClassName . '" could not be loaded.');
556  }
557  }
558  } else {
559  throw new \RuntimeException('Class "' . $filterClassName . '" not registered via TypoScript.');
560  }
561  }
562  }
563 
564  if ($element->getHtmlAttribute('value') !== null) {
565  $element->setHtmlAttribute('value', $this->getIncomingData()->getIncomingField($elementName));
566  } else {
567  $element->setAdditionalArgument('value', $this->getIncomingData()->getIncomingField($elementName));
568  }
569  }
570  $this->signalSlotDispatcher->dispatch(
571  __CLASS__,
572  'txFormHandleIncomingValues',
573  [
574  $element,
575  $this->getIncomingData(),
576  $modelValue,
577  $this
578  ]
579  );
580  }
581 
589  protected function setValidationMessages(Element $element)
590  {
591  $elementName = $element->getName();
592  $mandatoryMessages = $this->validationBuilder->getMandatoryValidationMessagesByElementName($elementName);
593  $element->setMandatoryValidationMessages($mandatoryMessages);
594  if (
595  $this->getValidationErrors()
596  && $this->getValidationErrors()->forProperty($elementName)->hasErrors()
597  ) {
599  $errors = $this->getValidationErrors()->forProperty($elementName)->getErrors();
600  $errorMessages = [];
601  foreach ($errors as $error) {
602  $errorMessages[] = $error->getMessage();
603  }
604  $element->setValidationErrorMessages($errorMessages);
605  }
606  }
607 
613  public function getFormPrefix()
614  {
615  return $this->configuration->getPrefix();
616  }
617 
624  {
625  return !$this->configuration->getContentElementRendering();
626  }
627 
633  public function getControllerAction()
634  {
635  return $this->controllerContext->getRequest()->getControllerActionName();
636  }
637 
643  public function getCompatibilityMode()
644  {
645  return $this->configuration->getCompatibility();
646  }
647 
653  public function getIncomingData()
654  {
655  return $this->controllerContext->getValidationElement();
656  }
657 
664  public function setValidationErrors(\TYPO3\CMS\Extbase\Error\Result $validationErrors)
665  {
666  $this->validationErrors = $validationErrors;
667  }
668 
674  public function getValidationErrors()
675  {
677  }
678 }
addChildElement(Element $element)
Definition: Element.php:193
injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
setControllerContext(ControllerContext $controllerContext)
setFormUtility(FormUtility $formUtility)
setValidationErrorMessages(array $validationErrorMessages)
Definition: Element.php:287
handleIncomingValues(Element $element, array $userConfiguredElementTypoScript)
distinguishElementType(Element $element, array $userConfiguredElementTypoScript, $elementType='')
setAttributes(ElementBuilder $elementBuilder, Element $element)
static sortedKeyList($setupArr, $acceptOnlyProperties=false)
setValidationBuilder(ValidationBuilder $validationBuilder)
setElementCounter($elementCounter=0)
Definition: Element.php:235
injectElementCounter(\TYPO3\CMS\Form\Utility\ElementCounter $elementCounter)
reviveElement(Element $element, array $userConfiguredElementTypoScript, $elementType='')
setHtmlAttributes($htmlAttributes=[])
Definition: Element.php:308
setHtmlAttribute($key='', $value=null)
Definition: Element.php:342
injectTypoScriptRepository(\TYPO3\CMS\Form\Domain\Repository\TypoScriptRepository $typoScriptRepository)
setChildElementsByIntegerKey(Element $element, array $userConfiguredElementTypoScript)
injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher)
injectSessionUtility(\TYPO3\CMS\Form\Utility\SessionUtility $sessionUtility)
setAdditionalArguments($additionalArguments=[])
Definition: Element.php:159
setMandatoryValidationMessages(array $mandatoryValidationMessages)
Definition: Element.php:405
setCompatibilityService(CompatibilityLayerUtility $compatibilityService)
injectTypoScriptService(\TYPO3\CMS\Extbase\Service\TypoScriptService $typoScriptService)
setValidationErrors(\TYPO3\CMS\Extbase\Error\Result $validationErrors)
addChildElement(Element $element, array $userConfiguredElementTypoScript, $elementType='')
setConfiguration(Configuration $configuration)
setAdditionalArgument($key='', $value=null)
Definition: Element.php:182