Skip to content

Commit 9130f8f

Browse files
nunomadurocosmastechtaylorotwellStyleCIBot
authored
[10.x] Adds inline attachments support for "notifications" markdown mailables (#47643)
* failing test * leverage closure for rendering markdown * passing test * fix unrelated test expectations * Update MailChannel.php * clone Markdown converter to avoid overwriting theme overwriting * add test to prove that default theme is used * set default markdown theme * Apply fixes from StyleCI * Adjusts coding style * Apply fixes from StyleCI * Fixes tests * Adds test on custom theme * Simplifies test * Message data takes priority on message * Update MailChannel.php * Update MailChannel.php --------- Co-authored-by: Luke Kuzmish <[email protected]> Co-authored-by: Taylor Otwell <[email protected]> Co-authored-by: StyleCI Bot <[email protected]>
1 parent b8f385b commit 9130f8f

File tree

6 files changed

+294
-76
lines changed

6 files changed

+294
-76
lines changed

src/Illuminate/Notifications/Channels/MailChannel.php

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Illuminate\Notifications\Channels;
44

5+
use Illuminate\Config\Repository as ConfigRepository;
6+
use Illuminate\Container\Container;
57
use Illuminate\Contracts\Mail\Factory as MailFactory;
68
use Illuminate\Contracts\Mail\Mailable;
79
use Illuminate\Contracts\Queue\ShouldQueue;
@@ -95,16 +97,53 @@ protected function buildView($message)
9597
return $message->view;
9698
}
9799

98-
if (property_exists($message, 'theme') && ! is_null($message->theme)) {
99-
$this->markdown->theme($message->theme);
100-
}
101-
102100
return [
103-
'html' => $this->markdown->render($message->markdown, $message->data()),
104-
'text' => $this->markdown->renderText($message->markdown, $message->data()),
101+
'html' => $this->buildMarkdownHtml($message),
102+
'text' => $this->buildMarkdownText($message),
105103
];
106104
}
107105

