diff --git a/src/Illuminate/Mail/Message.php b/src/Illuminate/Mail/Message.php index b9a4eb5352ca..e3eb8e1a8d1b 100755 --- a/src/Illuminate/Mail/Message.php +++ b/src/Illuminate/Mail/Message.php @@ -4,7 +4,6 @@ use Illuminate\Contracts\Mail\Attachable; use Illuminate\Support\Collection; -use Illuminate\Support\Str; use Illuminate\Support\Traits\ForwardsCalls; use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Email; @@ -344,31 +343,27 @@ public function embed($file) if ($file instanceof Attachment) { return $file->attachWith( function ($path) use ($file) { - $cid = $file->as ?? Str::random(); + $part = (new DataPart(new File($path), $file->as, $file->mime))->asInline(); - $this->message->addPart( - (new DataPart(new File($path), $cid, $file->mime))->asInline() - ); + $this->message->addPart($part); - return "cid:{$cid}"; + return "cid:{$part->getContentId()}"; }, function ($data) use ($file) { - $this->message->addPart( - (new DataPart($data(), $file->as, $file->mime))->asInline() - ); + $part = (new DataPart($data(), $file->as, $file->mime))->asInline(); + $this->message->addPart($part); - return "cid:{$file->as}"; + return "cid:{$part->getContentId()}"; } ); } - $cid = Str::random(10); + $fileObject = new File($file); + $part = (new DataPart($fileObject, $fileObject->getFilename()))->asInline(); - $this->message->addPart( - (new DataPart(new File($file), $cid))->asInline() - ); + $this->message->addPart($part); - return "cid:$cid"; + return "cid:{$part->getContentId()}"; } /** @@ -381,11 +376,11 @@ function ($data) use ($file) { */ public function embedData($data, $name, $contentType = null) { - $this->message->addPart( - (new DataPart($data, $name, $contentType))->asInline() - ); + $part = (new DataPart($data, $name, $contentType))->asInline(); + + $this->message->addPart($part); - return "cid:$name"; + return "cid:{$part->getContentId()}"; } /** diff --git a/tests/Integration/Mail/Fixtures/embed-image.blade.php b/tests/Integration/Mail/Fixtures/embed-image.blade.php new file mode 100644 index 000000000000..0be2b5242fea --- /dev/null +++ b/tests/Integration/Mail/Fixtures/embed-image.blade.php @@ -0,0 +1 @@ +Embedded image: Embedded test image diff --git a/tests/Integration/Mail/Fixtures/embed.blade.php b/tests/Integration/Mail/Fixtures/embed.blade.php index 4a19e330d84e..7b3839aa472d 100644 --- a/tests/Integration/Mail/Fixtures/embed.blade.php +++ b/tests/Integration/Mail/Fixtures/embed.blade.php @@ -1 +1,3 @@ +Embed file: {{ basename(__FILE__) }} + Embed content: {{ $message->embed(__FILE__) }} diff --git a/tests/Integration/Mail/Fixtures/empty_image.jpg b/tests/Integration/Mail/Fixtures/empty_image.jpg new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/Integration/Mail/SendingMarkdownMailTest.php b/tests/Integration/Mail/SendingMarkdownMailTest.php index 712fe0b7cecb..b4d3c474815e 100644 --- a/tests/Integration/Mail/SendingMarkdownMailTest.php +++ b/tests/Integration/Mail/SendingMarkdownMailTest.php @@ -52,13 +52,19 @@ public function testEmbed() $email = app('mailer')->getSymfonyTransport()->messages()[0]->getOriginalMessage()->toString(); $cid = explode(' cid:', (new Stringable($email))->explode("\r\n") - ->filter(fn ($line) => str_contains($line, 'Embed content: cid:')) + ->filter(fn ($line) => str_contains($line, ' content: cid:')) + ->first())[1]; + + $filename = explode('Embed file: ', (new Stringable($email))->explode("\r\n") + ->filter(fn ($line) => str_contains($line, ' file:')) ->first())[1]; $this->assertStringContainsString(<<\r EOT, $email); } @@ -66,9 +72,8 @@ public function testEmbedData() { Mail::to('test@mail.com')->send($mailable = new EmbedDataMailable()); - $mailable->assertSeeInHtml('Embed data content: cid:foo.jpg'); $mailable->assertSeeInText('Embed data content: '); - $mailable->assertDontSeeInText('Embed data content: cid:foo.jpg'); + $mailable->assertSeeInHtml('Embed data content: cid:'); $email = app('mailer')->getSymfonyTransport()->messages()[0]->getOriginalMessage()->toString(); @@ -87,8 +92,7 @@ public function testEmbedMultilineImage() $this->assertStringContainsString('Embed multiline content: assertStringContainsString('alt="multiline image"', $html); - $this->assertStringContainsString('data:image/png;base64', $html); - $this->assertStringNotContainsString('cid:foo.jpg', $html); + $this->assertStringContainsString(' tag. + preg_match('/]+src="cid:([^"]+)"/', $rawEmail, $htmlMatches); + $htmlImageCid = $htmlMatches[1] ?? null; + + // Extract CID from MIME attachment Content-ID header. + preg_match('/Content-ID:\s*<([^>]+)>/', $rawEmail, $headerMatches); + $attachmentContentId = $headerMatches[1] ?? null; + + return [$htmlImageCid, $attachmentContentId]; + } } class BasicMailable extends Mailable @@ -236,6 +283,26 @@ public function content() } } +class EmbedImageMailable extends Mailable +{ + public function envelope() + { + return new Envelope( + subject: 'My basic title', + ); + } + + public function content() + { + return new Content( + markdown: 'embed-image', + with: [ + 'image' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'empty_image.jpg', + ] + ); + } +} + class MessageAsPublicPropertyMailable extends Mailable { public $message = 'My message'; diff --git a/tests/Integration/Notifications/Fixtures/markdown.blade.php b/tests/Integration/Notifications/Fixtures/markdown.blade.php index 4a19e330d84e..6e7b0a25097d 100644 --- a/tests/Integration/Notifications/Fixtures/markdown.blade.php +++ b/tests/Integration/Notifications/Fixtures/markdown.blade.php @@ -1 +1,2 @@ +Embed file: {{ basename(__FILE__) }} Embed content: {{ $message->embed(__FILE__) }} diff --git a/tests/Integration/Notifications/SendingMailableNotificationsTest.php b/tests/Integration/Notifications/SendingMailableNotificationsTest.php index dd0f44a00186..f1307006a349 100644 --- a/tests/Integration/Notifications/SendingMailableNotificationsTest.php +++ b/tests/Integration/Notifications/SendingMailableNotificationsTest.php @@ -49,16 +49,24 @@ public function testMarkdownNotification() $user->notify(new MarkdownNotification()); - $email = app('mailer')->getSymfonyTransport()->messages()[0]->getOriginalMessage()->toString(); + $message = app('mailer')->getSymfonyTransport()->messages()[0]->getOriginalMessage(); + $email = $message->toString(); + $textBody = $message->getTextBody(); - $cid = explode(' cid:', (new Stringable($email))->explode("\r\n") + $cid = explode(' cid:', (new Stringable($textBody))->explode("\n") ->filter(fn ($line) => str_contains($line, 'Embed content: cid:')) ->first())[1]; + $filename = explode(' file: ', (new Stringable($textBody))->explode("\n") + ->filter(fn ($line) => str_contains($line, 'Embed file: ')) + ->first())[1]; + $this->assertStringContainsString(<<\r EOT, $email); } diff --git a/tests/Mail/MailMessageTest.php b/tests/Mail/MailMessageTest.php index 330777cd674a..eff028ddadfe 100755 --- a/tests/Mail/MailMessageTest.php +++ b/tests/Mail/MailMessageTest.php @@ -165,13 +165,14 @@ public function testEmbedPath(): void $cid = $this->message->embed($path); $this->assertStringStartsWith('cid:', $cid); - $name = Str::after($cid, 'cid:'); + $contentId = Str::after($cid, 'cid:'); $attachment = $this->message->getSymfonyMessage()->getAttachments()[0]; $headers = $attachment->getPreparedHeaders()->toArray(); $this->assertSame('bar', $attachment->getBody()); - $this->assertSame("Content-Type: image/jpeg; name={$name}", $headers[0]); + $this->assertSame($contentId, $attachment->getContentId()); + $this->assertStringContainsString('Content-Type: image/jpeg', $headers[0]); $this->assertSame('Content-Transfer-Encoding: base64', $headers[1]); - $this->assertSame("Content-Disposition: inline; name={$name}; filename={$name}", $headers[2]); + $this->assertStringContainsString('Content-Disposition: inline', $headers[2]); unlink($path); } @@ -182,7 +183,9 @@ public function testDataEmbed(): void $attachment = $this->message->getSymfonyMessage()->getAttachments()[0]; $headers = $attachment->getPreparedHeaders()->toArray(); - $this->assertSame('cid:foo.jpg', $cid); + $this->assertStringStartsWith('cid:', $cid); + $contentId = Str::after($cid, 'cid:'); + $this->assertSame($contentId, $attachment->getContentId()); $this->assertSame('bar', $attachment->getBody()); $this->assertSame('Content-Type: image/png; name=foo.jpg', $headers[0]); $this->assertSame('Content-Transfer-Encoding: base64', $headers[1]); @@ -201,9 +204,11 @@ public function toMailAttachment() } }); - $this->assertSame('cid:baz', $cid); + $this->assertStringStartsWith('cid:', $cid); + $contentId = Str::after($cid, 'cid:'); $attachment = $this->message->getSymfonyMessage()->getAttachments()[0]; $headers = $attachment->getPreparedHeaders()->toArray(); + $this->assertSame($contentId, $attachment->getContentId()); $this->assertSame('bar', $attachment->getBody()); $this->assertSame('Content-Type: image/png; name=baz', $headers[0]); $this->assertSame('Content-Transfer-Encoding: base64', $headers[1]); @@ -225,14 +230,14 @@ public function toMailAttachment() }); $this->assertStringStartsWith('cid:', $cid); - $name = Str::after($cid, 'cid:'); - $this->assertSame(16, mb_strlen($name)); + $contentId = Str::after($cid, 'cid:'); $attachment = $this->message->getSymfonyMessage()->getAttachments()[0]; + $this->assertSame($contentId, $attachment->getContentId()); $headers = $attachment->getPreparedHeaders()->toArray(); $this->assertSame('bar', $attachment->getBody()); - $this->assertSame("Content-Type: image/jpeg; name={$name}", $headers[0]); + $this->assertStringContainsString('Content-Type: image/jpeg', $headers[0]); $this->assertSame('Content-Transfer-Encoding: base64', $headers[1]); - $this->assertSame("Content-Disposition: inline; name={$name};\r\n filename={$name}", $headers[2]); + $this->assertStringContainsString('Content-Disposition: inline', $headers[2]); unlink($path); }