diff --git a/Model/Magento/Framework/Mail/Template/TransportBuilder.php b/Model/Magento/Framework/Mail/Template/TransportBuilder.php new file mode 100644 index 0000000..e51c3ff --- /dev/null +++ b/Model/Magento/Framework/Mail/Template/TransportBuilder.php @@ -0,0 +1,545 @@ +templateFactory = $templateFactory; + $this->objectManager = $objectManager; + $this->_senderResolver = $senderResolver; + $this->mailTransportFactory = $mailTransportFactory; + $this->productMetadata = $productMetadata; + $this->emailMessageInterfaceFactory = $emailMessageInterfaceFactory ?: $this->objectManager + ->get(EmailMessageInterfaceFactory::class); + $this->mimeMessageInterfaceFactory = $mimeMessageInterfaceFactory ?: $this->objectManager + ->get(MimeMessageInterfaceFactory::class); + $this->mimePartInterfaceFactory = $mimePartInterfaceFactory ?: $this->objectManager + ->get(MimePartInterfaceFactory::class); + $this->addressConverter = $addressConverter ?: $this->objectManager + ->get(AddressConverter::class); + } + + /** + * Add cc address + * + * @param array|string $address + * @param string $name + * + * @return $this + */ + public function addCc($address, $name = '') + { + $this->addAddressByType('cc', $address, $name); + + return $this; + } + + /** + * Add to address + * + * @param array|string $address + * @param string $name + * + * @return $this + * @throws InvalidArgumentException + */ + public function addTo($address, $name = '') + { + $this->addAddressByType('to', $address, $name); + + return $this; + } + + /** + * Add bcc address + * + * @param array|string $address + * + * @return $this + * @throws InvalidArgumentException + */ + public function addBcc($address) + { + $this->addAddressByType('bcc', $address); + + return $this; + } + + /** + * Set Reply-To Header + * + * @param string $email + * @param string|null $name + * + * @return $this + * @throws InvalidArgumentException + */ + public function setReplyTo($email, $name = null) + { + $this->addAddressByType('replyTo', $email, $name); + + return $this; + } + + /** + * Set mail from address + * + * @param string|array $from + * + * @return $this + * @throws InvalidArgumentException + * @see setFromByScope() + * + * @deprecated 102.0.1 This function sets the from address but does not provide + * a way of setting the correct from addresses based on the scope. + */ + public function setFrom($from) + { + return $this->setFromByScope($from); + } + + /** + * Set mail from address by scopeId + * + * @param string|array $from + * @param string|int $scopeId + * + * @return $this + * @throws InvalidArgumentException + * @throws MailException + * @since 102.0.1 + */ + public function setFromByScope($from, $scopeId = null) + { + $result = $this->_senderResolver->resolve($from, $scopeId); + $this->addAddressByType('from', $result['email'], $result['name']); + + return $this; + } + + /** + * Set template identifier + * + * @param string $templateIdentifier + * + * @return $this + */ + public function setTemplateIdentifier($templateIdentifier) + { + $this->templateIdentifier = $templateIdentifier; + + return $this; + } + + /** + * Set template model + * + * @param string $templateModel + * + * @return $this + */ + public function setTemplateModel($templateModel) + { + $this->templateModel = $templateModel; + return $this; + } + + /** + * Set template vars + * + * @param array $templateVars + * + * @return $this + */ + public function setTemplateVars($templateVars) + { + $this->templateVars = $templateVars; + + return $this; + } + + /** + * Set template options + * + * @param array $templateOptions + * @return $this + */ + public function setTemplateOptions($templateOptions) + { + $this->templateOptions = $templateOptions; + + return $this; + } + + /** + * Get mail transport + * + * @return TransportInterface + * @throws LocalizedException + */ + public function getTransport() + { + try { + $this->prepareMessage(); + $mailTransport = $this->mailTransportFactory->create(['message' => clone $this->message]); + } finally { + $this->reset(); + } + + return $mailTransport; + } + + /** + * @param string $content + * @param string $name + * @param string $type + * @param $encoding + * @param $disposition + * @return $this + */ + public function addAttachment(string $content, string $name, string $type, $encoding = null, $disposition = null) + { + $this->attachments[] = [ + 'content' => $content, + 'name' => $name, + 'type' => $type, + 'encoding' => $encoding, + 'disposition' => $disposition + ]; + + return $this; + } + + /** + * @return array + */ + private function getLaminasParts() + { + $parts = []; + + foreach ($this->attachments as $attachmentData) { + + $attachment = new \Laminas\Mime\Part($attachmentData['content']); + $attachment->filename = $attachmentData['name']; + $attachment->type = $attachmentData['type']; + $attachment->encoding = $attachmentData['encoding'] ?: \Laminas\Mime\Mime::ENCODING_BASE64; + $attachment->disposition = $attachmentData['disposition'] ?: \Laminas\Mime\Mime::DISPOSITION_ATTACHMENT; + + $parts[] = $attachment; + } + + return $parts; + } + + /** + * @return array + */ + private function getSymfonyParts() + { + $parts = []; + + foreach ($this->attachments as $attachmentData) { + $attachment = new \Symfony\Component\Mime\Part\DataPart($attachmentData['content'], $attachmentData['name'], $attachmentData['type']); + + $parts[] = $attachment; + } + + return $parts; + } + + /** + * Reset object state + * + * @return $this + */ + protected function reset() + { + $this->messageData = []; + $this->templateIdentifier = null; + $this->templateVars = null; + $this->templateOptions = null; + return $this; + } + + /** + * Get template + * + * @return TemplateInterface + */ + protected function getTemplate() + { + return $this->templateFactory->get($this->templateIdentifier, $this->templateModel) + ->setVars($this->templateVars) + ->setOptions($this->templateOptions); + } + + /** + * Prepare message. + * + * @return $this + * @throws LocalizedException if template type is unknown + */ + protected function prepareMessage() + { + $template = $this->getTemplate(); + $content = $template->processTemplate(); + + switch ($template->getType()) { + case TemplateTypesInterface::TYPE_TEXT: + $part['type'] = MimeInterface::TYPE_TEXT; + break; + + case TemplateTypesInterface::TYPE_HTML: + $part['type'] = MimeInterface::TYPE_HTML; + break; + + default: + throw new LocalizedException( + new Phrase('Unknown template type') + ); + } + + /** @var \Magento\Framework\Mail\MimePartInterface $mimePart */ + $mimePart = $this->mimePartInterfaceFactory->create(['content' => $content]); + /* Custom code */ + $parts = [$mimePart]; + + $attachmentParts = $this->isMagentoVersionLte248() + ? $this->getLaminasParts() + : $this->getSymfonyParts(); + + $parts = array_merge($parts, $attachmentParts); + /* End custom code */ + + $this->messageData['encoding'] = $mimePart->getCharset(); + $this->messageData['body'] = $this->mimeMessageInterfaceFactory->create( + ['parts' => $parts] + ); + + $this->messageData['subject'] = html_entity_decode( + (string)$template->getSubject(), + ENT_QUOTES + ); + + $this->message = $this->emailMessageInterfaceFactory->create($this->messageData); + + $this->addSymfonyAttachment($attachmentParts); + + return $this; + } + + /** + * @param array $attachmentParts + * @return void + */ + private function addSymfonyAttachment(array $attachmentParts): void + { + if ($this->message instanceof \Magento\Framework\Mail\EmailMessage && method_exists($this->message, 'getSymfonyMessage')) { + $symfonyEmail = $this->message->getSymfonyMessage(); + + // Decode the original HTML body (quoted-printable) + $decodedBody = quoted_printable_decode($symfonyEmail->getBody()->bodyToString()); + + // Create main HTML body part + $htmlPart = new \Symfony\Component\Mime\Part\TextPart($decodedBody, 'utf-8', 'html'); + + // Rebuild the email body with attachments + $symfonyEmail->setBody( + new \Symfony\Component\Mime\Part\Multipart\MixedPart($htmlPart, ...$attachmentParts) + ); + } + } + + /** + * Handles possible incoming types of email (string or array) + * + * @param string $addressType + * @param string|array $email + * @param string|null $name + * + * @return void + * @throws InvalidArgumentException + */ + private function addAddressByType(string $addressType, $email, ?string $name = null): void + { + if (is_string($email)) { + $this->messageData[$addressType][] = $this->addressConverter->convert($email, $name); + return; + } + $convertedAddressArray = $this->addressConverter->convertMany($email); + if (isset($this->messageData[$addressType])) { + $this->messageData[$addressType] = array_merge( + $this->messageData[$addressType], + $convertedAddressArray + ); + } else { + $this->messageData[$addressType] = $convertedAddressArray; + } + } + + /** + * @return bool + */ + private function isMagentoVersionLte248(): bool + { + $version = $this->productMetadata->getVersion(); + return version_compare($version, '2.4.8', '<'); + } +}