‪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;
58 use ‪TYPO3\CMS\Form\Exception as FormException;
63 
105 class ‪FormRuntime implements ‪RootRenderableInterface, \ArrayAccess
106 {
107  public const ‪HONEYPOT_NAME_SESSION_IDENTIFIER = 'tx_form_honeypot_name_';
108 
111  protected ResponseInterface ‪$response;
112 
116  protected ‪$formState;
117 
124  protected ‪$formSession;
125 
136  protected ‪$currentPage;
137 
144  protected ‪$lastDisplayedPage;
145 
151  protected ‪$currentSiteLanguage;
152 
158  protected ‪$currentFinisher;
159 
160  public function ‪__construct(
161  protected readonly ContainerInterface $container,
162  protected readonly ‪ConfigurationManagerInterface $configurationManager,
163  protected readonly ‪HashService $hashService,
164  protected readonly ‪ValidatorResolver $validatorResolver,
165  private readonly ‪Context $context,
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, HashScope::FormState->‪prefix());
240  } catch (InvalidHashStringException $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 
452  protected function ‪isFrontendUserAuthenticated(): bool
453  {
454  return (bool)$this->context->getPropertyFromAspect('frontend.user', 'isLoggedIn', false);
455  }
456 
457  protected function ‪processVariants(): void
458  {
459  $conditionResolver = $this->‪getConditionResolver();
460  $renderables = array_merge([$this->formDefinition], $this->formDefinition->getRenderablesRecursively());
461  foreach ($renderables as $renderable) {
462  if ($renderable instanceof VariableRenderableInterface) {
463  $variants = $renderable->getVariants();
464  foreach ($variants as $variant) {
465  if ($variant->conditionMatches($conditionResolver)) {
466  $variant->apply();
467  }
468  }
469  }
470  }
471  }
472 
476  protected function ‪isAfterLastPage(): bool
477  {
478  return $this->currentPage === null;
479  }
480 
484  protected function ‪isFirstRequest(): bool
485  {
486  return $this->lastDisplayedPage === null;
487  }
488 
489  protected function ‪isPostRequest(): bool
490  {
491  return $this->‪getRequest()->getMethod() === 'POST';
492  }
493 
500  protected function ‪isRenderedCached(): bool
501  {
502  $contentObject = $this->request->getAttribute('currentContentObject');
503  // @todo: this does not work when rendering a cached `FLUIDTEMPLATE` (not nested in `COA_INT`)
504  // Rendering the form other than with the controller, will never work out cleanly.
505  // This likely can only be resolved by deprecating using the form render view helper
506  // other than in a template for the form plugin and covering the use cases the VH was introduced
507  // with a different concept
508  return $contentObject === null || $contentObject->getUserObjectType() === ‪ContentObjectRenderer::OBJECTTYPE_USER;
509  }
510 
514  protected function ‪processSubmittedFormValues()
515  {
516  $result = $this->‪mapAndValidatePage($this->lastDisplayedPage);
517  if ($result->hasErrors() && !$this->userWentBackToPreviousStep()) {
518  $this->currentPage = ‪$this->lastDisplayedPage;
520  $extbaseRequestParameters = clone $this->request->getAttribute('extbase');
521  $extbaseRequestParameters->setOriginalRequestMappingResults($result);
522  $this->request = $this->request->withAttribute('extbase', $extbaseRequestParameters);
523  }
524  }
525 
529  protected function ‪userWentBackToPreviousStep(): bool
530  {
531  return !$this->‪isAfterLastPage() && !$this->‪isFirstRequest() && $this->currentPage->getIndex() < $this->lastDisplayedPage->getIndex();
532  }
533 
537  protected function ‪mapAndValidatePage(‪Page $page): ‪Result
538  {
539  $result = GeneralUtility::makeInstance(Result::class);
540  $requestArguments = $this->request->getArguments();
541 
542  $propertyPathsForWhichPropertyMappingShouldHappen = [];
543  $registerPropertyPaths = static function ($propertyPath) use (&$propertyPathsForWhichPropertyMappingShouldHappen) {
544  $propertyPathParts = explode('.', $propertyPath);
545  $accumulatedPropertyPathParts = [];
546  foreach ($propertyPathParts as $propertyPathPart) {
547  $accumulatedPropertyPathParts[] = $propertyPathPart;
548  $temporaryPropertyPath = implode('.', $accumulatedPropertyPathParts);
549  $propertyPathsForWhichPropertyMappingShouldHappen[$temporaryPropertyPath] = $temporaryPropertyPath;
550  }
551  };
552 
553  $value = null;
554 
555  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterSubmit'] ?? [] as $className) {
556  $hookObj = GeneralUtility::makeInstance($className);
557  if (method_exists($hookObj, 'afterSubmit')) {
558  $value = $hookObj->afterSubmit(
559  $this,
560  $page,
561  $value,
562  $requestArguments
563  );
564  }
565  }
566 
567  foreach ($page->‪getElementsRecursively() as $element) {
568  if (!$this->‪isRenderableEnabled($element)) {
569  continue;
570  }
571 
572  try {
573  $value = ‪ArrayUtility::getValueByPath($requestArguments, $element->getIdentifier(), '.');
574  } catch (MissingArrayPathException $exception) {
575  $value = null;
576  }
577 
578  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterSubmit'] ?? [] as $className) {
579  $hookObj = GeneralUtility::makeInstance($className);
580  if (method_exists($hookObj, 'afterSubmit')) {
581  $value = $hookObj->afterSubmit(
582  $this,
583  $element,
584  $value,
585  $requestArguments
586  );
587  }
588  }
589 
590  $this->formState->setFormValue($element->getIdentifier(), $value);
591  $registerPropertyPaths($element->getIdentifier());
592  }
593 
594  // The more parts the path has, the more early it is processed
595  usort($propertyPathsForWhichPropertyMappingShouldHappen, static function ($a, $b) {
596  return substr_count($b, '.') - substr_count($a, '.');
597  });
598 
599  $processingRules = $this->formDefinition->getProcessingRules();
600 
601  foreach ($propertyPathsForWhichPropertyMappingShouldHappen as $propertyPath) {
602  if (isset($processingRules[$propertyPath])) {
603  $processingRule = $processingRules[$propertyPath];
604  $value = $this->formState->getFormValue($propertyPath);
605  try {
606  $value = $processingRule->process($value);
607  } catch (‪PropertyException $exception) {
608  throw new PropertyMappingException(
609  'Failed to process FormValue at "' . $propertyPath . '" from "' . gettype($value) . '" to "' . $processingRule->getDataType() . '"',
610  1480024933,
611  $exception
612  );
613  }
614  $result->forProperty($this->‪getIdentifier() . '.' . $propertyPath)->merge($processingRule->getProcessingMessages());
615  $this->formState->setFormValue($propertyPath, $value);
616  }
617  }
618 
619  return $result;
620  }
621 
630  public function ‪overrideCurrentPage(int $pageIndex)
631  {
632  $this->currentPage = $this->formDefinition->getPageByIndex($pageIndex);
633  }
634 
641  public function ‪render()
642  {
643  if ($this->‪isAfterLastPage()) {
644  return $this->‪invokeFinishers();
645  }
646  $this->‪processVariants();
647 
648  $this->formState->setLastDisplayedPageIndex($this->currentPage->getIndex());
649 
650  if ($this->formDefinition->getRendererClassName() === '') {
651  throw new RenderingException(sprintf('The form definition "%s" does not have a rendererClassName set.', $this->formDefinition->getIdentifier()), 1326095912);
652  }
653  $rendererClassName = $this->formDefinition->getRendererClassName();
654  $renderer = $this->container->get($rendererClassName);
655  if (!($renderer instanceof RendererInterface)) {
656  throw new RenderingException(sprintf('The renderer "%s" des not implement RendererInterface', $rendererClassName), 1326096024);
657  }
658 
659  $renderer->setFormRuntime($this);
660  return $renderer->render();
661  }
662 
666  protected function ‪invokeFinishers(): string
667  {
668  $finisherContext = GeneralUtility::makeInstance(
669  FinisherContext::class,
670  $this,
671  $this->request
672  );
673 
674  ‪$output = '';
675  $this->response->getBody()->rewind();
676  $originalContent = $this->response->getBody()->getContents();
677  $this->response->getBody()->write('');
678  foreach ($this->formDefinition->getFinishers() as $finisher) {
679  $this->currentFinisher = $finisher;
680  $this->‪processVariants();
681 
682  $finisherOutput = $finisher->execute($finisherContext);
683  if (is_string($finisherOutput) && !empty($finisherOutput)) {
684  ‪$output .= $finisherOutput;
685  } else {
686  $this->response->getBody()->rewind();
687  ‪$output .= $this->response->getBody()->getContents();
688  $this->response->getBody()->write('');
689  }
690 
691  if ($finisherContext->isCancelled()) {
692  break;
693  }
694  }
695  $this->response->getBody()->rewind();
696  $this->response->getBody()->write($originalContent);
697 
698  return ‪$output;
699  }
700 
704  public function ‪getIdentifier(): string
705  {
706  return $this->formDefinition->getIdentifier();
707  }
708 
717  public function ‪getRequest(): RequestInterface
718  {
719  return ‪$this->request;
720  }
721 
730  public function ‪getResponse(): ResponseInterface
731  {
732  return ‪$this->response;
733  }
734 
743  public function ‪canProcessFormSubmission(): bool
744  {
745  return $this->‪isPostRequest() && !$this->‪isRenderedCached();
746  }
747 
751  public function ‪getFormSession(): ?‪FormSession
752  {
754  }
755 
759  public function ‪getCurrentPage(): ?‪Page
760  {
762  }
763 
767  public function ‪getPreviousPage(): ?‪Page
768  {
769  $previousPageIndex = $this->currentPage->‪getIndex() - 1;
770  if ($this->formDefinition->hasPageWithIndex($previousPageIndex)) {
771  return $this->formDefinition->getPageByIndex($previousPageIndex);
772  }
773  return null;
774  }
775 
779  public function ‪getNextPage(): ?‪Page
780  {
781  $nextPageIndex = $this->currentPage->‪getIndex() + 1;
782  if ($this->formDefinition->hasPageWithIndex($nextPageIndex)) {
783  return $this->formDefinition->getPageByIndex($nextPageIndex);
784  }
785  return null;
786  }
787 
792  public function ‪getPreviousEnabledPage(): ?‪Page
793  {
794  $previousPage = null;
795  $previousPageIndex = $this->currentPage->‪getIndex() - 1;
796  while ($previousPageIndex >= 0) {
797  if ($this->formDefinition->hasPageWithIndex($previousPageIndex)) {
798  $previousPage = $this->formDefinition->getPageByIndex($previousPageIndex);
799 
800  if ($previousPage->isEnabled()) {
801  break;
802  }
803 
804  $previousPage = null;
805  $previousPageIndex--;
806  } else {
807  $previousPage = null;
808  break;
809  }
810  }
811 
812  return $previousPage;
813  }
814 
819  public function ‪getNextEnabledPage(): ?‪Page
820  {
821  $nextPage = null;
822  $pageCount = count($this->formDefinition->getPages());
823  $nextPageIndex = $this->currentPage->getIndex() + 1;
824 
825  while ($nextPageIndex < $pageCount) {
826  if ($this->formDefinition->hasPageWithIndex($nextPageIndex)) {
827  $nextPage = $this->formDefinition->getPageByIndex($nextPageIndex);
828  $renderingOptions = $nextPage->getRenderingOptions();
829  if (
830  !isset($renderingOptions['enabled'])
831  || (bool)$renderingOptions['enabled']
832  ) {
833  break;
834  }
835  $nextPage = null;
836  $nextPageIndex++;
837  } else {
838  $nextPage = null;
839  break;
840  }
841  }
842 
843  return $nextPage;
844  }
845 
851  public function ‪getType(): string
852  {
853  return $this->formDefinition->getType();
854  }
855 
860  public function ‪offsetExists(mixed ‪$identifier): bool
861  {
862  ‪$identifier = (string)‪$identifier;
863  if ($this->‪getElementValue($identifier) !== null) {
864  return true;
865  }
866 
867  if (is_callable([$this, 'get' . ucfirst(‪$identifier)])) {
868  return true;
869  }
870  if (is_callable([$this, 'has' . ucfirst(‪$identifier)])) {
871  return true;
872  }
873  if (is_callable([$this, 'is' . ucfirst(‪$identifier)])) {
874  return true;
875  }
876  if (property_exists($this, ‪$identifier)) {
877  $propertyReflection = new \ReflectionProperty($this, ‪$identifier);
878  return $propertyReflection->isPublic();
879  }
880 
881  return false;
882  }
883 
888  public function ‪offsetGet(mixed ‪$identifier): mixed
889  {
890  ‪$identifier = (string)‪$identifier;
891  if ($this->‪getElementValue($identifier) !== null) {
892  return $this->‪getElementValue($identifier);
893  }
894  $getterMethodName = 'get' . ucfirst(‪$identifier);
895  if (is_callable([$this, $getterMethodName])) {
896  return $this->{$getterMethodName}();
897  }
898  return null;
899  }
900 
905  public function ‪offsetSet(mixed ‪$identifier, mixed $value): void
906  {
907  ‪$identifier = (string)‪$identifier;
908  $this->formState->setFormValue(‪$identifier, $value);
909  }
910 
915  public function ‪offsetUnset(mixed ‪$identifier): void
916  {
917  ‪$identifier = (string)‪$identifier;
918  $this->formState->setFormValue(‪$identifier, null);
919  }
920 
926  public function ‪getElementValue(string ‪$identifier)
927  {
928  $formValue = $this->formState->getFormValue(‪$identifier);
929  if ($formValue !== null) {
930  return $formValue;
931  }
932  return $this->formDefinition->getElementDefaultValueByIdentifier(‪$identifier);
933  }
934 
938  public function ‪getPages(): array
939  {
940  return $this->formDefinition->getPages();
941  }
942 
946  public function ‪getFormState(): ?‪FormState
947  {
948  return ‪$this->formState;
949  }
950 
956  public function ‪getRenderingOptions(): array
957  {
958  return $this->formDefinition->getRenderingOptions();
959  }
960 
967  public function ‪getRendererClassName(): string
968  {
969  return $this->formDefinition->getRendererClassName();
970  }
971 
975  public function ‪getLabel(): string
976  {
977  return $this->formDefinition->getLabel();
978  }
979 
983  public function ‪getTemplateName(): string
984  {
985  return $this->formDefinition->getTemplateName();
986  }
987 
991  public function ‪getFormDefinition(): ‪FormDefinition
992  {
994  }
995 
1001  public function ‪getCurrentSiteLanguage(): ?‪SiteLanguage
1002  {
1004  }
1005 
1015  {
1016  $this->currentSiteLanguage = ‪$currentSiteLanguage;
1017  }
1018 
1023  protected function ‪initializeCurrentSiteLanguage(): void
1024  {
1025  if ($this->request->getAttribute('language') instanceof ‪SiteLanguage) {
1026  $this->currentSiteLanguage = $this->request->getAttribute('language');
1027  } else {
1028  $languageId = (int)$this->context->getPropertyFromAspect('language', 'id', 0);
1029  $pageId = $this->request->getAttribute('frontend.page.information')?->getId() ?? 0;
1030  $fakeSiteConfiguration = [
1031  'languages' => [
1032  [
1033  'languageId' => $languageId,
1034  'title' => 'Dummy',
1035  'navigationTitle' => '',
1036  'flag' => '',
1037  'locale' => '',
1038  ],
1039  ],
1040  ];
1041  $this->currentSiteLanguage = GeneralUtility::makeInstance(Site::class, 'form-dummy', $pageId, $fakeSiteConfiguration)
1042  ->getLanguageById($languageId);
1043  }
1044  }
1045 
1049  public function ‪getCurrentFinisher(): ?‪FinisherInterface
1050  {
1052  }
1053 
1054  protected function ‪getConditionResolver(): Resolver
1055  {
1056  $formValues = array_replace_recursive(
1057  $this->‪getFormState()->getFormValues(),
1058  $this->‪getRequest()->getArguments()
1059  );
1060  $page = $this->‪getCurrentPage() ?? $this->‪getFormDefinition()->getPageByIndex(0);
1061 
1062  $finisherIdentifier = '';
1063  if ($this->‪getCurrentFinisher() !== null) {
1064  if (method_exists($this->‪getCurrentFinisher(), 'getFinisherIdentifier')) {
1065  $finisherIdentifier = $this->‪getCurrentFinisher()->getFinisherIdentifier();
1066  } else {
1067  $finisherIdentifier = (new \ReflectionClass($this->‪getCurrentFinisher()))->getShortName();
1068  $finisherIdentifier = preg_replace('/Finisher$/', '', $finisherIdentifier);
1069  }
1070  }
1071 
1072  $contentObjectData = $this->request->getAttribute('currentContentObject')?->data ?? [];
1073 
1074  return GeneralUtility::makeInstance(
1075  Resolver::class,
1076  'form',
1077  [
1078  'formRuntime' => $this,
1079  'formValues' => $formValues,
1080  'stepIdentifier' => $page->‪getIdentifier(),
1081  'stepType' => $page->‪getType(),
1082  'finisherIdentifier' => $finisherIdentifier,
1083  'contentObject' => $contentObjectData,
1084  'request' => new RequestWrapper($this->‪getRequest()),
1085  'site' => $this->‪getRequest()->getAttribute('site'),
1086  'siteLanguage' => $this->‪getRequest()->getAttribute('language'),
1087  ]
1088  );
1089  }
1092  {
1093  return $this->request->getAttribute('frontend.user');
1094  }
1095 
1096  protected function ‪isRenderableEnabled(‪RenderableInterface $renderable): bool
1097  {
1098  if (!$renderable->‪isEnabled()) {
1099  return false;
1100  }
1101 
1102  while ($renderable = $renderable->‪getParentRenderable()) {
1103  if ($renderable instanceof RenderableInterface && !$renderable->‪isEnabled()) {
1104  return false;
1105  }
1106  }
1107 
1108  return true;
1109  }
1110 }
‪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:523
‪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:106
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\overrideCurrentPage
‪overrideCurrentPage(int $pageIndex)
Definition: FormRuntime.php:624
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\HONEYPOT_NAME_SESSION_IDENTIFIER
‪const HONEYPOT_NAME_SESSION_IDENTIFIER
Definition: FormRuntime.php:107
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\OBJECTTYPE_USER
‪const OBJECTTYPE_USER
Definition: ContentObjectRenderer.php:365
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\$formState
‪FormState $formState
Definition: FormRuntime.php:115
‪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:950
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getPreviousEnabledPage
‪getPreviousEnabledPage()
Definition: FormRuntime.php:786
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getType
‪getType()
Definition: FormRuntime.php:845
‪TYPO3\CMS\Form\Domain\Finishers\FinisherInterface
Definition: FinisherInterface.php:31
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\isFrontendUserAuthenticated
‪isFrontendUserAuthenticated()
Definition: FormRuntime.php:446
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\offsetUnset
‪offsetUnset(mixed $identifier)
Definition: FormRuntime.php:909
‪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:470
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getNextEnabledPage
‪getNextEnabledPage()
Definition: FormRuntime.php:813
‪TYPO3\CMS\Form\Security\HashScope
‪HashScope
Definition: HashScope.php:25
‪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:140
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getCurrentSiteLanguage
‪SiteLanguage getCurrentSiteLanguage()
Definition: FormRuntime.php:995
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\$formSession
‪FormSession null $formSession
Definition: FormRuntime.php:122
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getCurrentFinisher
‪getCurrentFinisher()
Definition: FormRuntime.php:1043
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\canProcessFormSubmission
‪canProcessFormSubmission()
Definition: FormRuntime.php:737
‪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:977
‪TYPO3\CMS\Core\Error\Http\BadRequestException
Definition: BadRequestException.php:24
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getRendererClassName
‪string getRendererClassName()
Definition: FormRuntime.php:961
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\offsetSet
‪offsetSet(mixed $identifier, mixed $value)
Definition: FormRuntime.php:899
‪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:1085
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\$currentFinisher
‪FinisherInterface $currentFinisher
Definition: FormRuntime.php:152
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getNextPage
‪getNextPage()
Definition: FormRuntime.php:773
‪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:508
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getFormState
‪getFormState()
Definition: FormRuntime.php:940
‪TYPO3\CMS\Form\Domain\Model\FormElements\Page
Definition: Page.php:44
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getCurrentPage
‪getCurrentPage()
Definition: FormRuntime.php:753
‪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:1048
‪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:932
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\offsetGet
‪offsetGet(mixed $identifier)
Definition: FormRuntime.php:882
‪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:920
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\isRenderableEnabled
‪isRenderableEnabled(RenderableInterface $renderable)
Definition: FormRuntime.php:1090
‪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:531
‪TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface
Definition: RootRenderableInterface.php:31
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\$response
‪ResponseInterface $response
Definition: FormRuntime.php:111
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\$request
‪RequestInterface $request
Definition: FormRuntime.php:110
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\setCurrentSiteLanguage
‪setCurrentSiteLanguage(SiteLanguage $currentSiteLanguage)
Definition: FormRuntime.php:1008
‪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:451
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\invokeFinishers
‪invokeFinishers()
Definition: FormRuntime.php:660
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\$currentPage
‪Page null $currentPage
Definition: FormRuntime.php:133
‪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:854
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getResponse
‪ResponseInterface getResponse()
Definition: FormRuntime.php:724
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getFormSession
‪getFormSession()
Definition: FormRuntime.php:745
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\__construct
‪__construct(protected readonly ContainerInterface $container, protected readonly ConfigurationManagerInterface $configurationManager, protected readonly HashService $hashService, protected readonly ValidatorResolver $validatorResolver, private readonly Context $context,)
Definition: FormRuntime.php:154
‪TYPO3\CMS\Form\Domain\Finishers\FinisherContext
Definition: FinisherContext.php:37
‪$output
‪$output
Definition: annotationChecker.php:114
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\isRenderedCached
‪isRenderedCached()
Definition: FormRuntime.php:494
‪TYPO3\CMS\Form\Domain\Model\FormDefinition
Definition: FormDefinition.php:224
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\$formDefinition
‪FormDefinition $formDefinition
Definition: FormRuntime.php:109
‪TYPO3\CMS\Extbase\Mvc\RequestInterface
Definition: RequestInterface.php:24
‪TYPO3\CMS\Form\Domain\Model\Renderable\VariableRenderableInterface
Definition: VariableRenderableInterface.php:29
‪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:1017
‪TYPO3\CMS\Form\Domain\Exception\RenderingException
Definition: RenderingException.php:29
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
Definition: ContentObjectRenderer.php:102
‪TYPO3\CMS\Core\Http\fromRequest
‪@ fromRequest
Definition: ApplicationType.php:66
‪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:146
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getIdentifier
‪string getIdentifier()
Definition: FormRuntime.php:698
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Extbase\Security\prefix
‪@ prefix
Definition: HashScope.php:30
‪TYPO3\CMS\Core\Exception\Crypto\InvalidHashStringException
Definition: InvalidHashStringException.php:25
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\isFirstRequest
‪isFirstRequest()
Definition: FormRuntime.php:478
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getFormDefinition
‪getFormDefinition()
Definition: FormRuntime.php:985
‪TYPO3\CMS\Core\Crypto\HashService
Definition: HashService.php:27
‪TYPO3\CMS\Form\Domain\Model\Renderable\AbstractRenderable\getIdentifier
‪getIdentifier()
Definition: AbstractRenderable.php:106
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\isPostRequest
‪isPostRequest()
Definition: FormRuntime.php:483
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\render
‪string null render()
Definition: FormRuntime.php:635
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getPreviousPage
‪getPreviousPage()
Definition: FormRuntime.php:761
‪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:711
‪TYPO3\CMS\Core\Http\ApplicationType
‪ApplicationType
Definition: ApplicationType.php:55
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime\getLabel
‪getLabel()
Definition: FormRuntime.php:969