From 24b5dddd3d34ffc05c784a3b6bc9ec1a4cc41a81 Mon Sep 17 00:00:00 2001 From: Jos Kolenberg Date: Mon, 2 Oct 2023 13:42:56 +0200 Subject: [PATCH] Added handling embedded/inline images --- src/MicrosoftGraphTransport.php | 25 +++-- tests/MicrosoftGraphTransportTest.php | 100 ++++++++++++++++++ tests/Resources/files/blue.jpg | Bin 0 -> 640 bytes .../html-mail-with-inline-image.blade.php | 1 + tests/Stubs/TestMailWithInlineImage.php | 62 +++++++++++ 5 files changed, 182 insertions(+), 6 deletions(-) create mode 100644 tests/Resources/files/blue.jpg create mode 100644 tests/Resources/views/html-mail-with-inline-image.blade.php create mode 100644 tests/Stubs/TestMailWithInlineImage.php diff --git a/src/MicrosoftGraphTransport.php b/src/MicrosoftGraphTransport.php index 56cd96a..1e82dda 100644 --- a/src/MicrosoftGraphTransport.php +++ b/src/MicrosoftGraphTransport.php @@ -34,19 +34,23 @@ protected function doSend(SentMessage $message): void $email = MessageConverter::toEmail($message->getOriginalMessage()); $envelope = $message->getEnvelope(); + $html = $email->getHtmlBody(); + + [$attachments, $html] = $this->prepareAttachments($email, $html); + $payload = [ 'message' => [ 'subject' => $email->getSubject(), 'body' => [ - 'contentType' => $email->getHtmlBody() === null ? 'Text' : 'HTML', - 'content' => $email->getHtmlBody() ?: $email->getTextBody(), + 'contentType' => $html === null ? 'Text' : 'HTML', + 'content' => $html ?: $email->getTextBody(), ], 'toRecipients' => $this->transformEmailAddresses($this->getRecipients($email, $envelope)), 'ccRecipients' => $this->transformEmailAddresses(collect($email->getCc())), 'bccRecipients' => $this->transformEmailAddresses(collect($email->getBcc())), 'replyTo' => $this->transformEmailAddresses(collect($email->getReplyTo())), 'sender' => $this->transformEmailAddress($envelope->getSender()), - 'attachments' => $this->getAttachments($email), + 'attachments' => $attachments, ], 'saveToSentItems' => config('mail.mailers.microsoft-graph.save_to_sent_items', false), ]; @@ -89,19 +93,28 @@ protected function getRecipients(Email $email, Envelope $envelope): Collection ->filter(fn (Address $address) => !in_array($address, array_merge($email->getCc(), $email->getBcc()), true)); } - protected function getAttachments(Email $email): array + /** + * @param Email $email + * @param string|null $html + * @return array + */ + protected function prepareAttachments(Email $email, ?string $html): array { $attachments = []; foreach ($email->getAttachments() as $attachment) { - $fileName = $attachment->getPreparedHeaders()->getHeaderParameter('Content-Disposition', 'filename'); + $headers = $attachment->getPreparedHeaders(); + $fileName = $headers->getHeaderParameter('Content-Disposition', 'filename'); + $attachments[] = [ '@odata.type' => '#microsoft.graph.fileAttachment', 'name' => $fileName, 'contentType' => $attachment->getMediaType(), 'contentBytes' => base64_encode($attachment->getBody()), + 'contentId' => $fileName, + 'isInline' => $headers->getHeaderBody('Content-Disposition') === 'inline', ]; } - return $attachments; + return [$attachments, $html]; } } diff --git a/tests/MicrosoftGraphTransportTest.php b/tests/MicrosoftGraphTransportTest.php index b9f4863..db7f6af 100644 --- a/tests/MicrosoftGraphTransportTest.php +++ b/tests/MicrosoftGraphTransportTest.php @@ -8,6 +8,7 @@ use Illuminate\Support\Str; use InnoGE\LaravelMsGraphMail\Exceptions\ConfigurationMissing; use InnoGE\LaravelMsGraphMail\Tests\Stubs\TestMail; +use InnoGE\LaravelMsGraphMail\Tests\Stubs\TestMailWithInlineImage; it('sends html mails with microsoft graph', function () { Config::set('mail.mailers.microsoft-graph', [ @@ -75,12 +76,16 @@ 'name' => 'test-file-1.txt', 'contentType' => 'text', 'contentBytes' => 'Zm9vCg==', + 'contentId' => 'test-file-1.txt', + 'isInline' => false, ], [ '@odata.type' => '#microsoft.graph.fileAttachment', 'name' => 'test-file-2.txt', 'contentType' => 'text', 'contentBytes' => 'Zm9vCg==', + 'contentId' => 'test-file-2.txt', + 'isInline' => false, ], ], ], @@ -157,12 +162,16 @@ 'name' => 'test-file-1.txt', 'contentType' => 'text', 'contentBytes' => 'Zm9vCg==', + 'contentId' => 'test-file-1.txt', + 'isInline' => false, ], [ '@odata.type' => '#microsoft.graph.fileAttachment', 'name' => 'test-file-2.txt', 'contentType' => 'text', 'contentBytes' => 'Zm9vCg==', + 'contentId' => 'test-file-2.txt', + 'isInline' => false, ], ], ], @@ -272,3 +281,94 @@ 'The mail from address is missing from the configuration file.', ], ]); + +it('sends html mails with inline images with microsoft graph', function () { + Config::set('mail.mailers.microsoft-graph', [ + 'transport' => 'microsoft-graph', + 'client_id' => 'foo_client_id', + 'client_secret' => 'foo_client_secret', + 'tenant_id' => 'foo_tenant_id', + 'from' => [ + 'address' => 'taylor@laravel.com', + 'name' => 'Taylor Otwell', + ], + ]); + Config::set('mail.default', 'microsoft-graph'); + Config::set('filesystems.default', 'local'); + Config::set('filesystems.disks.local.root', realpath(__DIR__.'/Resources/files')); + + Cache::set('microsoft-graph-api-access-token', 'foo_access_token', 3600); + + Http::fake(); + + Mail::to('caleb@livewire.com') + ->bcc('tim@innoge.de') + ->cc('nuno@laravel.com') + ->send(new TestMailWithInlineImage()); + + Http::assertSent(function (Request $value) { + // ContentId gets random generated, so get this value first and check for equality later + $inlineImageContentId = json_decode($value->body())->message->attachments[1]->contentId; + + expect($value) + ->url()->toBe('https://graph.microsoft.com/v1.0/users/taylor@laravel.com/sendMail') + ->hasHeader('Authorization', 'Bearer foo_access_token')->toBeTrue() + ->body()->json()->toBe([ + 'message' => [ + 'subject' => 'Dev Test', + 'body' => [ + 'contentType' => 'HTML', + 'content' => 'Test'.PHP_EOL, + ], + 'toRecipients' => [ + [ + 'emailAddress' => [ + 'address' => 'caleb@livewire.com', + ], + ], + ], + 'ccRecipients' => [ + [ + 'emailAddress' => [ + 'address' => 'nuno@laravel.com', + ], + ], + ], + 'bccRecipients' => [ + [ + 'emailAddress' => [ + 'address' => 'tim@innoge.de', + ], + ], + ], + 'replyTo' => [], + 'sender' => [ + 'emailAddress' => [ + 'address' => 'taylor@laravel.com', + ], + ], + 'attachments' => [ + [ + '@odata.type' => '#microsoft.graph.fileAttachment', + 'name' => 'test-file-1.txt', + 'contentType' => 'text', + 'contentBytes' => 'Zm9vCg==', + 'contentId' => 'test-file-1.txt', + 'isInline' => false, + ], + [ + '@odata.type' => '#microsoft.graph.fileAttachment', + 'name' => $inlineImageContentId, + 'contentType' => 'image', + 'contentBytes' => '/9j/4AAQSkZJRgABAQEASABIAAD//gATQ3JlYXRlZCB3aXRoIEdJTVD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCABLAGQDAREAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAj/xAAWAQEBAQAAAAAAAAAAAAAAAAAABQj/2gAMAwEAAhADEAAAAZ71TDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/xAAUEAEAAAAAAAAAAAAAAAAAAABw/9oACAEBAAEFAgL/xAAUEQEAAAAAAAAAAAAAAAAAAABw/9oACAEDAQE/AQL/xAAUEQEAAAAAAAAAAAAAAAAAAABw/9oACAECAQE/AQL/xAAUEAEAAAAAAAAAAAAAAAAAAABw/9oACAEBAAY/AgL/xAAUEAEAAAAAAAAAAAAAAAAAAABw/9oACAEBAAE/IQL/2gAMAwEAAgADAAAAEEkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkv/xAAUEQEAAAAAAAAAAAAAAAAAAABw/9oACAEDAQE/EAL/xAAUEQEAAAAAAAAAAAAAAAAAAABw/9oACAECAQE/EAL/xAAUEAEAAAAAAAAAAAAAAAAAAABw/9oACAEBAAE/EAL/2Q==', + 'contentId' => $inlineImageContentId, + 'isInline' => true, + ], + ], + ], + 'saveToSentItems' => false, + ]); + + return true; + }); +}); diff --git a/tests/Resources/files/blue.jpg b/tests/Resources/files/blue.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e2f18651eae670475a62c59427443f8489f4444c GIT binary patch literal 640 zcmb7-~b#3aR`1kT!JlT zq6(x|v{A>i@#vfP=Ec|e2|JzC6A%$D_#u8{Fuq@2ua=9^!|m#3bY5Rx#Se@jg=m^9 z;Z|CyBvEO%+tFE?WhONyFZwnwTw#nYtsD3dLUwdKEd8+WgKtDg@=l_9O7(nha({5e zXE==+7Lpupw%7?!bs~OaLUJHmpFSScvz7?9Ey5bub0Gt7KiVc+wg3PC literal 0 HcmV?d00001 diff --git a/tests/Resources/views/html-mail-with-inline-image.blade.php b/tests/Resources/views/html-mail-with-inline-image.blade.php new file mode 100644 index 0000000..ee80cac --- /dev/null +++ b/tests/Resources/views/html-mail-with-inline-image.blade.php @@ -0,0 +1 @@ +Test diff --git a/tests/Stubs/TestMailWithInlineImage.php b/tests/Stubs/TestMailWithInlineImage.php new file mode 100644 index 0000000..39a29f2 --- /dev/null +++ b/tests/Stubs/TestMailWithInlineImage.php @@ -0,0 +1,62 @@ +isHtml) { + return new Content(text: 'text-mail'); + } + + return new Content(html: 'html-mail-with-inline-image'); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return [ + Attachment::fromPath('tests/Resources/files/test-file-1.txt'), + ]; + } +}