diff --git a/app/code/Magento/Email/Model/Transport.php b/app/code/Magento/Email/Model/Transport.php index 90a4e6571c9b..cbce1682cb5f 100644 --- a/app/code/Magento/Email/Model/Transport.php +++ b/app/code/Magento/Email/Model/Transport.php @@ -3,10 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Email\Model; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Exception\MailException; +use Magento\Framework\Mail\EmailMessageInterface; use Magento\Framework\Mail\MessageInterface; use Magento\Framework\Mail\TransportInterface; use Magento\Framework\Phrase; @@ -59,12 +62,12 @@ class Transport implements TransportInterface private $message; /** - * @param MessageInterface $message Email message object + * @param EmailMessageInterface $message Email message object * @param ScopeConfigInterface $scopeConfig Core store config * @param null|string|array|\Traversable $parameters Config options for sendmail parameters */ public function __construct( - MessageInterface $message, + EmailMessageInterface $message, ScopeConfigInterface $scopeConfig, $parameters = null ) { diff --git a/app/code/Magento/Newsletter/Model/Queue/TransportBuilder.php b/app/code/Magento/Newsletter/Model/Queue/TransportBuilder.php index 502a19d298c4..aa3a2bcfe0f5 100644 --- a/app/code/Magento/Newsletter/Model/Queue/TransportBuilder.php +++ b/app/code/Magento/Newsletter/Model/Queue/TransportBuilder.php @@ -3,10 +3,29 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Newsletter\Model\Queue; use Magento\Email\Model\AbstractTemplate; +use Magento\Framework\Exception\MailException; +use Magento\Framework\Mail\EmailMessageInterfaceFactory; +use Magento\Framework\Mail\AddressConverter; +use Magento\Framework\Mail\MessageInterface; +use Magento\Framework\Mail\MessageInterfaceFactory; +use Magento\Framework\Mail\MimeMessageInterfaceFactory; +use Magento\Framework\Mail\MimePartInterfaceFactory; +use Magento\Framework\Mail\Template\FactoryInterface; +use Magento\Framework\Mail\Template\SenderResolverInterface; +use Magento\Framework\Mail\TemplateInterface; +use Magento\Framework\Mail\TransportInterfaceFactory; +use Magento\Framework\ObjectManagerInterface; +/** + * Class TransportBuilder + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class TransportBuilder extends \Magento\Framework\Mail\Template\TransportBuilder { /** @@ -16,6 +35,194 @@ class TransportBuilder extends \Magento\Framework\Mail\Template\TransportBuilder */ protected $templateData = []; + /** + * Param that used for storing all message data until it will be used + * + * @var array + */ + private $messageData = []; + + /** + * @var EmailMessageInterfaceFactory + */ + private $emailMessageInterfaceFactory; + + /** + * @var MimeMessageInterfaceFactory + */ + private $mimeMessageInterfaceFactory; + + /** + * @var MimePartInterfaceFactory + */ + private $mimePartInterfaceFactory; + + /** + * @var AddressConverter|null + */ + private $addressConverter; + + /** + * TransportBuilder constructor + * + * @param FactoryInterface $templateFactory + * @param MessageInterface $message + * @param SenderResolverInterface $senderResolver + * @param ObjectManagerInterface $objectManager + * @param TransportInterfaceFactory $mailTransportFactory + * @param MessageInterfaceFactory|null $messageFactory + * @param EmailMessageInterfaceFactory|null $emailMessageInterfaceFactory + * @param MimeMessageInterfaceFactory|null $mimeMessageInterfaceFactory + * @param MimePartInterfaceFactory|null $mimePartInterfaceFactory + * @param AddressConverter|null $addressConverter + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function __construct( + FactoryInterface $templateFactory, + MessageInterface $message, + SenderResolverInterface $senderResolver, + ObjectManagerInterface $objectManager, + TransportInterfaceFactory $mailTransportFactory, + MessageInterfaceFactory $messageFactory = null, + EmailMessageInterfaceFactory $emailMessageInterfaceFactory = null, + MimeMessageInterfaceFactory $mimeMessageInterfaceFactory = null, + MimePartInterfaceFactory $mimePartInterfaceFactory = null, + AddressConverter $addressConverter = null + ) { + parent::__construct( + $templateFactory, + $message, + $senderResolver, + $objectManager, + $mailTransportFactory, + $messageFactory, + $emailMessageInterfaceFactory, + $mimeMessageInterfaceFactory, + $mimePartInterfaceFactory, + $addressConverter + ); + $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 \Magento\Framework\Mail\Template\TransportBuilder + * @throws MailException + */ + 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 MailException + */ + public function addTo($address, $name = '') + { + $this->addAddressByType('to', $address, $name); + + return $this; + } + + /** + * Add bcc address + * + * @param array|string $address + * + * @return $this + * @throws MailException + */ + public function addBcc($address) + { + $this->addAddressByType('bcc', $address); + + return $this; + } + + /** + * Set Reply-To Header + * + * @param string $email + * @param string|null $name + * + * @return $this + * @throws MailException + */ + public function setReplyTo($email, $name = null) + { + + $this->addAddressByType('replyTo', $email, $name); + + return $this; + } + + /** + * Set mail from address + * + * @param string|array $from + * + * @return $this + * @throws MailException + * @see setFromByScope() + * + * @deprecated 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 MailException + */ + public function setFromByScope($from, $scopeId = null) + { + $result = $this->_senderResolver->resolve($from, $scopeId); + $this->addAddressByType('from', $result['email'], $result['name']); + + return $this; + } + + /** + * @inheritDoc + */ + protected function reset() + { + $this->messageData = []; + $this->templateIdentifier = null; + $this->templateVars = null; + $this->templateOptions = null; + + return $this; + } + /** * Set template data * @@ -25,11 +232,15 @@ class TransportBuilder extends \Magento\Framework\Mail\Template\TransportBuilder public function setTemplateData($data) { $this->templateData = $data; + return $this; } /** + * Sets up template filter + * * @param AbstractTemplate $template + * * @return void */ protected function setTemplateFilter(AbstractTemplate $template) @@ -44,16 +255,44 @@ protected function setTemplateFilter(AbstractTemplate $template) */ protected function prepareMessage() { - /** @var AbstractTemplate $template */ + /** @var AbstractTemplate|TemplateInterface $template */ $template = $this->getTemplate()->setData($this->templateData); $this->setTemplateFilter($template); + $content = $template->getProcessedTemplate($this->templateVars); + $this->messageData['subject'] = $template->getSubject(); - $this->message->setBodyHtml( - $template->getProcessedTemplate($this->templateVars) - )->setSubject( - $template->getSubject() + $mimePart = $this->mimePartInterfaceFactory->create( + ['content' => $content] + ); + $this->messageData['body'] = $this->mimeMessageInterfaceFactory->create( + ['parts' => [$mimePart]] ); + $this->message = $this->emailMessageInterfaceFactory->create($this->messageData); + return $this; } + + /** + * Handles possible incoming types of email (string or array) + * + * @param string $addressType + * @param string|array $email + * @param string|null $name + * + * @return void + * @throws MailException + */ + private function addAddressByType(string $addressType, $email, ?string $name = null): void + { + if (is_array($email)) { + $this->messageData[$addressType] = array_merge( + $this->messageData[$addressType], + $this->addressConverter->convertMany($email) + ); + + return; + } + $this->messageData[$addressType][] = $this->addressConverter->convert($email, $name); + } } diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/Queue/TransportBuilderTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/Queue/TransportBuilderTest.php index e8b141a24c9e..8f5626b42ff3 100644 --- a/app/code/Magento/Newsletter/Test/Unit/Model/Queue/TransportBuilderTest.php +++ b/app/code/Magento/Newsletter/Test/Unit/Model/Queue/TransportBuilderTest.php @@ -1,78 +1,112 @@ -templateFactoryMock = $this->createMock(\Magento\Framework\Mail\Template\FactoryInterface::class); - $this->messageMock = $this->getMockBuilder(\Magento\Framework\Mail\MessageInterface::class) + $objectManagerHelper = new ObjectManager($this); + $this->templateFactoryMock = $this->createMock(FactoryInterface::class); + $this->messageMock = $this->getMockBuilder(MessageInterface::class) ->disableOriginalConstructor() ->setMethods(['setBodyHtml', 'setSubject']) ->getMockForAbstractClass(); - $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - $this->senderResolverMock = $this->createMock(\Magento\Framework\Mail\Template\SenderResolverInterface::class); - $this->mailTransportFactoryMock = $this->getMockBuilder( - \Magento\Framework\Mail\TransportInterfaceFactory::class - )->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->messageFactoryMock = $this->getMockBuilder(\Magento\Framework\Mail\MessageInterfaceFactory::class) + + $this->emailMessageInterfaceFactoryMock = $this->createMock(EmailMessageInterfaceFactory::class); + $this->mimePartFactoryMock = $this->createMock(MimePartInterfaceFactory::class); + + $this->objectManagerMock = $this->createMock(ObjectManagerInterface::class); + $this->senderResolverMock = $this->createMock(SenderResolverInterface::class); + $this->mailTransportFactoryMock = $this->getMockBuilder(TransportInterfaceFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) - ->getMockForAbstractClass(); - $this->messageFactoryMock->expects($this->atLeastOnce())->method('create')->willReturn($this->messageMock); + ->getMock(); + $this->builder = $objectManagerHelper->getObject( $this->builderClassName, [ @@ -81,7 +115,9 @@ public function setUp() 'objectManager' => $this->objectManagerMock, 'senderResolver' => $this->senderResolverMock, 'mailTransportFactory' => $this->mailTransportFactoryMock, - 'messageFactory' => $this->messageFactoryMock + 'messageFactory' => $this->messageFactoryMock, + 'emailMessageInterfaceFactory' => $this->emailMessageInterfaceFactoryMock, + 'mimePartInterfaceFactory' => $this->mimePartFactoryMock, ] ); } @@ -91,12 +127,13 @@ public function setUp() * @param string $bodyText * @return void * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @throws \Magento\Framework\Exception\LocalizedException */ public function testGetTransport( $templateType = TemplateTypesInterface::TYPE_HTML, $bodyText = '

Html message

' - ) { - $filter = $this->createMock(\Magento\Email\Model\Template\Filter::class); + ): void { + $filter = $this->createMock(Filter::class); $data = [ 'template_subject' => 'Email Subject', 'template_text' => $bodyText, @@ -106,39 +143,39 @@ public function testGetTransport( ]; $vars = ['reason' => 'Reason', 'customer' => 'Customer']; $options = ['area' => 'frontend', 'store' => 1]; - $template = $this->createMock(\Magento\Email\Model\Template::class); - $template->expects($this->once())->method('setVars')->with($this->equalTo($vars))->will($this->returnSelf()); - $template->expects( - $this->once() - )->method( - 'setOptions' - )->with( - $this->equalTo($options) - )->will( - $this->returnSelf() - ); - $template->expects($this->once())->method('getSubject')->will($this->returnValue('Email Subject')); - $template->expects($this->once())->method('setData')->with($this->equalTo($data))->will($this->returnSelf()); - $template->expects($this->once()) - ->method('getProcessedTemplate') - ->with($vars) - ->willReturn($bodyText); - $template->expects($this->once()) - ->method('setTemplateFilter') - ->with($filter); - $this->templateFactoryMock->expects( - $this->once() - )->method( - 'get' - )->with( - $this->equalTo('identifier') - )->will( - $this->returnValue($template) - ); + /** @var MimePartInterface|MockObject $mimePartMock */ + $mimePartMock = $this->createMock(MimePartInterface::class); + + $this->mimePartFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($mimePartMock); + + /** @var EmailMessageInterface|MockObject $emailMessage */ + $emailMessage = $this->createMock(EmailMessageInterface::class); + + $this->emailMessageInterfaceFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($emailMessage); + + $template = $this->createMock(Template::class); + $template->expects($this->once())->method('setVars') + ->with($this->equalTo($vars))->will($this->returnSelf()); + $template->expects($this->once())->method('setOptions') + ->with($this->equalTo($options))->will($this->returnSelf()); + $template->expects($this->once())->method('getSubject') + ->willReturn('Email Subject'); + $template->expects($this->once())->method('setData') + ->with($this->equalTo($data))->will($this->returnSelf()); + $template->expects($this->once())->method('getProcessedTemplate') + ->with($vars)->willReturn($bodyText); + $template->expects($this->once())->method('setTemplateFilter') + ->with($filter); - $this->messageMock->expects($this->once())->method('setBodyHtml')->willReturnSelf(); - $this->messageMock->expects($this->once())->method('setSubject')->willReturnSelf(); + $this->templateFactoryMock->expects($this->once()) + ->method('get') + ->with($this->equalTo('identifier')) + ->willReturn($template); $this->builder->setTemplateIdentifier( 'identifier' diff --git a/app/etc/di.xml b/app/etc/di.xml index afe8f36b7de8..1a74fd9d7f84 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -1771,4 +1771,10 @@ + + + diff --git a/composer.json b/composer.json index 7235e75f2326..c4744962b50f 100644 --- a/composer.json +++ b/composer.json @@ -68,6 +68,7 @@ "zendframework/zend-json": "^2.6.1", "zendframework/zend-log": "^2.9.1", "zendframework/zend-mail": "^2.9.0", + "zendframework/zend-mime": "^2.5.0", "zendframework/zend-modulemanager": "^2.7", "zendframework/zend-mvc": "~2.7.0", "zendframework/zend-serializer": "^2.7.2", diff --git a/composer.lock b/composer.lock index 8c9f79cac256..591e51c62e83 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0cf49b3b5a47076608e87c7ddb566073", + "content-hash": "f6c85e01a374b22a185f11a0c51e08fb", "packages": [ { "name": "braintree/braintree_php", diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index b80f00b16f0b..566dfbadedd2 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -769,7 +769,7 @@ public function testConfirmationEmailWithSpecialCharacters(): void $message = $this->transportBuilderMock->getSentMessage(); $rawMessage = $message->getRawMessage(); - $this->assertContains('To: ' . $email, $rawMessage); + $this->assertContains('To: John Smith <' . $email . '>', $rawMessage); $content = $message->getBody()->getParts()[0]->getRawContent(); $confirmationUrl = $this->getConfirmationUrlFromMessageContent($content); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Mail/EmailMessageTest.php b/dev/tests/integration/testsuite/Magento/Framework/Mail/EmailMessageTest.php new file mode 100644 index 000000000000..10a54b4e1b87 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Mail/EmailMessageTest.php @@ -0,0 +1,268 @@ + [ + ['email' => 'to@adobe.com', 'name' => 'Addressee'] + ], + 'replyTo' => ['email' => 'replyTo@adobe.com', 'name' => 'Reply To Address'], + 'from' => 'from@adobe.com', + 'sender' => ['email' => 'sender@adobe.com', 'name' => 'Sender'], + 'cc' => [ + 'cc1@adobe.com' => 'CC 1 Address', + 'cc2@adobe.com' => 'CC 2 Address', + 'cc3@adobe.com' => 'CC 3 Address', + ], + 'bcc' => ['bcc1@adobe.com', 'bcc2@adobe.com'], + ]; + + /** + * @var string + */ + private $subject = 'Test subject'; + + /** + * @var string + */ + private $description = 'Test description'; + + /** + * + * @return void + */ + protected function setUp() + { + $this->di = Bootstrap::getObjectManager(); + $this->mimePartFactory = $this->di->get(MimePartInterfaceFactory::class); + $this->mimeMessageFactory = $this->di->get(MimeMessageInterfaceFactory::class); + $this->messageConverter = $this->di->get(AddressConverter::class); + $this->messageFactory = $this->di->get(EmailMessageInterfaceFactory::class); + } + + /** + * @return array + */ + public function getEmailMessageDataProvider(): array + { + return [ + [ + 'Content Test', + MimeInterface::TYPE_TEXT + ], [ + + '

Html message

', + MimeInterface::TYPE_HTML + ] + ]; + } + + /** + * Tests Email Message with Addresses + * + * @dataProvider getEmailMessageDataProvider + * @param $content + * @param $type + * @return void + * @throws MailException + */ + public function testEmailMessage($content, $type): void + { + $mimePart = $this->mimePartFactory->create( + [ + 'content' => $content, + 'description' => $this->description, + 'type' => $type + ] + ); + + $mimeMessage = $this->mimeMessageFactory->create( + [ + 'parts' => [$mimePart] + ] + ); + + $this->addressFactory = $this->di->get(AddressFactory::class); + /** @var Address $addressTo */ + $to = [ + $this->addressFactory->create( + [ + 'email' => $this->addressList['to'][0]['email'], + 'name' => $this->addressList['to'][0]['name'] + ] + ) + ]; + + $from = [$this->messageConverter->convert($this->addressList['from'])]; + $cc = $this->messageConverter->convertMany($this->addressList['cc']); + $replyTo = [ + $this->messageConverter->convert( + $this->addressList['replyTo']['email'], + $this->addressList['replyTo']['name'] + ) + ]; + $bcc = $this->messageConverter->convertMany($this->addressList['bcc']); + $sender = $this->messageConverter->convert( + $this->addressList['sender']['email'], + $this->addressList['sender']['name'] + ); + $data = [ + 'body' => $mimeMessage, + 'subject' => $this->subject, + 'from' => $from, + 'to' => $to, + 'cc' => $cc, + 'replyTo' => $replyTo, + 'bcc' => $bcc, + 'sender' => $sender + ]; + $message = $this->messageFactory->create($data); + + $this->assertContains($content, $message->toString()); + $this->assertContains('Content-Type: ' . $type, $message->toString()); + $senderString = 'Sender: ' . $sender->getName() . ' <' . $sender->getEmail() . '>'; + $this->assertContains($senderString, $message->toString()); + $this->assertContains('From: ' . $from[0]->getEmail(), $message->toString()); + $replyToString = 'Reply-To: ' . $replyTo[0]->getName() . ' <' . $replyTo[0]->getEmail() . '>'; + $this->assertContains($replyToString, $message->toString()); + $toString = 'To: ' . $to[0]->getName() . ' <' . $to[0]->getEmail() . '>'; + $this->assertContains($toString, $message->toString()); + $ccString = 'Cc: ' . $cc[0]->getName() . ' <' . $cc[0]->getEmail() . '>'; + $this->assertContains($ccString, $message->toString()); + $this->assertContains('Bcc: ' . $bcc[0]->getEmail(), $message->toString()); + $this->assertContains('Content-Description: ' . $this->description, $message->toString()); + $this->assertContains('Subject: ' . $this->subject, $message->toString()); + $this->assertContains($content, $message->toString()); + //tests address factory + $this->assertInstanceOf(Address::class, $message->getTo()[0]); + //tests address converter convert method + $this->assertInstanceOf(Address::class, $message->getFrom()[0]); + //tests address converter convertMany method + $this->assertInstanceOf(Address::class, $message->getCc()[0]); + } + + /** + * Test Email Message with Xml Attachment + * + * @return void + */ + public function testEmailMessageWithAttachment(): void + { + $mimePartMain = $this->mimePartFactory->create( + [ + 'content' => 'Test', + 'description' => $this->description, + 'type' => MimeInterface::TYPE_TEXT + ] + ); + $mimePartAttachment = $this->mimePartFactory->create( + [ + 'content' => $this->getXmlContent(), + 'disposition' => MimeInterface::DISPOSITION_ATTACHMENT, + 'fileName' => self::ATTACHMENT_FILE_NAME, + 'encoding' => MimeInterface::ENCODING_8BIT, + 'type' => self::XML_TYPE + ] + ); + + $mimeMessage = $this->mimeMessageFactory->create( + [ + 'parts' => [$mimePartMain, $mimePartAttachment] + ] + ); + + $this->addressFactory = $this->di->get(AddressFactory::class); + /** @var Address $addressTo */ + $addressTo = $this->addressFactory + ->create( + [ + 'email' => $this->addressList['to'][0]['email'], + 'name' => $this->addressList['to'][0]['name'] + ] + ); + + $data = [ + 'body' => $mimeMessage, + 'subject' => $this->subject, + 'to' => [$addressTo], + ]; + $message = $this->messageFactory->create($data); + + $this->assertContains($this->getXmlContent(), $message->toString()); + $this->assertContains('Content-Type: ' . self::XML_TYPE, $message->toString()); + $contentDisposition = 'Content-Disposition: ' . MimeInterface::DISPOSITION_ATTACHMENT + . '; filename="' . self::ATTACHMENT_FILE_NAME . '"'; + $this->assertContains($contentDisposition, $message->toString()); + } + + /** + * Provides xml content + * + * @return string + */ + private function getXmlContent(): string + { + return ' + + + + + Furman\Test\Command\Testbed + + + + +'; + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt index e59cd0983da1..4653203f4d57 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt +++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt @@ -216,3 +216,5 @@ Magento/Elasticsearch6/Model/Client Magento/Config/App/Config/Type Magento/InventoryReservationCli/Test/Integration Magento/InventoryAdminUi/Controller/Adminhtml +Magento/Newsletter/Model/Queue/TransportBuilder +Magento/Framework/Mail/Template/TransportBuilder \ No newline at end of file diff --git a/lib/internal/Magento/Framework/Mail/Address.php b/lib/internal/Magento/Framework/Mail/Address.php new file mode 100644 index 000000000000..18e1a8c72f21 --- /dev/null +++ b/lib/internal/Magento/Framework/Mail/Address.php @@ -0,0 +1,58 @@ +email = $email; + $this->name = $name; + } + + /** + * Name getter + * + * @return string|null + */ + public function getName(): ?string + { + return $this->name; + } + + /** + * Email getter + * + * @return string + */ + public function getEmail(): string + { + return $this->email; + } +} diff --git a/lib/internal/Magento/Framework/Mail/AddressConverter.php b/lib/internal/Magento/Framework/Mail/AddressConverter.php new file mode 100644 index 000000000000..d787482f1757 --- /dev/null +++ b/lib/internal/Magento/Framework/Mail/AddressConverter.php @@ -0,0 +1,82 @@ +addressFactory = $addressFactory; + } + + /** + * Creates MailAddress from string values + * + * @param string $email + * @param string|null $name + * + * @return Address + */ + public function convert(string $email, ?string $name = null): Address + { + return $this->addressFactory->create( + [ + 'name' => $name, + 'email' => $email + ] + ); + } + + /** + * Converts array to list of MailAddresses + * + * @param array $addresses + * + * @return Address[] + * @throws InvalidArgumentException + */ + public function convertMany(array $addresses): array + { + $addressList = []; + foreach ($addresses as $key => $value) { + + if (is_int($key) || is_numeric($key)) { + $addressList[] = $this->convert($value); + continue; + } + + if (!is_string($key)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid key type in provided addresses array ("%s")', + (is_object($key) ? get_class($key) : var_export($key, 1)) + ) + ); + } + $addressList[] = $this->convert($key, $value); + } + + return $addressList; + } +} diff --git a/lib/internal/Magento/Framework/Mail/EmailMessage.php b/lib/internal/Magento/Framework/Mail/EmailMessage.php new file mode 100644 index 000000000000..aaef97507518 --- /dev/null +++ b/lib/internal/Magento/Framework/Mail/EmailMessage.php @@ -0,0 +1,257 @@ +message = new ZendMessage(); + $mimeMessage = new ZendMimeMessage(); + $mimeMessage->setParts($body->getParts()); + $this->message->setBody($mimeMessage); + if ($encoding) { + $this->message->setEncoding($encoding); + } + if ($subject) { + $this->message->setSubject($subject); + } + if ($sender) { + $this->message->setSender($sender->getEmail(), $sender->getName()); + } + if (count($to) < 1) { + throw new InvalidArgumentException('Email message must have at list one addressee'); + } + if ($to) { + $this->message->setTo($this->convertAddressArrayToAddressList($to)); + } + if ($replyTo) { + $this->message->setReplyTo($this->convertAddressArrayToAddressList($replyTo)); + } + if ($from) { + $this->message->setFrom($this->convertAddressArrayToAddressList($from)); + } + if ($cc) { + $this->message->setCc($this->convertAddressArrayToAddressList($cc)); + } + if ($bcc) { + $this->message->setBcc($this->convertAddressArrayToAddressList($bcc)); + } + $this->mimeMessageFactory = $mimeMessageFactory; + $this->addressFactory = $addressFactory; + } + + /** + * @inheritDoc + */ + public function getEncoding(): string + { + return $this->message->getEncoding(); + } + + /** + * @inheritDoc + */ + public function getHeaders(): array + { + return $this->message->getHeaders()->toArray(); + } + + /** + * @inheritDoc + */ + public function getFrom(): ?array + { + return $this->convertAddressListToAddressArray($this->message->getFrom()); + } + + /** + * @inheritDoc + */ + public function getTo(): array + { + return $this->convertAddressListToAddressArray($this->message->getTo()); + } + + /** + * @inheritDoc + */ + public function getCc(): ?array + { + return $this->convertAddressListToAddressArray($this->message->getCc()); + } + + /** + * @inheritDoc + */ + public function getBcc(): ?array + { + return $this->convertAddressListToAddressArray($this->message->getBcc()); + } + + /** + * @inheritDoc + */ + public function getReplyTo(): ?array + { + return $this->convertAddressListToAddressArray($this->message->getReplyTo()); + } + + /** + * @inheritDoc + */ + public function getSender(): ?Address + { + /** @var ZendAddress $zendSender */ + if (!$zendSender = $this->message->getSender()) { + return null; + } + + return $this->addressFactory->create( + [ + 'email' => $zendSender->getEmail(), + 'name' => $zendSender->getName() + ] + ); + } + + /** + * @inheritDoc + */ + public function getSubject(): ?string + { + return $this->message->getSubject(); + } + + /** + * @inheritDoc + */ + public function getBody(): MimeMessageInterface + { + return $this->mimeMessageFactory->create( + ['parts' => $this->message->getBody()->getParts()] + ); + } + + /** + * @inheritDoc + */ + public function getBodyText(): string + { + return $this->message->getBodyText(); + } + + /** + * @inheritdoc + */ + public function getRawMessage(): string + { + return $this->toString(); + } + + /** + * @inheritDoc + */ + public function toString(): string + { + return $this->message->toString(); + } + + /** + * Converts AddressList to array + * + * @param AddressList $addressList + * @return Address[] + */ + private function convertAddressListToAddressArray(AddressList $addressList): array + { + $arrayList = []; + foreach ($addressList as $address) { + $arrayList[] = + $this->addressFactory->create( + [ + 'email' => $address->getEmail(), + 'name' => $address->getName() + ] + ); + } + + return $arrayList; + } + + /** + * Converts MailAddress array to AddressList + * + * @param Address[] $arrayList + * @return AddressList + */ + private function convertAddressArrayToAddressList(array $arrayList): AddressList + { + $zendAddressList = new AddressList(); + foreach ($arrayList as $address) { + $zendAddressList->add($address->getEmail(), $address->getName()); + } + + return $zendAddressList; + } +} diff --git a/lib/internal/Magento/Framework/Mail/EmailMessageInterface.php b/lib/internal/Magento/Framework/Mail/EmailMessageInterface.php new file mode 100644 index 000000000000..95f83ff679cd --- /dev/null +++ b/lib/internal/Magento/Framework/Mail/EmailMessageInterface.php @@ -0,0 +1,97 @@ +[\x21\x23-\x26\x2a\x2b\x2d\x5e\5f\60\x7b-\x7ea-zA-Z0-9]+)\?(?P[\x21\x23-\x26\x2a\x2b\x2d\x5e\5f\60\x7b-\x7ea-zA-Z0-9]+)\?(?P[\x21-\x3e\x40-\x7e]+)#'; + // @codingStandardsIgnoreEnd +} diff --git a/lib/internal/Magento/Framework/Mail/MimeMessage.php b/lib/internal/Magento/Framework/Mail/MimeMessage.php new file mode 100644 index 000000000000..4d783dafd1d7 --- /dev/null +++ b/lib/internal/Magento/Framework/Mail/MimeMessage.php @@ -0,0 +1,80 @@ +mimeMessage = new ZendMimeMessage(); + $this->mimeMessage->setParts($parts); + } + + /** + * @inheritDoc + */ + public function getParts(): array + { + return $this->mimeMessage->getParts(); + } + + /** + * @inheritDoc + */ + public function isMultiPart(): bool + { + return $this->mimeMessage->isMultiPart(); + } + + /** + * @inheritDoc + */ + public function getMessage(string $endOfLine = MimeInterface::LINE_END): string + { + return $this->mimeMessage->generateMessage($endOfLine); + } + + /** + * @inheritDoc + */ + public function getPartHeadersAsArray(int $partNum): array + { + return $this->mimeMessage->getPartHeadersArray($partNum); + } + + /** + * @inheritDoc + */ + public function getPartHeaders(int $partNum, string $endOfLine = MimeInterface::LINE_END): string + { + return $this->mimeMessage->getPartHeaders($partNum, $endOfLine); + } + + /** + * @inheritDoc + */ + public function getPartContent(int $partNum, string $endOfLine = MimeInterface::LINE_END): string + { + return $this->mimeMessage->getPartContent($partNum, $endOfLine); + } +} diff --git a/lib/internal/Magento/Framework/Mail/MimeMessageInterface.php b/lib/internal/Magento/Framework/Mail/MimeMessageInterface.php new file mode 100644 index 000000000000..a272bcfd8980 --- /dev/null +++ b/lib/internal/Magento/Framework/Mail/MimeMessageInterface.php @@ -0,0 +1,65 @@ +mimePart = new ZendMimePart($content); + } catch (\Exception $e) { + throw new InvalidArgumentException($e->getMessage()); + } + $this->mimePart->setType($type); + $this->mimePart->setEncoding($encoding); + $this->mimePart->setFilters($filters); + if ($charset) { + $this->mimePart->setBoundary($boundary); + } + if ($charset) { + $this->mimePart->setCharset($charset); + } + if ($disposition) { + $this->mimePart->setDisposition($disposition); + } + if ($description) { + $this->mimePart->setDescription($description); + } + if ($fileName) { + $this->mimePart->setFileName($fileName); + } + if ($location) { + $this->mimePart->setLocation($location); + } + if ($language) { + $this->mimePart->setLanguage($language); + } + if ($isStream) { + $this->mimePart->setIsStream($isStream); + } + } + + /** + * @inheritDoc + */ + public function getType(): string + { + return $this->mimePart->getType(); + } + + /** + * @inheritDoc + */ + public function getEncoding(): string + { + return $this->mimePart->getEncoding(); + } + + /** + * @inheritDoc + */ + public function getDisposition(): string + { + return $this->mimePart->getDisposition(); + } + + /** + * @inheritDoc + */ + public function getDescription(): string + { + return $this->mimePart->getDescription(); + } + + /** + * @inheritDoc + */ + public function getFileName(): string + { + return $this->mimePart->getFileName(); + } + + /** + * @inheritDoc + */ + public function getCharset(): string + { + return $this->mimePart->getCharset(); + } + + /** + * @inheritDoc + */ + public function getBoundary(): string + { + return $this->mimePart->getBoundary(); + } + + /** + * @inheritDoc + */ + public function getLocation(): string + { + return $this->mimePart->getLocation(); + } + + /** + * @inheritDoc + */ + public function getLanguage(): string + { + return $this->mimePart->getLanguage(); + } + + /** + * @inheritDoc + */ + public function getFilters(): array + { + return $this->mimePart->getFilters(); + } + + /** + * @inheritDoc + */ + public function isStream(): bool + { + return $this->mimePart->isStream(); + } + + /** + * @inheritDoc + */ + public function getEncodedStream($endOfLine = MimeInterface::LINE_END) + { + return $this->mimePart->getEncodedStream($endOfLine); + } + + /** + * @inheritDoc + */ + public function getContent($endOfLine = MimeInterface::LINE_END) + { + return $this->mimePart->getContent($endOfLine); + } + + /** + * @inheritDoc + */ + public function getRawContent(): string + { + return $this->mimePart->getRawContent(); + } + + /** + * @inheritDoc + */ + public function getHeadersArray($endOfLine = MimeInterface::LINE_END): array + { + return $this->mimePart->getHeadersArray($endOfLine); + } + + /** + * @inheritDoc + */ + public function getHeaders($endOfLine = MimeInterface::LINE_END): string + { + return $this->mimePart->getHeaders($endOfLine); + } +} diff --git a/lib/internal/Magento/Framework/Mail/MimePartInterface.php b/lib/internal/Magento/Framework/Mail/MimePartInterface.php new file mode 100644 index 000000000000..8a658cdf975c --- /dev/null +++ b/lib/internal/Magento/Framework/Mail/MimePartInterface.php @@ -0,0 +1,133 @@ +templateFactory = $templateFactory; $this->objectManager = $objectManager; $this->_senderResolver = $senderResolver; $this->mailTransportFactory = $mailTransportFactory; - $this->messageFactory = $messageFactory ?: $this->objectManager->get(MessageInterfaceFactory::class); - $this->message = $this->messageFactory->create(); + $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); } /** @@ -128,11 +177,13 @@ public function __construct( * * @param array|string $address * @param string $name + * * @return $this */ public function addCc($address, $name = '') { - $this->message->addCc($address, $name); + $this->addAddressByType('cc', $address, $name); + return $this; } @@ -141,11 +192,14 @@ public function addCc($address, $name = '') * * @param array|string $address * @param string $name + * * @return $this + * @throws InvalidArgumentException */ public function addTo($address, $name = '') { - $this->message->addTo($address, $name); + $this->addAddressByType('to', $address, $name); + return $this; } @@ -153,11 +207,14 @@ public function addTo($address, $name = '') * Add bcc address * * @param array|string $address + * * @return $this + * @throws InvalidArgumentException */ public function addBcc($address) { - $this->message->addBcc($address); + $this->addAddressByType('bcc', $address); + return $this; } @@ -166,28 +223,32 @@ public function addBcc($address) * * @param string $email * @param string|null $name + * * @return $this + * @throws InvalidArgumentException */ public function setReplyTo($email, $name = null) { - $this->message->setReplyTo($email, $name); + $this->addAddressByType('replyTo', $email, $name); + return $this; } /** * Set mail from address * - * @deprecated This function sets the from address but does not provide - * a way of setting the correct from addresses based on the scope. - * @see setFromByScope() - * * @param string|array $from + * * @return $this - * @throws \Magento\Framework\Exception\MailException + * @throws InvalidArgumentException + * @see setFromByScope() + * + * @deprecated 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, null); + return $this->setFromByScope($from); } /** @@ -195,13 +256,16 @@ public function setFrom($from) * * @param string|array $from * @param string|int $scopeId + * * @return $this - * @throws \Magento\Framework\Exception\MailException + * @throws InvalidArgumentException + * @throws MailException */ public function setFromByScope($from, $scopeId = null) { $result = $this->_senderResolver->resolve($from, $scopeId); - $this->message->setFromAddress($result['email'], $result['name']); + $this->addAddressByType('from', $result['email'], $result['name']); + return $this; } @@ -209,11 +273,13 @@ public function setFromByScope($from, $scopeId = null) * Set template identifier * * @param string $templateIdentifier + * * @return $this */ public function setTemplateIdentifier($templateIdentifier) { $this->templateIdentifier = $templateIdentifier; + return $this; } @@ -221,6 +287,7 @@ public function setTemplateIdentifier($templateIdentifier) * Set template model * * @param string $templateModel + * * @return $this */ public function setTemplateModel($templateModel) @@ -233,11 +300,13 @@ public function setTemplateModel($templateModel) * Set template vars * * @param array $templateVars + * * @return $this */ public function setTemplateVars($templateVars) { $this->templateVars = $templateVars; + return $this; } @@ -250,13 +319,14 @@ public function setTemplateVars($templateVars) public function setTemplateOptions($templateOptions) { $this->templateOptions = $templateOptions; + return $this; } /** * Get mail transport * - * @return \Magento\Framework\Mail\TransportInterface + * @return TransportInterface * @throws LocalizedException */ public function getTransport() @@ -278,7 +348,7 @@ public function getTransport() */ protected function reset() { - $this->message = $this->messageFactory->create(); + $this->messageData = []; $this->templateIdentifier = null; $this->templateVars = null; $this->templateOptions = null; @@ -288,7 +358,7 @@ protected function reset() /** * Get template * - * @return \Magento\Framework\Mail\TemplateInterface + * @return TemplateInterface */ protected function getTemplate() { @@ -306,14 +376,14 @@ protected function getTemplate() protected function prepareMessage() { $template = $this->getTemplate(); - $body = $template->processTemplate(); + $content = $template->processTemplate(); switch ($template->getType()) { case TemplateTypesInterface::TYPE_TEXT: - $this->message->setBodyText($body); + $part['type'] = MimeInterface::TYPE_TEXT; break; case TemplateTypesInterface::TYPE_HTML: - $this->message->setBodyHtml($body); + $part['type'] = MimeInterface::TYPE_HTML; break; default: @@ -321,7 +391,42 @@ protected function prepareMessage() new Phrase('Unknown template type') ); } - $this->message->setSubject(html_entity_decode($template->getSubject(), ENT_QUOTES)); + $mimePart = $this->mimePartInterfaceFactory->create(['content' => $content]); + $this->messageData['body'] = $this->mimeMessageInterfaceFactory->create( + ['parts' => [$mimePart]] + ); + + $this->messageData['subject'] = html_entity_decode( + (string)$template->getSubject(), + ENT_QUOTES + ); + $this->message = $this->emailMessageInterfaceFactory->create($this->messageData); + return $this; } + + /** + * 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 + ); + } + } } diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php index c9781281d353..74f40e76353f 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php @@ -3,50 +3,67 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Framework\Mail\Test\Unit\Template; use Magento\Framework\App\TemplateTypesInterface; -use Magento\Framework\Mail\MessageInterface; +use Magento\Framework\Mail\EmailMessageInterface; +use Magento\Framework\Mail\EmailMessageInterfaceFactory; +use Magento\Framework\Mail\Message; use Magento\Framework\Mail\MessageInterfaceFactory; +use Magento\Framework\Mail\MimePartInterface; +use Magento\Framework\Mail\MimePartInterfaceFactory; +use Magento\Framework\Mail\Template\FactoryInterface; +use Magento\Framework\Mail\Template\SenderResolverInterface; +use Magento\Framework\Mail\Template\TransportBuilder; +use Magento\Framework\Mail\TemplateInterface; +use Magento\Framework\Mail\TransportInterface; +use Magento\Framework\Mail\TransportInterfaceFactory; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** + * Class TransportBuilderTest + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class TransportBuilderTest extends \PHPUnit\Framework\TestCase +class TransportBuilderTest extends TestCase { /** * @var string */ - protected $builderClassName = \Magento\Framework\Mail\Template\TransportBuilder::class; + protected $builderClassName = TransportBuilder::class; /** - * @var \Magento\Framework\Mail\Template\TransportBuilder + * @var TransportBuilder */ protected $builder; /** - * @var \Magento\Framework\Mail\Template\FactoryInterface | \PHPUnit_Framework_MockObject_MockObject + * @var FactoryInterface | \PHPUnit_Framework_MockObject_MockObject */ protected $templateFactoryMock; /** - * @var \Magento\Framework\Mail\Message | \PHPUnit_Framework_MockObject_MockObject + * @var Message | \PHPUnit_Framework_MockObject_MockObject */ protected $messageMock; /** - * @var \Magento\Framework\ObjectManagerInterface | \PHPUnit_Framework_MockObject_MockObject + * @var ObjectManagerInterface | \PHPUnit_Framework_MockObject_MockObject */ protected $objectManagerMock; /** - * @var \Magento\Framework\Mail\Template\SenderResolverInterface | \PHPUnit_Framework_MockObject_MockObject + * @var SenderResolverInterface | \PHPUnit_Framework_MockObject_MockObject */ protected $senderResolverMock; /** - * @var \Magento\Framework\Mail\MessageInterfaceFactory| \PHPUnit_Framework_MockObject_MockObject + * @var MessageInterfaceFactory| \PHPUnit_Framework_MockObject_MockObject */ private $messageFactoryMock; @@ -55,26 +72,39 @@ class TransportBuilderTest extends \PHPUnit\Framework\TestCase */ protected $mailTransportFactoryMock; + /** + * @var MimePartInterfaceFactory|MockObject + */ + private $mimePartFactoryMock; + + /** + * @var EmailMessageInterfaceFactory|MockObject + */ + private $emailMessageInterfaceFactoryMock; + /** * @return void */ protected function setUp() { - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->templateFactoryMock = $this->createMock(\Magento\Framework\Mail\Template\FactoryInterface::class); - $this->messageMock = $this->createMock(\Magento\Framework\Mail\Message::class); - $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - $this->senderResolverMock = $this->createMock(\Magento\Framework\Mail\Template\SenderResolverInterface::class); + $objectManagerHelper = new ObjectManager($this); + $this->templateFactoryMock = $this->createMock(FactoryInterface::class); + $this->messageMock = $this->createMock(Message::class); + $this->objectManagerMock = $this->createMock(ObjectManagerInterface::class); + $this->senderResolverMock = $this->createMock(SenderResolverInterface::class); $this->mailTransportFactoryMock = $this->getMockBuilder( - \Magento\Framework\Mail\TransportInterfaceFactory::class + TransportInterfaceFactory::class )->disableOriginalConstructor() ->setMethods(['create']) ->getMockForAbstractClass(); - $this->messageFactoryMock = $this->getMockBuilder(\Magento\Framework\Mail\MessageInterfaceFactory::class) + $this->messageFactoryMock = $this->getMockBuilder(MessageInterfaceFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMockForAbstractClass(); - $this->messageFactoryMock->expects($this->atLeastOnce())->method('create')->willReturn($this->messageMock); + + $this->emailMessageInterfaceFactoryMock = $this->createMock(EmailMessageInterfaceFactory::class); + $this->mimePartFactoryMock = $this->createMock(MimePartInterfaceFactory::class); + $this->builder = $objectManagerHelper->getObject( $this->builderClassName, [ @@ -83,7 +113,9 @@ protected function setUp() 'objectManager' => $this->objectManagerMock, 'senderResolver' => $this->senderResolverMock, 'mailTransportFactory' => $this->mailTransportFactoryMock, - 'messageFactory' => $this->messageFactoryMock + 'messageFactory' => $this->messageFactoryMock, + 'emailMessageInterfaceFactory' => $this->emailMessageInterfaceFactoryMock, + 'mimePartInterfaceFactory' => $this->mimePartFactoryMock, ] ); } @@ -91,19 +123,32 @@ protected function setUp() /** * @dataProvider getTransportDataProvider * @param int $templateType - * @param string $messageType * @param string $bodyText * @param string $templateNamespace * @return void */ - public function testGetTransport($templateType, $messageType, $bodyText, $templateNamespace) + public function testGetTransport($templateType, $bodyText, $templateNamespace) { $this->builder->setTemplateModel($templateNamespace); $vars = ['reason' => 'Reason', 'customer' => 'Customer']; $options = ['area' => 'frontend', 'store' => 1]; - $template = $this->createMock(\Magento\Framework\Mail\TemplateInterface::class); + /** @var MimePartInterface|MockObject $mimePartMock */ + $mimePartMock = $this->createMock(MimePartInterface::class); + + $this->mimePartFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($mimePartMock); + + /** @var EmailMessageInterface|MockObject $emailMessage */ + $emailMessage = $this->createMock(EmailMessageInterface::class); + + $this->emailMessageInterfaceFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($emailMessage); + + $template = $this->createMock(TemplateInterface::class); $template->expects($this->once())->method('setVars')->with($this->equalTo($vars))->willReturnSelf(); $template->expects($this->once())->method('setOptions')->with($this->equalTo($options))->willReturnSelf(); $template->expects($this->once())->method('getSubject')->willReturn('Email Subject'); @@ -115,32 +160,16 @@ public function testGetTransport($templateType, $messageType, $bodyText, $templa ->with($this->equalTo('identifier'), $this->equalTo($templateNamespace)) ->willReturn($template); - $this->messageMock->expects($this->once()) - ->method('setSubject') - ->with($this->equalTo('Email Subject')) - ->willReturnSelf(); - - $this->messageMock->expects($this->exactly((int)($messageType == MessageInterface::TYPE_TEXT))) - ->method('setBodyText') - ->with($this->equalTo($bodyText)) - ->willReturnSelf(); - - $this->messageMock->expects($this->exactly((int)($messageType == MessageInterface::TYPE_HTML))) - ->method('setBodyHtml') - ->with($this->equalTo($bodyText)) - ->willReturnSelf(); - - $transport = $this->createMock(\Magento\Framework\Mail\TransportInterface::class); + $transport = $this->createMock(TransportInterface::class); $this->mailTransportFactoryMock->expects($this->at(0)) ->method('create') - ->with($this->equalTo(['message' => $this->messageMock])) ->willReturn($transport); - $this->messageFactoryMock->expects($this->once())->method('create')->willReturn($transport); - $this->builder->setTemplateIdentifier('identifier')->setTemplateVars($vars)->setTemplateOptions($options); - $this->assertInstanceOf(\Magento\Framework\Mail\TransportInterface::class, $this->builder->getTransport()); + + $result = $this->builder->getTransport(); + $this->assertInstanceOf(TransportInterface::class, $result); } /** @@ -156,11 +185,10 @@ public function testGetTransportWithException() $vars = ['reason' => 'Reason', 'customer' => 'Customer']; $options = ['area' => 'frontend', 'store' => 1]; - $template = $this->createMock(\Magento\Framework\Mail\TemplateInterface::class); + $template = $this->createMock(TemplateInterface::class); $template->expects($this->once())->method('setVars')->with($this->equalTo($vars))->willReturnSelf(); $template->expects($this->once())->method('setOptions')->with($this->equalTo($options))->willReturnSelf(); $template->expects($this->once())->method('getType')->willReturn('Unknown'); - $this->messageFactoryMock->expects($this->once())->method('create'); $this->templateFactoryMock->expects($this->once()) ->method('get') ->with($this->equalTo('identifier'), $this->equalTo('Test\Namespace\Template')) @@ -168,7 +196,7 @@ public function testGetTransportWithException() $this->builder->setTemplateIdentifier('identifier')->setTemplateVars($vars)->setTemplateOptions($options); - $this->assertInstanceOf(\Magento\Framework\Mail\TransportInterface::class, $this->builder->getTransport()); + $this->assertInstanceOf(TransportInterface::class, $this->builder->getTransport()); } /** @@ -179,13 +207,11 @@ public function getTransportDataProvider() return [ [ TemplateTypesInterface::TYPE_TEXT, - MessageInterface::TYPE_TEXT, 'Plain text', null ], [ TemplateTypesInterface::TYPE_HTML, - MessageInterface::TYPE_HTML, '

