Skip to content

Commit

Permalink
Merge pull request #9 from joskolenberg/main
Browse files Browse the repository at this point in the history
Added handling embedded/inline images
  • Loading branch information
geisi committed Oct 2, 2023
2 parents 96b86a1 + 24b5ddd commit f2cae9a
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 6 deletions.
25 changes: 19 additions & 6 deletions src/MicrosoftGraphTransport.php
Expand Up @@ -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);

Check failure on line 39 in src/MicrosoftGraphTransport.php

View workflow job for this annotation

GitHub Actions / phpstan

Parameter #2 $html of method InnoGE\LaravelMsGraphMail\MicrosoftGraphTransport::prepareAttachments() expects string|null, resource|string|null given.

$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),
];
Expand Down Expand Up @@ -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];
}
}
100 changes: 100 additions & 0 deletions tests/MicrosoftGraphTransportTest.php
Expand Up @@ -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', [
Expand Down Expand Up @@ -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,
],
],
],
Expand Down Expand Up @@ -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,
],
],
],
Expand Down Expand Up @@ -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' => '<b>Test</b><img src="cid:' . $inlineImageContentId . '">'.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;
});
});
Binary file added tests/Resources/files/blue.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1 @@
<b>Test</b><img src="{{ $message->embed(\Illuminate\Support\Facades\Storage::path('blue.jpg')) }}">
62 changes: 62 additions & 0 deletions tests/Stubs/TestMailWithInlineImage.php
@@ -0,0 +1,62 @@
<?php

namespace InnoGE\LaravelMsGraphMail\Tests\Stubs;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Attachment;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class TestMailWithInlineImage extends Mailable
{
use Queueable, SerializesModels;

/**
* Create a new message instance.
*
* @return void
*/
public function __construct(private readonly bool $isHtml = true)
{
}

/**
* Get the message envelope.
*
* @return \Illuminate\Mail\Mailables\Envelope
*/
public function envelope()
{
return new Envelope(
subject: 'Dev Test',
);
}

/**
* Get the message content definition.
*
* @return \Illuminate\Mail\Mailables\Content
*/
public function content()
{
if (! $this->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'),
];
}
}

0 comments on commit f2cae9a

Please sign in to comment.