Skip to content

Commit bee282a

Browse files
[11.x] Allows to toggle markdown email encoding (#55539)
* Allow to toggle markdown encoding Signed-off-by: Mior Muhammad Zaki <[email protected]> * wip Signed-off-by: Mior Muhammad Zaki <[email protected]> * Update Markdown.php * wip Signed-off-by: Mior Muhammad Zaki <[email protected]> * wip Signed-off-by: Mior Muhammad Zaki <[email protected]> * wip Signed-off-by: Mior Muhammad Zaki <[email protected]> * formatting --------- Signed-off-by: Mior Muhammad Zaki <[email protected]> Co-authored-by: Taylor Otwell <[email protected]>
1 parent 56dc3e2 commit bee282a

File tree

5 files changed

+230
-20
lines changed

5 files changed

+230
-20
lines changed

src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Illuminate\Foundation\Testing\WithoutMiddleware;
2424
use Illuminate\Http\Middleware\TrustHosts;
2525
use Illuminate\Http\Middleware\TrustProxies;
26+
use Illuminate\Mail\Markdown;
2627
use Illuminate\Queue\Console\WorkCommand;
2728
use Illuminate\Queue\Queue;
2829
use Illuminate\Support\Carbon;
@@ -175,6 +176,7 @@ protected function tearDownTheTestEnvironment(): void
175176
EncodedHtmlString::flushState();
176177
EncryptCookies::flushState();
177178
HandleExceptions::flushState();
179+
Markdown::flushState();
178180
Migrator::withoutMigrations([]);
179181
Once::flush();
180182
PreventRequestsDuringMaintenance::flushState();

src/Illuminate/Mail/Markdown.php

+60-19
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ class Markdown
3535
*/
3636
protected $componentPaths = [];
3737

38+
/**
39+
* Indicates if secure encoding should be enabled.
40+
*
41+
* @var bool
42+
*/
43+
protected static $withSecuredEncoding = false;
44+
3845
/**
3946
* Create a new Markdown renderer instance.
4047
*
@@ -69,15 +76,17 @@ public function render($view, array $data = [], $inliner = null)
6976
$contents = $bladeCompiler->usingEchoFormat(
7077
'new \Illuminate\Support\EncodedHtmlString(%s)',
7178
function () use ($view, $data) {
72-
EncodedHtmlString::encodeUsing(function ($value) {
73-
$replacements = [
74-
'[' => '\[',
75-
'<' => '&lt;',
76-
'>' => '&gt;',
77-
];
78-
79-
return str_replace(array_keys($replacements), array_values($replacements), $value);
80-
});
79+
if (static::$withSecuredEncoding === true) {
80+
EncodedHtmlString::encodeUsing(function ($value) {
81+
$replacements = [
82+
'[' => '\[',
83+
'<' => '&lt;',
84+
'>' => '&gt;',
85+
];
86+
87+
return str_replace(array_keys($replacements), array_values($replacements), $value);
88+
});
89+
}
8190

8291
try {
8392
$contents = $this->view->replaceNamespace(
@@ -137,18 +146,20 @@ public static function parse($text, bool $encoded = false)
137146
return new HtmlString(static::converter()->convert($text)->getContent());
138147
}
139148

140-
EncodedHtmlString::encodeUsing(function ($value) {
141-
$replacements = [
142-
'[' => '\[',
143-
'<' => '\<',
144-
];
149+
if (static::$withSecuredEncoding === true || $encoded === true) {
150+
EncodedHtmlString::encodeUsing(function ($value) {
151+
$replacements = [
152+
'[' => '\[',
153+
'<' => '\<',
154+
];
145155

146-
$html = str_replace(array_keys($replacements), array_values($replacements), $value);
156+
$html = str_replace(array_keys($replacements), array_values($replacements), $value);
147157

148-
return static::converter([
149-
'html_input' => 'escape',
150-
])->convert($html)->getContent();
151-
});
158+
return static::converter([
159+
'html_input' => 'escape',
160+
])->convert($html)->getContent();
161+
});
162+
}
152163

153164
$html = '';
154165

@@ -250,4 +261,34 @@ public function getTheme()
250261
{
251262
return $this->theme;
252263
}
264+
265+
/**
266+
* Enable secured encoding when parsing Markdown.
267+
*
268+
* @return void
269+
*/
270+
public static function withSecuredEncoding()
271+
{
272+
static::$withSecuredEncoding = true;
273+
}
274+
275+
/**
276+
* Disable secured encoding when parsing Markdown.
277+
*
278+
* @return void
279+
*/
280+
public static function withoutSecuredEncoding()
281+
{
282+
static::$withSecuredEncoding = false;
283+
}
284+
285+
/**
286+
* Flush the class's global state.
287+
*
288+
* @return void
289+
*/
290+
public static function flushState()
291+
{
292+
static::$withSecuredEncoding = false;
293+
}
253294
}

tests/Integration/Mail/MailableTest.php renamed to tests/Integration/Mail/MailableWithSecuredEncodingTest.php

+15-1
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,34 @@
77
use Illuminate\Mail\Mailable;
88
use Illuminate\Mail\Mailables\Content;
99
use Illuminate\Mail\Mailables\Envelope;
10+
use Illuminate\Mail\Markdown;
11+
use Illuminate\Support\EncodedHtmlString;
1012
use Orchestra\Testbench\Attributes\WithMigration;
1113
use Orchestra\Testbench\Factories\UserFactory;
1214
use Orchestra\Testbench\TestCase;
1315
use PHPUnit\Framework\Attributes\DataProvider;
1416

