80cf2c70ab7c6a5836b57774ee3f56d01c973366
[Packages/TYPO3.CMS.git] / typo3 / sysext / form / Classes / Domain / Runtime / FormRuntime.php
1 <?php
2 declare(strict_types=1);
3 namespace TYPO3\CMS\Form\Domain\Runtime;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It originated from the Neos.Form package (www.neos.io)
9 *
10 * It is free software; you can redistribute it and/or modify it under
11 * the terms of the GNU General Public License, either version 2
12 * of the License, or any later version.
13 *
14 * For the full copyright and license information, please read the
15 * LICENSE.txt file that was distributed with this source code.
16 *
17 * The TYPO3 project - inspiring people to share!
18 */
19
20 use TYPO3\CMS\Core\Utility\ArrayUtility;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22 use TYPO3\CMS\Extbase\Error\Result;
23 use TYPO3\CMS\Extbase\Mvc\Controller\Arguments;
24 use TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext;
25 use TYPO3\CMS\Extbase\Mvc\Web\Request;
26 use TYPO3\CMS\Extbase\Mvc\Web\Response;
27 use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
28 use TYPO3\CMS\Extbase\Property\Exception as PropertyException;
29 use TYPO3\CMS\Extbase\Reflection\PropertyReflection;
30 use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
31 use TYPO3\CMS\Form\Domain\Exception\RenderingException;
32 use TYPO3\CMS\Form\Domain\Finishers\FinisherContext;
33 use TYPO3\CMS\Form\Domain\Model\FormDefinition;
34 use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface;
35 use TYPO3\CMS\Form\Domain\Model\FormElements\Page;
36 use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface;
37 use TYPO3\CMS\Form\Domain\Renderer\RendererInterface;
38 use TYPO3\CMS\Form\Domain\Runtime\Exception\PropertyMappingException;
39 use TYPO3\CMS\Form\Mvc\Validation\EmptyValidator;
40 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
41
42 /**
43 * This class implements the *runtime logic* of a form, i.e. deciding which
44 * page is shown currently, what the current values of the form are, trigger
45 * validation and property mapping.
46 *
47 * You generally receive an instance of this class by calling {@link \TYPO3\CMS\Form\Domain\Model\FormDefinition::bind}.
48 *
49 * Rendering a Form
50 * ================
51 *
52 * That's easy, just call render() on the FormRuntime:
53 *
54 * /---code php
55 * $form = $formDefinition->bind($request, $response);
56 * $renderedForm = $form->render();
57 * \---
58 *
59 * Accessing Form Values
60 * =====================
61 *
62 * In order to get the values the user has entered into the form, you can access
63 * this object like an array: If a form field with the identifier *firstName*
64 * exists, you can do **$form['firstName']** to retrieve its current value.
65 *
66 * You can also set values in the same way.
67 *
68 * Rendering Internals
69 * ===================
70 *
71 * The FormRuntime asks the FormDefinition about the configured Renderer
72 * which should be used ({@link \TYPO3\CMS\Form\Domain\Model\FormDefinition::getRendererClassName}),
73 * and then trigger render() on this Renderer.
74 *
75 * This makes it possible to declaratively define how a form should be rendered.
76 *
77 * Scope: frontend
78 * **This class is NOT meant to be sub classed by developers.**
79 * @api
80 */
81 class FormRuntime implements RootRenderableInterface, \ArrayAccess
82 {
83 const HONEYPOT_NAME_SESSION_IDENTIFIER = 'tx_form_honeypot_name_';
84
85 /**
86 * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
87 */
88 protected $objectManager;
89
90 /**
91 * @var \TYPO3\CMS\Form\Domain\Model\FormDefinition
92 */
93 protected $formDefinition;
94
95 /**
96 * @var \TYPO3\CMS\Extbase\Mvc\Web\Request
97 */
98 protected $request;
99
100 /**
101 * @var \TYPO3\CMS\Extbase\Mvc\Web\Response
102 */
103 protected $response;
104
105 /**
106 * @var \TYPO3\CMS\Form\Domain\Runtime\FormState
107 */
108 protected $formState;
109
110 /**
111 * The current page is the page which will be displayed to the user
112 * during rendering.
113 *
114 * If $currentPage is NULL, the *last* page has been submitted and
115 * finishing actions need to take place. You should use $this->isAfterLastPage()
116 * instead of explicitely checking for NULL.
117 *
118 * @var \TYPO3\CMS\Form\Domain\Model\FormElements\Page
119 */
120 protected $currentPage = null;
121
122 /**
123 * Reference to the page which has been shown on the last request (i.e.
124 * we have to handle the submitted data from lastDisplayedPage)
125 *
126 * @var \TYPO3\CMS\Form\Domain\Model\FormElements\Page
127 */
128 protected $lastDisplayedPage = null;
129
130 /**
131 * @var \TYPO3\CMS\Extbase\Security\Cryptography\HashService
132 */
133 protected $hashService;
134
135 /**
136 * @param \TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService
137 * @return void
138 * @internal
139 */
140 public function injectHashService(\TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService)
141 {
142 $this->hashService = $hashService;
143 }
144
145 /**
146 * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
147 * @internal
148 */
149 public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
150 {
151 $this->objectManager = $objectManager;
152 }
153
154 /**
155 * @param FormDefinition $formDefinition
156 * @param Request $request
157 * @param Response $response
158 * @api
159 */
160 public function __construct(FormDefinition $formDefinition, Request $request, Response $response)
161 {
162 $this->formDefinition = $formDefinition;
163 $arguments = $request->getArguments();
164 $this->request = clone $request;
165 $formIdentifier = $this->formDefinition->getIdentifier();
166 if (isset($arguments[$formIdentifier])) {
167 $this->request->setArguments($arguments[$formIdentifier]);
168 }
169
170 $this->response = $response;
171 }
172
173 /**
174 * @return void
175 * @internal
176 */
177 public function initializeObject()
178 {
179 $this->initializeFormStateFromRequest();
180 $this->initializeCurrentPageFromRequest();
181 $this->initializeHoneypotFromRequest();
182
183 if (!$this->isFirstRequest() && $this->getRequest()->getMethod() === 'POST') {
184 $this->processSubmittedFormValues();
185 }
186
187 $this->renderHoneypot();
188 }
189
190 /**
191 * @return void
192 */
193 protected function initializeFormStateFromRequest()
194 {
195 $serializedFormStateWithHmac = $this->request->getInternalArgument('__state');
196 if ($serializedFormStateWithHmac === null) {
197 $this->formState = GeneralUtility::makeInstance(FormState::class);
198 } else {
199 $serializedFormState = $this->hashService->validateAndStripHmac($serializedFormStateWithHmac);
200 $this->formState = unserialize(base64_decode($serializedFormState));
201 }
202 }
203
204 /**
205 * @return void
206 */
207 protected function initializeCurrentPageFromRequest()
208 {
209 if (!$this->formState->isFormSubmitted()) {
210 $this->currentPage = $this->formDefinition->getPageByIndex(0);
211 return;
212 }
213 $this->lastDisplayedPage = $this->formDefinition->getPageByIndex($this->formState->getLastDisplayedPageIndex());
214
215 // We know now that lastDisplayedPage is filled
216 $currentPageIndex = (int)$this->request->getInternalArgument('__currentPage');
217 if ($currentPageIndex > $this->lastDisplayedPage->getIndex() + 1) {
218 // We only allow jumps to following pages
219 $currentPageIndex = $this->lastDisplayedPage->getIndex() + 1;
220 }
221
222 // We now know that the user did not try to skip a page
223 if ($currentPageIndex === count($this->formDefinition->getPages())) {
224 // Last Page
225 $this->currentPage = null;
226 } else {
227 $this->currentPage = $this->formDefinition->getPageByIndex($currentPageIndex);
228 }
229 }
230
231 /**
232 * @return void
233 */
234 protected function initializeHoneypotFromRequest()
235 {
236 $renderingOptions = $this->formDefinition->getRenderingOptions();
237 if (!isset($renderingOptions['honeypot']['enable']) || $renderingOptions['honeypot']['enable'] === false || TYPO3_MODE === 'BE') {
238 return;
239 }
240
241 ArrayUtility::assertAllArrayKeysAreValid($renderingOptions['honeypot'], ['enable', 'formElementToUse']);
242
243 if (!$this->isFirstRequest()) {
244 $elementsCount = count($this->lastDisplayedPage->getElements());
245 if ($elementsCount === 0) {
246 return;
247 }
248
249 $honeypotNameFromSession = $this->getHoneypotNameFromSession($this->lastDisplayedPage);
250 if ($honeypotNameFromSession) {
251 $honeypotElement = $this->lastDisplayedPage->createElement($honeypotNameFromSession, $renderingOptions['honeypot']['formElementToUse']);
252 $validator = $this->objectManager->get(EmptyValidator::class);
253 $honeypotElement->addValidator($validator);
254 }
255 }
256 }
257
258 /**
259 * @return void
260 */
261 protected function renderHoneypot()
262 {
263 $renderingOptions = $this->formDefinition->getRenderingOptions();
264 if (!isset($renderingOptions['honeypot']['enable']) || $renderingOptions['honeypot']['enable'] === false || TYPO3_MODE === 'BE') {
265 return;
266 }
267
268 ArrayUtility::assertAllArrayKeysAreValid($renderingOptions['honeypot'], ['enable', 'formElementToUse']);
269
270 if (!$this->isAfterLastPage()) {
271 $elementsCount = count($this->currentPage->getElements());
272 if ($elementsCount === 0) {
273 return;
274 }
275
276 if (!$this->isFirstRequest()) {
277 $honeypotNameFromSession = $this->getHoneypotNameFromSession($this->lastDisplayedPage);
278 if ($honeypotNameFromSession) {
279 $honeypotElement = $this->formDefinition->getElementByIdentifier($honeypotNameFromSession);
280 if ($honeypotElement instanceof FormElementInterface) {
281 $this->lastDisplayedPage->removeElement($honeypotElement);
282 }
283 }
284 }
285
286 $elementsCount = count($this->currentPage->getElements());
287 $randomElementNumber = mt_rand(0, ($elementsCount - 1));
288 $honeypotName = substr(str_shuffle('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'), 0, mt_rand(5, 26));
289
290 $referenceElement = $this->currentPage->getElements()[$randomElementNumber];
291 $honeypotElement = $this->currentPage->createElement($honeypotName, $renderingOptions['honeypot']['formElementToUse']);
292 $validator = $this->objectManager->get(EmptyValidator::class);
293
294 $honeypotElement->addValidator($validator);
295 if (mt_rand(0, 1) === 1) {
296 $this->currentPage->moveElementAfter($honeypotElement, $referenceElement);
297 } else {
298 $this->currentPage->moveElementBefore($honeypotElement, $referenceElement);
299 }
300 $this->setHoneypotNameInSession($this->currentPage, $honeypotName);
301 }
302 }
303
304 /**
305 * @param Page $page
306 * return null|string
307 */
308 protected function getHoneypotNameFromSession(Page $page)
309 {
310 if ($this->getTypoScriptFrontendController()->loginUser) {
311 $honeypotNameFromSession = $this->getTypoScriptFrontendController()->fe_user->getKey(
312 'user',
313 self::HONEYPOT_NAME_SESSION_IDENTIFIER . $this->getIdentifier() . $page->getIdentifier()
314 );
315 } else {
316 $honeypotNameFromSession = $this->getTypoScriptFrontendController()->fe_user->getKey(
317 'ses',
318 self::HONEYPOT_NAME_SESSION_IDENTIFIER . $this->getIdentifier() . $page->getIdentifier()
319 );
320 }
321 return $honeypotNameFromSession;
322 }
323
324 /**
325 * @param Page $page
326 * @param string $honeypotName
327 * @return void
328 */
329 protected function setHoneypotNameInSession(Page $page, string $honeypotName)
330 {
331 if ($this->getTypoScriptFrontendController()->loginUser) {
332 $this->getTypoScriptFrontendController()->fe_user->setKey(
333 'user',
334 self::HONEYPOT_NAME_SESSION_IDENTIFIER . $this->getIdentifier() . $page->getIdentifier(),
335 $honeypotName
336 );
337 } else {
338 $this->getTypoScriptFrontendController()->fe_user->setKey(
339 'ses',
340 self::HONEYPOT_NAME_SESSION_IDENTIFIER . $this->getIdentifier() . $page->getIdentifier(),
341 $honeypotName
342 );
343 }
344 }
345
346 /**
347 * Returns TRUE if the last page of the form has been submitted, otherwise FALSE
348 *
349 * @return bool
350 */
351 protected function isAfterLastPage(): bool
352 {
353 return $this->currentPage === null;
354 }
355
356 /**
357 * Returns TRUE if no previous page is stored in the FormState, otherwise FALSE
358 *
359 * @return bool
360 */
361 protected function isFirstRequest(): bool
362 {
363 return $this->lastDisplayedPage === null;
364 }
365
366 /**
367 * @return void
368 */
369 protected function processSubmittedFormValues()
370 {
371 $result = $this->mapAndValidatePage($this->lastDisplayedPage);
372 if ($result->hasErrors() && !$this->userWentBackToPreviousStep()) {
373 $this->currentPage = $this->lastDisplayedPage;
374 $this->request->setOriginalRequestMappingResults($result);
375 }
376 }
377
378 /**
379 * returns TRUE if the user went back to any previous step in the form.
380 *
381 * @return bool
382 */
383 protected function userWentBackToPreviousStep(): bool
384 {
385 return !$this->isAfterLastPage() && !$this->isFirstRequest() && $this->currentPage->getIndex() < $this->lastDisplayedPage->getIndex();
386 }
387
388 /**
389 * @param Page $page
390 * @return Result
391 * @throws PropertyMappingException
392 */
393 protected function mapAndValidatePage(Page $page): Result
394 {
395 $result = $this->objectManager->get(Result::class);
396 $requestArguments = $this->request->getArguments();
397
398 $propertyPathsForWhichPropertyMappingShouldHappen = [];
399 $registerPropertyPaths = function ($propertyPath) use (&$propertyPathsForWhichPropertyMappingShouldHappen) {
400 $propertyPathParts = explode('.', $propertyPath);
401 $accumulatedPropertyPathParts = [];
402 foreach ($propertyPathParts as $propertyPathPart) {
403 $accumulatedPropertyPathParts[] = $propertyPathPart;
404 $temporaryPropertyPath = implode('.', $accumulatedPropertyPathParts);
405 $propertyPathsForWhichPropertyMappingShouldHappen[$temporaryPropertyPath] = $temporaryPropertyPath;
406 }
407 };
408
409 $value = null;
410
411 GeneralUtility::deprecationLog('EXT:form - calls for "onSubmit" are deprecated since TYPO3 v8 and will be removed in TYPO3 v9');
412 $page->onSubmit($this, $value, $requestArguments);
413
414 $this->objectManager
415 ->get(Dispatcher::class)
416 ->dispatch(
417 self::class,
418 'onSubmit',
419 [$this, $page, &$value, $requestArguments]
420 );
421
422 foreach ($page->getElementsRecursively() as $element) {
423 try {
424 $value = ArrayUtility::getValueByPath($requestArguments, $element->getIdentifier(), '.');
425 } catch (\RuntimeException $exception) {
426 $value = null;
427 }
428
429 GeneralUtility::deprecationLog('EXT:form - calls for "onSubmit" are deprecated since TYPO3 v8 and will be removed in TYPO3 v9');
430 $element->onSubmit($this, $value, $requestArguments);
431
432 $this->objectManager
433 ->get(Dispatcher::class)
434 ->dispatch(
435 self::class,
436 'onSubmit',
437 [$this, $element, &$value, $requestArguments]
438 );
439
440 $this->formState->setFormValue($element->getIdentifier(), $value);
441 $registerPropertyPaths($element->getIdentifier());
442 }
443
444 // The more parts the path has, the more early it is processed
445 usort($propertyPathsForWhichPropertyMappingShouldHappen, function ($a, $b) {
446 return substr_count($b, '.') - substr_count($a, '.');
447 });
448
449 $processingRules = $this->formDefinition->getProcessingRules();
450
451 foreach ($propertyPathsForWhichPropertyMappingShouldHappen as $propertyPath) {
452 if (isset($processingRules[$propertyPath])) {
453 $processingRule = $processingRules[$propertyPath];
454 $value = $this->formState->getFormValue($propertyPath);
455 try {
456 $value = $processingRule->process($value);
457 } catch (PropertyException $exception) {
458 throw new PropertyMappingException(
459 'Failed to process FormValue at "' . $propertyPath . '" from "' . gettype($value) . '" to "' . $processingRule->getDataType() . '"',
460 1480024933,
461 $exception
462 );
463 }
464 $result->forProperty($propertyPath)->merge($processingRule->getProcessingMessages());
465 $this->formState->setFormValue($propertyPath, $value);
466 }
467 }
468
469 return $result;
470 }
471
472 /**
473 * Override the current page taken from the request, rendering the page with index $pageIndex instead.
474 *
475 * This is typically not needed in production code, but it is very helpful when displaying
476 * some kind of "preview" of the form.
477 *
478 * @param int $pageIndex
479 * @return void
480 * @api
481 */
482 public function overrideCurrentPage(int $pageIndex)
483 {
484 $this->currentPage = $this->formDefinition->getPageByIndex($pageIndex);
485 }
486
487 /**
488 * Render this form.
489 *
490 * @return null|string rendered form
491 * @throws RenderingException
492 * @api
493 */
494 public function render()
495 {
496 if ($this->isAfterLastPage()) {
497 $this->invokeFinishers();
498 return $this->response->getContent();
499 }
500
501 $this->formState->setLastDisplayedPageIndex($this->currentPage->getIndex());
502
503 if ($this->formDefinition->getRendererClassName() === '') {
504 throw new RenderingException(sprintf('The form definition "%s" does not have a rendererClassName set.', $this->formDefinition->getIdentifier()), 1326095912);
505 }
506 $rendererClassName = $this->formDefinition->getRendererClassName();
507 $renderer = $this->objectManager->get($rendererClassName);
508 if (!($renderer instanceof RendererInterface)) {
509 throw new RenderingException(sprintf('The renderer "%s" des not implement RendererInterface', $rendererClassName), 1326096024);
510 }
511
512 $controllerContext = $this->getControllerContext();
513
514 $renderer->setControllerContext($controllerContext);
515 $renderer->setFormRuntime($this);
516 return $renderer->render($this);
517 }
518
519 /**
520 * Executes all finishers of this form
521 *
522 * @return void
523 */
524 protected function invokeFinishers()
525 {
526 $finisherContext = $this->objectManager->get(FinisherContext::class,
527 $this,
528 $this->getControllerContext()
529 );
530 foreach ($this->formDefinition->getFinishers() as $finisher) {
531 $finisher->execute($finisherContext);
532 if ($finisherContext->isCancelled()) {
533 break;
534 }
535 }
536 }
537
538 /**
539 * @return string The identifier of underlying form
540 * @api
541 */
542 public function getIdentifier(): string
543 {
544 return $this->formDefinition->getIdentifier();
545 }
546
547 /**
548 * Get the request this object is bound to.
549 *
550 * This is mostly relevant inside Finishers, where you f.e. want to redirect
551 * the user to another page.
552 *
553 * @return Request the request this object is bound to
554 * @api
555 */
556 public function getRequest(): Request
557 {
558 return $this->request;
559 }
560
561 /**
562 * Get the response this object is bound to.
563 *
564 * This is mostly relevant inside Finishers, where you f.e. want to set response
565 * headers or output content.
566 *
567 * @return Response the response this object is bound to
568 * @api
569 */
570 public function getResponse(): Response
571 {
572 return $this->response;
573 }
574
575 /**
576 * Returns the currently selected page
577 *
578 * @return Page
579 * @api
580 */
581 public function getCurrentPage(): Page
582 {
583 return $this->currentPage;
584 }
585
586 /**
587 * Returns the previous page of the currently selected one or NULL if there is no previous page
588 *
589 * @return null|Page
590 * @api
591 */
592 public function getPreviousPage()
593 {
594 $previousPageIndex = $this->currentPage->getIndex() - 1;
595 if ($this->formDefinition->hasPageWithIndex($previousPageIndex)) {
596 return $this->formDefinition->getPageByIndex($previousPageIndex);
597 }
598 return null;
599 }
600
601 /**
602 * Returns the next page of the currently selected one or NULL if there is no next page
603 *
604 * @return null|Page
605 * @api
606 */
607 public function getNextPage()
608 {
609 $nextPageIndex = $this->currentPage->getIndex() + 1;
610 if ($this->formDefinition->hasPageWithIndex($nextPageIndex)) {
611 return $this->formDefinition->getPageByIndex($nextPageIndex);
612 }
613 return null;
614 }
615
616 /**
617 * @return ControllerContext
618 */
619 protected function getControllerContext(): ControllerContext
620 {
621 $uriBuilder = $this->objectManager->get(UriBuilder::class);
622 $uriBuilder->setRequest($this->request);
623 $controllerContext = $this->objectManager->get(ControllerContext::class);
624 $controllerContext->setRequest($this->request);
625 $controllerContext->setResponse($this->response);
626 $controllerContext->setArguments($this->objectManager->get(Arguments::class, []));
627 $controllerContext->setUriBuilder($uriBuilder);
628 return $controllerContext;
629 }
630
631 /**
632 * Abstract "type" of this Renderable. Is used during the rendering process
633 * to determine the template file or the View PHP class being used to render
634 * the particular element.
635 *
636 * @return string
637 * @api
638 */
639 public function getType(): string
640 {
641 return $this->formDefinition->getType();
642 }
643
644 /**
645 * @param string $identifier
646 * @return bool
647 * @internal
648 */
649 public function offsetExists($identifier)
650 {
651 if ($this->getElementValue($identifier) !== null) {
652 return true;
653 }
654
655 if (is_callable([$this, 'get' . ucfirst($identifier)])) {
656 return true;
657 }
658 if (is_callable([$this, 'has' . ucfirst($identifier)])) {
659 return true;
660 }
661 if (is_callable([$this, 'is' . ucfirst($identifier)])) {
662 return true;
663 }
664 if (property_exists($this, $identifier)) {
665 $propertyReflection = new PropertyReflection($this, $identifier);
666 return $propertyReflection->isPublic();
667 }
668
669 return false;
670 }
671
672 /**
673 * @param string $identifier
674 * @return mixed
675 * @internal
676 */
677 public function offsetGet($identifier)
678 {
679 if ($this->getElementValue($identifier) !== null) {
680 return $this->getElementValue($identifier);
681 }
682 $getterMethodName = 'get' . ucfirst($identifier);
683 if (is_callable([$this, $getterMethodName])) {
684 return $this->{$getterMethodName}();
685 }
686 return null;
687 }
688
689 /**
690 * @param string $identifier
691 * @param mixed $value
692 * @return void
693 * @internal
694 */
695 public function offsetSet($identifier, $value)
696 {
697 $this->formState->setFormValue($identifier, $value);
698 }
699
700 /**
701 * @param string $identifier
702 * @return void
703 * @internal
704 */
705 public function offsetUnset($identifier)
706 {
707 $this->formState->setFormValue($identifier, null);
708 }
709
710 /**
711 * Returns the value of the specified element
712 *
713 * @param string $identifier
714 * @return mixed
715 * @api
716 */
717 public function getElementValue(string $identifier)
718 {
719 $formValue = $this->formState->getFormValue($identifier);
720 if ($formValue !== null) {
721 return $formValue;
722 }
723 return $this->formDefinition->getElementDefaultValueByIdentifier($identifier);
724 }
725
726 /**
727 * @return array<Page> The Form's pages in the correct order
728 * @api
729 */
730 public function getPages(): array
731 {
732 return $this->formDefinition->getPages();
733 }
734
735 /**
736 * @return FormState
737 * @internal
738 */
739 public function getFormState(): FormState
740 {
741 return $this->formState;
742 }
743
744 /**
745 * Get all rendering options
746 *
747 * @return array associative array of rendering options
748 * @api
749 */
750 public function getRenderingOptions(): array
751 {
752 return $this->formDefinition->getRenderingOptions();
753 }
754
755 /**
756 * Get the renderer class name to be used to display this renderable;
757 * must implement RendererInterface
758 *
759 * @return string the renderer class name
760 * @api
761 */
762 public function getRendererClassName(): string
763 {
764 return $this->formDefinition->getRendererClassName();
765 }
766
767 /**
768 * Get the label which shall be displayed next to the form element
769 *
770 * @return string
771 * @api
772 */
773 public function getLabel(): string
774 {
775 return $this->formDefinition->getLabel();
776 }
777
778 /**
779 * Get the template name of the renderable
780 *
781 * @return string
782 * @api
783 */
784 public function getTemplateName(): string
785 {
786 return $this->formDefinition->getTemplateName();
787 }
788
789 /**
790 * Get the underlying form definition from the runtime
791 *
792 * @return FormDefinition
793 * @api
794 */
795 public function getFormDefinition(): FormDefinition
796 {
797 return $this->formDefinition;
798 }
799
800 /**
801 * This is a callback that is invoked by the Renderer before the corresponding element is rendered.
802 * Use this to access previously submitted values and/or modify the $formRuntime before an element
803 * is outputted to the browser.
804 *
805 * @param FormRuntime $formRuntime
806 * @return void
807 * @api
808 * @deprecated since TYPO3 v8, will be removed in TYPO3 v9
809 */
810 public function beforeRendering(FormRuntime $formRuntime)
811 {
812 GeneralUtility::logDeprecatedFunction();
813 }
814
815 /**
816 * @return TypoScriptFrontendController
817 */
818 protected function getTypoScriptFrontendController()
819 {
820 return $GLOBALS['TSFE'];
821 }
822 }