‪TYPO3CMS  ‪main
FormRuntime.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
18 /*
19  * Inspired by and partially taken from the Neos.Form package (www.neos.io)
20  */
21 
23 
24 use Psr\Container\ContainerInterface;
25 use Psr\Http\Message\ResponseInterface;
59 use ‪TYPO3\CMS\Form\Exception as FormException;
64 
106 class ‪FormRuntime implements ‪RootRenderableInterface, \ArrayAccess
107 {
108  public const ‪HONEYPOT_NAME_SESSION_IDENTIFIER = 'tx_form_honeypot_name_';
109 
112  protected ResponseInterface ‪$response;
113 
117  protected ‪$formState;
118 
125  protected ‪$formSession;
126 
137  protected ‪$currentPage;
138 
145  protected ‪$lastDisplayedPage;
146 
152  protected ‪$currentSiteLanguage;
153 
159  protected ‪$currentFinisher;
160 
161  public function ‪__construct(
162  protected readonly ContainerInterface $container,
163  protected readonly ‪ConfigurationManagerInterface $configurationManager,
164  protected readonly ‪HashService $hashService,
165  protected readonly ‪ValidatorResolver $validatorResolver,
166  ) {
167  $this->response = new ‪Response();
168  }
169 
171  {
172  $this->formDefinition = ‪$formDefinition;
173  }
174 
176  {
177  $this->request = clone ‪$request;
178  }
179 
180  public function ‪initialize()
181  {
182  $arguments = array_merge_recursive($this->request->getArguments(), $this->request->getUploadedFiles());
183  $formIdentifier = $this->formDefinition->getIdentifier();
184  if (isset($arguments[$formIdentifier])) {
185  $this->request = $this->request->withArguments($arguments[$formIdentifier]);
186  }
187 
192  $this->‪processVariants();
195 
196  // Only validate and set form values within the form state
197  // if the current request is not the very first request
198  // and the current request can be processed (POST request and uncached).
199  if (!$this->‪isFirstRequest() && $this->‪canProcessFormSubmission()) {
201  }
202 
204  }
205 
209  protected function ‪initializeFormSessionFromRequest(): void
210  {
211  // Initialize the form session only if the current request can be processed
212  // (POST request and uncached) to ensure unique sessions for each form submitter.
213  if (!$this->‪canProcessFormSubmission()) {
214  return;
215  }
216 
218  $extbaseRequestParameters = $this->request->getAttribute('extbase');
219  $sessionIdentifierFromRequest = $extbaseRequestParameters->getInternalArgument('__session');
220  $this->formSession = GeneralUtility::makeInstance(FormSession::class, $sessionIdentifierFromRequest);
221  }
222 
227  protected function ‪initializeFormStateFromRequest()
228  {
229  // Only try to reconstitute the form state if the current request
230  // is not the very first request and if the current request can
231  // be processed (POST request and uncached).
233  $extbaseRequestParameters = $this->request->getAttribute('extbase');
234  $serializedFormStateWithHmac = $extbaseRequestParameters->getInternalArgument('__state');
235  if ($serializedFormStateWithHmac === null || !$this->‪canProcessFormSubmission()) {
236  $this->formState = GeneralUtility::makeInstance(FormState::class);
237  } else {
238  try {
239  $serializedFormState = $this->hashService->validateAndStripHmac($serializedFormStateWithHmac);
240  } catch (InvalidHashException | InvalidArgumentForHashGenerationException $e) {
241  throw new ‪BadRequestException('The HMAC of the form state could not be validated.', 1581862823);
242  }
243  $this->formState = unserialize(base64_decode($serializedFormState));
244  }
245  }
246 
247  protected function ‪triggerAfterFormStateInitialized(): void
248  {
249  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterFormStateInitialized'] ?? [] as $className) {
250  $hookObj = GeneralUtility::makeInstance($className);
251  if ($hookObj instanceof AfterFormStateInitializedInterface) {
252  $hookObj->afterFormStateInitialized($this);
253  }
254  }
255  }
256 
260  protected function ‪initializeCurrentPageFromRequest()
261  {
262  // If there was no previous form submissions or if the current request
263  // can't be processed (no POST request and/or cached) then display the first
264  // form step
265  if (!$this->formState->isFormSubmitted() || !$this->canProcessFormSubmission()) {
266  $this->currentPage = $this->formDefinition->getPageByIndex(0);
267 
268  if (!$this->currentPage->isEnabled()) {
269  throw new FormException('Disabling the first page is not allowed', 1527186844);
270  }
271 
272  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterInitializeCurrentPage'] ?? [] as $className) {
273  $hookObj = GeneralUtility::makeInstance($className);
274  if (method_exists($hookObj, 'afterInitializeCurrentPage')) {
275  $this->currentPage = $hookObj->afterInitializeCurrentPage(
276  $this,
277  $this->currentPage,
278  null,
279  $this->request->getArguments()
280  );
281  }
282  }
283  return;
284  }
285 
286  $this->lastDisplayedPage = $this->formDefinition->getPageByIndex($this->formState->getLastDisplayedPageIndex());
288  $extbaseRequestParameters = $this->request->getAttribute('extbase');
289  $currentPageIndex = (int)$extbaseRequestParameters->getInternalArgument('__currentPage');
290 
291  if ($this->‪userWentBackToPreviousStep()) {
292  if ($currentPageIndex < $this->lastDisplayedPage->getIndex()) {
293  $currentPageIndex = $this->lastDisplayedPage->getIndex();
294  }
295  } else {
296  if ($currentPageIndex > $this->lastDisplayedPage->getIndex() + 1) {
297  $currentPageIndex = $this->lastDisplayedPage->getIndex() + 1;
298  }
299  }
300 
301  if ($currentPageIndex >= count($this->formDefinition->getPages())) {
302  // Last Page
303  $this->currentPage = null;
304  } else {
305  $this->currentPage = $this->formDefinition->getPageByIndex($currentPageIndex);
306 
307  if (!$this->currentPage->isEnabled()) {
308  if ($currentPageIndex === 0) {
309  throw new FormException('Disabling the first page is not allowed', 1527186845);
310  }
311 
312  if ($this->‪userWentBackToPreviousStep()) {
313  $this->currentPage = $this->‪getPreviousEnabledPage();
314  } else {
315  $this->currentPage = $this->‪getNextEnabledPage();
316  }
317  }
318  }
319 
320  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterInitializeCurrentPage'] ?? [] as $className) {
321  $hookObj = GeneralUtility::makeInstance($className);
322  if (method_exists($hookObj, 'afterInitializeCurrentPage')) {
323  $this->currentPage = $hookObj->afterInitializeCurrentPage(
324  $this,
325  $this->currentPage,
326  $this->lastDisplayedPage,
327  $this->request->getArguments()
328  );
329  }
330  }
331  }
332 
336  protected function ‪initializeHoneypotFromRequest()
337  {
338  $renderingOptions = $this->formDefinition->getRenderingOptions();
339  if (!isset($renderingOptions['honeypot']['enable'])
340  || $renderingOptions['honeypot']['enable'] === false
341  || ‪ApplicationType::fromRequest($this->request)->isBackend()
342  ) {
343  return;
344  }
345 
346  ‪ArrayUtility::assertAllArrayKeysAreValid($renderingOptions['honeypot'], ['enable', 'formElementToUse']);
347 
348  if (!$this->‪isFirstRequest()) {
349  $elementsCount = count($this->lastDisplayedPage->getElements());
350  if ($elementsCount === 0) {
351  return;
352  }
353 
354  $honeypotNameFromSession = $this->‪getHoneypotNameFromSession($this->lastDisplayedPage);
355  if ($honeypotNameFromSession) {
356  $honeypotElement = $this->lastDisplayedPage->createElement($honeypotNameFromSession, $renderingOptions['honeypot']['formElementToUse']);
357  ‪$validator = $this->validatorResolver->createValidator(EmptyValidator::class);
358  $honeypotElement->addValidator(‪$validator);
359  }
360  }
361  }
362 
366  protected function ‪renderHoneypot()
367  {
368  $renderingOptions = $this->formDefinition->getRenderingOptions();
369  if (!isset($renderingOptions['honeypot']['enable'])
370  || $this->currentPage === null
371  || $renderingOptions['honeypot']['enable'] === false
372  || ‪ApplicationType::fromRequest($this->request)->isBackend()
373  ) {
374  return;
375  }
376 
377  ‪ArrayUtility::assertAllArrayKeysAreValid($renderingOptions['honeypot'], ['enable', 'formElementToUse']);
378 
379  if (!$this->‪isAfterLastPage()) {
380  $elementsCount = count($this->currentPage->getElements());
381  if ($elementsCount === 0) {
382  return;
383  }
384 
385  if (!$this->‪isFirstRequest()) {
386  $honeypotNameFromSession = $this->‪getHoneypotNameFromSession($this->lastDisplayedPage);
387  if ($honeypotNameFromSession) {
388  $honeypotElement = $this->formDefinition->getElementByIdentifier($honeypotNameFromSession);
389  if ($honeypotElement instanceof FormElementInterface) {
390  $this->lastDisplayedPage->removeElement($honeypotElement);
391  }
392  }
393  }
394 
395  $elementsCount = count($this->currentPage->getElements());
396  $randomElementNumber = random_int(0, $elementsCount - 1);
397  $honeypotName = substr(str_shuffle('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'), 0, random_int(5, 26));
398 
399  $referenceElement = $this->currentPage->getElements()[$randomElementNumber];
400  $honeypotElement = $this->currentPage->createElement($honeypotName, $renderingOptions['honeypot']['formElementToUse']);
401  ‪$validator = $this->validatorResolver->createValidator(EmptyValidator::class);
402 
403  $honeypotElement->addValidator(‪$validator);
404  if (random_int(0, 1) === 1) {
405  $this->currentPage->moveElementAfter($honeypotElement, $referenceElement);
406  } else {
407  $this->currentPage->moveElementBefore($honeypotElement, $referenceElement);
408  }
409  $this->‪setHoneypotNameInSession($this->currentPage, $honeypotName);
410  }
411  }
412 
416  protected function ‪getHoneypotNameFromSession(Page $page)
417  {
418  if ($this->‪isFrontendUserAuthenticated()) {
419  $honeypotNameFromSession = $this->‪getFrontendUser()->getKey(
420  'user',
421  self::HONEYPOT_NAME_SESSION_IDENTIFIER . $this->‪getIdentifier() . $page->getIdentifier()
422  );
423  } else {
424  $honeypotNameFromSession = $this->‪getFrontendUser()->getKey(
425  'ses',
426  self::HONEYPOT_NAME_SESSION_IDENTIFIER . $this->‪getIdentifier() . $page->getIdentifier()
427  );
428  }
429  return $honeypotNameFromSession;
430  }
431 
432  protected function ‪setHoneypotNameInSession(Page $page, string $honeypotName)
433  {
434  if ($this->‪isFrontendUserAuthenticated()) {
435  $this->‪getFrontendUser()->setKey(
436  'user',
437  self::HONEYPOT_NAME_SESSION_IDENTIFIER . $this->‪getIdentifier() . $page->getIdentifier(),
438  $honeypotName
439  );
440  } else {
441  $this->‪getFrontendUser()->setKey(
442  'ses',
443  self::HONEYPOT_NAME_SESSION_IDENTIFIER . $this->‪getIdentifier() . $page->getIdentifier(),
444  $honeypotName
445  );
446  }
447  }
448 
454  protected function ‪isFrontendUserAuthenticated(): bool
455  {
456  return (bool)GeneralUtility::makeInstance(Context::class)
457  ->getPropertyFromAspect('frontend.user', 'isLoggedIn', false);
458  }
459 
460  protected function ‪processVariants()
461  {
462  $conditionResolver = $this->‪getConditionResolver();
463 
464  $renderables = array_merge([$this->formDefinition], $this->formDefinition->getRenderablesRecursively());
465  foreach ($renderables as $renderable) {
466  if ($renderable instanceof ‪VariableRenderableInterface) {
467  $variants = $renderable->getVariants();
468  foreach ($variants as $variant) {
469  if ($variant->conditionMatches($conditionResolver)) {
470  $variant->apply();
471  }
472  }
473  }
474  }
475  }
476 
480  protected function ‪isAfterLastPage(): bool
481  {
482  return $this->currentPage === null;
483  }
484 
488  protected function ‪isFirstRequest(): bool
489  {
490  return $this->lastDisplayedPage === null;
491  }
492 
493  protected function ‪isPostRequest(): bool
494  {
495  return $this->‪getRequest()->getMethod() === 'POST';
496  }
497 
504  protected function ‪isRenderedCached(): bool
505  {
506  $contentObject = $this->request->getAttribute('currentContentObject');
507  // @todo: this does not work when rendering a cached `FLUIDTEMPLATE` (not nested in `COA_INT`)
508  // Rendering the form other than with the controller, will never work out cleanly.
509  // This likely can only be resolved by deprecating using the form render view helper
510  // other than in a template for the form plugin and covering the use cases the VH was introduced
511  // with a different concept
512  return $contentObject === null || $contentObject->getUserObjectType() === ‪ContentObjectRenderer::OBJECTTYPE_USER;
513  }
514 
518  protected function ‪processSubmittedFormValues()
519  {
520  $result = $this->‪mapAndValidatePage($this->lastDisplayedPage);
521  if ($result->hasErrors() && !$this->userWentBackToPreviousStep()) {
522  $this->currentPage = ‪$this->lastDisplayedPage;
524  $extbaseRequestParameters = clone $this->request->getAttribute('extbase');
525  $extbaseRequestParameters->setOriginalRequestMappingResults($result);
526  $this->request = $this->request->withAttribute('extbase', $extbaseRequestParameters);
527  }
528  }
529 
533  protected function ‪userWentBackToPreviousStep(): bool
534  {
535  return !$this->‪isAfterLastPage() && !$this->‪isFirstRequest() && $this->currentPage->getIndex() < $this->lastDisplayedPage->getIndex();
536  }
537 
541  protected function ‪mapAndValidatePage(‪Page $page): ‪Result
542  {
543  $result = GeneralUtility::makeInstance(Result::class);
544  $requestArguments = $this->request->getArguments();
545 
546  $propertyPathsForWhichPropertyMappingShouldHappen = [];
547  $registerPropertyPaths = static function ($propertyPath) use (&$propertyPathsForWhichPropertyMappingShouldHappen) {
548  $propertyPathParts = explode('.', $propertyPath);
549  $accumulatedPropertyPathParts = [];
550  foreach ($propertyPathParts as $propertyPathPart) {
551  $accumulatedPropertyPathParts[] = $propertyPathPart;
552  $temporaryPropertyPath = implode('.', $accumulatedPropertyPathParts);
553  $propertyPathsForWhichPropertyMappingShouldHappen[$temporaryPropertyPath] = $temporaryPropertyPath;
554  }
555  };
556 
557  $value = null;
558 
559  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterSubmit'] ?? [] as $className) {
560  $hookObj = GeneralUtility::makeInstance($className);
561  if (method_exists($hookObj, 'afterSubmit')) {
562  $value = $hookObj->afterSubmit(
563  $this,
564  $page,
565  $value,
566  $requestArguments
567  );
568  }
569  }
570 
571  foreach ($page->‪getElementsRecursively() as $element) {
572  if (!$this->‪isRenderableEnabled($element)) {
573  continue;
574  }
575 
576  try {
577  $value = ‪ArrayUtility::getValueByPath($requestArguments, $element->getIdentifier(), '.');
578  } catch (MissingArrayPathException $exception) {
579  $value = null;
580  }
581 
582  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterSubmit'] ?? [] as $className) {
583  $hookObj = GeneralUtility::makeInstance($className);
584  if (method_exists($hookObj, 'afterSubmit')) {
585  $value = $hookObj->afterSubmit(
586  $this,
587  $element,
588  $value,
589  $requestArguments
590  );
591  }
592  }
593 
594  $this->formState->setFormValue($element->getIdentifier(), $value);
595  $registerPropertyPaths($element->getIdentifier());
596  }
597 
598  // The more parts the path has, the more early it is processed
599  usort($propertyPathsForWhichPropertyMappingShouldHappen, static function ($a, $b) {
600  return substr_count($b, '.') - substr_count($a, '.');
601  });
602 
603  $processingRules = $this->formDefinition->getProcessingRules();
604 
605  foreach ($propertyPathsForWhichPropertyMappingShouldHappen as $propertyPath) {
606  if (isset($processingRules[$propertyPath])) {
607  $processingRule = $processingRules[$propertyPath];
608  $value = $this->formState->getFormValue($propertyPath);
609  try {
610  $value = $processingRule->process($value);
611  } catch (‪PropertyException $exception) {
612  throw new PropertyMappingException(
613  'Failed to process FormValue at "' . $propertyPath . '" from "' . gettype($value) . '" to "' . $processingRule->getDataType() . '"',
614  1480024933,
615  $exception
616  );
617  }
618  $result->forProperty($this->‪getIdentifier() . '.' . $propertyPath)->merge($processingRule->getProcessingMessages());
619  $this->formState->setFormValue($propertyPath, $value);
620  }
621  }
622 
623  return $result;
624  }
625 
634  public function ‪overrideCurrentPage(int $pageIndex)
635  {
636  $this->currentPage = $this->formDefinition->getPageByIndex($pageIndex);
637  }
638 
645  public function ‪render()
646  {
647  if ($this->‪isAfterLastPage()) {
648  return $this->‪invokeFinishers();
649  }
650  $this->‪processVariants();
651 
652  $this->formState->setLastDisplayedPageIndex($this->currentPage->getIndex());
653 
654  if ($this->formDefinition->getRendererClassName() === '') {
655  throw new RenderingException(sprintf('The form definition "%s" does not have a rendererClassName set.', $this->formDefinition->getIdentifier()), 1326095912);
656  }
657  $rendererClassName = $this->formDefinition->getRendererClassName();
658  $renderer = $this->container->get($rendererClassName);
659  if (!($renderer instanceof RendererInterface)) {
660  throw new RenderingException(sprintf('The renderer "%s" des not implement RendererInterface', $rendererClassName), 1326096024);
661  }
662 
663  $renderer->setFormRuntime($this);
664  return $renderer->render();
665  }
666 
670  protected function ‪invokeFinishers(): string
671  {
672  $finisherContext = GeneralUtility::makeInstance(
673  FinisherContext::class,
674  $this,
675  $this->request
676  );
677 
678  ‪$output = '';
679  $this->response->getBody()->rewind();
680  $originalContent = $this->response->getBody()->getContents();
681  $this->response->getBody()->write('');
682  foreach ($this->formDefinition->getFinishers() as $finisher) {
683  $this->currentFinisher = $finisher;
684  $this->‪processVariants();
685 
686  $finisherOutput = $finisher->execute($finisherContext);
687  if (is_string($finisherOutput) && !empty($finisherOutput)) {
688  ‪$output .= $finisherOutput;
689  } else {
690  $this->response->getBody()->rewind();
691  ‪$output .= $this->response->getBody()->getContents();
692  $this->response->getBody()->write('');
693  }
694 
695  if ($finisherContext->isCancelled()) {
696  break;
697  }
698  }
699  $this->response->getBody()->rewind();
700  $this->response->getBody()->write($originalContent);
701 
702  return ‪$output;
703  }
704 
708  public function ‪getIdentifier(): string
709  {
710  return $this->formDefinition->getIdentifier();
711  }
712 
721  public function ‪getRequest(): RequestInterface
722  {
723  return ‪$this->request;
724  }
725 
734  public function ‪getResponse(): ResponseInterface
735  {
736  return ‪$this->response;
737  }
738 
747  public function ‪canProcessFormSubmission(): bool
748  {
749  return $this->‪isPostRequest() && !$this->‪isRenderedCached();
750  }
751 
755  public function ‪getFormSession(): ?‪FormSession
756  {
758  }
759 
763  public function ‪getCurrentPage(): ?‪Page
764  {
766  }
767 
771  public function ‪getPreviousPage(): ?‪Page
772  {
773  $previousPageIndex = $this->currentPage->‪getIndex() - 1;
774  if ($this->formDefinition->hasPageWithIndex($previousPageIndex)) {
775  return $this->formDefinition->getPageByIndex($previousPageIndex);
776  }
777  return null;
778  }
779 
783  public function ‪getNextPage(): ?‪Page
784  {
785  $nextPageIndex = $this->currentPage->‪getIndex() + 1;
786  if ($this->formDefinition->hasPageWithIndex($nextPageIndex)) {
787  return $this->formDefinition->getPageByIndex($nextPageIndex);
788  }
789  return null;
790  }
791 
796  public function ‪getPreviousEnabledPage(): ?‪Page
797  {
798  $previousPage = null;
799  $previousPageIndex = $this->currentPage->‪getIndex() - 1;
800  while ($previousPageIndex >= 0) {
801  if ($this->formDefinition->hasPageWithIndex($previousPageIndex)) {
802  $previousPage = $this->formDefinition->getPageByIndex($previousPageIndex);
803 
804  if ($previousPage->isEnabled()) {
805  break;
806  }
807 
808  $previousPage = null;
809  $previousPageIndex--;
810  } else {
811  $previousPage = null;
812  break;
813  }
814  }
815 
816  return $previousPage;
817  }
818 
823  public function ‪getNextEnabledPage(): ?‪Page
824  {
825  $nextPage = null;
826  $pageCount = count($this->formDefinition->getPages());
827  $nextPageIndex = $this->currentPage->getIndex() + 1;
828 
829  while ($nextPageIndex < $pageCount) {
830  if ($this->formDefinition->hasPageWithIndex($nextPageIndex)) {
831  $nextPage = $this->formDefinition->getPageByIndex($nextPageIndex);
832  $renderingOptions = $nextPage->getRenderingOptions();
833  if (
834  !isset($renderingOptions['enabled'])
835  || (bool)$renderingOptions['enabled']
836  ) {
837  break;
838  }
839  $nextPage = null;
840  $nextPageIndex++;
841  } else {
842  $nextPage = null;
843  break;
844  }
845  }
846 
847  return $nextPage;
848  }
849 
855  public function ‪getType(): string
856  {
857  return $this->formDefinition->getType();
858  }
859 
864  public function ‪offsetExists(mixed ‪$identifier): bool
865  {
866  ‪$identifier = (string)‪$identifier;
867  if ($this->‪getElementValue((string)$identifier) !== null) {
868  return true;
869  }
870 
871  if (is_callable([$this, 'get' . ucfirst(‪$identifier)])) {
872  return true;
873  }
874  if (is_callable([$this, 'has' . ucfirst(‪$identifier)])) {
875  return true;
876  }
877  if (is_callable([$this, 'is' . ucfirst(‪$identifier)])) {
878  return true;
879  }
880  if (property_exists($this, ‪$identifier)) {
881  $propertyReflection = new \ReflectionProperty($this, ‪$identifier);
882  return $propertyReflection->isPublic();
883  }
884 
885  return false;
886  }
887 
892  public function ‪offsetGet(mixed ‪$identifier): mixed
893  {
894  ‪$identifier = (string)‪$identifier;
895  if ($this->‪getElementValue($identifier) !== null) {
896  return $this->‪getElementValue($identifier);
897  }
898  $getterMethodName = 'get' . ucfirst(‪$identifier);
899  if (is_callable([$this, $getterMethodName])) {
900  return $this->{$getterMethodName}();
901  }
902  return null;
903  }
904 
909  public function ‪offsetSet(mixed ‪$identifier, mixed $value): void
910  {
911  ‪$identifier = (string)‪$identifier;
912  $this->formState->setFormValue(‪$identifier, $value);
913  }
914 
919  public function ‪offsetUnset(mixed ‪$identifier): void
920  {
921  ‪$identifier = (string)‪$identifier;
922  $this->formState->setFormValue(‪$identifier, null);
923  }
924 
930  public function ‪getElementValue(string ‪$identifier)
931  {
932  $formValue = $this->formState->getFormValue(‪$identifier);
933  if ($formValue !== null) {
934  return $formValue;
935  }
936  return $this->formDefinition->getElementDefaultValueByIdentifier(‪$identifier);
937  }
938 
942  public function ‪getPages(): array
943  {
944  return $this->formDefinition->getPages();
945  }
946 
950  public function ‪getFormState(): ?‪FormState
951  {
952  return ‪$this->formState;
953  }
954 
960  public function ‪getRenderingOptions(): array
961  {
962  return $this->formDefinition->getRenderingOptions();
963  }
964 
971  public function ‪getRendererClassName(): string
972  {
973  return $this->formDefinition->getRendererClassName();
974  }
975 
979  public function ‪getLabel(): string
980  {
981  return $this->formDefinition->getLabel();
982  }
983 
987  public function ‪getTemplateName(): string
988  {
989  return $this->formDefinition->getTemplateName();
990  }
991 
995  public function ‪getFormDefinition(): ‪FormDefinition
996  {
998  }
999 
1005  public function ‪getCurrentSiteLanguage(): ?‪SiteLanguage
1006  {
1008  }
1009 
1019  {
1020  $this->currentSiteLanguage = ‪$currentSiteLanguage;
1021  }
1022 
1027  protected function ‪initializeCurrentSiteLanguage(): void
1028  {
1029  if ($this->request->getAttribute('language') instanceof ‪SiteLanguage) {
1030  $this->currentSiteLanguage = $this->request->getAttribute('language');
1031  } else {
1032  $pageId = 0;
1033  $languageId = (int)GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('language', 'id', 0);
1034 
1035  if ($this->‪getTypoScriptFrontendController() !== null) {
1036  $pageId = $this->‪getTypoScriptFrontendController()->id;
1037  }
1038 
1039  $fakeSiteConfiguration = [
1040  'languages' => [
1041  [
1042  'languageId' => $languageId,
1043  'title' => 'Dummy',
1044  'navigationTitle' => '',
1045  'flag' => '',
1046  'locale' => '',
1047  ],
1048  ],
1049  ];
1050 
1051  $this->currentSiteLanguage = GeneralUtility::makeInstance(Site::class, 'form-dummy', $pageId, $fakeSiteConfiguration)
1052  ->getLanguageById($languageId);
1053  }
1054  }
1055 
1059  public function ‪getCurrentFinisher(): ?‪FinisherInterface
1060  {
1062  }
1063 
1064  protected function ‪getConditionResolver(): Resolver
1065  {
1066  $formValues = array_replace_recursive(
1067  $this->‪getFormState()->getFormValues(),
1068  $this->‪getRequest()->getArguments()
1069  );
1070  $page = $this->‪getCurrentPage() ?? $this->‪getFormDefinition()->getPageByIndex(0);
1071 
1072  $finisherIdentifier = '';
1073  if ($this->‪getCurrentFinisher() !== null) {
1074  if (method_exists($this->‪getCurrentFinisher(), 'getFinisherIdentifier')) {
1075  $finisherIdentifier = $this->‪getCurrentFinisher()->getFinisherIdentifier();
1076  } else {
1077  $finisherIdentifier = (new \ReflectionClass($this->‪getCurrentFinisher()))->getShortName();
1078  $finisherIdentifier = preg_replace('/Finisher$/', '', $finisherIdentifier);
1079  }
1080  }
1081 
1082  $contentObjectData = $this->request->getAttribute('currentContentObject')?->data ?? [];
1083 
1084  return GeneralUtility::makeInstance(
1085  Resolver::class,
1086  'form',
1087  [
1088  'formRuntime' => $this,
1089  'formValues' => $formValues,
1090  'stepIdentifier' => $page->‪getIdentifier(),
1091  'stepType' => $page->‪getType(),
1092  'finisherIdentifier' => $finisherIdentifier,
1093  'contentObject' => $contentObjectData,
1094  'request' => new RequestWrapper($this->‪getRequest()),
1095  'site' => $this->‪getRequest()->getAttribute('site'),
1096  'siteLanguage' => $this->‪getRequest()->getAttribute('language'),
1097  ]
1098  );
1099  }
1102  {
1103  return $this->request->getAttribute('frontend.user');
1104  }
1107  {
1108  return ‪$GLOBALS['TSFE'] ?? null;
1109  }
1110 
1111  protected function ‪isRenderableEnabled(‪RenderableInterface $renderable): bool
1112  {
1113  if (!$renderable->‪isEnabled()) {
1114  return false;
1115  }
1116 
1117  while ($renderable = $renderable->‪getParentRenderable()) {
1118  if ($renderable instanceof RenderableInterface && !$renderable->‪isEnabled()) {
1119  return false;
1120  }
1121  }
1122 
1123  return true;
1124  }
1125 }
‪TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface\isEnabled
‪isEnabled()
‪TYPO3\CMS\Form\Domain\Model\Renderable\AbstractRenderable\getIndex
‪getIndex()
Definition: AbstractRenderable.php:342
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\userWentBackToPreviousStep
‪userWentBackToPreviousStep()
Definition: FormRuntime.php:527
‪TYPO3\CMS\Core\ExpressionLanguage\RequestWrapper
Definition: RequestWrapper.php:36
‪TYPO3\CMS\Form\Domain\Model\FormElements\AbstractSection\getElementsRecursively
‪FormElementInterface[] getElementsRecursively()
Definition: AbstractSection.php:76
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime
Definition: FormRuntime.php:107
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\overrideCurrentPage
‪overrideCurrentPage(int $pageIndex)
Definition: FormRuntime.php:628
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\HONEYPOT_NAME_SESSION_IDENTIFIER
‪const HONEYPOT_NAME_SESSION_IDENTIFIER
Definition: FormRuntime.php:108
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\OBJECTTYPE_USER
‪const OBJECTTYPE_USER
Definition: ContentObjectRenderer.php:373
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\$formState
‪FormState $formState
Definition: FormRuntime.php:116
‪TYPO3\CMS\Form\Mvc\Validation\EmptyValidator
Definition: EmptyValidator.php:28
‪TYPO3\CMS\Extbase\Property\Exception
Definition: DuplicateObjectException.php:18
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getRenderingOptions
‪array getRenderingOptions()
Definition: FormRuntime.php:954
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getPreviousEnabledPage
‪getPreviousEnabledPage()
Definition: FormRuntime.php:790
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getType
‪getType()
Definition: FormRuntime.php:849
‪TYPO3\CMS\Form\Domain\Finishers\FinisherInterface
Definition: FinisherInterface.php:31
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\offsetUnset
‪offsetUnset(mixed $identifier)
Definition: FormRuntime.php:913
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getHoneypotNameFromSession
‪string null getHoneypotNameFromSession(Page $page)
Definition: FormRuntime.php:410
‪TYPO3\CMS\Form\Domain\Runtime\Exception\PropertyMappingException
Definition: PropertyMappingException.php:27
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\initializeFormSessionFromRequest
‪initializeFormSessionFromRequest()
Definition: FormRuntime.php:203
‪TYPO3\CMS\Extbase\Validation\ValidatorResolver
Definition: ValidatorResolver.php:38
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\isAfterLastPage
‪isAfterLastPage()
Definition: FormRuntime.php:474
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getNextEnabledPage
‪getNextEnabledPage()
Definition: FormRuntime.php:817
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\initialize
‪initialize()
Definition: FormRuntime.php:174
‪TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException
Definition: MissingArrayPathException.php:27
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\$lastDisplayedPage
‪Page $lastDisplayedPage
Definition: FormRuntime.php:141
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getCurrentSiteLanguage
‪SiteLanguage getCurrentSiteLanguage()
Definition: FormRuntime.php:999
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\isFrontendUserAuthenticated
‪bool isFrontendUserAuthenticated()
Definition: FormRuntime.php:448
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\$formSession
‪FormSession null $formSession
Definition: FormRuntime.php:123
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getCurrentFinisher
‪getCurrentFinisher()
Definition: FormRuntime.php:1053
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\canProcessFormSubmission
‪canProcessFormSubmission()
Definition: FormRuntime.php:741
‪TYPO3\CMS\Form\Domain\Renderer\RendererInterface
Definition: RendererInterface.php:34
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\initializeHoneypotFromRequest
‪initializeHoneypotFromRequest()
Definition: FormRuntime.php:330
‪TYPO3\CMS\Form\Domain\Runtime\FormState
Definition: FormState.php:36
‪TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
Definition: ConfigurationManagerInterface.php:28
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getTemplateName
‪getTemplateName()
Definition: FormRuntime.php:981
‪TYPO3\CMS\Extbase\Security\Exception\InvalidArgumentForHashGenerationException
Definition: InvalidArgumentForHashGenerationException.php:25
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getTypoScriptFrontendController
‪getTypoScriptFrontendController()
Definition: FormRuntime.php:1100
‪TYPO3\CMS\Core\Error\Http\BadRequestException
Definition: BadRequestException.php:24
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getRendererClassName
‪string getRendererClassName()
Definition: FormRuntime.php:965
‪TYPO3\CMS\Extbase\Security\Cryptography\HashService
Definition: HashService.php:31
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\offsetSet
‪offsetSet(mixed $identifier, mixed $value)
Definition: FormRuntime.php:903
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\setRequest
‪setRequest(RequestInterface $request)
Definition: FormRuntime.php:169
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getFrontendUser
‪getFrontendUser()
Definition: FormRuntime.php:1095
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\$currentFinisher
‪FinisherInterface $currentFinisher
Definition: FormRuntime.php:153
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getNextPage
‪getNextPage()
Definition: FormRuntime.php:777
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:54
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\Lifecycle\AfterFormStateInitializedInterface
Definition: AfterFormStateInitializedInterface.php:29
‪TYPO3\CMS\Extbase\Error\Result
Definition: Result.php:24
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\processSubmittedFormValues
‪processSubmittedFormValues()
Definition: FormRuntime.php:512
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getFormState
‪getFormState()
Definition: FormRuntime.php:944
‪TYPO3\CMS\Form\Domain\Model\FormElements\Page
Definition: Page.php:44
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getCurrentPage
‪getCurrentPage()
Definition: FormRuntime.php:757
‪TYPO3\CMS\Form\Domain\Model\Renderable\AbstractRenderable\getType
‪getType()
Definition: AbstractRenderable.php:98
‪TYPO3\CMS\Core\Site\Entity\Site
Definition: Site.php:42
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\initializeFormStateFromRequest
‪initializeFormStateFromRequest()
Definition: FormRuntime.php:221
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getConditionResolver
‪getConditionResolver()
Definition: FormRuntime.php:1058
‪TYPO3\CMS\Core\Utility\ArrayUtility\getValueByPath
‪static getValueByPath(array $array, array|string $path, string $delimiter='/')
Definition: ArrayUtility.php:176
‪TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface
Definition: RenderableInterface.php:32
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getPages
‪array Page[] getPages()
Definition: FormRuntime.php:936
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\offsetGet
‪offsetGet(mixed $identifier)
Definition: FormRuntime.php:886
‪TYPO3\CMS\Core\Site\Entity\SiteLanguage
Definition: SiteLanguage.php:27
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\FormSession
Definition: FormSession.php:31
‪TYPO3\CMS\Form\Domain\Runtime
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getElementValue
‪mixed getElementValue(string $identifier)
Definition: FormRuntime.php:924
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\isRenderableEnabled
‪isRenderableEnabled(RenderableInterface $renderable)
Definition: FormRuntime.php:1105
‪TYPO3\CMS\Form\Exception
Definition: Exception.php:25
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\triggerAfterFormStateInitialized
‪triggerAfterFormStateInitialized()
Definition: FormRuntime.php:241
‪TYPO3\CMS\Core\Http\Response
Definition: Response.php:32
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\initializeCurrentPageFromRequest
‪initializeCurrentPageFromRequest()
Definition: FormRuntime.php:254
‪TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface
Definition: FormElementInterface.php:40
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\mapAndValidatePage
‪mapAndValidatePage(Page $page)
Definition: FormRuntime.php:535
‪TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface
Definition: RootRenderableInterface.php:31
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\$response
‪ResponseInterface $response
Definition: FormRuntime.php:112
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\$request
‪RequestInterface $request
Definition: FormRuntime.php:111
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\setCurrentSiteLanguage
‪setCurrentSiteLanguage(SiteLanguage $currentSiteLanguage)
Definition: FormRuntime.php:1012
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\setFormDefinition
‪setFormDefinition(FormDefinition $formDefinition)
Definition: FormRuntime.php:164
‪$validator
‪if(isset($args['d'])) $validator
Definition: validateRstFiles.php:262
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\processVariants
‪processVariants()
Definition: FormRuntime.php:454
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\invokeFinishers
‪invokeFinishers()
Definition: FormRuntime.php:664
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\$currentPage
‪Page null $currentPage
Definition: FormRuntime.php:134
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\setHoneypotNameInSession
‪setHoneypotNameInSession(Page $page, string $honeypotName)
Definition: FormRuntime.php:426
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\renderHoneypot
‪renderHoneypot()
Definition: FormRuntime.php:360
‪TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface\getParentRenderable
‪CompositeRenderableInterface null getParentRenderable()
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\offsetExists
‪offsetExists(mixed $identifier)
Definition: FormRuntime.php:858
‪TYPO3\CMS\Extbase\Security\Exception\InvalidHashException
Definition: InvalidHashException.php:25
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getResponse
‪ResponseInterface getResponse()
Definition: FormRuntime.php:728
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getFormSession
‪getFormSession()
Definition: FormRuntime.php:749
‪TYPO3\CMS\Form\Domain\Finishers\FinisherContext
Definition: FinisherContext.php:37
‪$output
‪$output
Definition: annotationChecker.php:119
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\isRenderedCached
‪isRenderedCached()
Definition: FormRuntime.php:498
‪TYPO3\CMS\Form\Domain\Model\FormDefinition
Definition: FormDefinition.php:224
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\$formDefinition
‪FormDefinition $formDefinition
Definition: FormRuntime.php:110
‪TYPO3\CMS\Extbase\Mvc\RequestInterface
Definition: RequestInterface.php:24
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\__construct
‪__construct(protected readonly ContainerInterface $container, protected readonly ConfigurationManagerInterface $configurationManager, protected readonly HashService $hashService, protected readonly ValidatorResolver $validatorResolver,)
Definition: FormRuntime.php:155
‪TYPO3\CMS\Form\Domain\Model\Renderable\VariableRenderableInterface
Definition: VariableRenderableInterface.php:29
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
Definition: TypoScriptFrontendController.php:102
‪TYPO3\CMS\Core\ExpressionLanguage\Resolver
Definition: Resolver.php:32
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:26
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Utility\ArrayUtility\assertAllArrayKeysAreValid
‪static assertAllArrayKeysAreValid(array $arrayToTest, array $allowedArrayKeys)
Definition: ArrayUtility.php:33
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\initializeCurrentSiteLanguage
‪initializeCurrentSiteLanguage()
Definition: FormRuntime.php:1021
‪TYPO3\CMS\Form\Domain\Exception\RenderingException
Definition: RenderingException.php:29
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
Definition: ContentObjectRenderer.php:92
‪TYPO3\CMS\Core\Http\fromRequest
‪@ fromRequest
Definition: ApplicationType.php:67
‪TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication
Definition: FrontendUserAuthentication.php:33
‪TYPO3\CMS\Extbase\Mvc\ExtbaseRequestParameters
Definition: ExtbaseRequestParameters.php:35
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\$currentSiteLanguage
‪SiteLanguage $currentSiteLanguage
Definition: FormRuntime.php:147
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getIdentifier
‪string getIdentifier()
Definition: FormRuntime.php:702
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\isFirstRequest
‪isFirstRequest()
Definition: FormRuntime.php:482
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getFormDefinition
‪getFormDefinition()
Definition: FormRuntime.php:989
‪TYPO3\CMS\Form\Domain\Model\Renderable\AbstractRenderable\getIdentifier
‪getIdentifier()
Definition: AbstractRenderable.php:106
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\isPostRequest
‪isPostRequest()
Definition: FormRuntime.php:487
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\render
‪string null render()
Definition: FormRuntime.php:639
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getPreviousPage
‪getPreviousPage()
Definition: FormRuntime.php:765
‪TYPO3\CMS\Webhooks\Message\$identifier
‪identifier readonly string $identifier
Definition: FileAddedMessage.php:37
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getRequest
‪RequestInterface getRequest()
Definition: FormRuntime.php:715
‪TYPO3\CMS\Core\Http\ApplicationType
‪ApplicationType
Definition: ApplicationType.php:56
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getLabel
‪getLabel()
Definition: FormRuntime.php:973