[TASK] Performance optimizations for the form manager module
[Packages/TYPO3.CMS.git] / typo3 / sysext / form / Classes / Hooks / FormFileExtensionUpdate.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Form\Hooks;
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 Symfony\Component\Console\Output\OutputInterface;
19 use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
20 use TYPO3\CMS\Core\Database\Connection;
21 use TYPO3\CMS\Core\Database\ConnectionPool;
22 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
23 use TYPO3\CMS\Core\Database\ReferenceIndex;
24 use TYPO3\CMS\Core\Localization\LanguageService;
25 use TYPO3\CMS\Core\Resource\DuplicationBehavior;
26 use TYPO3\CMS\Core\Resource\File;
27 use TYPO3\CMS\Core\Resource\ResourceFactory;
28 use TYPO3\CMS\Core\Utility\GeneralUtility;
29 use TYPO3\CMS\Core\Utility\PathUtility;
30 use TYPO3\CMS\Core\Utility\StringUtility;
31 use TYPO3\CMS\Extbase\Object\ObjectManager;
32 use TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager;
33 use TYPO3\CMS\Form\Slot\FilePersistenceSlot;
34 use TYPO3\CMS\Install\Updates\ChattyInterface;
35 use TYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite;
36 use TYPO3\CMS\Install\Updates\ReferenceIndexUpdatedPrerequisite;
37 use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
38
39 /**
40 * Update wizard to migrate all forms currently in use to new ending
41 */
42 class FormFileExtensionUpdate implements ChattyInterface, UpgradeWizardInterface
43 {
44 /**
45 * @var OutputInterface
46 */
47 protected $output;
48
49 /**
50 * @var FormPersistenceManager
51 */
52 protected $persistenceManager;
53
54 /**
55 * @var ResourceFactory
56 */
57 protected $resourceFactory;
58
59 /**
60 * @var ReferenceIndex
61 */
62 protected $referenceIndex;
63
64 /**
65 * @var FlexFormTools
66 */
67 protected $flexFormTools;
68
69 /**
70 * @var Connection
71 */
72 protected $connection;
73
74 /**
75 * Return the identifier for this wizard
76 * This should be the same string as used in the ext_localconf class registration
77 *
78 * @return string
79 */
80 public function getIdentifier(): string
81 {
82 return 'formFileExtension';
83 }
84
85 /**
86 * Return the speaking name of this wizard
87 *
88 * @return string
89 */
90 public function getTitle(): string
91 {
92 return 'Rename form definition file extension from .yaml to .form.yaml';
93 }
94
95 /**
96 * Return the description for this wizard
97 *
98 * @return string
99 */
100 public function getDescription(): string
101 {
102 return 'Form definition files need to be named *.form.yaml to have a way of distinguishing form yaml ' .
103 'configuration files from other yaml configuration files. This wizard will analyze and rename found files.';
104 }
105
106 /**
107 * Returns an array of class names of Prerequisite classes
108 * This way a wizard can define dependencies like "database up-to-date" or
109 * "reference index updated"
110 *
111 * @return string[]
112 */
113 public function getPrerequisites(): array
114 {
115 return [
116 ReferenceIndexUpdatedPrerequisite::class,
117 DatabaseUpdatedPrerequisite::class
118 ];
119 }
120
121 public function setOutput(OutputInterface $output): void
122 {
123 $this->output = $output;
124 }
125
126 /**
127 * Checks whether updates are required.
128 *
129 * @return bool Whether an update is required (TRUE) or not (FALSE)
130 */
131 public function updateNecessary(): bool
132 {
133 $updateNeeded = false;
134
135 $this->persistenceManager = $this->getObjectManager()->get(FormPersistenceManager::class);
136 $this->resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
137
138 foreach ($this->getFormDefinitionsInformation() as $formDefinitionInformation) {
139 if (
140 (
141 $formDefinitionInformation['hasNewFileExtension'] === true
142 && $formDefinitionInformation['hasReferencesForOldFileExtension'] === false
143 && $formDefinitionInformation['hasReferencesForNewFileExtension'] === false
144 )
145 || (
146 $formDefinitionInformation['hasNewFileExtension'] === false
147 && $formDefinitionInformation['location'] === 'extension'
148 && $formDefinitionInformation['hasReferencesForOldFileExtension'] === false
149 && $formDefinitionInformation['hasReferencesForNewFileExtension'] === false
150 )
151 ) {
152 continue;
153 }
154
155 if (
156 $formDefinitionInformation['hasNewFileExtension'] === false
157 && $formDefinitionInformation['location'] === 'storage'
158 ) {
159 $updateNeeded = true;
160 $this->output->writeln('Form definition files were found that should be migrated to be named .form.yaml.');
161 }
162
163 if (
164 $formDefinitionInformation['hasNewFileExtension']
165 && $formDefinitionInformation['hasReferencesForOldFileExtension']
166 ) {
167 $updateNeeded = true;
168 $this->output->writeln('Referenced form definition files found that should be updated.');
169 }
170
171 if (
172 $formDefinitionInformation['referencesForOldFileExtensionNeedsFlexformUpdates'] === true
173 || $formDefinitionInformation['referencesForNewFileExtensionNeedsFlexformUpdates'] === true
174 ) {
175 $updateNeeded = true;
176 if ($formDefinitionInformation['hasNewFileExtension'] === true) {
177 $this->output->writeln('Referenced form definition files found that should be updated.');
178 } elseif ($formDefinitionInformation['location'] === 'storage') {
179 $this->output->writeln('Referenced form definition files found that should be updated.');
180 } else {
181 $this->output->writeln(
182 '<warning>There are references to form definitions which are located in extensions and thus cannot be renamed automatically by this wizard.'
183 . 'This form definitions from extensions that do not end with .form.yaml have to be renamed by hand!'
184 . 'After that you can run this wizard again to migrate the references.</warning>'
185 );
186 }
187 }
188 }
189
190 return $updateNeeded;
191 }
192
193 /**
194 * Performs the accordant updates.
195 *
196 * @return bool Whether everything went smoothly or not
197 */
198 public function executeUpdate(): bool
199 {
200 $success = true;
201
202 $GLOBALS['LANG'] = GeneralUtility::makeInstance(LanguageService::class);
203 $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
204 $filePersistenceSlot = GeneralUtility::makeInstance(FilePersistenceSlot::class);
205
206 $this->connection = $connectionPool->getConnectionForTable('tt_content');
207 $this->persistenceManager = $this->getObjectManager()->get(FormPersistenceManager::class);
208 $this->resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
209 $this->referenceIndex = GeneralUtility::makeInstance(ReferenceIndex::class);
210 $this->flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
211
212 $filePersistenceSlot->defineInvocation(
213 FilePersistenceSlot::COMMAND_FILE_RENAME,
214 true
215 );
216
217 $formDefinitionsInformation = $this->getFormDefinitionsInformation();
218 foreach ($formDefinitionsInformation as $currentPersistenceIdentifier => $formDefinitionInformation) {
219 if (
220 (
221 $formDefinitionInformation['hasNewFileExtension'] === true
222 && $formDefinitionInformation['hasReferencesForOldFileExtension'] === false
223 && $formDefinitionInformation['hasReferencesForNewFileExtension'] === false
224 )
225 || (
226 $formDefinitionInformation['hasNewFileExtension'] === false
227 && $formDefinitionInformation['location'] === 'extension'
228 && $formDefinitionInformation['hasReferencesForOldFileExtension'] === false
229 && $formDefinitionInformation['hasReferencesForNewFileExtension'] === false
230 )
231 ) {
232 continue;
233 }
234
235 if (
236 $formDefinitionInformation['hasNewFileExtension'] === true
237 && (
238 $formDefinitionInformation['hasReferencesForOldFileExtension'] === true
239 || $formDefinitionInformation['hasReferencesForNewFileExtension'] === true
240 )
241 ) {
242 foreach ($formDefinitionInformation['referencesForOldFileExtension'] as $referenceForOldFileExtension) {
243 $newFlexformXml = $this->generateNewFlexformForReference(
244 $referenceForOldFileExtension,
245 $referenceForOldFileExtension['sheetIdentifiersWhichNeedsUpdate'],
246 $formDefinitionInformation['persistenceIdentifier']
247 );
248 $this->updateContentReference(
249 $referenceForOldFileExtension['ttContentUid'],
250 $newFlexformXml,
251 true
252 );
253 }
254
255 foreach ($formDefinitionInformation['referencesForNewFileExtension'] as $referenceForNewFileExtension) {
256 $newFlexformXml = $this->generateNewFlexformForReference(
257 $referenceForNewFileExtension,
258 $referenceForNewFileExtension['sheetIdentifiersWhichNeedsUpdate']
259 );
260 $this->updateContentReference(
261 $referenceForNewFileExtension['ttContentUid'],
262 $newFlexformXml
263 );
264 }
265
266 continue;
267 }
268
269 if ($formDefinitionInformation['location'] === 'storage') {
270 $file = $formDefinitionInformation['file'];
271
272 $newPossiblePersistenceIdentifier = $this->persistenceManager->getUniquePersistenceIdentifier(
273 $file->getNameWithoutExtension(),
274 $file->getParentFolder()->getCombinedIdentifier()
275 );
276 $newFileName = PathUtility::pathinfo(
277 $newPossiblePersistenceIdentifier,
278 PATHINFO_BASENAME
279 );
280
281 try {
282 $file->rename($newFileName, DuplicationBehavior::RENAME);
283 $newPersistenceIdentifier = $file->getCombinedIdentifier();
284 } catch (\Exception $e) {
285 $this->output->writeln(sprintf(
286 '<error>Failed to rename form definition "%s" to "%s".</error>',
287 $formDefinitionInformation['persistenceIdentifier'],
288 $newFileName
289 ));
290 $success = false;
291 continue;
292 }
293
294 if (
295 $formDefinitionInformation['hasReferencesForOldFileExtension'] === true
296 || $formDefinitionInformation['hasReferencesForNewFileExtension'] === true
297 ) {
298 foreach ($formDefinitionInformation['referencesForOldFileExtension'] as $referenceForOldFileExtension) {
299 $sheetIdentifiersWhichNeedsUpdate = $this->getSheetIdentifiersWhichNeedsUpdate(
300 $referenceForOldFileExtension['flexform'],
301 $formDefinitionsInformation,
302 $currentPersistenceIdentifier,
303 $formDefinitionInformation['persistenceIdentifier'],
304 $newPersistenceIdentifier
305 );
306 $newFlexformXml = $this->generateNewFlexformForReference(
307 $referenceForOldFileExtension,
308 $sheetIdentifiersWhichNeedsUpdate,
309 $newPersistenceIdentifier
310 );
311 $this->updateContentReference(
312 $referenceForOldFileExtension['ttContentUid'],
313 $newFlexformXml
314 );
315 }
316
317 foreach ($formDefinitionInformation['referencesForNewFileExtension'] as $referenceForNewFileExtension) {
318 $sheetIdentifiersWhichNeedsUpdate = $this->getSheetIdentifiersWhichNeedsUpdate(
319 $referenceForNewFileExtension['flexform'],
320 $formDefinitionsInformation,
321 $currentPersistenceIdentifier,
322 $formDefinitionInformation['persistenceIdentifier'],
323 $newPersistenceIdentifier
324 );
325 $newFlexformXml = $this->generateNewFlexformForReference(
326 $referenceForNewFileExtension,
327 $sheetIdentifiersWhichNeedsUpdate,
328 $newPersistenceIdentifier
329 );
330 $this->updateContentReference(
331 $referenceForNewFileExtension['ttContentUid'],
332 $newFlexformXml
333 );
334 }
335 }
336 } else {
337 $success = false;
338 $this->output->writeln(sprintf(
339 '<error>Failed to rename form definition "%s" to "%s". You have to be rename it by hand!. '
340 . 'After that you can run this wizard again to migrate the references.</error>',
341 $formDefinitionInformation['persistenceIdentifier'],
342 $this->getNewPersistenceIdentifier($formDefinitionInformation['persistenceIdentifier'])
343 ));
344 }
345 }
346
347 $filePersistenceSlot->defineInvocation(
348 FilePersistenceSlot::COMMAND_FILE_RENAME,
349 null
350 );
351
352 return $success;
353 }
354
355 /**
356 * @return array
357 */
358 protected function getFormDefinitionsInformation(): array
359 {
360 $formDefinitionsInformation = array_merge(
361 $this->getFormDefinitionsInformationFromStorages(),
362 $this->getFormDefinitionsInformationFromExtensions()
363 );
364
365 $formDefinitionsInformation = $this->enrichFormDefinitionsInformationWithDataFromReferences($formDefinitionsInformation);
366
367 return $formDefinitionsInformation;
368 }
369
370 /**
371 * @return array
372 */
373 protected function getFormDefinitionsInformationFromStorages(): array
374 {
375 $formDefinitionsInformation = [];
376
377 foreach ($this->persistenceManager->retrieveYamlFilesFromStorageFolders() as $file) {
378 $persistenceIdentifier = $file->getCombinedIdentifier();
379
380 $formDefinition = $this->getFormDefinition($file);
381 if (empty($formDefinition)) {
382 continue;
383 }
384
385 $formDefinitionsInformation[$persistenceIdentifier] = $this->setFormDefinitionInformationData(
386 $persistenceIdentifier,
387 $formDefinition,
388 $file,
389 'storage'
390 );
391 }
392
393 return $formDefinitionsInformation;
394 }
395
396 /**
397 * @return array
398 */
399 protected function getFormDefinitionsInformationFromExtensions(): array
400 {
401 $formDefinitionsInformation = [];
402
403 foreach ($this->persistenceManager->retrieveYamlFilesFromExtensionFolders() as $persistenceIdentifier => $_) {
404 try {
405 $file = $this->resourceFactory->retrieveFileOrFolderObject($persistenceIdentifier);
406 } catch (\Exception $exception) {
407 continue;
408 }
409
410 $formDefinition = $this->getFormDefinition($file);
411 if (empty($formDefinition)) {
412 continue;
413 }
414
415 $formDefinitionsInformation[$persistenceIdentifier] = $this->setFormDefinitionInformationData(
416 $persistenceIdentifier,
417 $formDefinition,
418 $file,
419 'extension'
420 );
421 }
422
423 return $formDefinitionsInformation;
424 }
425
426 /**
427 * @param string $persistenceIdentifier
428 * @param array $formDefinition
429 * @param File $file
430 * @param string $localtion
431 * @return array
432 */
433 protected function setFormDefinitionInformationData(
434 string $persistenceIdentifier,
435 array $formDefinition,
436 File $file,
437 string $localtion
438 ): array {
439 return [
440 'location' => $localtion,
441 'persistenceIdentifier' => $persistenceIdentifier,
442 'prototypeName' => $formDefinition['prototypeName'],
443 'formIdentifier' => $formDefinition['identifier'],
444 'file' => $file,
445 'referencesForOldFileExtension' => [],
446 'referencesForNewFileExtension' => [],
447 'hasNewFileExtension' => $this->hasNewFileExtension($persistenceIdentifier),
448 'hasReferencesForOldFileExtension' => false,
449 'hasReferencesForNewFileExtension' => false,
450 'referencesForOldFileExtensionNeedsFlexformUpdates' => false,
451 'referencesForNewFileExtensionNeedsFlexformUpdates' => false,
452 ];
453 }
454
455 /**
456 * @param array $formDefinitionsInformation
457 * @return array
458 */
459 protected function enrichFormDefinitionsInformationWithDataFromReferences(array $formDefinitionsInformation): array
460 {
461 foreach ($this->getAllFlexformFieldsFromFormPlugins() as $pluginData) {
462 if (empty($pluginData['pi_flexform'])) {
463 continue;
464 }
465 $flexform = GeneralUtility::xml2array($pluginData['pi_flexform']);
466 $referencedPersistenceIdentifier = $this->getPersistenceIdentifierFromFlexform($flexform);
467 $referenceHasNewFileExtension = $this->hasNewFileExtension($referencedPersistenceIdentifier);
468 $possibleOldReferencedPersistenceIdentifier = $this->getOldPersistenceIdentifier($referencedPersistenceIdentifier);
469 $possibleNewReferencedPersistenceIdentifier = $this->getNewPersistenceIdentifier($referencedPersistenceIdentifier);
470
471 $referenceData = [
472 'scope' => null,
473 'ttContentUid' => (int)$pluginData['uid'],
474 'flexform' => $flexform,
475 'sheetIdentifiersWhichNeedsUpdate' => [],
476 ];
477
478 $targetPersistenceIdentifier = null;
479 if (array_key_exists($referencedPersistenceIdentifier, $formDefinitionsInformation)) {
480 $targetPersistenceIdentifier = $referencedPersistenceIdentifier;
481 if ($referenceHasNewFileExtension) {
482 $referenceData['scope'] = 'referencesForNewFileExtension';
483 } else {
484 $referenceData['scope'] = 'referencesForOldFileExtension';
485 }
486 } else {
487 if ($referenceHasNewFileExtension) {
488 if (array_key_exists($possibleOldReferencedPersistenceIdentifier, $formDefinitionsInformation)) {
489 $targetPersistenceIdentifier = $possibleOldReferencedPersistenceIdentifier;
490 $referenceData['scope'] = 'referencesForNewFileExtension';
491 } else {
492 // There is no existing file for this reference
493 continue;
494 }
495 } else {
496 if (array_key_exists($possibleNewReferencedPersistenceIdentifier, $formDefinitionsInformation)) {
497 $targetPersistenceIdentifier = $possibleNewReferencedPersistenceIdentifier;
498 $referenceData['scope'] = 'referencesForOldFileExtension';
499 } else {
500 // There is no existing file for this reference
501 continue;
502 }
503 }
504 }
505
506 $referenceData['sheetIdentifiersWhichNeedsUpdate'] = $this->getSheetIdentifiersWhichNeedsUpdate(
507 $flexform,
508 $formDefinitionsInformation,
509 $targetPersistenceIdentifier,
510 $possibleOldReferencedPersistenceIdentifier,
511 $possibleNewReferencedPersistenceIdentifier
512 );
513
514 $scope = $referenceData['scope'];
515
516 $formDefinitionsInformation[$targetPersistenceIdentifier][$scope][] = $referenceData;
517 if ($scope === 'referencesForOldFileExtension') {
518 $formDefinitionsInformation[$targetPersistenceIdentifier]['hasReferencesForOldFileExtension'] = true;
519 $formDefinitionsInformation[$targetPersistenceIdentifier]['referencesForOldFileExtensionNeedsFlexformUpdates'] = !empty($referenceData['sheetIdentifiersWhichNeedsUpdate']);
520 } else {
521 $formDefinitionsInformation[$targetPersistenceIdentifier]['hasReferencesForNewFileExtension'] = true;
522 $formDefinitionsInformation[$targetPersistenceIdentifier]['referencesForNewFileExtensionNeedsFlexformUpdates'] = !empty($referenceData['sheetIdentifiersWhichNeedsUpdate']);
523 }
524 }
525
526 return $formDefinitionsInformation;
527 }
528
529 /**
530 * @param array $flexform
531 * @param array $formDefinitionsInformation
532 * @param string $targetPersistenceIdentifier
533 * @param string $possibleOldReferencedPersistenceIdentifier
534 * @param string $possibleNewReferencedPersistenceIdentifier
535 * @return array
536 */
537 protected function getSheetIdentifiersWhichNeedsUpdate(
538 array $flexform,
539 array $formDefinitionsInformation,
540 string $targetPersistenceIdentifier,
541 string $possibleOldReferencedPersistenceIdentifier,
542 string $possibleNewReferencedPersistenceIdentifier
543 ): array {
544 $sheetIdentifiersWhichNeedsUpdate = [];
545
546 $sheetIdentifiers = $this->getSheetIdentifiersForFinisherOverrides($flexform);
547 foreach ($sheetIdentifiers as $currentSheetIdentifier => $finisherIdentifier) {
548 $sheetIdentifierForOldPersistenceIdentifier = $this->buildExpectedSheetIdentifier(
549 $possibleOldReferencedPersistenceIdentifier,
550 $formDefinitionsInformation[$targetPersistenceIdentifier]['prototypeName'],
551 $formDefinitionsInformation[$targetPersistenceIdentifier]['formIdentifier'],
552 $finisherIdentifier
553 );
554
555 $sheetIdentifierForNewPersistenceIdentifier = $this->buildExpectedSheetIdentifier(
556 $possibleNewReferencedPersistenceIdentifier,
557 $formDefinitionsInformation[$targetPersistenceIdentifier]['prototypeName'],
558 $formDefinitionsInformation[$targetPersistenceIdentifier]['formIdentifier'],
559 $finisherIdentifier
560 );
561
562 if (
563 $currentSheetIdentifier === $sheetIdentifierForOldPersistenceIdentifier
564 && !array_key_exists($sheetIdentifierForNewPersistenceIdentifier, $sheetIdentifiers)
565 ) {
566 $sheetIdentifiersWhichNeedsUpdate[$currentSheetIdentifier] = $sheetIdentifierForNewPersistenceIdentifier;
567 }
568 }
569
570 return $sheetIdentifiersWhichNeedsUpdate;
571 }
572
573 /**
574 * @param array $flexform
575 * @return array
576 */
577 protected function getSheetIdentifiersForFinisherOverrides(array $flexform): array
578 {
579 $sheetIdentifiers = [];
580 foreach ($this->getFinisherSheetsFromFlexform($flexform) as $sheetIdentifier => $sheetData) {
581 $itemOptionPath = array_keys($sheetData['lDEF']);
582 $firstSheetItemOptionPath = array_shift($itemOptionPath);
583 preg_match('#^settings\.finishers\.(.*)\..+$#', $firstSheetItemOptionPath, $matches);
584 if (!isset($matches[1])) {
585 continue;
586 }
587 $sheetIdentifiers[$sheetIdentifier] = $matches[1];
588 }
589
590 return $sheetIdentifiers;
591 }
592
593 /**
594 * @param array $flexform
595 * @return array
596 */
597 protected function getFinisherSheetsFromFlexform(array $flexform): array
598 {
599 if (!isset($flexform['data'])) {
600 return [];
601 }
602
603 return array_filter(
604 $flexform['data'],
605 function ($key) {
606 return $key !== 'sDEF' && strlen($key) === 32;
607 },
608 ARRAY_FILTER_USE_KEY
609 );
610 }
611
612 /**
613 * @param array $flexform
614 * @return string
615 */
616 protected function getPersistenceIdentifierFromFlexform(array $flexform): string
617 {
618 return $flexform['data']['sDEF']['lDEF']['settings.persistenceIdentifier']['vDEF'] ?? '';
619 }
620
621 /**
622 * @param array $referenceData
623 * @param array $sheetIdentifiersWhichNeedsUpdate
624 * @param string $newPersistenceIdentifier
625 * @return string
626 */
627 protected function generateNewFlexformForReference(
628 array $referenceData,
629 array $sheetIdentifiersWhichNeedsUpdate,
630 string $newPersistenceIdentifier = ''
631 ): string {
632 $flexform = $referenceData['flexform'];
633 if (!empty($newPersistenceIdentifier)) {
634 $flexform['data']['sDEF']['lDEF']['settings.persistenceIdentifier']['vDEF'] = $newPersistenceIdentifier;
635 }
636
637 foreach ($sheetIdentifiersWhichNeedsUpdate as $oldSheetIdentifier => $newSheetIdentifier) {
638 $flexform['data'][$newSheetIdentifier] = $flexform['data'][$oldSheetIdentifier];
639 unset($flexform['data'][$oldSheetIdentifier]);
640 }
641
642 return $this->flexFormTools->flexArray2Xml($flexform, true);
643 }
644
645 /**
646 * @param string $persistenceIdentifier
647 * @return bool
648 */
649 protected function hasNewFileExtension(string $persistenceIdentifier): bool
650 {
651 return StringUtility::endsWith(
652 $persistenceIdentifier,
653 FormPersistenceManager::FORM_DEFINITION_FILE_EXTENSION
654 );
655 }
656
657 /**
658 * @param array $formDefinition
659 * @return bool
660 */
661 protected function looksLikeAFormDefinition(array $formDefinition): bool
662 {
663 return isset($formDefinition['identifier'], $formDefinition['type']) && $formDefinition['type'] === 'Form';
664 }
665
666 /**
667 * @param string $persistenceIdentifier
668 * @return string
669 */
670 protected function getOldPersistenceIdentifier(string $persistenceIdentifier): string
671 {
672 return preg_replace(
673 '
674 #^(.*)(\.form\.yaml)$#',
675 '${1}.yaml',
676 $persistenceIdentifier
677 );
678 }
679
680 /**
681 * @param string $persistenceIdentifier
682 * @return string
683 */
684 protected function getNewPersistenceIdentifier(string $persistenceIdentifier): string
685 {
686 return preg_replace(
687 '#(?<!\.form).yaml$#',
688 '.form.yaml',
689 $persistenceIdentifier
690 );
691 }
692
693 /**
694 * @param string $persistenceIdentifier
695 * @param string $prototypeName
696 * @param string $formIdentifier
697 * @param string $finisherIdentifier
698 * @return string
699 */
700 protected function buildExpectedSheetIdentifier(
701 string $persistenceIdentifier,
702 string $prototypeName,
703 string $formIdentifier,
704 string $finisherIdentifier
705 ): string {
706 return md5(
707 implode('', [
708 $persistenceIdentifier,
709 $prototypeName,
710 $formIdentifier,
711 $finisherIdentifier
712 ])
713 );
714 }
715
716 /**
717 * @param File $file
718 * @return array
719 */
720 protected function getFormDefinition(File $file): array
721 {
722 try {
723 $rawYamlContent = $file->getContents();
724 $formDefinition = $this->extractMetaDataFromCouldBeFormDefinition($rawYamlContent);
725
726 if (!$this->looksLikeAFormDefinition($formDefinition)) {
727 $formDefinition = [];
728 }
729 } catch (\Exception $exception) {
730 $formDefinition = [];
731 }
732
733 return $formDefinition;
734 }
735
736 /**
737 * @param string $maybeRawFormDefinition
738 * @return array
739 */
740 protected function extractMetaDataFromCouldBeFormDefinition(string $maybeRawFormDefinition): array
741 {
742 $metaDataProperties = ['identifier', 'type', 'label', 'prototypeName'];
743 $metaData = [];
744 foreach (explode("\n", $maybeRawFormDefinition) as $line) {
745 if (empty($line) || $line[0] === ' ') {
746 continue;
747 }
748
749 [$key, $value] = explode(':', $line);
750 if (
751 empty($key)
752 || empty($value)
753 || !in_array($key, $metaDataProperties)
754 ) {
755 continue;
756 }
757
758 $value = trim($value, ' \'"');
759 $metaData[$key] = $value;
760 }
761
762 return $metaData;
763 }
764
765 /**
766 * @return array
767 */
768 protected function getAllFlexformFieldsFromFormPlugins(): array
769 {
770 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
771 ->getQueryBuilderForTable('tt_content');
772 $queryBuilder->getRestrictions()
773 ->removeAll()
774 ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
775
776 $records = $queryBuilder
777 ->select('uid', 'pi_flexform')
778 ->from('tt_content')
779 ->where(
780 $queryBuilder->expr()->eq(
781 'CType',
782 $queryBuilder->createNamedParameter('form_formframework', \PDO::PARAM_STR)
783 )
784 )
785 ->execute()
786 ->fetchAll();
787
788 return $records;
789 }
790
791 /**
792 * @param int $uid
793 * @param string $flexform
794 * @param bool $updateRefindex
795 */
796 protected function updateContentReference(
797 int $uid,
798 string $flexform,
799 bool $updateRefindex = false
800 ): void {
801 $this->connection->update(
802 'tt_content',
803 ['pi_flexform' => $flexform],
804 ['uid' => $uid]
805 );
806
807 if (!$updateRefindex) {
808 return;
809 }
810
811 $this->referenceIndex->updateRefIndexTable(
812 'tt_content',
813 $uid
814 );
815 }
816
817 /**
818 * @return ObjectManager
819 */
820 protected function getObjectManager(): ObjectManager
821 {
822 return GeneralUtility::makeInstance(ObjectManager::class);
823 }
824 }