ensureValidPersistenceIdentifier($identifier->identifier); $file = $identifier->identifier; $formDefinition = $this->yamlSource->load([$file]); $this->generateErrorsIfFormDefinitionIsInvalidOrHasInvalidFileExtension($formDefinition, $identifier->identifier); return FormData::fromArray($formDefinition); } public function write(FormIdentifier $identifier, FormData $data, ?StorageContext $context = null): FormIdentifier { if (!$this->hasValidFileExtension($identifier->identifier)) { throw new PersistenceManagerException(sprintf('The file "%s" could not be saved.', $identifier->identifier), 1764879569); } if (!$this->storageConfiguration->isAllowedToSaveToExtensionPaths()) { throw new PersistenceManagerException('Save to extension paths is not allowed.', 1764879520); } if (!$this->isFileWithinAccessibleExtensionFolders($identifier->identifier)) { throw new PersistenceManagerException( sprintf('The file "%s" could not be saved. Please check your configuration option "persistenceManager.allowedExtensionPaths"', $identifier->identifier), 1484073571 ); } $fileToSave = GeneralUtility::getFileAbsFileName($identifier->identifier); try { $this->yamlSource->save($fileToSave, $data->toArray()); } catch (\Exception $e) { throw new PersistenceManagerException( sprintf('The file "%s" could not be saved: %s', $identifier->identifier, $e->getMessage()), 1764879589, $e ); } return $identifier; } public function delete(FormIdentifier $identifier): void { if (!$this->hasValidFileExtension($identifier->identifier)) { throw new PersistenceManagerException(sprintf('The file "%s" could not be removed.', $identifier->identifier), 1764879609); } if (!$this->exists($identifier)) { throw new PersistenceManagerException(sprintf('The file "%s" could not be removed.', $identifier->identifier), 1764879543); } if (!$this->storageConfiguration->isAllowedToDeleteFromExtensionPaths()) { throw new PersistenceManagerException(sprintf('The file "%s" could not be removed.', $identifier->identifier), 1472239536); } if (!$this->isFileWithinAccessibleExtensionFolders($identifier->identifier)) { $message = sprintf('The file "%s" could not be removed. Please check your configuration option "persistenceManager.allowedExtensionPaths"', $identifier->identifier); throw new PersistenceManagerException($message, 1484073878); } $fileToDelete = GeneralUtility::getFileAbsFileName($identifier->identifier); unlink($fileToDelete); } public function exists(FormIdentifier $identifier): bool { $exists = false; if ($this->hasValidFileExtension($identifier->identifier)) { if ($this->isFileWithinAccessibleExtensionFolders($identifier->identifier)) { $exists = file_exists(GeneralUtility::getFileAbsFileName($identifier->identifier)); } } return $exists; } public function existsByFormIdentifier(string $formIdentifier): bool { foreach ($this->retrieveYamlFilesFromExtensionFolders() as $identifier) { $formMetadata = $this->loadMetaData($identifier); if ($this->looksLikeAFormDefinition($formMetadata) && $formMetadata->identifier === $formIdentifier) { return true; } } return false; } public function findAll(SearchCriteria $criteria): array { $results = []; foreach ($this->retrieveYamlFilesFromExtensionFolders() as $identifier) { $formMetadata = $this->loadMetaData($identifier); if (!$this->looksLikeAFormDefinition($formMetadata)) { continue; } if (!$this->hasValidFileExtension($identifier)) { continue; } $readOnly = !$this->storageConfiguration->isAllowedToSaveToExtensionPaths(); $formMetadata = $formMetadata->withReadOnly($readOnly); $removable = $this->storageConfiguration->isAllowedToDeleteFromExtensionPaths(); $formMetadata = $formMetadata->withRemovable($removable); if (!$this->matchesCriteria($formMetadata, $criteria)) { continue; } $results[] = $formMetadata; } return $results; } /** * Return a list of all accessible extension folders * * Only registered mount points from * persistenceManager.allowedExtensionPaths * are listed. */ public function getAccessibleExtensionFolders(): array { $cacheKey = 'ext-form-accessibleExtensionFolders'; if ($this->runtimeCache->has($cacheKey)) { return $this->runtimeCache->get($cacheKey); } $extensionFolders = []; $allowedExtensionPaths = $this->storageConfiguration->getAllowedExtensionPaths(); if (empty($allowedExtensionPaths)) { $this->runtimeCache->set($cacheKey, $extensionFolders); return $extensionFolders; } foreach ($allowedExtensionPaths as $allowedExtensionPath) { if (!PathUtility::isExtensionPath($allowedExtensionPath)) { continue; } $allowedExtensionFullPath = GeneralUtility::getFileAbsFileName($allowedExtensionPath); if (!file_exists($allowedExtensionFullPath)) { continue; } $allowedExtensionPath = rtrim($allowedExtensionPath, '/') . '/'; $extensionFolders[$allowedExtensionPath] = $allowedExtensionFullPath; } $this->runtimeCache->set($cacheKey, $extensionFolders); return $extensionFolders; } /** * Retrieves yaml files from extension folders for further processing. * At this time it's not determined yet, whether these files contain form data. * * @return string[] */ protected function retrieveYamlFilesFromExtensionFolders(): array { $filesFromExtensionFolders = []; foreach ($this->getAccessibleExtensionFolders() as $relativePath => $fullPath) { foreach (new \DirectoryIterator($fullPath) as $fileInfo) { if ($fileInfo->getExtension() !== 'yaml') { continue; } $filesFromExtensionFolders[] = $relativePath . $fileInfo->getFilename(); } } return $filesFromExtensionFolders; } protected function loadMetaData(string $fileOrIdentifier): FormMetadata { $this->ensureValidPersistenceIdentifier($fileOrIdentifier); $persistenceIdentifier = $fileOrIdentifier; $rawYamlContent = false; $absoluteFilePath = GeneralUtility::getFileAbsFileName($fileOrIdentifier); if ($absoluteFilePath !== '' && file_exists($absoluteFilePath)) { $rawYamlContent = file_get_contents($absoluteFilePath); } try { if ($rawYamlContent === false) { throw new NoSuchFileException(sprintf('YAML file "%s" could not be loaded', $persistenceIdentifier), 1524684462); } $yaml = $this->extractMetaDataFromCouldBeFormDefinition($rawYamlContent); $this->generateErrorsIfFormDefinitionIsInvalidOrHasInvalidFileExtension($yaml, $persistenceIdentifier); return FormMetadata::createFromYaml( $yaml, $persistenceIdentifier, )->withStorageLocation($this->buildStorageLocationLabel($persistenceIdentifier)); } catch (\Exception $e) { return FormMetadata::createInvalid($persistenceIdentifier, $e->getMessage()); } } protected function isAccessibleExtensionFolder(string $folderName): bool { $folderName = rtrim($folderName, '/') . '/'; return array_key_exists($folderName, $this->getAccessibleExtensionFolders()); } protected function isFileWithinAccessibleExtensionFolders(string $fileName): bool { $pathInfo = PathUtility::pathinfo($fileName, PATHINFO_DIRNAME); $dirName = rtrim($pathInfo, '/') . '/'; return array_key_exists($dirName, $this->getAccessibleExtensionFolders()); } /** * @throws PersistenceManagerException */ protected function ensureValidPersistenceIdentifier(string $identifier): void { if (pathinfo($identifier, PATHINFO_EXTENSION) !== 'yaml') { throw new PersistenceManagerException(sprintf('The file "%s" could not be loaded.', $identifier), 1764879628); } if (PathUtility::isExtensionPath($identifier) && !$this->isFileWithinAccessibleExtensionFolders($identifier) ) { throw new PersistenceManagerException( sprintf('The file "%s" could not be loaded. Please check your configuration option "persistenceManager.allowedExtensionPaths"', $identifier), 1484071985 ); } } /** * Check if a storage location (extension folder) is allowed */ public function isAllowedStorageLocation(string $storageLocation): bool { // For extension storage, storageLocation is a folder path within allowed extensions return $this->isAccessibleExtensionFolder($storageLocation); } /** * Check if a persistence identifier (full file path) is allowed */ public function isAllowedPersistenceIdentifier(string $persistenceIdentifier): bool { // For extension storage, persistence identifier is a full file path (EXT:...) return $this->hasValidFileExtension($persistenceIdentifier) && $this->isFileWithinAccessibleExtensionFolders($persistenceIdentifier); } public function getFormManagerOptions(): array { $preparedAccessibleFormStorageFolders = []; if ($this->storageConfiguration->isAllowedToSaveToExtensionPaths()) { foreach ($this->getAccessibleExtensionFolders() as $relativePath => $fullPath) { $preparedAccessibleFormStorageFolders[] = [ 'label' => $relativePath, 'value' => $relativePath, ]; } } return [ 'allowedStorageLocations' => $preparedAccessibleFormStorageFolders, ]; } public function isAccessible(): bool { return $this->storageConfiguration->isAllowedToSaveToExtensionPaths() && !empty($this->getAccessibleExtensionFolders()); } /** * Build a user-friendly storage location label * Format: "extension_key/Configuration/Forms/file.form.yaml" */ protected function buildStorageLocationLabel(string $persistenceIdentifier): string { return $persistenceIdentifier; } }