From a1e4ac18ff611d19bf4bee78f61c7556b8bf5179 Mon Sep 17 00:00:00 2001 From: Savin Date: Tue, 2 Jan 2024 09:05:52 +0530 Subject: [PATCH] Multiple file upload fix --- Classes/Xclass/EmailFinisherXclass.php | 226 ++++++++++++++++++ .../UploadedFileReferenceConverterXclass.php | 12 +- ext_emconf.php | 4 +- ext_localconf.php | 4 + 4 files changed, 241 insertions(+), 5 deletions(-) create mode 100644 Classes/Xclass/EmailFinisherXclass.php diff --git a/Classes/Xclass/EmailFinisherXclass.php b/Classes/Xclass/EmailFinisherXclass.php new file mode 100644 index 0000000..98f245f --- /dev/null +++ b/Classes/Xclass/EmailFinisherXclass.php @@ -0,0 +1,226 @@ + '', + 'senderName' => '', + 'addHtmlPart' => true, + 'attachUploads' => true, + ]; + + /** + * Executes this finisher + * @see AbstractFinisher::execute() + * + * @throws FinisherException + */ + protected function executeInternal() + { + $languageBackup = null; + // Flexform overrides write strings instead of integers so + // we need to cast the string '0' to false. + if ( + isset($this->options['addHtmlPart']) + && $this->options['addHtmlPart'] === '0' + ) { + $this->options['addHtmlPart'] = false; + } + + $subject = (string)$this->parseOption('subject'); + $recipients = $this->getRecipients('recipients'); + $senderAddress = $this->parseOption('senderAddress'); + $senderAddress = is_string($senderAddress) ? $senderAddress : ''; + $senderName = $this->parseOption('senderName'); + $senderName = is_string($senderName) ? $senderName : ''; + $replyToRecipients = $this->getRecipients('replyToRecipients'); + $carbonCopyRecipients = $this->getRecipients('carbonCopyRecipients'); + $blindCarbonCopyRecipients = $this->getRecipients('blindCarbonCopyRecipients'); + $addHtmlPart = $this->parseOption('addHtmlPart') ? true : false; + $attachUploads = $this->parseOption('attachUploads'); + $title = (string)$this->parseOption('title') ?: $subject; + + if ($subject === '') { + throw new FinisherException('The option "subject" must be set for the EmailFinisher.', 1327060320); + } + if (empty($recipients)) { + throw new FinisherException('The option "recipients" must be set for the EmailFinisher.', 1327060200); + } + if (empty($senderAddress)) { + throw new FinisherException('The option "senderAddress" must be set for the EmailFinisher.', 1327060210); + } + + $formRuntime = $this->finisherContext->getFormRuntime(); + + $translationService = GeneralUtility::makeInstance(TranslationService::class); + if (is_string($this->options['translation']['language'] ?? null) && $this->options['translation']['language'] !== '') { + $languageBackup = $translationService->getLanguage(); + $translationService->setLanguage($this->options['translation']['language']); + } + + $mail = $this + ->initializeFluidEmail($formRuntime) + ->from(new Address($senderAddress, $senderName)) + ->to(...$recipients) + ->subject($subject) + ->format($addHtmlPart ? FluidEmail::FORMAT_BOTH : FluidEmail::FORMAT_PLAIN) + ->assign('title', $title); + + if (!empty($replyToRecipients)) { + $mail->replyTo(...$replyToRecipients); + } + + if (!empty($carbonCopyRecipients)) { + $mail->cc(...$carbonCopyRecipients); + } + + if (!empty($blindCarbonCopyRecipients)) { + $mail->bcc(...$blindCarbonCopyRecipients); + } + + if (!empty($languageBackup)) { + $translationService->setLanguage($languageBackup); + } + + if ($attachUploads) { + foreach ($formRuntime->getFormDefinition()->getRenderablesRecursively() as $element) { + if (!$element instanceof FileUpload) { + continue; + } + $files = $formRuntime[$element->getIdentifier()]; + foreach ($files as $file) { + if ($file) { + if ($file instanceof FileReference) { + + $file = $file->getOriginalResource(); + } + $mail->attach($file->getContents(), $file->getOriginalFile()->getName(),$file->getOriginalFile()->getProperties()['mime_type']); + } + } + } + } + + // TODO: DI should be used to inject the MailerInterface + GeneralUtility::makeInstance(MailerInterface::class)->send($mail); + } + + protected function initializeFluidEmail(FormRuntime $formRuntime): FluidEmail + { + $templateConfiguration = $GLOBALS['TYPO3_CONF_VARS']['MAIL']; + + if (is_array($this->options['templateRootPaths'] ?? null)) { + $templateConfiguration['templateRootPaths'] = array_replace_recursive( + $templateConfiguration['templateRootPaths'], + $this->options['templateRootPaths'] + ); + ksort($templateConfiguration['templateRootPaths']); + } + + if (is_array($this->options['partialRootPaths'] ?? null)) { + $templateConfiguration['partialRootPaths'] = array_replace_recursive( + $templateConfiguration['partialRootPaths'], + $this->options['partialRootPaths'] + ); + ksort($templateConfiguration['partialRootPaths']); + } + + if (is_array($this->options['layoutRootPaths'] ?? null)) { + $templateConfiguration['layoutRootPaths'] = array_replace_recursive( + $templateConfiguration['layoutRootPaths'], + $this->options['layoutRootPaths'] + ); + ksort($templateConfiguration['layoutRootPaths']); + } + + $fluidEmail = GeneralUtility::makeInstance( + FluidEmail::class, + GeneralUtility::makeInstance(TemplatePaths::class, $templateConfiguration) + ); + + if (!isset($this->options['templateName']) || $this->options['templateName'] === '') { + throw new FinisherException('The option "templateName" must be set to use FluidEmail.', 1599834020); + } + + // Migrate old template name to default FluidEmail name + if ($this->options['templateName'] === '{@format}.html') { + $this->options['templateName'] = 'Default'; + } + + // Set the PSR-7 request object if available + if (($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface) { + $fluidEmail->setRequest($GLOBALS['TYPO3_REQUEST']); + } + + $fluidEmail + ->setTemplate($this->options['templateName']) + ->assignMultiple([ + 'finisherVariableProvider' => $this->finisherContext->getFinisherVariableProvider(), + 'form' => $formRuntime, + ]); + + if (is_array($this->options['variables'] ?? null)) { + $fluidEmail->assignMultiple($this->options['variables']); + } + + $fluidEmail + ->getViewHelperVariableContainer() + ->addOrUpdate(RenderRenderableViewHelper::class, 'formRuntime', $formRuntime); + return $fluidEmail; + } + + /** + * Get mail recipients + * + * @param string $listOption List option name + */ + protected function getRecipients(string $listOption): array + { + $recipients = $this->parseOption($listOption) ?? []; + if (!is_array($recipients) || $recipients === []) { + return []; + } + + $addresses = []; + foreach ($recipients as $address => $name) { + // The if is needed to set address and name with TypoScript + if (MathUtility::canBeInterpretedAsInteger($address)) { + if (is_array($name)) { + $address = $name[0] ?? ''; + $name = $name[1] ?? ''; + } else { + $address = $name; + $name = ''; + } + } + + if (!GeneralUtility::validEmail($address)) { + // Drop entries without valid address + continue; + } + $addresses[] = new Address($address, $name); + } + return $addresses; + } +} diff --git a/Classes/Xclass/UploadedFileReferenceConverterXclass.php b/Classes/Xclass/UploadedFileReferenceConverterXclass.php index 95edce3..bcaa031 100644 --- a/Classes/Xclass/UploadedFileReferenceConverterXclass.php +++ b/Classes/Xclass/UploadedFileReferenceConverterXclass.php @@ -2,6 +2,7 @@ namespace StudioMitte\FormMultipleUploads\Xclass; +use TYPO3\CMS\Core\Http\UploadedFile; use TYPO3\CMS\Extbase\Domain\Model\AbstractFileFolder; use TYPO3\CMS\Extbase\Domain\Model\FileReference; use TYPO3\CMS\Extbase\Domain\Model\FileReference as ExtbaseFileReference; @@ -26,9 +27,15 @@ class UploadedFileReferenceConverterXclass extends UploadedFileReferenceConverte */ public function convertFrom($source, $targetType, array $convertedChildProperties = [], PropertyMappingConfigurationInterface $configuration = null) { + if ($source instanceof UploadedFile) { + $source = $this->convertUploadedFileToUploadInfoArray($source); + } if (isset($source[0])) { $result = []; foreach ($source as $singleSource) { + if ($singleSource instanceof UploadedFile) { + $singleSource = $this->convertUploadedFileToUploadInfoArray($singleSource); + } $converted = $this->convertSingleFile($singleSource, $configuration); $result[] = $converted; } @@ -49,9 +56,8 @@ public function convertFrom($source, $targetType, array $convertedChildPropertie */ protected function convertSingleFile($source, ?PropertyMappingConfigurationInterface $configuration) { - if ($source instanceof FileReference) { - return $source; - } + + if (!isset($source['error']) || $source['error'] === \UPLOAD_ERR_NO_FILE) { if (isset($source['submittedFile']['resourcePointer'])) { try { diff --git a/ext_emconf.php b/ext_emconf.php index f4e82db..af7950f 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -6,13 +6,13 @@ 'category' => 'fe', 'author' => 'Georg Ringer', 'author_email' => 'gr@studiomitte.com', - 'state' => 'alpha', + 'state' => 'stable', 'createDirs' => '', 'clearCacheOnLoad' => 0, 'version' => '1.0.0', 'constraints' => [ 'depends' => [ - 'typo3' => '10.4.0-10.4.99', + 'typo3' => '10.4.0-12.4.7', 'form' => '', ], 'conflicts' => [], diff --git a/ext_localconf.php b/ext_localconf.php index 5993896..e81c87f 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -7,3 +7,7 @@ $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][\TYPO3\CMS\Form\Domain\Finishers\SaveToDatabaseFinisher::class] = [ 'className' => \StudioMitte\FormMultipleUploads\Xclass\SaveToDabaseFinisherXclass::class ]; + +$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][\TYPO3\CMS\Form\Domain\Finishers\EmailFinisher::class] = [ + 'className' => \StudioMitte\FormMultipleUploads\Xclass\EmailFinisherXclass::class +];