106+
/**
107+
* Build the HTML view for a Markdown message.
108+
*
109+
* @param \Illuminate\Notifications\Messages\MailMessage $message
110+
* @return \Closure
111+
*/
112+
protected function buildMarkdownHtml($message)
113+
{
114+
return fn ($data) => $this->markdownRenderer($message)->render(
115+
$message->markdown, array_merge($data, $message->data()),
116+
);
117+
}
118+
119+
/**
120+
* Build the text view for a Markdown message.
121+
*
122+
* @param \Illuminate\Notifications\Messages\MailMessage $message
123+
* @return \Closure
124+
*/
125+
protected function buildMarkdownText($message)
126+
{
127+
return fn ($data) => $this->markdownRenderer($message)->renderText(
128+
$message->markdown, array_merge($data, $message->data()),
129+
);
130+
}
131+
132+
/**
133+
* Get the Markdown implementation.
134+
*
135+
* @param \Illuminate\Notifications\Messages\MailMessage $message
136+
* @return \Illuminate\Mail\Markdown
137+
*/
138+
protected function markdownRenderer($message)
139+
{
140+
$config = Container::getInstance()->get(ConfigRepository::class);
141+
142+
$theme = $message->theme ?? $config->get('mail.markdown.theme', 'default');
143+
144+
return $this->markdown->theme($theme);
145+
}
146+
108147
/**
109148
* Get additional meta-data to pass along with the view data.
110149
*

tests/Integration/Notifications/Fixtures/mail/blank.blade.php

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
body{color: test;}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Embed content: {{ $message->embed(__FILE__) }}

tests/Integration/Notifications/SendingMailNotificationsTest.php

Lines changed: 136 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -74,42 +74,108 @@ public function testMailIsSent()
7474
'email' => '[email protected]',
7575
]);
7676

77+
$this->markdown->shouldReceive('theme')->twice()->with('default')->andReturn($this->markdown);
7778
$this->markdown->shouldReceive('render')->once()->andReturn('htmlContent');
7879
$this->markdown->shouldReceive('renderText')->once()->andReturn('textContent');
7980

80-
$this->mailer->shouldReceive('send')->once()->with(
81-
['html' => 'htmlContent', 'text' => 'textContent'],
82-
array_merge($notification->toMail($user)->toArray(), [
83-
'__laravel_notification_id' => $notification->id,
84-
'__laravel_notification' => get_class($notification),
85-
'__laravel_notification_queued' => false,
86-
]),
87-
m::on(function ($closure) {
88-
$message = m::mock(Message::class);
81+
$this->setMailerSendAssertions($notification, $user, function ($closure) {
82+
$message = m::mock(Message::class);
8983

90-
$message->shouldReceive('to')->once()->with(['[email protected]']);
84+
$message->shouldReceive('to')->once()->with(['[email protected]']);
9185

92-
$message->shouldReceive('cc')->once()->with('[email protected]', 'cc');
86+
$message->shouldReceive('cc')->once()->with('[email protected]', 'cc');
9387

94-
$message->shouldReceive('bcc')->once()->with('[email protected]', 'bcc');
88+
$message->shouldReceive('bcc')->once()->with('[email protected]', 'bcc');
9589

96-
$message->shouldReceive('from')->once()->with('[email protected]', 'Jacques Mayol');
90+
$message->shouldReceive('from')->once()->with('[email protected]', 'Jacques Mayol');
9791

98-
$message->shouldReceive('replyTo')->once()->with('[email protected]', 'Jacques Mayol');
92+
$message->shouldReceive('replyTo')->once()->with('[email protected]', 'Jacques Mayol');
9993

100-
$message->shouldReceive('subject')->once()->with('Test Mail Notification');
94+
$message->shouldReceive('subject')->once()->with('Test Mail Notification');
10195

102-
$message->shouldReceive('priority')->once()->with(1);
96+
$message->shouldReceive('priority')->once()->with(1);
10397

104-
$closure($message);
98+
$closure($message);
10599

106-
return true;
107-
})
108-
);
100+
return true;
101+
});
109102

110103
$user->notify($notification);
111104
}
112105

106+
public function testMailIsSentWithCustomTheme()
107+
{
108+
$notification = new TestMailNotificationWithCustomTheme;
109+
$notification->id = Str::uuid()->toString();
110+
111+
$user = NotifiableUser::forceCreate([
112+
'email' => '[email protected]',
113+
]);
114+
115+
$this->markdown->shouldReceive('theme')->twice()->with('my-custom-theme')->andReturn($this->markdown);
116+
$this->markdown->shouldReceive('render')->once()->andReturn('htmlContent');
117+
$this->markdown->shouldReceive('renderText')->once()->andReturn('textContent');
118+
119+
$this->setMailerSendAssertions($notification, $user, function ($closure) {
120+
$message = m::mock(Message::class);
121+
122+
$message->shouldReceive('to')->once()->with(['[email protected]']);
123+
124+
$message->shouldReceive('cc')->once()->with('[email protected]', 'cc');
125+
126+
$message->shouldReceive('bcc')->once()->with('[email protected]', 'bcc');
127+
128+
$message->shouldReceive('from')->once()->with('[email protected]', 'Jacques Mayol');
129+
130+
$message->shouldReceive('replyTo')->once()->with('[email protected]', 'Jacques Mayol');
131+
132+
$message->shouldReceive('subject')->once()->with('Test Mail Notification With Custom Theme');
133+
134+
$message->shouldReceive('priority')->once()->with(1);
135+
136+
$closure($message);
137+
138+
return true;
139+
});
140+
141+
$user->notify($notification);
142+
}
143+
144+
private function setMailerSendAssertions(
145+
Notification $notification,
146+
NotifiableUser $user,
147+
callable $callbackExpectationClosure
148+
) {
149+
$this->mailer->shouldReceive('send')->once()->withArgs(function (...$args) use ($notification, $user, $callbackExpectationClosure) {
150+
$viewArray = $args[0];
151+
152+
if (! m::on(fn ($closure) => $closure([]) === 'htmlContent')->match($viewArray['html'])) {
153+
return false;
154+
}
155+
156+
if (! m::on(fn ($closure) => $closure([]) === 'textContent')->match($viewArray['text'])) {
157+
return false;
158+
}
159+
160+
$data = $args[1];
161+
162+
$expected = array_merge($notification->toMail($user)->toArray(), [
163+
'__laravel_notification_id' => $notification->id,
164+
'__laravel_notification' => get_class($notification),
165+
'__laravel_notification_queued' => false,
166+
]);
167+
168+
if (array_keys($data) !== array_keys($expected)) {
169+
return false;
170+
}
171+
if (array_values($data) !== array_values($expected)) {
172+
return false;
173+
}
174+
175+
return m::on($callbackExpectationClosure)->match($args[2]);
176+
});
177+
}
178+
113179
public function testMailIsSentToNamedAddress()
114180
{
115181
$notification = new TestMailNotification;
@@ -120,38 +186,31 @@ public function testMailIsSentToNamedAddress()
120186
'name' => 'Taylor Otwell',
121187
]);
122188

189+
$this->markdown->shouldReceive('theme')->twice()->with('default')->andReturn($this->markdown);
123190
$this->markdown->shouldReceive('render')->once()->andReturn('htmlContent');
124191
$this->markdown->shouldReceive('renderText')->once()->andReturn('textContent');
125192

126-
$this->mailer->shouldReceive('send')->once()->with(
127-
['html' => 'htmlContent', 'text' => 'textContent'],
128-
array_merge($notification->toMail($user)->toArray(), [
129-
'__laravel_notification_id' => $notification->id,
130-
'__laravel_notification' => get_class($notification),
131-
'__laravel_notification_queued' => false,
132-
]),
133-
m::on(function ($closure) {
134-
$message = m::mock(Message::class);
193+
$this->setMailerSendAssertions($notification, $user, function ($closure) {
194+
$message = m::mock(Message::class);
135195

136-
$message->shouldReceive('to')->once()->with(['[email protected]' => 'Taylor Otwell', '[email protected]']);
196+
$message->shouldReceive('to')->once()->with(['[email protected]' => 'Taylor Otwell', '[email protected]']);
137197

138-
$message->shouldReceive('cc')->once()->with('[email protected]', 'cc');
198+
$message->shouldReceive('cc')->once()->with('[email protected]', 'cc');
139199

140-
$message->shouldReceive('bcc')->once()->with('[email protected]', 'bcc');
200+
$message->shouldReceive('bcc')->once()->with('[email protected]', 'bcc');
141201

142-
$message->shouldReceive('from')->once()->with('[email protected]', 'Jacques Mayol');
202+
$message->shouldReceive('from')->once()->with('[email protected]', 'Jacques Mayol');
143203

144-
$message->shouldReceive('replyTo')->once()->with('[email protected]', 'Jacques Mayol');
204+
$message->shouldReceive('replyTo')->once()->with('[email protected]', 'Jacques Mayol');
145205

146-
$message->shouldReceive('subject')->once()->with('Test Mail Notification');
206+
$message->shouldReceive('subject')->once()->with('Test Mail Notification');
147207

148-
$message->shouldReceive('priority')->once()->with(1);
208+
$message->shouldReceive('priority')->once()->with(1);
149209

150-
$closure($message);
210+
$closure($message);
151211

152-
return true;
153-
})
154-
);
212+
return true;
213+
});
155214

156215
$user->notify($notification);
157216
}
@@ -165,28 +224,21 @@ public function testMailIsSentWithSubject()
165224
'email' => '[email protected]',
166225
]);
167226

227+
$this->markdown->shouldReceive('theme')->with('default')->twice()->andReturn($this->markdown);
168228
$this->markdown->shouldReceive('render')->once()->andReturn('htmlContent');
169229
$this->markdown->shouldReceive('renderText')->once()->andReturn('textContent');
170230

171-
$this->mailer->shouldReceive('send')->once()->with(
172-
['html' => 'htmlContent', 'text' => 'textContent'],
173-
array_merge($notification->toMail($user)->toArray(), [
174-
'__laravel_notification_id' => $notification->id,
175-
'__laravel_notification' => get_class($notification),
176-
'__laravel_notification_queued' => false,
177-
]),
178-
m::on(function ($closure) {
179-
$message = m::mock(Message::class);
231+
$this->setMailerSendAssertions($notification, $user, function ($closure) {
232+
$message = m::mock(Message::class);
180233

181-
$message->shouldReceive('to')->once()->with(['[email protected]']);
234+
$message->shouldReceive('to')->once()->with(['[email protected]']);
182235

183-
$message->shouldReceive('subject')->once()->with('mail custom subject');
236+
$message->shouldReceive('subject')->once()->with('mail custom subject');
184237

185-
$closure($message);
238+
$closure($message);
186239

187-
return true;
188-
})
189-
);
240+
return true;
241+
});
190242

191243
$user->notify($notification);
192244
}
@@ -200,28 +252,21 @@ public function testMailIsSentToMultipleAddresses()
200252
'email' => '[email protected]',
201253
]);
202254

255+
$this->markdown->shouldReceive('theme')->with('default')->twice()->andReturn($this->markdown);
203256
$this->markdown->shouldReceive('render')->once()->andReturn('htmlContent');
204257
$this->markdown->shouldReceive('renderText')->once()->andReturn('textContent');
205258

206-
$this->mailer->shouldReceive('send')->once()->with(
207-
['html' => 'htmlContent', 'text' => 'textContent'],
208-
array_merge($notification->toMail($user)->toArray(), [
209-
'__laravel_notification_id' => $notification->id,
210-
'__laravel_notification' => get_class($notification),
211-
'__laravel_notification_queued' => false,
212-
]),
213-
m::on(function ($closure) {
214-
$message = m::mock(Message::class);
259+
$this->setMailerSendAssertions($notification, $user, function ($closure) {
260+
$message = m::mock(Message::class);
215261

216-
$message->shouldReceive('to')->once()->with(['[email protected]', '[email protected]']);
262+
$message->shouldReceive('to')->once()->with(['[email protected]', '[email protected]']);
217263

218-
$message->shouldReceive('subject')->once()->with('mail custom subject');
264+
$message->shouldReceive('subject')->once()->with('mail custom subject');
219265

220-
$closure($message);
266+
$closure($message);
221267

222-
return true;
223-
})
224-
);
268+
return true;
269+
});
225270

226271
$user->notify($notification);
227272
}
@@ -457,3 +502,24 @@ public function toMail($notifiable)
457502
->view([null, 'plain']);
458503
}
459504
}
505+
506+
class TestMailNotificationWithCustomTheme extends Notification
507+
{
508+
public function via($notifiable)
509+
{
510+
return [MailChannel::class];
511+
}
512+
513+
public function toMail($notifiable)
514+
{
515+
return (new MailMessage)
516+
->priority(1)
517+
->cc('[email protected]', 'cc')
518+
->bcc('[email protected]', 'bcc')
519+
->from('[email protected]', 'Jacques Mayol')
520+
->replyTo('[email protected]', 'Jacques Mayol')
521+
->line('The introduction to the notification.')
522+
->theme('my-custom-theme')
523+
->mailer('foo');
524+
}
525+
}

0 commit comments

Comments
 (0)