Html message

', 'Test\Namespace\Template' ] @@ -203,60 +229,7 @@ public function testSetFromByScope() ->method('resolve') ->with($sender, $scopeId) ->willReturn($sender); - $this->messageMock->expects($this->once()) - ->method('setFromAddress') - ->with($sender['email'], $sender['name']) - ->willReturnSelf(); $this->builder->setFromByScope($sender, $scopeId); } - - /** - * @return void - */ - public function testSetCc() - { - $this->messageMock->expects($this->once())->method('addCc')->with('cc@example.com')->willReturnSelf(); - - $this->builder->addCc('cc@example.com'); - } - - /** - * @return void - */ - public function testAddTo() - { - $this->messageMock->expects($this->once()) - ->method('addTo') - ->with('to@example.com', 'recipient') - ->willReturnSelf(); - - $this->builder->addTo('to@example.com', 'recipient'); - } - - /** - * @return void - */ - public function testAddBcc() - { - $this->messageMock->expects($this->once()) - ->method('addBcc') - ->with('bcc@example.com') - ->willReturnSelf(); - - $this->builder->addBcc('bcc@example.com'); - } - - /** - * @return void - */ - public function testSetReplyTo() - { - $this->messageMock->expects($this->once()) - ->method('setReplyTo') - ->with('replyTo@example.com', 'replyName') - ->willReturnSelf(); - - $this->builder->setReplyTo('replyTo@example.com', 'replyName'); - } } diff --git a/lib/internal/Magento/Framework/Mail/TransportInterfaceFactory.php b/lib/internal/Magento/Framework/Mail/TransportInterfaceFactory.php index db86275954cb..5448df4e16ca 100644 --- a/lib/internal/Magento/Framework/Mail/TransportInterfaceFactory.php +++ b/lib/internal/Magento/Framework/Mail/TransportInterfaceFactory.php @@ -3,9 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Framework\Mail; +use Magento\Framework\ObjectManagerInterface; + /** * Factory class for \Magento\Framework\Mail\TransportInterface */ @@ -14,39 +17,39 @@ class TransportInterfaceFactory /** * Object Manager instance * - * @var \Magento\Framework\ObjectManagerInterface + * @var ObjectManagerInterface */ - protected $_objectManager = null; + private $objectManager; /** * Instance name to create * * @var string */ - protected $_instanceName = null; + private $instanceName; /** * Factory constructor * - * @param \Magento\Framework\ObjectManagerInterface $objectManager + * @param ObjectManagerInterface $objectManager * @param string $instanceName */ public function __construct( - \Magento\Framework\ObjectManagerInterface $objectManager, - $instanceName = \Magento\Framework\Mail\TransportInterface::class + ObjectManagerInterface $objectManager, + $instanceName = TransportInterface::class ) { - $this->_objectManager = $objectManager; - $this->_instanceName = $instanceName; + $this->objectManager = $objectManager; + $this->instanceName = $instanceName; } /** * Create class instance with specified parameters * * @param array $data - * @return \Magento\Framework\Mail\TransportInterface + * @return TransportInterface */ - public function create(array $data = []) + public function create(array $data = []): TransportInterface { - return $this->_objectManager->create($this->_instanceName, $data); + return $this->objectManager->create($this->instanceName, $data); } } diff --git a/lib/internal/Magento/Framework/composer.json b/lib/internal/Magento/Framework/composer.json index ed7e9f1cd1a1..c42323a2ecc0 100644 --- a/lib/internal/Magento/Framework/composer.json +++ b/lib/internal/Magento/Framework/composer.json @@ -37,6 +37,8 @@ "zendframework/zend-stdlib": "^3.2.1", "zendframework/zend-uri": "^2.5.1", "zendframework/zend-validator": "^2.6.0", + "zendframework/zend-mail": "^2.9.0", + "zendframework/zend-mime": "^2.5.0", "guzzlehttp/guzzle": "^6.3.3" }, "archive": {