From 9f40ca7b3d6aa3cd33a446c72f3387f624ef747e Mon Sep 17 00:00:00 2001 From: Demian Katz <demian.katz@villanova.edu> Date: Wed, 6 May 2020 13:23:04 -0400 Subject: [PATCH] allow multipart messages in mails (#1610) Co-authored-by: Robert Lange <robert.lange@uni-leipzig.de> --- module/VuFind/src/VuFind/Mailer/Mailer.php | 71 +++++++++++++++++-- .../src/VuFindTest/Mailer/MailerTest.php | 27 +++++++ 2 files changed, 91 insertions(+), 7 deletions(-) diff --git a/module/VuFind/src/VuFind/Mailer/Mailer.php b/module/VuFind/src/VuFind/Mailer/Mailer.php index dd61e501841..1d84e450eef 100644 --- a/module/VuFind/src/VuFind/Mailer/Mailer.php +++ b/module/VuFind/src/VuFind/Mailer/Mailer.php @@ -32,6 +32,9 @@ use Laminas\Mail\AddressList; use Laminas\Mail\Header\ContentType; use Laminas\Mail\Message; use Laminas\Mail\Transport\TransportInterface; +use Laminas\Mime\Message as MimeMessage; +use Laminas\Mime\Mime; +use Laminas\Mime\Part as MimePart; use VuFind\Exception\Mail as MailException; /** @@ -89,17 +92,16 @@ class Mailer implements \VuFind\I18n\Translator\TranslatorAwareInterface } /** - * Get a blank email message object. + * Get a text email message object. * * @return Message */ public function getNewMessage() { - $message = new Message(); - $message->setEncoding('UTF-8'); + $message = $this->getNewBlankMessage(); $headers = $message->getHeaders(); $ctype = new ContentType(); - $ctype->setType('text/plain'); + $ctype->setType(Mime::TYPE_TEXT); $ctype->addParameter('charset', 'UTF-8'); $headers->addHeader($ctype); return $message; @@ -120,6 +122,18 @@ class Mailer implements \VuFind\I18n\Translator\TranslatorAwareInterface return $this; } + /** + * Get a blank email message object. + * + * @return Message + */ + public function getNewBlankMessage() + { + $message = new Message(); + $message->setEncoding('UTF-8'); + return $message; + } + /** * Set the mail transport object. * @@ -152,6 +166,47 @@ class Mailer implements \VuFind\I18n\Translator\TranslatorAwareInterface return $list; } + /** + * Constructs a {@see MimeMessage} body from given text and html content. + * + * @param string|null $text Mail content used for plain text part + * @param string|null $html Mail content used for html part + * + * @return MimeMessage + */ + public function buildMultipartBody( + string $text = null, + string $html = null + ): MimeMessage { + $parts = new MimeMessage(); + + if ($text) { + $textPart = new MimePart($text); + $textPart->setType(Mime::TYPE_TEXT); + $textPart->setCharset('utf-8'); + $textPart->setEncoding(Mime::ENCODING_QUOTEDPRINTABLE); + $parts->addPart($textPart); + } + + if ($html) { + $htmlPart = new MimePart($html); + $htmlPart->setType(Mime::TYPE_HTML); + $htmlPart->setCharset('utf-8'); + $htmlPart->setEncoding(Mime::ENCODING_QUOTEDPRINTABLE); + $parts->addPart($htmlPart); + } + + $alternativePart = new MimePart($parts->generateMessage()); + $alternativePart->setType('multipart/alternative'); + $alternativePart->setBoundary($parts->getMime()->boundary()); + $alternativePart->setCharset('utf-8'); + + $body = new MimeMessage(); + $body->setParts([$alternativePart]); + + return $body; + } + /** * Send an email message. * @@ -159,7 +214,7 @@ class Mailer implements \VuFind\I18n\Translator\TranslatorAwareInterface * delimited list) * @param string|Address $from Sender name and email address * @param string $subject Subject line for message - * @param string $body Message body + * @param string|MimeMessage $body Message body * @param string $cc CC recipient (null for none) * @param string|Address|AddressList $replyTo Reply-To address (or delimited * list, null for none) @@ -220,8 +275,10 @@ class Mailer implements \VuFind\I18n\Translator\TranslatorAwareInterface // Convert all exceptions thrown by mailer into MailException objects: try { // Send message - $message = $this->getNewMessage() - ->addFrom($from) + $message = $body instanceof MimeMessage + ? $this->getNewBlankMessage() + : $this->getNewMessage(); + $message->addFrom($from) ->addTo($recipients) ->setBody($body) ->setSubject($subject); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Mailer/MailerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Mailer/MailerTest.php index f79aba37c17..3d69b969e42 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Mailer/MailerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Mailer/MailerTest.php @@ -426,6 +426,33 @@ class MailerTest extends \VuFindTest\Unit\TestCase $mailer = new Mailer($transport); $mailer->resetConnection(); } + + /** + * Test sending an email using with text part and html part and multipart content type. + * + * @return void + * @throws \VuFind\Exception\Mail + */ + public function testSendMimeMessageWithMultipartAlternativeContentType() + { + $this->html = '<!DOCTYPE html><head><title>html</title></head><body>html body part</body></html>'; + $this->text = 'this is the text part'; + $callback = function ($message) { + $fromString = $message->getFrom()->current()->toString(); + return '<to@example.com>' == $message->getTo()->current()->toString() + && 'Sender TextName <from@example.com>' == $fromString + && 'subject' == $message->getSubject() + && 0 <= strpos($message->getBody()->getParts()[0]->getContent(), $this->html) + && 0 <= strpos($message->getBody()->getParts()[0]->getContent(), $this->text) + && 'multipart/alternative' == $message->getHeaders()->get('Content-Type')->getType(); + }; + $transport = $this->createMock(\Laminas\Mail\Transport\TransportInterface::class); + $transport->expects($this->once())->method('send')->with($this->callback($callback)); + $address = new Address('from@example.com', 'Sender TextName'); + $mailer = new Mailer($transport); + $body = $mailer->buildMultipartBody($this->text, $this->html); + $mailer->send('to@example.com', $address, 'subject', $body); + } } class MockEmailRenderer extends \Laminas\View\Renderer\PhpRenderer -- GitLab