‪TYPO3CMS  11.5
FormPersistenceManager.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 Symfony\Component\Yaml\Exception\ParseException;
25 use Symfony\Component\Yaml\Yaml;
47 
54 {
55  public const ‪FORM_DEFINITION_FILE_EXTENSION = '.form.yaml';
56 
61  protected array ‪$formSettings;
63 
64  public function ‪__construct(
69  ‪ConfigurationManagerInterface $configurationManager,
70  ‪CacheManager $cacheManager
71  ) {
72  $this->yamlSource = ‪$yamlSource;
73  $this->storageRepository = ‪$storageRepository;
74  $this->filePersistenceSlot = ‪$filePersistenceSlot;
75  $this->resourceFactory = ‪$resourceFactory;
76  $this->formSettings = $configurationManager->‪getConfiguration(‪ConfigurationManagerInterface::CONFIGURATION_TYPE_YAML_SETTINGS, 'form');
77  $this->runtimeCache = $cacheManager->‪getCache('runtime');
78  }
79 
88  public function ‪load(string $persistenceIdentifier): array
89  {
90  $cacheKey = 'formLoad' . md5($persistenceIdentifier);
91 
92  $yaml = $this->runtimeCache->get($cacheKey);
93  if ($yaml !== false) {
94  return $yaml;
95  }
96 
97  if (‪PathUtility::isExtensionPath($persistenceIdentifier)) {
98  $this->‪ensureValidPersistenceIdentifier($persistenceIdentifier);
99  $file = $persistenceIdentifier;
100  } else {
101  $file = $this->‪retrieveFileByPersistenceIdentifier($persistenceIdentifier);
102  }
103 
104  try {
105  $yaml = $this->yamlSource->load([$file]);
106  $this->‪generateErrorsIfFormDefinitionIsValidButHasInvalidFileExtension($yaml, $persistenceIdentifier);
107  } catch (\‪Exception $e) {
108  $yaml = [
109  'type' => 'Form',
110  'identifier' => $persistenceIdentifier,
111  'label' => $e->getMessage(),
112  'invalid' => true,
113  ];
114  }
115  $this->runtimeCache->set($cacheKey, $yaml);
116 
117  return $yaml;
118  }
119 
133  public function ‪save(string $persistenceIdentifier, array $formDefinition)
134  {
135  if (!$this->‪hasValidFileExtension($persistenceIdentifier)) {
136  throw new ‪PersistenceManagerException(sprintf('The file "%s" could not be saved.', $persistenceIdentifier), 1477679820);
137  }
138 
139  if ($this->‪pathIsIntendedAsExtensionPath($persistenceIdentifier)) {
140  if (!$this->formSettings['persistenceManager']['allowSaveToExtensionPaths']) {
141  throw new ‪PersistenceManagerException('Save to extension paths is not allowed.', 1477680881);
142  }
143  if (!$this->‪isFileWithinAccessibleExtensionFolders($persistenceIdentifier)) {
144  $message = sprintf('The file "%s" could not be saved. Please check your configuration option "persistenceManager.allowedExtensionPaths"', $persistenceIdentifier);
145  throw new ‪PersistenceManagerException($message, 1484073571);
146  }
147  $fileToSave = GeneralUtility::getFileAbsFileName($persistenceIdentifier);
148  } else {
149  $fileToSave = $this->‪getOrCreateFile($persistenceIdentifier);
150  }
151 
152  try {
153  $this->yamlSource->save($fileToSave, $formDefinition);
154  } catch (‪FileWriteException $e) {
155  throw new ‪PersistenceManagerException(sprintf(
156  'The file "%s" could not be saved: %s',
157  $persistenceIdentifier,
158  $e->getMessage()
159  ), 1512582637, $e);
160  }
161  }
162 
171  public function delete(string $persistenceIdentifier)
172  {
173  if (!$this->‪hasValidFileExtension($persistenceIdentifier)) {
174  throw new ‪PersistenceManagerException(sprintf('The file "%s" could not be removed.', $persistenceIdentifier), 1472239534);
175  }
176  if (!$this->‪exists($persistenceIdentifier)) {
177  throw new ‪PersistenceManagerException(sprintf('The file "%s" could not be removed.', $persistenceIdentifier), 1472239535);
178  }
179  if ($this->‪pathIsIntendedAsExtensionPath($persistenceIdentifier)) {
180  if (!$this->formSettings['persistenceManager']['allowDeleteFromExtensionPaths']) {
181  throw new ‪PersistenceManagerException(sprintf('The file "%s" could not be removed.', $persistenceIdentifier), 1472239536);
182  }
183  if (!$this->‪isFileWithinAccessibleExtensionFolders($persistenceIdentifier)) {
184  $message = sprintf('The file "%s" could not be removed. Please check your configuration option "persistenceManager.allowedExtensionPaths"', $persistenceIdentifier);
185  throw new ‪PersistenceManagerException($message, 1484073878);
186  }
187  $fileToDelete = GeneralUtility::getFileAbsFileName($persistenceIdentifier);
188  unlink($fileToDelete);
189  } else {
190  [$storageUid, $fileIdentifier] = explode(':', $persistenceIdentifier, 2);
191  $storage = $this->‪getStorageByUid((int)$storageUid);
192  $file = $storage->getFile($fileIdentifier);
193  if (!$storage->checkFileActionPermission('delete', $file)) {
194  throw new ‪PersistenceManagerException(sprintf('No delete access to file "%s".', $persistenceIdentifier), 1472239516);
195  }
196  $storage->deleteFile($file);
197  }
198  }
199 
207  public function ‪exists(string $persistenceIdentifier): bool
208  {
209  $exists = false;
210  if ($this->‪hasValidFileExtension($persistenceIdentifier)) {
211  if ($this->‪pathIsIntendedAsExtensionPath($persistenceIdentifier)) {
212  if ($this->‪isFileWithinAccessibleExtensionFolders($persistenceIdentifier)) {
213  $exists = file_exists(GeneralUtility::getFileAbsFileName($persistenceIdentifier));
214  }
215  } else {
216  [$storageUid, $fileIdentifier] = explode(':', $persistenceIdentifier, 2);
217  $storage = $this->‪getStorageByUid((int)$storageUid);
218  $exists = $storage->hasFile($fileIdentifier);
219  }
220  }
221  return $exists;
222  }
223 
234  public function ‪listForms(): array
235  {
236  $identifiers = [];
237  $forms = [];
238 
239  foreach ($this->‪retrieveYamlFilesFromStorageFolders() as $file) {
240  $form = $this->‪loadMetaData($file);
241 
242  if (!$this->‪looksLikeAFormDefinition($form)) {
243  continue;
244  }
245 
246  $persistenceIdentifier = $file->getCombinedIdentifier();
247  if ($this->‪hasValidFileExtension($persistenceIdentifier)) {
248  $forms[] = [
249  'identifier' => $form['identifier'],
250  'name' => $form['label'] ?? $form['identifier'],
251  'persistenceIdentifier' => $persistenceIdentifier,
252  'readOnly' => false,
253  'removable' => true,
254  'location' => 'storage',
255  'duplicateIdentifier' => false,
256  'invalid' => $form['invalid'] ?? false,
257  'fileUid' => $form['fileUid'] ?? 0,
258  ];
259  if (!isset($identifiers[$form['identifier']])) {
260  $identifiers[$form['identifier']] = 0;
261  }
262  $identifiers[$form['identifier']]++;
263  }
264  }
265 
266  foreach ($this->retrieveYamlFilesFromExtensionFolders() as $fullPath => $fileName) {
267  $form = $this->‪loadMetaData($fullPath);
268 
269  if ($this->‪looksLikeAFormDefinition($form)) {
270  if ($this->‪hasValidFileExtension($fileName)) {
271  $forms[] = [
272  'identifier' => $form['identifier'],
273  'name' => $form['label'] ?? $form['identifier'],
274  'persistenceIdentifier' => $fullPath,
275  'readOnly' => $this->formSettings['persistenceManager']['allowSaveToExtensionPaths'] ? false : true,
276  'removable' => $this->formSettings['persistenceManager']['allowDeleteFromExtensionPaths'] ? true : false,
277  'location' => 'extension',
278  'duplicateIdentifier' => false,
279  'invalid' => $form['invalid'] ?? false,
280  'fileUid' => $form['fileUid'] ?? 0,
281  ];
282  if (!isset($identifiers[$form['identifier']])) {
283  $identifiers[$form['identifier']] = 0;
284  }
285  $identifiers[$form['identifier']]++;
286  }
287  }
288  }
289 
290  foreach ($identifiers as $identifier => $count) {
291  if ($count > 1) {
292  foreach ($forms as &$formDefinition) {
293  if ($formDefinition['identifier'] === $identifier) {
294  $formDefinition['duplicateIdentifier'] = true;
295  }
296  }
297  }
298  }
299 
300  return $this->‪sortForms($forms);
301  }
302 
310  public function ‪retrieveYamlFilesFromStorageFolders(): array
311  {
312  $filesFromStorageFolders = [];
313 
314  $fileExtensionFilter = GeneralUtility::makeInstance(FileExtensionFilter::class);
315  $fileExtensionFilter->setAllowedFileExtensions(['yaml']);
316 
317  foreach ($this->‪getAccessibleFormStorageFolders() as $folder) {
318  $storage = $folder->getStorage();
319  $storage->setFileAndFolderNameFilters([
320  [$fileExtensionFilter, 'filterFileList'],
321  ]);
322 
323  $files = $folder->getFiles(
324  0,
325  0,
327  true
328  );
329  $filesFromStorageFolders = array_merge($filesFromStorageFolders, array_values($files));
330  $storage->resetFileAndFolderNameFiltersToDefault();
331  }
332 
333  return $filesFromStorageFolders;
334  }
335 
343  public function retrieveYamlFilesFromExtensionFolders(): array
344  {
346 
347  foreach ($this->‪getAccessibleExtensionFolders() as $relativePath => $fullPath) {
348  foreach (new \DirectoryIterator($fullPath) as $fileInfo) {
349  if ($fileInfo->getExtension() !== 'yaml') {
350  continue;
351  }
352  ‪$filesFromExtensionFolders[$relativePath . $fileInfo->getFilename()] = $fileInfo->getFilename();
353  }
354  }
355 
357  }
358 
370  public function ‪getAccessibleFormStorageFolders(): array
371  {
372  $storageFolders = [];
373 
374  if (
375  !isset($this->formSettings['persistenceManager']['allowedFileMounts'])
376  || !is_array($this->formSettings['persistenceManager']['allowedFileMounts'])
377  || empty($this->formSettings['persistenceManager']['allowedFileMounts'])
378  ) {
379  return $storageFolders;
380  }
381 
382  foreach ($this->formSettings['persistenceManager']['allowedFileMounts'] as $allowedFileMount) {
383  $allowedFileMount = rtrim($allowedFileMount, '/') . '/';
384  // $fileMountPath is like "/form_definitions/" or "/group_homes/1/form_definitions/"
385  [$storageUid, $fileMountPath] = explode(':', $allowedFileMount, 2);
386 
387  try {
388  $storage = $this->‪getStorageByUid((int)$storageUid);
389  } catch (‪PersistenceManagerException $e) {
390  continue;
391  }
392 
393  $isStorageFileMount = false;
394  $parentFolder = $storage->getRootLevelFolder(false);
395 
396  foreach ($storage->getFileMounts() as $storageFileMount) {
398  $storageFileMountFolder = $storageFileMount['folder'];
399 
400  // Normally should use ResourceStorage::isWithinFolder() to check if the configured file mount path is within a storage file mount but this requires a valid Folder object and thus a directory which already exists. And the folder could simply not exist yet.
401  if (str_starts_with($fileMountPath, $storageFileMountFolder->getIdentifier())) {
402  $isStorageFileMount = true;
403  $parentFolder = $storageFileMountFolder;
404  }
405  }
406 
407  // Get storage folder object, create it if missing
408  try {
409  $fileMountFolder = $storage->getFolder($fileMountPath);
411  continue;
412  } catch (‪FolderDoesNotExistException $e) {
413  if ($isStorageFileMount) {
414  $fileMountPath = substr(
415  $fileMountPath,
416  strlen($parentFolder->getIdentifier())
417  );
418  }
419 
420  try {
421  $fileMountFolder = $storage->createFolder($fileMountPath, $parentFolder);
423  continue;
424  }
425  }
426 
427  $storageFolders[$allowedFileMount] = $fileMountFolder;
428  }
429  return $storageFolders;
430  }
431 
442  public function ‪getAccessibleExtensionFolders(): array
443  {
444  $extensionFolders = $this->runtimeCache->get('formAccessibleExtensionFolders');
445 
446  if ($extensionFolders !== false) {
447  return $extensionFolders;
448  }
449 
450  $extensionFolders = [];
451  if (
452  !isset($this->formSettings['persistenceManager']['allowedExtensionPaths'])
453  || !is_array($this->formSettings['persistenceManager']['allowedExtensionPaths'])
454  || empty($this->formSettings['persistenceManager']['allowedExtensionPaths'])
455  ) {
456  $this->runtimeCache->set('formAccessibleExtensionFolders', $extensionFolders);
457  return $extensionFolders;
458  }
459 
460  foreach ($this->formSettings['persistenceManager']['allowedExtensionPaths'] as $allowedExtensionPath) {
461  if (!$this->‪pathIsIntendedAsExtensionPath($allowedExtensionPath)) {
462  continue;
463  }
464 
465  $allowedExtensionFullPath = GeneralUtility::getFileAbsFileName($allowedExtensionPath);
466  if (!file_exists($allowedExtensionFullPath)) {
467  continue;
468  }
469  $allowedExtensionPath = rtrim($allowedExtensionPath, '/') . '/';
470  $extensionFolders[$allowedExtensionPath] = $allowedExtensionFullPath;
471  }
472 
473  $this->runtimeCache->set('formAccessibleExtensionFolders', $extensionFolders);
474  return $extensionFolders;
475  }
476 
488  public function ‪getUniquePersistenceIdentifier(string $formIdentifier, string $savePath): string
489  {
490  $savePath = rtrim($savePath, '/') . '/';
491  $formPersistenceIdentifier = $savePath . $formIdentifier . ‪self::FORM_DEFINITION_FILE_EXTENSION;
492  if (!$this->‪exists($formPersistenceIdentifier)) {
493  return $formPersistenceIdentifier;
494  }
495  for ($attempts = 1; $attempts < 100; $attempts++) {
496  $formPersistenceIdentifier = $savePath . sprintf('%s_%d', $formIdentifier, $attempts) . ‪self::FORM_DEFINITION_FILE_EXTENSION;
497  if (!$this->‪exists($formPersistenceIdentifier)) {
498  return $formPersistenceIdentifier;
499  }
500  }
501  $formPersistenceIdentifier = $savePath . sprintf('%s_%d', $formIdentifier, time()) . ‪self::FORM_DEFINITION_FILE_EXTENSION;
502  if (!$this->‪exists($formPersistenceIdentifier)) {
503  return $formPersistenceIdentifier;
504  }
505 
507  sprintf('Could not find a unique persistence identifier for form identifier "%s" after %d attempts', $formIdentifier, $attempts),
508  1476010403
509  );
510  }
511 
522  public function ‪getUniqueIdentifier(string $identifier): string
523  {
524  $originalIdentifier = $identifier;
525  if ($this->‪checkForDuplicateIdentifier($identifier)) {
526  for ($attempts = 1; $attempts < 100; $attempts++) {
527  $identifier = sprintf('%s_%d', $originalIdentifier, $attempts);
528  if (!$this->‪checkForDuplicateIdentifier($identifier)) {
529  return $identifier;
530  }
531  }
532  $identifier = $originalIdentifier . '_' . time();
533  if ($this->‪checkForDuplicateIdentifier($identifier)) {
535  sprintf('Could not find a unique identifier for form identifier "%s" after %d attempts', $identifier, $attempts),
536  1477688567
537  );
538  }
539  }
540  return $identifier;
541  }
542 
550  public function ‪checkForDuplicateIdentifier(string $identifier): bool
551  {
552  $identifierUsed = false;
553  foreach ($this->‪listForms() as $formDefinition) {
554  if ($formDefinition['identifier'] === $identifier) {
555  $identifierUsed = true;
556  break;
557  }
558  }
559  return $identifierUsed;
560  }
561 
573  public function ‪isAllowedPersistencePath(string $persistencePath): bool
574  {
575  $pathinfo = ‪PathUtility::pathinfo($persistencePath);
576  $persistencePathIsFile = isset($pathinfo['extension']);
577 
578  if (
579  $persistencePathIsFile
580  && $this->‪pathIsIntendedAsExtensionPath($persistencePath)
581  && $this->‪hasValidFileExtension($persistencePath)
582  && $this->‪isFileWithinAccessibleExtensionFolders($persistencePath)
583  ) {
584  return true;
585  }
586  if (
587  $persistencePathIsFile
588  && $this->‪pathIsIntendedAsFileMountPath($persistencePath)
589  && $this->‪hasValidFileExtension($persistencePath)
590  && $this->‪isFileWithinAccessibleFormStorageFolders($persistencePath)
591  ) {
592  return true;
593  }
594  if (
595  !$persistencePathIsFile
596  && $this->‪pathIsIntendedAsExtensionPath($persistencePath)
597  && $this->‪isAccessibleExtensionFolder($persistencePath)
598  ) {
599  return true;
600  }
601  if (
602  !$persistencePathIsFile
603  && $this->‪pathIsIntendedAsFileMountPath($persistencePath)
604  && $this->‪isAccessibleFormStorageFolder($persistencePath)
605  ) {
606  return true;
607  }
608 
609  return false;
610  }
611 
616  protected function ‪pathIsIntendedAsExtensionPath(string $path): bool
617  {
618  return ‪PathUtility::isExtensionPath($path);
619  }
620 
625  protected function ‪pathIsIntendedAsFileMountPath(string $path): bool
626  {
627  if (empty($path)) {
628  return false;
629  }
630 
631  [$storageUid, $pathIdentifier] = explode(':', $path, 2);
632  if (empty($storageUid) || empty($pathIdentifier)) {
633  return false;
634  }
635 
636  return ‪MathUtility::canBeInterpretedAsInteger($storageUid);
637  }
638 
648  protected function ‪getOrCreateFile(string $persistenceIdentifier): ‪File
649  {
650  [$storageUid, $fileIdentifier] = explode(':', $persistenceIdentifier, 2);
651  $storage = $this->‪getStorageByUid((int)$storageUid);
652  $pathinfo = ‪PathUtility::pathinfo($fileIdentifier);
653 
654  if (!$storage->hasFolder($pathinfo['dirname'])) {
655  throw new ‪PersistenceManagerException(sprintf('Could not create folder "%s".', $pathinfo['dirname']), 1471630579);
656  }
657 
658  try {
659  $folder = $storage->getFolder($pathinfo['dirname']);
661  throw new ‪PersistenceManagerException(sprintf('No read access to folder "%s".', $pathinfo['dirname']), 1512583307);
662  }
663 
664  if (!$storage->checkFolderActionPermission('write', $folder)) {
665  throw new ‪PersistenceManagerException(sprintf('No write access to folder "%s".', $pathinfo['dirname']), 1471630580);
666  }
667 
668  if (!$storage->hasFile($fileIdentifier)) {
669  $this->filePersistenceSlot->allowInvocation(
671  $folder->getCombinedIdentifier() . $pathinfo['basename']
672  );
673  $file = $folder->createFile($pathinfo['basename']);
674  } else {
675  $file = $storage->getFile($fileIdentifier);
676  }
677  return $file;
678  }
679 
687  protected function ‪getStorageByUid(int $storageUid): ‪ResourceStorage
688  {
689  $storage = $this->storageRepository->findByUid($storageUid);
690  if (
691  !$storage instanceof ‪ResourceStorage
692  || !$storage->‪isBrowsable()
693  ) {
694  throw new ‪PersistenceManagerException(sprintf('Could not access storage with uid "%d".', $storageUid), 1471630581);
695  }
696  return $storage;
697  }
698 
704  protected function ‪loadMetaData($persistenceIdentifier): array
705  {
706  $file = null;
707  if ($persistenceIdentifier instanceof ‪File) {
708  $file = $persistenceIdentifier;
709  $persistenceIdentifier = $file->getCombinedIdentifier();
710  $rawYamlContent = $file->getContents();
711  } elseif (‪PathUtility::isExtensionPath($persistenceIdentifier)) {
712  $this->‪ensureValidPersistenceIdentifier($persistenceIdentifier);
713  $rawYamlContent = false;
714  $absoluteFilePath = GeneralUtility::getFileAbsFileName($persistenceIdentifier);
715  if ($absoluteFilePath !== '' && file_exists($absoluteFilePath)) {
716  $rawYamlContent = file_get_contents($absoluteFilePath);
717  }
718  } else {
719  $file = $this->‪retrieveFileByPersistenceIdentifier($persistenceIdentifier);
720  $rawYamlContent = $file->getContents();
721  }
722 
723  try {
724  if ($rawYamlContent === false) {
725  throw new ‪NoSuchFileException(sprintf('YAML file "%s" could not be loaded', $persistenceIdentifier), 1524684462);
726  }
727 
728  $yaml = $this->‪extractMetaDataFromCouldBeFormDefinition($rawYamlContent);
729  $this->‪generateErrorsIfFormDefinitionIsValidButHasInvalidFileExtension($yaml, $persistenceIdentifier);
730  if ($file !== null) {
731  $yaml['fileUid'] = $file->getUid();
732  }
733  } catch (\‪Exception $e) {
734  $yaml = [
735  'type' => 'Form',
736  'identifier' => $persistenceIdentifier,
737  'label' => $e->getMessage(),
738  'invalid' => true,
739  ];
740  }
741 
742  return $yaml;
743  }
744 
749  protected function ‪extractMetaDataFromCouldBeFormDefinition(string $maybeRawFormDefinition): array
750  {
751  $metaDataProperties = ['identifier', 'type', 'label', 'prototypeName'];
752  $metaData = [];
753  foreach (explode(LF, $maybeRawFormDefinition) as $line) {
754  if (empty($line) || $line[0] === ' ') {
755  continue;
756  }
757 
758  $parts = explode(':', $line, 2);
759  $key = trim($parts[0]);
760  if (!($parts[1] ?? null) || !in_array($key, $metaDataProperties, true)) {
761  continue;
762  }
763 
764  if ($key === 'label') {
765  try {
766  $parsedLabelLine = Yaml::parse($line);
767  $value = $parsedLabelLine['label'] ?? '';
768  } catch (ParseException $e) {
769  $value = '';
770  }
771  } else {
772  $value = trim($parts[1], " '\"\r");
773  }
774 
775  $metaData[$key] = $value;
776  }
777 
778  return $metaData;
779  }
780 
786  protected function ‪generateErrorsIfFormDefinitionIsValidButHasInvalidFileExtension(array $formDefinition, string $persistenceIdentifier): void
787  {
788  if (
789  $this->‪looksLikeAFormDefinition($formDefinition)
790  && !$this->‪hasValidFileExtension($persistenceIdentifier)
791  ) {
792  throw new ‪PersistenceManagerException(sprintf('Form definition "%s" does not end with ".form.yaml".', $persistenceIdentifier), 1531160649);
793  }
794  }
795 
802  protected function ‪retrieveFileByPersistenceIdentifier(string $persistenceIdentifier): ‪File
803  {
804  $this->‪ensureValidPersistenceIdentifier($persistenceIdentifier);
805 
806  try {
807  $file = $this->resourceFactory->retrieveFileOrFolderObject($persistenceIdentifier);
808  } catch (\‪Exception $e) {
809  // Top level catch to ensure useful following exception handling, because FAL throws top level exceptions.
810  $file = null;
811  }
812 
813  if ($file === null) {
814  throw new ‪NoSuchFileException(sprintf('YAML file "%s" could not be loaded', $persistenceIdentifier), 1524684442);
815  }
816 
817  if (!$file->getStorage()->checkFileActionPermission('read', $file)) {
818  throw new ‪PersistenceManagerException(sprintf('No read access to file "%s".', $persistenceIdentifier), 1471630578);
819  }
820 
821  return $file;
822  }
823 
829  protected function ‪ensureValidPersistenceIdentifier(string $persistenceIdentifier): void
830  {
831  if (pathinfo($persistenceIdentifier, PATHINFO_EXTENSION) !== 'yaml') {
832  throw new ‪PersistenceManagerException(sprintf('The file "%s" could not be loaded.', $persistenceIdentifier), 1477679819);
833  }
834 
835  if (
836  $this->‪pathIsIntendedAsExtensionPath($persistenceIdentifier)
837  && !$this->‪isFileWithinAccessibleExtensionFolders($persistenceIdentifier)
838  ) {
839  $message = sprintf('The file "%s" could not be loaded. Please check your configuration option "persistenceManager.allowedExtensionPaths"', $persistenceIdentifier);
840  throw new ‪PersistenceManagerException($message, 1484071985);
841  }
842  }
843 
849  public function ‪hasValidFileExtension(string $fileName): bool
850  {
851  return str_ends_with($fileName, self::FORM_DEFINITION_FILE_EXTENSION);
852  }
853 
858  protected function ‪isFileWithinAccessibleExtensionFolders(string $fileName): bool
859  {
860  $pathInfo = ‪PathUtility::pathinfo($fileName, PATHINFO_DIRNAME);
861  $pathInfo = is_string($pathInfo) ? $pathInfo : '';
862  $dirName = rtrim($pathInfo, '/') . '/';
863  return array_key_exists($dirName, $this->‪getAccessibleExtensionFolders());
864  }
865 
870  protected function ‪isFileWithinAccessibleFormStorageFolders(string $fileName): bool
871  {
872  $pathInfo = ‪PathUtility::pathinfo($fileName, PATHINFO_DIRNAME);
873  $pathInfo = is_string($pathInfo) ? $pathInfo : '';
874  $dirName = rtrim($pathInfo, '/') . '/';
875 
876  foreach (array_keys($this->‪getAccessibleFormStorageFolders()) as $allowedPath) {
877  if (str_starts_with($dirName, $allowedPath)) {
878  return true;
879  }
880  }
881  return false;
882  }
883 
888  protected function ‪isAccessibleExtensionFolder(string $folderName): bool
889  {
890  $folderName = rtrim($folderName, '/') . '/';
891  return array_key_exists($folderName, $this->‪getAccessibleExtensionFolders());
892  }
893 
898  protected function ‪isAccessibleFormStorageFolder(string $folderName): bool
899  {
900  $folderName = rtrim($folderName, '/') . '/';
901  return array_key_exists($folderName, $this->‪getAccessibleFormStorageFolders());
902  }
903 
908  protected function ‪looksLikeAFormDefinition(array $data): bool
909  {
910  return isset($data['identifier'], $data['type']) && !empty($data['identifier']) && trim($data['type']) === 'Form';
911  }
912 
917  protected function ‪sortForms(array $forms): array
918  {
919  $keys = $this->formSettings['persistenceManager']['sortByKeys'] ?? ['name', 'fileUid'];
920  $ascending = $this->formSettings['persistenceManager']['sortAscending'] ?? true;
921 
922  usort($forms, static function (array $a, array $b) use ($keys) {
923  foreach ($keys as $key) {
924  if (isset($a[$key]) && isset($b[$key])) {
925  $diff = strcasecmp((string)$a[$key], (string)$b[$key]);
926  if ($diff) {
927  return $diff;
928  }
929  }
930  }
931  });
932 
933  return ($ascending) ? $forms : array_reverse($forms);
934  }
935 }
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\listForms
‪array listForms()
Definition: FormPersistenceManager.php:234
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\ensureValidPersistenceIdentifier
‪ensureValidPersistenceIdentifier(string $persistenceIdentifier)
Definition: FormPersistenceManager.php:829
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\isAccessibleFormStorageFolder
‪bool isAccessibleFormStorageFolder(string $folderName)
Definition: FormPersistenceManager.php:898
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\hasValidFileExtension
‪bool hasValidFileExtension(string $fileName)
Definition: FormPersistenceManager.php:849
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:25
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger($var)
Definition: MathUtility.php:74
‪TYPO3\CMS\Core\Cache\CacheManager\getCache
‪FrontendInterface getCache($identifier)
Definition: CacheManager.php:142
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\$filePersistenceSlot
‪FilePersistenceSlot $filePersistenceSlot
Definition: FormPersistenceManager.php:59
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\isFileWithinAccessibleExtensionFolders
‪bool isFileWithinAccessibleExtensionFolders(string $fileName)
Definition: FormPersistenceManager.php:858
‪TYPO3\CMS\Form\Mvc\Configuration\Exception\FileWriteException
Definition: FileWriteException.php:24
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\sortForms
‪array sortForms(array $forms)
Definition: FormPersistenceManager.php:917
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\checkForDuplicateIdentifier
‪bool checkForDuplicateIdentifier(string $identifier)
Definition: FormPersistenceManager.php:550
‪TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException
Definition: InsufficientFolderAccessPermissionsException.php:23
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\$storageRepository
‪StorageRepository $storageRepository
Definition: FormPersistenceManager.php:58
‪TYPO3\CMS\Form\Mvc\Persistence\Exception\PersistenceManagerException
Definition: PersistenceManagerException.php:27
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\isFileWithinAccessibleFormStorageFolders
‪bool isFileWithinAccessibleFormStorageFolders(string $fileName)
Definition: FormPersistenceManager.php:870
‪TYPO3\CMS\Core\Utility\PathUtility\isExtensionPath
‪static bool isExtensionPath(string $path)
Definition: PathUtility.php:121
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\retrieveFileByPersistenceIdentifier
‪File retrieveFileByPersistenceIdentifier(string $persistenceIdentifier)
Definition: FormPersistenceManager.php:802
‪TYPO3\CMS\Form\Mvc\Persistence\Exception\NoUniqueIdentifierException
Definition: NoUniqueIdentifierException.php:25
‪TYPO3\CMS\Core\Resource\Folder\FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS
‪const FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS
Definition: Folder.php:69
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\getStorageByUid
‪ResourceStorage getStorageByUid(int $storageUid)
Definition: FormPersistenceManager.php:687
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\pathIsIntendedAsExtensionPath
‪bool pathIsIntendedAsExtensionPath(string $path)
Definition: FormPersistenceManager.php:616
‪TYPO3\CMS\Form\Mvc\Persistence
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\isAllowedPersistencePath
‪bool isAllowedPersistencePath(string $persistencePath)
Definition: FormPersistenceManager.php:573
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\COMMAND_FILE_CREATE
‪const COMMAND_FILE_CREATE
Definition: FilePersistenceSlot.php:39
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\$formSettings
‪array $formSettings
Definition: FormPersistenceManager.php:61
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\looksLikeAFormDefinition
‪bool looksLikeAFormDefinition(array $data)
Definition: FormPersistenceManager.php:908
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\getAccessibleFormStorageFolders
‪Folder[] getAccessibleFormStorageFolders()
Definition: FormPersistenceManager.php:370
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager
Definition: FormPersistenceManager.php:54
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\load
‪array load(string $persistenceIdentifier)
Definition: FormPersistenceManager.php:88
‪TYPO3\CMS\Core\Resource\ResourceStorage\isBrowsable
‪bool isBrowsable()
Definition: ResourceStorage.php:413
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\$yamlSource
‪YamlSource $yamlSource
Definition: FormPersistenceManager.php:57
‪TYPO3\CMS\Core\Resource\Folder
Definition: Folder.php:37
‪TYPO3\CMS\Core\Resource\ResourceFactory
Definition: ResourceFactory.php:41
‪TYPO3\CMS\Core\Resource\Exception\FolderDoesNotExistException
Definition: FolderDoesNotExistException.php:21
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\exists
‪bool exists(string $persistenceIdentifier)
Definition: FormPersistenceManager.php:207
‪TYPO3\CMS\Core\Resource\StorageRepository
Definition: StorageRepository.php:38
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:24
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:36
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\extractMetaDataFromCouldBeFormDefinition
‪array extractMetaDataFromCouldBeFormDefinition(string $maybeRawFormDefinition)
Definition: FormPersistenceManager.php:749
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\__construct
‪__construct(YamlSource $yamlSource, StorageRepository $storageRepository, FilePersistenceSlot $filePersistenceSlot, ResourceFactory $resourceFactory, ConfigurationManagerInterface $configurationManager, CacheManager $cacheManager)
Definition: FormPersistenceManager.php:64
‪TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter
Definition: FileExtensionFilter.php:28
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\getUniquePersistenceIdentifier
‪string getUniquePersistenceIdentifier(string $formIdentifier, string $savePath)
Definition: FormPersistenceManager.php:488
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\$filesFromExtensionFolders
‪return $filesFromExtensionFolders
Definition: FormPersistenceManager.php:356
‪TYPO3\CMS\Core\Utility\PathUtility\pathinfo
‪static string string[] pathinfo($path, $options=null)
Definition: PathUtility.php:277
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\$runtimeCache
‪FrontendInterface $runtimeCache
Definition: FormPersistenceManager.php:62
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot
Definition: FilePersistenceSlot.php:37
‪TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface\getConfiguration
‪array getConfiguration(string $configurationType, ?string $extensionName=null, ?string $pluginName=null)
‪TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
Definition: FrontendInterface.php:22
‪TYPO3\CMS\Form\Mvc\Configuration\YamlSource
Definition: YamlSource.php:44
‪TYPO3\CMS\Core\Resource\ResourceStorage
Definition: ResourceStorage.php:125
‪TYPO3\CMS\Form\Mvc\Persistence\Exception
Definition: Exception.php:27
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\getAccessibleExtensionFolders
‪array getAccessibleExtensionFolders()
Definition: FormPersistenceManager.php:442
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\getOrCreateFile
‪File getOrCreateFile(string $persistenceIdentifier)
Definition: FormPersistenceManager.php:648
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\getUniqueIdentifier
‪string getUniqueIdentifier(string $identifier)
Definition: FormPersistenceManager.php:522
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:22
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\save
‪save(string $persistenceIdentifier, array $formDefinition)
Definition: FormPersistenceManager.php:133
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\loadMetaData
‪array loadMetaData($persistenceIdentifier)
Definition: FormPersistenceManager.php:704
‪TYPO3\CMS\Form\Mvc\Configuration\ConfigurationManagerInterface\CONFIGURATION_TYPE_YAML_SETTINGS
‪const CONFIGURATION_TYPE_YAML_SETTINGS
Definition: ConfigurationManagerInterface.php:30
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\$resourceFactory
‪ResourceFactory $resourceFactory
Definition: FormPersistenceManager.php:60
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\generateErrorsIfFormDefinitionIsValidButHasInvalidFileExtension
‪generateErrorsIfFormDefinitionIsValidButHasInvalidFileExtension(array $formDefinition, string $persistenceIdentifier)
Definition: FormPersistenceManager.php:786
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\isAccessibleExtensionFolder
‪bool isAccessibleExtensionFolder(string $folderName)
Definition: FormPersistenceManager.php:888
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface
Definition: FormPersistenceManagerInterface.php:32
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\pathIsIntendedAsFileMountPath
‪bool pathIsIntendedAsFileMountPath(string $path)
Definition: FormPersistenceManager.php:625
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\FORM_DEFINITION_FILE_EXTENSION
‪const FORM_DEFINITION_FILE_EXTENSION
Definition: FormPersistenceManager.php:55
‪TYPO3\CMS\Form\Mvc\Configuration\Exception\NoSuchFileException
Definition: NoSuchFileException.php:24
‪TYPO3\CMS\Form\Mvc\Persistence\Exception\NoUniquePersistenceIdentifierException
Definition: NoUniquePersistenceIdentifierException.php:25
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\retrieveYamlFilesFromStorageFolders
‪File[] retrieveYamlFilesFromStorageFolders()
Definition: FormPersistenceManager.php:310
‪TYPO3\CMS\Form\Mvc\Configuration\ConfigurationManagerInterface
Definition: ConfigurationManagerInterface.php:29