[!!!][TASK] Drop evaluation of @inject annotations
[Packages/TYPO3.CMS.git] / typo3 / sysext / form / Classes / Controller / FormEditorController.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Form\Controller;
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 use TYPO3\CMS\Backend\Routing\UriBuilder;
19 use TYPO3\CMS\Backend\Template\Components\ButtonBar;
20 use TYPO3\CMS\Backend\View\BackendTemplateView;
21 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
22 use TYPO3\CMS\Core\Imaging\Icon;
23 use TYPO3\CMS\Core\Localization\LanguageService;
24 use TYPO3\CMS\Core\Site\Entity\Site;
25 use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
26 use TYPO3\CMS\Core\Utility\ArrayUtility;
27 use TYPO3\CMS\Core\Utility\GeneralUtility;
28 use TYPO3\CMS\Extbase\Mvc\View\JsonView;
29 use TYPO3\CMS\Fluid\View\TemplateView;
30 use TYPO3\CMS\Form\Domain\Configuration\ConfigurationService;
31 use TYPO3\CMS\Form\Domain\Configuration\FormDefinitionConversionService;
32 use TYPO3\CMS\Form\Domain\Exception\RenderingException;
33 use TYPO3\CMS\Form\Domain\Factory\ArrayFormFactory;
34 use TYPO3\CMS\Form\Exception;
35 use TYPO3\CMS\Form\Mvc\Persistence\Exception\PersistenceManagerException;
36 use TYPO3\CMS\Form\Service\TranslationService;
37 use TYPO3\CMS\Form\Type\FormDefinitionArray;
38
39 /**
40 * The form editor controller
41 *
42 * Scope: backend
43 * @internal
44 */
45 class FormEditorController extends AbstractBackendController
46 {
47
48 /**
49 * Default View Container
50 *
51 * @var BackendTemplateView
52 */
53 protected $defaultViewObjectName = BackendTemplateView::class;
54
55 /**
56 * @var array
57 */
58 protected $prototypeConfiguration;
59
60 /**
61 * Displays the form editor
62 *
63 * @param string $formPersistenceIdentifier
64 * @param string $prototypeName
65 * @throws PersistenceManagerException
66 * @internal
67 */
68 public function indexAction(string $formPersistenceIdentifier, string $prototypeName = null)
69 {
70 $this->registerDocheaderButtons();
71 $this->view->getModuleTemplate()->setModuleName($this->request->getPluginName() . '_' . $this->request->getControllerName());
72 $this->view->getModuleTemplate()->setFlashMessageQueue($this->controllerContext->getFlashMessageQueue());
73
74 if (
75 strpos($formPersistenceIdentifier, 'EXT:') === 0
76 && !$this->formSettings['persistenceManager']['allowSaveToExtensionPaths']
77 ) {
78 throw new PersistenceManagerException('Edit a extension formDefinition is not allowed.', 1478265661);
79 }
80
81 $configurationService = $this->objectManager->get(ConfigurationService::class);
82 $formDefinition = $this->formPersistenceManager->load($formPersistenceIdentifier);
83
84 if ($prototypeName === null) {
85 $prototypeName = $formDefinition['prototypeName'] ?? 'standard';
86 } else {
87 // Loading a form definition with another prototype is currently not implemented but is planned in the future.
88 // This safety check is a preventive measure.
89 $selectablePrototypeNames = $configurationService->getSelectablePrototypeNamesDefinedInFormEditorSetup();
90 if (!in_array($prototypeName, $selectablePrototypeNames, true)) {
91 throw new Exception(sprintf('The prototype name "%s" is not configured within "formManager.selectablePrototypesConfiguration" ', $prototypeName), 1528625039);
92 }
93 }
94
95 $formDefinition['prototypeName'] = $prototypeName;
96 $this->prototypeConfiguration = $configurationService->getPrototypeConfiguration($prototypeName);
97
98 $formDefinition = $this->transformFormDefinitionForFormEditor($formDefinition);
99 $formEditorDefinitions = $this->getFormEditorDefinitions();
100
101 $formEditorAppInitialData = [
102 'formEditorDefinitions' => $formEditorDefinitions,
103 'formDefinition' => $formDefinition,
104 'formPersistenceIdentifier' => $formPersistenceIdentifier,
105 'prototypeName' => $prototypeName,
106 'endpoints' => [
107 'formPageRenderer' => $this->controllerContext->getUriBuilder()->uriFor('renderFormPage'),
108 'saveForm' => $this->controllerContext->getUriBuilder()->uriFor('saveForm')
109 ],
110 'additionalViewModelModules' => $this->prototypeConfiguration['formEditor']['dynamicRequireJsModules']['additionalViewModelModules'],
111 'maximumUndoSteps' => $this->prototypeConfiguration['formEditor']['maximumUndoSteps'],
112 ];
113
114 $this->view->assign('formEditorAppInitialData', json_encode($formEditorAppInitialData));
115 $this->view->assign('stylesheets', $this->resolveResourcePaths($this->prototypeConfiguration['formEditor']['stylesheets']));
116 $this->view->assign('formEditorTemplates', $this->renderFormEditorTemplates($formEditorDefinitions));
117 $this->view->assign('dynamicRequireJsModules', $this->prototypeConfiguration['formEditor']['dynamicRequireJsModules']);
118
119 $popupWindowWidth = 700;
120 $popupWindowHeight = 750;
121 $popupWindowSize = \trim($this->getBackendUser()->getTSConfig()['options.']['popupWindowSize'] ?? '');
122 if (!empty($popupWindowSize)) {
123 list($popupWindowWidth, $popupWindowHeight) = GeneralUtility::intExplode('x', $popupWindowSize);
124 }
125 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
126 $addInlineSettings = [
127 'FormEditor' => [
128 'typo3WinBrowserUrl' => (string)$uriBuilder->buildUriFromRoute('wizard_element_browser'),
129 ],
130 'Popup' => [
131 'PopupWindow' => [
132 'width' => $popupWindowWidth,
133 'height' => $popupWindowHeight
134 ],
135 ]
136 ];
137
138 $addInlineSettings = array_replace_recursive(
139 $addInlineSettings,
140 $this->prototypeConfiguration['formEditor']['addInlineSettings']
141 );
142 $this->view->assign('addInlineSettings', $addInlineSettings);
143 }
144
145 /**
146 * Initialize the save action.
147 * This action uses the Fluid JsonView::class as view.
148 *
149 * @internal
150 */
151 public function initializeSaveFormAction()
152 {
153 $this->defaultViewObjectName = JsonView::class;
154 }
155
156 /**
157 * Save a formDefinition which was build by the form editor.
158 *
159 * @param string $formPersistenceIdentifier
160 * @param FormDefinitionArray $formDefinition
161 * @internal
162 */
163 public function saveFormAction(string $formPersistenceIdentifier, FormDefinitionArray $formDefinition)
164 {
165 $formDefinition = $formDefinition->getArrayCopy();
166
167 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeFormSave'] ?? [] as $className) {
168 $hookObj = GeneralUtility::makeInstance($className);
169 if (method_exists($hookObj, 'beforeFormSave')) {
170 $formDefinition = $hookObj->beforeFormSave(
171 $formPersistenceIdentifier,
172 $formDefinition
173 );
174 }
175 }
176
177 $response = [
178 'status' => 'success',
179 ];
180
181 try {
182 $this->formPersistenceManager->save($formPersistenceIdentifier, $formDefinition);
183 $configurationService = $this->objectManager->get(ConfigurationService::class);
184 $this->prototypeConfiguration = $configurationService->getPrototypeConfiguration($formDefinition['prototypeName']);
185 $formDefinition = $this->transformFormDefinitionForFormEditor($formDefinition);
186 $response['formDefinition'] = $formDefinition;
187 } catch (PersistenceManagerException $e) {
188 $response = [
189 'status' => 'error',
190 'message' => $e->getMessage(),
191 'code' => $e->getCode(),
192 ];
193 }
194
195 $this->view->assign('response', $response);
196 // saveFormAction uses the extbase JsonView::class.
197 // That's why we have to set the view variables in this way.
198 $this->view->setVariablesToRender([
199 'response',
200 ]);
201 }
202
203 /**
204 * Render a page from the formDefinition which was build by the form editor.
205 * Use the frontend rendering and set the form framework to preview mode.
206 *
207 * @param FormDefinitionArray $formDefinition
208 * @param int $pageIndex
209 * @param string $prototypeName
210 * @return string
211 * @internal
212 */
213 public function renderFormPageAction(FormDefinitionArray $formDefinition, int $pageIndex, string $prototypeName = null): string
214 {
215 $prototypeName = $prototypeName ?: $formDefinition['prototypeName'] ?? 'standard';
216 $formDefinition = $formDefinition->getArrayCopy();
217
218 $formFactory = $this->objectManager->get(ArrayFormFactory::class);
219 $formDefinition = $formFactory->build($formDefinition, $prototypeName);
220 $formDefinition->setRenderingOption('previewMode', true);
221 $form = $formDefinition->bind($this->request, $this->response);
222 $form->setCurrentSiteLanguage($this->buildFakeSiteLanguage(0, 0));
223 $form->overrideCurrentPage($pageIndex);
224
225 return $form->render();
226 }
227
228 /**
229 * Build a SiteLanguage object to render the form preview with a
230 * specific language.
231 *
232 * @param int $pageId
233 * @param int $languageId
234 * @return SiteLanguage
235 */
236 protected function buildFakeSiteLanguage(int $pageId, int $languageId): SiteLanguage
237 {
238 $fakeSiteConfiguration = [
239 'languages' => [
240 [
241 'languageId' => $languageId,
242 'title' => 'Dummy',
243 'navigationTitle' => '',
244 'typo3Language' => '',
245 'flag' => '',
246 'locale' => '',
247 'iso-639-1' => '',
248 'hreflang' => '',
249 'direction' => '',
250 ],
251 ],
252 ];
253
254 /** @var \TYPO3\CMS\Core\Site\Entity\SiteLanguage $currentSiteLanguage */
255 $currentSiteLanguage = GeneralUtility::makeInstance(Site::class, 'form-dummy', $pageId, $fakeSiteConfiguration)
256 ->getLanguageById($languageId);
257 return $currentSiteLanguage;
258 }
259
260 /**
261 * Prepare the formElements.*.formEditor section from the YAML settings.
262 * Sort all formElements into groups and add additional data.
263 *
264 * @param array $formElementsDefinition
265 * @return array
266 */
267 protected function getInsertRenderablesPanelConfiguration(array $formElementsDefinition): array
268 {
269 $formElementsByGroup = [];
270
271 foreach ($formElementsDefinition as $formElementName => $formElementConfiguration) {
272 if (!isset($formElementConfiguration['group'])) {
273 continue;
274 }
275 if (!isset($formElementsByGroup[$formElementConfiguration['group']])) {
276 $formElementsByGroup[$formElementConfiguration['group']] = [];
277 }
278
279 $formElementConfiguration = TranslationService::getInstance()->translateValuesRecursive(
280 $formElementConfiguration,
281 $this->prototypeConfiguration['formEditor']['translationFile'] ?? null
282 );
283
284 $formElementsByGroup[$formElementConfiguration['group']][] = [
285 'key' => $formElementName,
286 'cssKey' => preg_replace('/[^a-z0-9]/', '-', strtolower($formElementName)),
287 'label' => $formElementConfiguration['label'],
288 'sorting' => $formElementConfiguration['groupSorting'],
289 'iconIdentifier' => $formElementConfiguration['iconIdentifier'],
290 ];
291 }
292
293 $formGroups = [];
294 foreach ($this->prototypeConfiguration['formEditor']['formElementGroups'] ?? [] as $groupName => $groupConfiguration) {
295 if (!isset($formElementsByGroup[$groupName])) {
296 continue;
297 }
298
299 usort($formElementsByGroup[$groupName], function ($a, $b) {
300 return $a['sorting'] - $b['sorting'];
301 });
302 unset($formElementsByGroup[$groupName]['sorting']);
303
304 $groupConfiguration = TranslationService::getInstance()->translateValuesRecursive(
305 $groupConfiguration,
306 $this->prototypeConfiguration['formEditor']['translationFile'] ?? null
307 );
308
309 $formGroups[] = [
310 'key' => $groupName,
311 'elements' => $formElementsByGroup[$groupName],
312 'label' => $groupConfiguration['label'],
313 ];
314 }
315
316 return $formGroups;
317 }
318
319 /**
320 * Reduce the YAML settings by the 'formEditor' keyword.
321 *
322 * @return array
323 */
324 protected function getFormEditorDefinitions(): array
325 {
326 $formEditorDefinitions = [];
327 foreach ([$this->prototypeConfiguration, $this->prototypeConfiguration['formEditor']] as $configuration) {
328 foreach ($configuration as $firstLevelItemKey => $firstLevelItemValue) {
329 if (substr($firstLevelItemKey, -10) !== 'Definition') {
330 continue;
331 }
332 $reducedKey = substr($firstLevelItemKey, 0, -10);
333 foreach ($configuration[$firstLevelItemKey] as $formEditorDefinitionKey => $formEditorDefinitionValue) {
334 if (isset($formEditorDefinitionValue['formEditor'])) {
335 $formEditorDefinitionValue = array_intersect_key($formEditorDefinitionValue, array_flip(['formEditor']));
336 $formEditorDefinitions[$reducedKey][$formEditorDefinitionKey] = $formEditorDefinitionValue['formEditor'];
337 } else {
338 $formEditorDefinitions[$reducedKey][$formEditorDefinitionKey] = $formEditorDefinitionValue;
339 }
340 }
341 }
342 }
343 $formEditorDefinitions = ArrayUtility::reIndexNumericArrayKeysRecursive($formEditorDefinitions);
344 $formEditorDefinitions = TranslationService::getInstance()->translateValuesRecursive(
345 $formEditorDefinitions,
346 $this->prototypeConfiguration['formEditor']['translationFile'] ?? null
347 );
348 return $formEditorDefinitions;
349 }
350
351 /**
352 * Registers the Icons into the docheader
353 *
354 * @throws \InvalidArgumentException
355 */
356 protected function registerDocheaderButtons()
357 {
358 /** @var ButtonBar $buttonBar */
359 $buttonBar = $this->view->getModuleTemplate()->getDocHeaderComponent()->getButtonBar();
360 $getVars = $this->request->getArguments();
361
362 if (isset($getVars['action']) && $getVars['action'] === 'index') {
363 $newPageButton = $buttonBar->makeInputButton()
364 ->setDataAttributes(['action' => 'formeditor-new-page', 'identifier' => 'headerNewPage'])
365 ->setTitle($this->getLanguageService()->sL('LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.new_page_button'))
366 ->setName('formeditor-new-page')
367 ->setValue('new-page')
368 ->setClasses('t3-form-element-new-page-button hidden')
369 ->setIcon($this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-page-new', Icon::SIZE_SMALL));
370 /** @var UriBuilder $uriBuilder */
371 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
372
373 $closeButton = $buttonBar->makeLinkButton()
374 ->setDataAttributes(['identifier' => 'closeButton'])
375 ->setHref((string)$uriBuilder->buildUriFromRoute('web_FormFormbuilder'))
376 ->setClasses('t3-form-element-close-form-button hidden')
377 ->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.closeDoc'))
378 ->setIcon($this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-close', Icon::SIZE_SMALL));
379
380 $saveButton = $buttonBar->makeInputButton()
381 ->setDataAttributes(['identifier' => 'saveButton'])
382 ->setTitle($this->getLanguageService()->sL('LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.save_button'))
383 ->setName('formeditor-save-form')
384 ->setValue('save')
385 ->setClasses('t3-form-element-save-form-button hidden')
386 ->setIcon($this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-document-save', Icon::SIZE_SMALL))
387 ->setShowLabelText(true);
388
389 $formSettingsButton = $buttonBar->makeInputButton()
390 ->setDataAttributes(['identifier' => 'formSettingsButton'])
391 ->setTitle($this->getLanguageService()->sL('LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.form_settings_button'))
392 ->setName('formeditor-form-settings')
393 ->setValue('settings')
394 ->setClasses('t3-form-element-form-settings-button hidden')
395 ->setIcon($this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-system-extension-configure', Icon::SIZE_SMALL))
396 ->setShowLabelText(true);
397
398 $undoButton = $buttonBar->makeInputButton()
399 ->setDataAttributes(['identifier' => 'undoButton'])
400 ->setTitle($this->getLanguageService()->sL('LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.undo_button'))
401 ->setName('formeditor-undo-form')
402 ->setValue('undo')
403 ->setClasses('t3-form-element-undo-form-button hidden disabled')
404 ->setIcon($this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-edit-undo', Icon::SIZE_SMALL));
405
406 $redoButton = $buttonBar->makeInputButton()
407 ->setDataAttributes(['identifier' => 'redoButton'])
408 ->setTitle($this->getLanguageService()->sL('LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.redo_button'))
409 ->setName('formeditor-redo-form')
410 ->setValue('redo')
411 ->setClasses('t3-form-element-redo-form-button hidden disabled')
412 ->setIcon($this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-edit-redo', Icon::SIZE_SMALL));
413
414 $buttonBar->addButton($newPageButton, ButtonBar::BUTTON_POSITION_LEFT, 1);
415 $buttonBar->addButton($closeButton, ButtonBar::BUTTON_POSITION_LEFT, 2);
416 $buttonBar->addButton($saveButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
417 $buttonBar->addButton($formSettingsButton, ButtonBar::BUTTON_POSITION_LEFT, 4);
418 $buttonBar->addButton($undoButton, ButtonBar::BUTTON_POSITION_LEFT, 5);
419 $buttonBar->addButton($redoButton, ButtonBar::BUTTON_POSITION_LEFT, 5);
420 }
421 }
422
423 /**
424 * Render the "text/x-formeditor-template" templates.
425 *
426 * @param array $formEditorDefinitions
427 * @return string
428 */
429 protected function renderFormEditorTemplates(array $formEditorDefinitions): string
430 {
431 $fluidConfiguration = $this->prototypeConfiguration['formEditor']['formEditorFluidConfiguration'] ?? null;
432 $formEditorPartials = $this->prototypeConfiguration['formEditor']['formEditorPartials'] ?? null;
433
434 if (!isset($fluidConfiguration['templatePathAndFilename'])) {
435 throw new RenderingException(
436 'The option templatePathAndFilename must be set.',
437 1485636499
438 );
439 }
440 if (
441 !isset($fluidConfiguration['layoutRootPaths'])
442 || !is_array($fluidConfiguration['layoutRootPaths'])
443 ) {
444 throw new RenderingException(
445 'The option layoutRootPaths must be set.',
446 1480294721
447 );
448 }
449 if (
450 !isset($fluidConfiguration['partialRootPaths'])
451 || !is_array($fluidConfiguration['partialRootPaths'])
452 ) {
453 throw new RenderingException(
454 'The option partialRootPaths must be set.',
455 1480294722
456 );
457 }
458
459 $insertRenderablesPanelConfiguration = $this->getInsertRenderablesPanelConfiguration($formEditorDefinitions['formElements']);
460
461 $view = $this->objectManager->get(TemplateView::class);
462 $view->setControllerContext(clone $this->controllerContext);
463 $view->getRenderingContext()->getTemplatePaths()->fillFromConfigurationArray($fluidConfiguration);
464 $view->setTemplatePathAndFilename($fluidConfiguration['templatePathAndFilename']);
465 $view->assignMultiple([
466 'insertRenderablesPanelConfiguration' => $insertRenderablesPanelConfiguration,
467 'formEditorPartials' => $formEditorPartials,
468 ]);
469
470 return $view->render();
471 }
472
473 /**
474 * @todo move this to FormDefinitionConversionService
475 * @param array $formDefinition
476 * @return array
477 */
478 protected function transformFormDefinitionForFormEditor(array $formDefinition): array
479 {
480 $multiValueProperties = [];
481 foreach ($this->prototypeConfiguration['formElementsDefinition'] as $type => $configuration) {
482 if (!isset($configuration['formEditor']['editors'])) {
483 continue;
484 }
485 foreach ($configuration['formEditor']['editors'] as $editorConfiguration) {
486 if ($editorConfiguration['templateName'] === 'Inspector-PropertyGridEditor') {
487 $multiValueProperties[$type][] = $editorConfiguration['propertyPath'];
488 }
489 }
490 }
491
492 $formDefinition = $this->filterEmptyArrays($formDefinition);
493
494 // @todo: replace with rte parsing
495 $formDefinition = ArrayUtility::stripTagsFromValuesRecursive($formDefinition);
496 $formDefinition = $this->transformMultiValueElementsForFormEditor($formDefinition, $multiValueProperties);
497
498 $formDefinitionConversionService = $this->getFormDefinitionConversionService();
499 $formDefinition = $formDefinitionConversionService->addHmacData($formDefinition);
500
501 return $formDefinition;
502 }
503
504 /**
505 * Some data needs a transformation before it can be used by the
506 * form editor. This rules for multivalue elements like select
507 * elements. To ensure the right sorting if the data goes into
508 * javascript, we need to do transformations:
509 *
510 * [
511 * '5' => '5',
512 * '4' => '4',
513 * '3' => '3'
514 * ]
515 *
516 *
517 * This method transform this into:
518 *
519 * [
520 * [
521 * _label => '5'
522 * _value => 5
523 * ],
524 * [
525 * _label => '4'
526 * _value => 4
527 * ],
528 * [
529 * _label => '3'
530 * _value => 3
531 * ],
532 * ]
533 *
534 * @param array $formDefinition
535 * @param array $multiValueProperties
536 * @return array
537 */
538 protected function transformMultiValueElementsForFormEditor(
539 array $formDefinition,
540 array $multiValueProperties
541 ): array {
542 $output = $formDefinition;
543 foreach ($formDefinition as $key => $value) {
544 if (isset($value['type']) && array_key_exists($value['type'], $multiValueProperties)) {
545 $multiValuePropertiesForType = $multiValueProperties[$value['type']];
546 foreach ($multiValuePropertiesForType as $multiValueProperty) {
547 if (!ArrayUtility::isValidPath($value, $multiValueProperty, '.')) {
548 continue;
549 }
550 $multiValuePropertyData = ArrayUtility::getValueByPath($value, $multiValueProperty, '.');
551 if (!is_array($multiValuePropertyData)) {
552 continue;
553 }
554 $newMultiValuePropertyData = [];
555 foreach ($multiValuePropertyData as $k => $v) {
556 $newMultiValuePropertyData[] = [
557 '_label' => $v,
558 '_value' => $k
559 ];
560 }
561 $value = ArrayUtility::setValueByPath($value, $multiValueProperty, $newMultiValuePropertyData, '.');
562 }
563 }
564
565 $output[$key] = $value;
566 if (is_array($value)) {
567 $output[$key] = $this->transformMultiValueElementsForFormEditor($value, $multiValueProperties);
568 }
569 }
570
571 return $output;
572 }
573
574 /**
575 * Remove keys from an array if the key value is an empty array
576 *
577 * @param array $array
578 * @return array
579 */
580 protected function filterEmptyArrays(array $array): array
581 {
582 foreach ($array as $key => $value) {
583 if (!is_array($value)) {
584 continue;
585 }
586 if (empty($value)) {
587 unset($array[$key]);
588 continue;
589 }
590 $array[$key] = $this->filterEmptyArrays($value);
591 if (empty($array[$key])) {
592 unset($array[$key]);
593 }
594 }
595
596 return $array;
597 }
598
599 /**
600 * @return FormDefinitionConversionService
601 */
602 protected function getFormDefinitionConversionService(): FormDefinitionConversionService
603 {
604 return GeneralUtility::makeInstance(FormDefinitionConversionService::class);
605 }
606
607 /**
608 * Returns the current BE user.
609 *
610 * @return BackendUserAuthentication
611 */
612 protected function getBackendUser(): BackendUserAuthentication
613 {
614 return $GLOBALS['BE_USER'];
615 }
616
617 /**
618 * Returns the language service
619 *
620 * @return LanguageService
621 */
622 protected function getLanguageService(): LanguageService
623 {
624 return $GLOBALS['LANG'];
625 }
626 }