‪TYPO3CMS  ‪main
DataStructureIdentifierListener.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 
19 
38 
49 {
55  #[AsEventListener('form-framework/modify-data-structure-identifier')]
57  {
58  $row = $event->‪getRow();
59  if (($row['CType'] ?? '') !== 'form_formframework'
60  || $event->‪getTableName() !== 'tt_content'
61  || $event->‪getFieldName() !== 'pi_flexform'
62  ) {
63  return;
64  }
65 
66  ‪$identifier = $event->‪getIdentifier();
67 
68  $currentFlexData = [];
69  if (!empty($row['pi_flexform']) && !\is_array($row['pi_flexform'])) {
70  $currentFlexData = ‪GeneralUtility::xml2array($row['pi_flexform']);
71  }
72 
73  // Add selected form value
74  ‪$identifier['ext-form-persistenceIdentifier'] = '';
75  if (!empty($currentFlexData['data']['sDEF']['lDEF']['settings.persistenceIdentifier']['vDEF'])) {
76  ‪$identifier['ext-form-persistenceIdentifier'] = $currentFlexData['data']['sDEF']['lDEF']['settings.persistenceIdentifier']['vDEF'];
77  }
78 
79  // Add bool - finisher override active or not
80  ‪$identifier['ext-form-overrideFinishers'] = '';
81  if (
82  isset($currentFlexData['data']['sDEF']['lDEF']['settings.overrideFinishers']['vDEF'])
83  && (int)$currentFlexData['data']['sDEF']['lDEF']['settings.overrideFinishers']['vDEF'] === 1
84  ) {
85  ‪$identifier['ext-form-overrideFinishers'] = 'enabled';
86  }
87 
89  }
90 
95  #[AsEventListener('form-framework/modify-data-structure')]
97  {
98  ‪$identifier = $event->‪getIdentifier();
99  if (!isset(‪$identifier['ext-form-persistenceIdentifier'])) {
100  return;
101  }
102 
103  $dataStructure = $event->‪getDataStructure();
104  try {
105  // Add list of existing forms to drop down if we find our key in the identifier
106  $formPersistenceManager = GeneralUtility::makeInstance(FormPersistenceManagerInterface::class);
107  $formIsAccessible = false;
108  foreach ($formPersistenceManager->listForms() as $form) {
109  $invalidFormDefinition = $form['invalid'] ?? false;
110 
111  if ($form['persistenceIdentifier'] === ‪$identifier['ext-form-persistenceIdentifier']) {
112  $formIsAccessible = true;
113  }
114 
115  if ($invalidFormDefinition) {
116  $dataStructure['sheets']['sDEF']['ROOT']['el']['settings.persistenceIdentifier']['config']['items'][] = [
117  'label' => $form['name'] . ' (' . $form['persistenceIdentifier'] . ')',
118  'value' => $form['persistenceIdentifier'],
119  'icon' => 'overlay-missing',
120  ];
121  } else {
122  $dataStructure['sheets']['sDEF']['ROOT']['el']['settings.persistenceIdentifier']['config']['items'][] = [
123  'label' => $form['name'] . ' (' . $form['persistenceIdentifier'] . ')',
124  'value' => $form['persistenceIdentifier'],
125  'icon' => 'content-form',
126  ];
127  }
128  }
129 
130  if (!empty(‪$identifier['ext-form-persistenceIdentifier']) && !$formIsAccessible) {
131  $dataStructure['sheets']['sDEF']['ROOT']['el']['settings.persistenceIdentifier']['config']['items'][] = [
132  'label' => sprintf(
133  $this->‪getLanguageService()->sL('LLL:EXT:form/Resources/Private/Language/Database.xlf:tt_content.preview.inaccessiblePersistenceIdentifier'),
134  ‪$identifier['ext-form-persistenceIdentifier']
135  ),
136  'value' => ‪$identifier['ext-form-persistenceIdentifier'],
137  ];
138  }
139 
140  // If a specific form is selected and if finisher override is active, add finisher sheets
141  if (!empty(‪$identifier['ext-form-persistenceIdentifier']) && $formIsAccessible) {
142  $persistenceIdentifier = ‪$identifier['ext-form-persistenceIdentifier'];
143  $formDefinition = $formPersistenceManager->load($persistenceIdentifier);
144 
145  $translationFile = 'LLL:EXT:form/Resources/Private/Language/Database.xlf';
146  $dataStructure['sheets']['sDEF']['ROOT']['el']['settings.overrideFinishers'] = [
147  'label' => $translationFile . ':tt_content.pi_flexform.formframework.overrideFinishers',
148  'onChange' => 'reload',
149  'config' => [
150  'type' => 'check',
151  ],
152  ];
153 
154  $newSheets = [];
155 
156  if (isset($formDefinition['finishers']) && !empty($formDefinition['finishers'])) {
157  $newSheets = $this->‪getAdditionalFinisherSheets($persistenceIdentifier, $formDefinition);
158  }
159 
160  if (empty($newSheets)) {
161  ArrayUtility::mergeRecursiveWithOverrule(
162  $dataStructure['sheets']['sDEF']['ROOT']['el']['settings.overrideFinishers'],
163  [
164  'description' => $translationFile . ':tt_content.pi_flexform.formframework.overrideFinishers.empty',
165  'config' => [
166  'readOnly' => true,
167  ],
168  ]
169  );
170  }
171 
172  if (‪$identifier['ext-form-overrideFinishers'] === 'enabled') {
173  ArrayUtility::mergeRecursiveWithOverrule(
174  $dataStructure,
175  $newSheets
176  );
177  }
178  }
180  $dataStructure = $this->‪addSelectedPersistenceIdentifier(‪$identifier['ext-form-persistenceIdentifier'], $dataStructure);
182  }
183 
184  $event->‪setDataStructure($dataStructure);
185  }
186 
193  protected function ‪getAdditionalFinisherSheets(string $persistenceIdentifier, array $formDefinition): array
194  {
195  if (empty($formDefinition['finishers'])) {
196  return [];
197  }
198 
199  $prototypeName = $formDefinition['prototypeName'] ?? 'standard';
200  $prototypeConfiguration = GeneralUtility::makeInstance(ConfigurationService::class)
201  ->getPrototypeConfiguration($prototypeName);
202 
203  if (empty($prototypeConfiguration['finishersDefinition'])) {
204  return [];
205  }
206 
207  $formIdentifier = $formDefinition['identifier'];
208  $finishersDefinition = $prototypeConfiguration['finishersDefinition'];
209 
210  $sheets = ['sheets' => []];
211  foreach ($formDefinition['finishers'] as $formFinisherDefinition) {
212  $finisherIdentifier = $formFinisherDefinition['identifier'];
213  if (!isset($finishersDefinition[$finisherIdentifier]['FormEngine']['elements'])) {
214  continue;
215  }
216  $sheetIdentifier = $this->‪buildFlexformSheetIdentifier(
217  $persistenceIdentifier,
218  $prototypeName,
219  $formIdentifier,
220  $finisherIdentifier
221  );
222 
223  $finishersDefinition = $this->‪translateFinisherDefinitionByIdentifier(
224  $finisherIdentifier,
225  $finishersDefinition,
226  $prototypeConfiguration
227  );
228 
229  $prototypeFinisherDefinition = $finishersDefinition[$finisherIdentifier];
230  $finisherLabel = $prototypeFinisherDefinition['FormEngine']['label'] ?? '';
231  $sheet = $this->‪initializeNewSheetArray($sheetIdentifier, $finisherLabel);
232 
233  $converterDto = GeneralUtility::makeInstance(
234  ProcessorDto::class,
235  $finisherIdentifier,
236  $prototypeFinisherDefinition,
237  $formFinisherDefinition
238  );
239 
240  // Remove all container elements "el" from sections beforehand.
241  // These should not be matched by the regex below.
242  // This greatly reduces headaches.
243  $elements = $prototypeFinisherDefinition['FormEngine']['elements'];
244  foreach ($elements as $key => $element) {
245  if ($element['section'] ?? false) {
246  unset($elements[$key]['el']);
247  }
248  }
249 
250  // Iterate over all `prototypes.<prototypeName>.finishersDefinition.<finisherIdentifier>.FormEngine.elements` values
251  // and convert them to FlexForm elements
252  GeneralUtility::makeInstance(ArrayProcessor::class, $elements)->forEach(
253  GeneralUtility::makeInstance(
254  ArrayProcessing::class,
255  'convertToFlexFormSheets',
256  // Parse top level elements and section containers.
257  '^(.*)(?:\.config\.type|\.section)$',
258  GeneralUtility::makeInstance(FinisherOptionGenerator::class, $converterDto)
259  )
260  );
261 
262  $sheet[$sheetIdentifier]['ROOT']['el'] = $converterDto->getResult();
263  ArrayUtility::mergeRecursiveWithOverrule($sheets['sheets'], $sheet);
264  }
265 
266  return $sheets;
267  }
268 
274  protected function ‪initializeNewSheetArray(string $sheetIdentifier, string $finisherName): array
275  {
276  if (empty($sheetIdentifier)) {
277  throw new \InvalidArgumentException('$sheetIdentifier must not be empty.', 1472060918);
278  }
279  if (empty($finisherName)) {
280  throw new \InvalidArgumentException('$finisherName must not be empty.', 1472060919);
281  }
282 
283  return [
284  $sheetIdentifier => [
285  'ROOT' => [
286  'sheetTitle' => $finisherName,
287  'type' => 'array',
288  'el' => [],
289  ],
290  ],
291  ];
292  }
293 
294  protected function ‪addSelectedPersistenceIdentifier(string $persistenceIdentifier, array $dataStructure): array
295  {
296  if (!empty($persistenceIdentifier)) {
297  $dataStructure['sheets']['sDEF']['ROOT']['el']['settings.persistenceIdentifier']['config']['items'][] = [
298  'label' => sprintf(
299  $this->‪getLanguageService()->sL('LLL:EXT:form/Resources/Private/Language/Database.xlf:tt_content.preview.inaccessiblePersistenceIdentifier'),
300  $persistenceIdentifier
301  ),
302  'value' => $persistenceIdentifier,
303  ];
304  }
305 
306  return $dataStructure;
307  }
308 
310  {
311  $messageText = sprintf(
312  $this->‪getLanguageService()->sL('LLL:EXT:form/Resources/Private/Language/Database.xlf:tt_content.preview.invalidFrameworkConfiguration.text'),
313  $e->getMessage()
314  );
315 
316  GeneralUtility::makeInstance(FlashMessageService::class)
317  ->getMessageQueueByIdentifier('core.template.flashMessages')
318  ->enqueue(
319  GeneralUtility::makeInstance(
320  FlashMessage::class,
321  $messageText,
322  $this->‪getLanguageService()->sL('LLL:EXT:form/Resources/Private/Language/Database.xlf:tt_content.preview.invalidFrameworkConfiguration.title'),
323  ContextualFeedbackSeverity::ERROR,
324  true
325  )
326  );
327  }
328 
330  string $persistenceIdentifier,
331  string $prototypeName,
332  string $formIdentifier,
333  string $finisherIdentifier
334  ): string {
335  return md5(
336  implode('', [
337  $persistenceIdentifier,
338  $prototypeName,
339  $formIdentifier,
340  $finisherIdentifier,
341  ])
342  );
343  }
344 
346  string $finisherIdentifier,
347  array $finishersDefinition,
348  array $prototypeConfiguration
349  ): array {
350  if (isset($finishersDefinition[$finisherIdentifier]['FormEngine']['translationFiles'])) {
351  $translationFiles = $finishersDefinition[$finisherIdentifier]['FormEngine']['translationFiles'];
352  } else {
353  $translationFiles = $prototypeConfiguration['formEngine']['translationFiles'];
354  }
355 
356  $finishersDefinition[$finisherIdentifier]['FormEngine'] = GeneralUtility::makeInstance(TranslationService::class)->translateValuesRecursive(
357  $finishersDefinition[$finisherIdentifier]['FormEngine'],
358  $translationFiles
359  );
360 
361  return $finishersDefinition;
362  }
363 
365  {
366  return ‪$GLOBALS['LANG'];
367  }
368 }
‪TYPO3\CMS\Core\Configuration\Event\AfterFlexFormDataStructureParsedEvent\setDataStructure
‪setDataStructure(array $dataStructure)
Definition: AfterFlexFormDataStructureParsedEvent.php:57
‪TYPO3\CMS\Form\EventListener\DataStructureIdentifierListener\addInvalidFrameworkConfigurationFlashMessage
‪addInvalidFrameworkConfigurationFlashMessage(\Exception $e)
Definition: DataStructureIdentifierListener.php:309
‪TYPO3\CMS\Form\Service\TranslationService
Definition: TranslationService.php:48
‪TYPO3\CMS\Core\Attribute\AsEventListener
Definition: AsEventListener.php:25
‪TYPO3\CMS\Form\Domain\Configuration\FlexformConfiguration\Processors\ProcessorDto
Definition: ProcessorDto.php:26
‪TYPO3\CMS\Form\EventListener\DataStructureIdentifierListener\translateFinisherDefinitionByIdentifier
‪translateFinisherDefinitionByIdentifier(string $finisherIdentifier, array $finishersDefinition, array $prototypeConfiguration)
Definition: DataStructureIdentifierListener.php:345
‪TYPO3\CMS\Form\EventListener\DataStructureIdentifierListener\getLanguageService
‪getLanguageService()
Definition: DataStructureIdentifierListener.php:364
‪TYPO3\CMS\Form\EventListener\DataStructureIdentifierListener\initializeNewSheetArray
‪initializeNewSheetArray(string $sheetIdentifier, string $finisherName)
Definition: DataStructureIdentifierListener.php:274
‪TYPO3\CMS\Form\EventListener
Definition: DataStructureIdentifierListener.php:18
‪TYPO3\CMS\Form\EventListener\DataStructureIdentifierListener\addSelectedPersistenceIdentifier
‪addSelectedPersistenceIdentifier(string $persistenceIdentifier, array $dataStructure)
Definition: DataStructureIdentifierListener.php:294
‪TYPO3\CMS\Form\EventListener\DataStructureIdentifierListener
Definition: DataStructureIdentifierListener.php:49
‪TYPO3\CMS\Core\Configuration\Event\AfterFlexFormDataStructureIdentifierInitializedEvent\setIdentifier
‪setIdentifier(array $identifier)
Definition: AfterFlexFormDataStructureIdentifierInitializedEvent.php:82
‪TYPO3\CMS\Core\Configuration\Event\AfterFlexFormDataStructureParsedEvent
Definition: AfterFlexFormDataStructureParsedEvent.php:32
‪TYPO3\CMS\Form\EventListener\DataStructureIdentifierListener\buildFlexformSheetIdentifier
‪buildFlexformSheetIdentifier(string $persistenceIdentifier, string $prototypeName, string $formIdentifier, string $finisherIdentifier)
Definition: DataStructureIdentifierListener.php:329
‪TYPO3\CMS\Core\Type\ContextualFeedbackSeverity
‪ContextualFeedbackSeverity
Definition: ContextualFeedbackSeverity.php:25
‪TYPO3\CMS\Form\Domain\Configuration\ArrayProcessing\ArrayProcessing
Definition: ArrayProcessing.php:27
‪TYPO3\CMS\Core\Configuration\Event\AfterFlexFormDataStructureIdentifierInitializedEvent\getTableName
‪getTableName()
Definition: AfterFlexFormDataStructureIdentifierInitializedEvent.php:60
‪TYPO3\CMS\Form\Exception
Definition: Exception.php:25
‪TYPO3\CMS\Form\EventListener\DataStructureIdentifierListener\modifyDataStructure
‪modifyDataStructure(AfterFlexFormDataStructureParsedEvent $event)
Definition: DataStructureIdentifierListener.php:96
‪TYPO3\CMS\Form\Domain\Configuration\ArrayProcessing\ArrayProcessor
Definition: ArrayProcessor.php:30
‪TYPO3\CMS\Core\Configuration\Event\AfterFlexFormDataStructureParsedEvent\getIdentifier
‪getIdentifier()
Definition: AfterFlexFormDataStructureParsedEvent.php:38
‪TYPO3\CMS\Form\EventListener\DataStructureIdentifierListener\getAdditionalFinisherSheets
‪getAdditionalFinisherSheets(string $persistenceIdentifier, array $formDefinition)
Definition: DataStructureIdentifierListener.php:193
‪TYPO3\CMS\Core\Configuration\Event\AfterFlexFormDataStructureIdentifierInitializedEvent
Definition: AfterFlexFormDataStructureIdentifierInitializedEvent.php:35
‪TYPO3\CMS\Form\Domain\Configuration\FlexformConfiguration\Processors\FinisherOptionGenerator
Definition: FinisherOptionGenerator.php:30
‪TYPO3\CMS\Core\Configuration\Event\AfterFlexFormDataStructureIdentifierInitializedEvent\getRow
‪getRow()
Definition: AfterFlexFormDataStructureIdentifierInitializedEvent.php:73
‪TYPO3\CMS\Form\Domain\Configuration\ConfigurationService
Definition: ConfigurationService.php:50
‪TYPO3\CMS\Form\Mvc\Configuration\Exception\ParseErrorException
Definition: ParseErrorException.php:24
‪TYPO3\CMS\Core\Messaging\FlashMessage
Definition: FlashMessage.php:27
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:26
‪TYPO3\CMS\Core\Configuration\Event\AfterFlexFormDataStructureIdentifierInitializedEvent\getIdentifier
‪getIdentifier()
Definition: AfterFlexFormDataStructureIdentifierInitializedEvent.php:92
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Form\EventListener\DataStructureIdentifierListener\modifyDataStructureIdentifier
‪modifyDataStructureIdentifier(AfterFlexFormDataStructureIdentifierInitializedEvent $event)
Definition: DataStructureIdentifierListener.php:56
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface
Definition: FormPersistenceManagerInterface.php:32
‪TYPO3\CMS\Core\Utility\GeneralUtility\xml2array
‪static array string xml2array(string $string, string $NSprefix='', bool $reportDocTag=false)
Definition: GeneralUtility.php:1264
‪TYPO3\CMS\Core\Messaging\FlashMessageService
Definition: FlashMessageService.php:27
‪TYPO3\CMS\Core\Configuration\Event\AfterFlexFormDataStructureIdentifierInitializedEvent\getFieldName
‪getFieldName()
Definition: AfterFlexFormDataStructureIdentifierInitializedEvent.php:65
‪TYPO3\CMS\Webhooks\Message\$identifier
‪identifier readonly string $identifier
Definition: FileAddedMessage.php:37
‪TYPO3\CMS\Form\Mvc\Configuration\Exception\NoSuchFileException
Definition: NoSuchFileException.php:24
‪TYPO3\CMS\Core\Configuration\Event\AfterFlexFormDataStructureParsedEvent\getDataStructure
‪getDataStructure()
Definition: AfterFlexFormDataStructureParsedEvent.php:48