15-
class MailableTest extends TestCase
17+
class MailableWithSecuredEncodingTest extends TestCase
1618
{
1719
use LazilyRefreshDatabase;
1820

21+
/** {@inheritdoc} */
22+
#[\Override]
23+
protected function tearDown(): void
24+
{
25+
Markdown::flushState();
26+
EncodedHtmlString::flushState();
27+
28+
parent::tearDown();
29+
}
30+
1931
/** {@inheritdoc} */
2032
#[\Override]
2133
protected function defineEnvironment($app)
2234
{
2335
$app['view']->addLocation(__DIR__.'/Fixtures');
36+
37+
Markdown::withSecuredEncoding();
2438
}
2539

2640
#[DataProvider('markdownEncodedDataProvider')]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Integration\Mail;
4+
5+
use Illuminate\Foundation\Auth\User;
6+
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
7+
use Illuminate\Mail\Mailable;
8+
use Illuminate\Mail\Mailables\Content;
9+
use Illuminate\Mail\Mailables\Envelope;
10+
use Illuminate\Mail\Markdown;
11+
use Illuminate\Support\EncodedHtmlString;
12+
use Orchestra\Testbench\Attributes\WithMigration;
13+
use Orchestra\Testbench\Factories\UserFactory;
14+
use Orchestra\Testbench\TestCase;
15+
use PHPUnit\Framework\Attributes\DataProvider;
16+
17+
class MailableWithoutSecuredEncodingTest extends TestCase
18+
{
19+
use LazilyRefreshDatabase;
20+
21+
/** {@inheritdoc} */
22+
#[\Override]
23+
protected function tearDown(): void
24+
{
25+
Markdown::flushState();
26+
EncodedHtmlString::flushState();
27+
28+
parent::tearDown();
29+
}
30+
31+
/** {@inheritdoc} */
32+
#[\Override]
33+
protected function defineEnvironment($app)
34+
{
35+
$app['view']->addLocation(__DIR__.'/Fixtures');
36+
37+
Markdown::withoutSecuredEncoding();
38+
}
39+
40+
#[DataProvider('markdownEncodedDataProvider')]
41+
public function testItCanAssertMarkdownEncodedString($given, $expected)
42+
{
43+
$mailable = new class($given) extends Mailable
44+
{
45+
public function __construct(public string $message)
46+
{
47+
//
48+
}
49+
50+
public function envelope()
51+
{
52+
return new Envelope(
53+
subject: 'My basic title',
54+
);
55+
}
56+
57+
public function content()
58+
{
59+
return new Content(
60+
markdown: 'message',
61+
);
62+
}
63+
};
64+
65+
$mailable->assertSeeInHtml($expected, false);
66+
}
67+
68+
public static function markdownEncodedDataProvider()
69+
{
70+
yield ['[Laravel](https://laravel.com)', 'My message is: [Laravel](https://laravel.com)'];
71+
72+
yield [
73+
'![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)',
74+
'My message is: ![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)',
75+
];
76+
77+
yield [
78+
'Visit https://laravel.com/docs to browse the documentation',
79+
'My message is: Visit https://laravel.com/docs to browse the documentation',
80+
];
81+
82+
yield [
83+
'Visit <https://laravel.com/docs> to browse the documentation',
84+
'My message is: Visit &lt;https://laravel.com/docs&gt; to browse the documentation',
85+
];
86+
87+
yield [
88+
'Visit <span>https://laravel.com/docs</span> to browse the documentation',
89+
'My message is: Visit &lt;span&gt;https://laravel.com/docs&lt;/span&gt; to browse the documentation',
90+
];
91+
}
92+
93+
#[WithMigration]
94+
#[DataProvider('markdownEncodedTemplateDataProvider')]
95+
public function testItCanAssertMarkdownEncodedStringUsingTemplate($given, $expected)
96+
{
97+
$user = UserFactory::new()->create([
98+
'name' => $given,
99+
]);
100+
101+
$mailable = new class($user) extends Mailable
102+
{
103+
public $theme = 'taylor';
104+
105+
public function __construct(public User $user)
106+
{
107+
//
108+
}
109+
110+
public function build()
111+
{
112+
return $this->markdown('message-with-template');
113+
}
114+
};
115+
116+
$mailable->assertSeeInHtml($expected, false);
117+
}
118+
119+
public static function markdownEncodedTemplateDataProvider()
120+
{
121+
yield ['[Laravel](https://laravel.com)', '<p><em>Hi</em> <a href="https://laravel.com">Laravel</a></p>'];
122+
123+
yield [
124+
'![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)',
125+
'<p><em>Hi</em> <img src="https://laravel.com/assets/img/welcome/background.svg" alt="Welcome to Laravel"></p>',
126+
];
127+
128+
yield [
129+
'Visit https://laravel.com/docs to browse the documentation',
130+
'<em>Hi</em> Visit https://laravel.com/docs to browse the documentation',
131+
];
132+
133+
yield [
134+
'Visit <https://laravel.com/docs> to browse the documentation',
135+
'<em>Hi</em> Visit &lt;https://laravel.com/docs&gt; to browse the documentation',
136+
];
137+
138+
yield [
139+
'Visit <span>https://laravel.com/docs</span> to browse the documentation',
140+
'<em>Hi</em> Visit &lt;span&gt;https://laravel.com/docs&lt;/span&gt; to browse the documentation',
141+
];
142+
}
143+
}

tests/Integration/Mail/MarkdownParserTest.php

+10
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@
1010

1111
class MarkdownParserTest extends TestCase
1212
{
13+
/** {@inheritdoc} */
14+
#[\Override]
15+
protected function tearDown(): void
16+
{
17+
Markdown::flushState();
18+
EncodedHtmlString::flushState();
19+
20+
parent::tearDown();
21+
}
22+
1323
#[DataProvider('markdownDataProvider')]
1424
public function testItCanParseMarkdownString($given, $expected)
1525
{

0 commit comments

Comments
 (0)