From 333db9d629c24452c9de41259720db89d8dd2c6f Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Fri, 3 Jun 2022 08:56:24 +1000 Subject: [PATCH] ensure embeds are returning CIDs --- src/Illuminate/Mail/Attachment.php | 4 +- src/Illuminate/Mail/Mailable.php | 40 ++++++++++----- tests/Mail/MailMailableTest.php | 80 ++++++++++++++++++++++++++---- 3 files changed, 99 insertions(+), 25 deletions(-) diff --git a/src/Illuminate/Mail/Attachment.php b/src/Illuminate/Mail/Attachment.php index 2fbb5c1a6c55..042589210bc4 100644 --- a/src/Illuminate/Mail/Attachment.php +++ b/src/Illuminate/Mail/Attachment.php @@ -162,8 +162,8 @@ public function attachTo($mail) public function embedIn($mail) { return $this->attachWith( - fn ($path) => $mail->embed($path), - fn ($data) => $mail->embedData($data(), $this->as, $this->mime) + fn ($path) => $mail->embed($path, ['as' => $this->as, 'mime' => $this->mime]), + fn ($data) => $mail->embedData($data(), $this->as, ['mime' => $this->mime]) ); } } diff --git a/src/Illuminate/Mail/Mailable.php b/src/Illuminate/Mail/Mailable.php index 49fee04e9182..bd299814a2e0 100644 --- a/src/Illuminate/Mail/Mailable.php +++ b/src/Illuminate/Mail/Mailable.php @@ -454,11 +454,17 @@ protected function buildAttachments($message) } foreach ($this->embeds as $embed) { - $message->embed($embed); + $message->embed(Attachment::fromPath($embed['file']) + ->as($embed['options']['as']) + ->withMime($embed['options']['mime'] ?? null)); } foreach ($this->rawEmbeds as $embed) { - $message->embedData($embed['data'], $embed['name'], $embed['contentType']); + $message->embedData( + $embed['data'], + $embed['name'], + $embed['options']['mime'] ?? null + ); } $this->buildDiskAttachments($message); @@ -971,9 +977,10 @@ public function attachData($data, $name, array $options = []) * Embed a file into the message. * * @param string|\Illuminate\Contracts\Mail\Attachable|\Illuminate\Mail\Attachment $file - * @return $this + * @param array $options + * @return string */ - public function embed($file) + public function embed($file, array $options = []) { if ($file instanceof Attachable) { $file = $file->toMailAttachment(); @@ -983,34 +990,41 @@ public function embed($file) return $file->embedIn($this); } + $options['as'] ??= Str::random(); + $this->embeds = collect($this->embeds) - ->push($file) - ->unique() + ->push([ + 'file' => $file, + 'options' => $options, + ]) + ->unique('file') ->all(); - return $this; + return "cid:{$options['as']}"; } /** * Embed in-memory data in the message. * * @param string|\Illuminate\Contracts\Mail\Attachable|\Illuminate\Mail\Attachment $file - * @param string $name - * @param string|null $contentType - * @return $this + * @param string|null $name + * @param array $options + * @return string */ - public function embedData($data, $name, $contentType = null) + public function embedData($data, $name = null, array $options = []) { + $name ??= Str::random(); + $this->rawEmbeds = collect($this->rawEmbeds) ->push([ 'data' => $data, 'name' => $name, - 'contentType' => $contentType, + 'options' => $options, ]) ->unique(fn ($file) => $file['name'].$file['data']) ->all(); - return $this; + return "cid:{$name}"; } /** diff --git a/tests/Mail/MailMailableTest.php b/tests/Mail/MailMailableTest.php index 980d24f8b54e..b4c997e0e359 100644 --- a/tests/Mail/MailMailableTest.php +++ b/tests/Mail/MailMailableTest.php @@ -8,6 +8,7 @@ use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailer; use Illuminate\Mail\Transport\ArrayTransport; +use Illuminate\Support\Str; use Mockery as m; use PHPUnit\Framework\TestCase; @@ -507,7 +508,7 @@ public function toMailAttachment() { return Attachment::fromPath(__DIR__.'/foo.jpg') ->as('bar') - ->withMime('image/jpeg'); + ->withMime('image/png'); } }); @@ -515,7 +516,7 @@ public function toMailAttachment() 'file' => __DIR__.'/foo.jpg', 'options' => [ 'as' => 'bar', - 'mime' => 'image/jpeg', + 'mime' => 'image/png', ], ], $mailable->attachments[0]); } @@ -530,7 +531,7 @@ public function toMailAttachment() { return Attachment::fromData(fn () => 'expected attachment body', 'foo.jpg') ->as('bar') - ->withMime('image/jpeg'); + ->withMime('image/png'); } }); @@ -538,29 +539,81 @@ public function toMailAttachment() 'data' => 'expected attachment body', 'name' => 'bar', 'options' => [ - 'mime' => 'image/jpeg', + 'mime' => 'image/png', ], ], $mailable->rawAttachments[0]); } + public function testItEmbedsFromData() + { + $mailable = new WelcomeMailableStub; + + $cid = $mailable->embedData('expected attachment body', 'foo', ['mime' => 'image/png']); + + $this->assertSame([ + 'data' => 'expected attachment body', + 'name' => 'foo', + 'options' => [ + 'mime' => 'image/png', + ], + ], $mailable->rawEmbeds[0]); + $this->assertSame('cid:foo', $cid); + } + + public function testItEmbedsFromPath() + { + $mailable = new WelcomeMailableStub; + + $cid = $mailable->embed('logo.png', ['as' => 'logo', 'mime' => 'image/png']); + + $this->assertSame([ + 'file' => 'logo.png', + 'options' => [ + 'as' => 'logo', + 'mime' => 'image/png', + ], + ], $mailable->embeds[0]); + $this->assertSame('cid:logo', $cid); + } + + public function testItGeneratesRandomeNameWhenNonProvided() + { + $mailable = new WelcomeMailableStub; + + $cid = $mailable->embed('logo.png'); + + $as = Str::after($cid, 'cid:'); + $this->assertStringStartsWith('cid:', $cid); + $this->assertSame(16, mb_strlen($as)); + $this->assertSame([ + 'file' => 'logo.png', + 'options' => [ + 'as' => $as, + ] + ], $mailable->embeds[0]); + } + public function testItEmbedsFilesViaAttachableContractFromData() { $mailable = new WelcomeMailableStub; - $mailable->embed(new class() implements Attachable + $cid = $mailable->embed(new class() implements Attachable { public function toMailAttachment() { return Attachment::fromData(fn () => 'expected attachment body', 'foo.jpg') ->as('bar') - ->withMime('image/jpeg'); + ->withMime('image/png'); } }); + $this->assertSame('cid:bar', $cid); $this->assertSame([ 'data' => 'expected attachment body', 'name' => 'bar', - 'contentType' => 'image/jpeg', + 'options' => [ + 'mime' => 'image/png', + ], ], $mailable->rawEmbeds[0]); } @@ -568,17 +621,24 @@ public function testItEmbedsFilesViaAttachableContractFromPath() { $mailable = new WelcomeMailableStub; - $mailable->embed(new class() implements Attachable + $cid = $mailable->embed(new class() implements Attachable { public function toMailAttachment() { return Attachment::fromPath(__DIR__.'/foo.jpg') ->as('bar') - ->withMime('image/jpeg'); + ->withMime('image/png'); } }); - $this->assertSame(__DIR__.'/foo.jpg', $mailable->embeds[0]); + $this->assertSame('cid:bar', $cid); + $this->assertSame([ + 'file' => __DIR__.'/foo.jpg', + 'options' => [ + 'as' => 'bar', + 'mime' => 'image/png', + ], + ], $mailable->embeds[0]); } }