vendor/dachcom-digital/formbuilder/src/FormBuilderBundle/Stream/AttachmentStream.php line 144

Open in your IDE?
  1. <?php
  2. namespace FormBuilderBundle\Stream;
  3. use Doctrine\DBAL\Query\QueryBuilder;
  4. use FormBuilderBundle\Event\OutputWorkflow\OutputWorkflowSignalEvent;
  5. use FormBuilderBundle\Event\OutputWorkflow\OutputWorkflowSignalsEvent;
  6. use FormBuilderBundle\EventSubscriber\SignalSubscribeHandler;
  7. use League\Flysystem\FilesystemOperator;
  8. use Pimcore\Logger;
  9. use Pimcore\Model\Asset;
  10. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  11. class AttachmentStream implements AttachmentStreamInterface
  12. {
  13.     public const SIGNAL_CLEAN_UP 'tmp_file_attachment_stream';
  14.     protected const PACKAGE_IDENTIFIER 'formbuilder_package_identifier';
  15.     public function __construct(
  16.         protected EventDispatcherInterface $eventDispatcher,
  17.         protected FilesystemOperator $formBuilderFilesStorage
  18.     ) {
  19.     }
  20.     /**
  21.      * @return array<int, File>
  22.      */
  23.     public function createAttachmentLinks(array $datastring $formName): array
  24.     {
  25.         return $this->extractFiles($data)->getFiles();
  26.     }
  27.     public function createAttachmentAsset($data$fieldName$formName): ?Asset
  28.     {
  29.         if (!is_array($data)) {
  30.             return null;
  31.         }
  32.         $fileStack $this->extractFiles($data);
  33.         if ($fileStack->count() === 0) {
  34.             return null;
  35.         }
  36.         $packageIdentifier '';
  37.         foreach ($fileStack->getFiles() as $file) {
  38.             $packageIdentifier .= sprintf('%s-%s-%s-%s'$this->formBuilderFilesStorage->fileSize($file->getPath()), $file->getId(), $file->getPath(), $file->getName());
  39.         }
  40.         // create package identifier to check if we just in another channel
  41.         $packageIdentifier md5($packageIdentifier);
  42.         $formName \Pimcore\File::getValidFilename($formName);
  43.         $zipKey substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyz'), 05);
  44.         $zipFileName sprintf('%s-%s.zip'\Pimcore\File::getValidFilename($fieldName), $zipKey);
  45.         $zipPath sprintf('%s/%s'PIMCORE_SYSTEM_TEMP_DIRECTORY$zipFileName);
  46.         $existingAssetPackage $this->findExistingAssetPackage($packageIdentifier$formName);
  47.         if ($existingAssetPackage instanceof Asset) {
  48.             return $existingAssetPackage;
  49.         }
  50.         try {
  51.             $zip = new \ZipArchive();
  52.             $zip->open($zipPath\ZipArchive::CREATE \ZipArchive::OVERWRITE);
  53.             foreach ($fileStack->getFiles() as $file) {
  54.                 $zip->addFromString($file->getName(), $this->formBuilderFilesStorage->read($file->getPath()));
  55.             }
  56.             $zip->close();
  57.         } catch (\Exception $e) {
  58.             Logger::error(sprintf('Error while creating attachment zip (%s): %s'$zipPath$e->getMessage()));
  59.             return null;
  60.         }
  61.         if (!file_exists($zipPath)) {
  62.             Logger::error(sprintf('zip path does not exist (%s)'$zipPath));
  63.             return null;
  64.         }
  65.         $formDataParentFolder Asset\Folder::getByPath('/formdata');
  66.         if (!$formDataParentFolder instanceof Asset\Folder) {
  67.             Logger::error('parent folder does not exist (/formdata)!');
  68.             return null;
  69.         }
  70.         $formFolderExists Asset\Service::pathExists(sprintf('/formdata/%s'$formName));
  71.         if ($formFolderExists === false) {
  72.             $formDataFolder = new Asset\Folder();
  73.             $formDataFolder->setCreationDate(time());
  74.             $formDataFolder->setLocked(true);
  75.             $formDataFolder->setUserOwner(1);
  76.             $formDataFolder->setUserModification(0);
  77.             $formDataFolder->setParentId($formDataParentFolder->getId());
  78.             $formDataFolder->setFilename($formName);
  79.             try {
  80.                 $formDataFolder->save();
  81.             } catch (\Exception $e) {
  82.                 // fail silently.
  83.             }
  84.         } else {
  85.             $formDataFolder Asset\Folder::getByPath(sprintf('/formdata/%s'$formName));
  86.         }
  87.         if (!$formDataFolder instanceof Asset\Folder) {
  88.             Logger::error(sprintf('Error while creating form data folder (/formdata/%s)'$formName));
  89.             return null;
  90.         }
  91.         $assetData = [
  92.             'data'     => file_get_contents($zipPath),
  93.             'filename' => $zipFileName
  94.         ];
  95.         try {
  96.             $asset Asset::create($formDataFolder->getId(), $assetDatafalse);
  97.             $asset->setProperty(self::PACKAGE_IDENTIFIER'text'$packageIdentifierfalsefalse);
  98.             $asset->save();
  99.             unlink($zipPath);
  100.         } catch (\Exception $e) {
  101.             Logger::error(sprintf('Error while storing asset in pimcore (%s): %s'$zipPath$e->getMessage()));
  102.             return null;
  103.         }
  104.         return $asset;
  105.     }
  106.     /**
  107.      * @internal
  108.      */
  109.     public function cleanUp(OutputWorkflowSignalsEvent $signalsEvent): void
  110.     {
  111.         // keep assets:
  112.         // - if broadcasting channel is initiating funnel
  113.         // - if broadcasting channel is processing funnel and not done yet
  114.         // - if guard exception occurs: user may want to retry!
  115.         if ($signalsEvent->getChannel() === SignalSubscribeHandler::CHANNEL_FUNNEL_INITIATE) {
  116.             return;
  117.         }
  118.         if ($signalsEvent->getChannel() === SignalSubscribeHandler::CHANNEL_FUNNEL_PROCESS && $signalsEvent->getContextItem('funnel_shutdown') === false) {
  119.             return;
  120.         }
  121.         if ($signalsEvent->hasGuardException() === true) {
  122.             return;
  123.         }
  124.         foreach ($signalsEvent->getSignalsByName(self::SIGNAL_CLEAN_UP) as $signal) {
  125.             $fileStack $signal->getData();
  126.             if (!$fileStack instanceof FileStack) {
  127.                 continue;
  128.             }
  129.             foreach ($fileStack->getFiles() as $attachmentFile) {
  130.                 $this->removeAttachmentFile($attachmentFile);
  131.             }
  132.         }
  133.     }
  134.     protected function removeAttachmentFile(File $attachmentFile): void
  135.     {
  136.         if ($this->formBuilderFilesStorage->directoryExists($attachmentFile->getId())) {
  137.             $this->formBuilderFilesStorage->deleteDirectory($attachmentFile->getId());
  138.         }
  139.     }
  140.     protected function extractFiles(array $data): FileStack
  141.     {
  142.         $files = new FileStack();
  143.         foreach ($data as $fileData) {
  144.             $fileId = (string) $fileData['id'];
  145.             if ($this->formBuilderFilesStorage->directoryExists($fileId)) {
  146.                 $dirFiles $this->formBuilderFilesStorage->listContents($fileId);
  147.                 $flyFiles iterator_to_array($dirFiles->getIterator());
  148.                 if (count($flyFiles) === 1) {
  149.                     $files->addFile(new File($fileId$fileData['fileName'], $flyFiles[0]->path()));
  150.                 }
  151.             }
  152.         }
  153.         // add signal for later clean up
  154.         $this->eventDispatcher->dispatch(new OutputWorkflowSignalEvent(self::SIGNAL_CLEAN_UP$files), OutputWorkflowSignalEvent::NAME);
  155.         return $files;
  156.     }
  157.     protected function findExistingAssetPackage(string $packageIdentifierstring $formName): ?Asset
  158.     {
  159.         $assetListing = new Asset\Listing();
  160.         $assetListing->addConditionParam('`assets`.path = ?'sprintf('/formdata/%s/'$formName));
  161.         $assetListing->addConditionParam('`properties`.data = ?'$packageIdentifier);
  162.         $assetListing->setLimit(1);
  163.         $assetListing->onCreateQueryBuilder(function (QueryBuilder $queryBuilder) {
  164.             $queryBuilder->leftJoin(
  165.                 'assets',
  166.                 'properties',
  167.                 'properties',
  168.                 sprintf('properties.`cid` = assets.`id` AND properties.`ctype` = "asset" AND properties.`name` = "%s"'self::PACKAGE_IDENTIFIER)
  169.             );
  170.         });
  171.         $assets $assetListing->getAssets();
  172.         if (count($assets) === 0) {
  173.             return null;
  174.         }
  175.         return $assets[0];
  176.     }
  177. }