From 1744ba552c2e15f3f2524fcf827fb31238fd755f Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 21 Feb 2025 09:19:38 +0800 Subject: [PATCH 01/43] wip Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Mail/Markdown.php | 4 +- src/Illuminate/Mail/MarkdownString.php | 57 +++++++++++++++++++ tests/Integration/Mail/MarkdownParserTest.php | 54 ++++++++++++++++++ 3 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 src/Illuminate/Mail/MarkdownString.php create mode 100644 tests/Integration/Mail/MarkdownParserTest.php diff --git a/src/Illuminate/Mail/Markdown.php b/src/Illuminate/Mail/Markdown.php index 8faf739eb393..19649b71bc2f 100644 --- a/src/Illuminate/Mail/Markdown.php +++ b/src/Illuminate/Mail/Markdown.php @@ -101,7 +101,7 @@ public function renderText($view, array $data = []) * Parse the given Markdown text into HTML. * * @param string $text - * @return \Illuminate\Support\HtmlString + * @return \Illuminate\Mail\MarkdownString */ public static function parse($text) { @@ -114,7 +114,7 @@ public static function parse($text) $converter = new MarkdownConverter($environment); - return new HtmlString($converter->convert($text)->getContent()); + return new MarkdownString($text, $converter); } /** diff --git a/src/Illuminate/Mail/MarkdownString.php b/src/Illuminate/Mail/MarkdownString.php new file mode 100644 index 000000000000..1c442aa1de2f --- /dev/null +++ b/src/Illuminate/Mail/MarkdownString.php @@ -0,0 +1,57 @@ +converter = $converter; + } + + /** + * Get the HTML string. + * + * @return string + */ + #[\Override] + public function toHtml() + { + return $this->converter->convert($this->html)->getContent(); + } + + /** + * Resolve the displayable value that the class is deferring. + * + * @return \Illuminate\Contracts\Support\Htmlable|string + */ + public function resolveDisplayableValue() + { + $replacements = [ + '[' => '\[', + ]; + + $html = str_replace(array_keys($replacements), array_values($replacements), $this->html); + + return $this->converter->convert($html)->getContent(); + } +} diff --git a/tests/Integration/Mail/MarkdownParserTest.php b/tests/Integration/Mail/MarkdownParserTest.php new file mode 100644 index 000000000000..4bd270a17022 --- /dev/null +++ b/tests/Integration/Mail/MarkdownParserTest.php @@ -0,0 +1,54 @@ +assertInstanceOf(MarkdownString::class, $html); + $this->assertInstanceOf(HtmlString::class, $html); + $this->assertInstanceOf(DeferringDisplayableValue::class, $html); + + $this->assertSame($expected.PHP_EOL, (string) $html); + $this->assertSame((string) $html, $html->toHtml()); + }); + } + + #[DataProvider('markdownEncodedDataProvider')] + public function testItCanParseMarkdownEncodedString($given, $expected) + { + tap(Markdown::parse($given), function ($html) use ($expected) { + $this->assertInstanceOf(MarkdownString::class, $html); + $this->assertInstanceOf(HtmlString::class, $html); + $this->assertInstanceOf(DeferringDisplayableValue::class, $html); + + $this->assertSame($expected.PHP_EOL, e($html)); + }); + } + + public static function markdownDataProvider() + { + yield ['[Laravel](https://laravel.com)', '

Laravel

']; + yield ['\[Laravel](https://laravel.com)', '

[Laravel](https://laravel.com)

']; + yield ['![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', '

Welcome to Laravel

']; + yield ['!\[Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', '

![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)

']; + yield ['Visit https://laravel.com/docs to browse the documentation', '

Visit https://laravel.com/docs to browse the documentation

']; + } + + public static function markdownEncodedDataProvider() + { + yield ['[Laravel](https://laravel.com)', '<p>[Laravel](https://laravel.com)</p>']; + yield ['![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', '<p>![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)</p>']; + yield ['Visit https://laravel.com/docs to browse the documentation', '<p>Visit https://laravel.com/docs to browse the documentation</p>']; + } +} From 37a4a89d57ae89f511e0f4e397f19fe077624f12 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 21 Feb 2025 09:22:31 +0800 Subject: [PATCH 02/43] wip Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Mail/MarkdownString.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Illuminate/Mail/MarkdownString.php b/src/Illuminate/Mail/MarkdownString.php index 1c442aa1de2f..3e0800e67245 100644 --- a/src/Illuminate/Mail/MarkdownString.php +++ b/src/Illuminate/Mail/MarkdownString.php @@ -19,6 +19,7 @@ class MarkdownString extends HtmlString implements DeferringDisplayableValue * Create a new HTML string instance. * * @param string $html + * @param \League\CommonMark\MarkdownConverter $converter * @return void */ public function __construct($html = '', MarkdownConverter $converter) From ba5c5b0f66dd6cbabc5530f5e2f819036b41ac33 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 21 Feb 2025 09:23:34 +0800 Subject: [PATCH 03/43] wip Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Mail/MarkdownString.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Mail/MarkdownString.php b/src/Illuminate/Mail/MarkdownString.php index 3e0800e67245..174a22276145 100644 --- a/src/Illuminate/Mail/MarkdownString.php +++ b/src/Illuminate/Mail/MarkdownString.php @@ -22,7 +22,7 @@ class MarkdownString extends HtmlString implements DeferringDisplayableValue * @param \League\CommonMark\MarkdownConverter $converter * @return void */ - public function __construct($html = '', MarkdownConverter $converter) + public function __construct($html, MarkdownConverter $converter) { parent::__construct($html); From 98b52b1f4180f94e831a23750d012e2ecc6e8a96 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 21 Feb 2025 09:28:04 +0800 Subject: [PATCH 04/43] wip Signed-off-by: Mior Muhammad Zaki --- tests/Integration/Mail/MarkdownParserTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Integration/Mail/MarkdownParserTest.php b/tests/Integration/Mail/MarkdownParserTest.php index 4bd270a17022..bf4edf402a7d 100644 --- a/tests/Integration/Mail/MarkdownParserTest.php +++ b/tests/Integration/Mail/MarkdownParserTest.php @@ -19,7 +19,7 @@ public function testItCanParseMarkdownString($given, $expected) $this->assertInstanceOf(HtmlString::class, $html); $this->assertInstanceOf(DeferringDisplayableValue::class, $html); - $this->assertSame($expected.PHP_EOL, (string) $html); + $this->assertSame($expected.(windows_os() ? "\r\n" : PHP_EOL), (string) $html); $this->assertSame((string) $html, $html->toHtml()); }); } @@ -32,7 +32,7 @@ public function testItCanParseMarkdownEncodedString($given, $expected) $this->assertInstanceOf(HtmlString::class, $html); $this->assertInstanceOf(DeferringDisplayableValue::class, $html); - $this->assertSame($expected.PHP_EOL, e($html)); + $this->assertSame($expected.(windows_os() ? "\r\n" : PHP_EOL), e($html)); }); } From f2317c36ca145fb93245dc6108e77514777114ce Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 21 Feb 2025 09:32:10 +0800 Subject: [PATCH 05/43] wip Signed-off-by: Mior Muhammad Zaki --- tests/Integration/Mail/MarkdownParserTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Integration/Mail/MarkdownParserTest.php b/tests/Integration/Mail/MarkdownParserTest.php index bf4edf402a7d..ec22a693176c 100644 --- a/tests/Integration/Mail/MarkdownParserTest.php +++ b/tests/Integration/Mail/MarkdownParserTest.php @@ -19,7 +19,7 @@ public function testItCanParseMarkdownString($given, $expected) $this->assertInstanceOf(HtmlString::class, $html); $this->assertInstanceOf(DeferringDisplayableValue::class, $html); - $this->assertSame($expected.(windows_os() ? "\r\n" : PHP_EOL), (string) $html); + $this->assertStringEqualsStringIgnoringLineEndings($expected, (string) $html); $this->assertSame((string) $html, $html->toHtml()); }); } @@ -32,7 +32,7 @@ public function testItCanParseMarkdownEncodedString($given, $expected) $this->assertInstanceOf(HtmlString::class, $html); $this->assertInstanceOf(DeferringDisplayableValue::class, $html); - $this->assertSame($expected.(windows_os() ? "\r\n" : PHP_EOL), e($html)); + $this->assertStringEqualsStringIgnoringLineEndings($expected, e($html)); }); } From 6aa1f01b5839d353f8ca9b05ab4ba86cd4425ab5 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 21 Feb 2025 09:32:58 +0800 Subject: [PATCH 06/43] wip Signed-off-by: Mior Muhammad Zaki --- tests/Integration/Mail/MarkdownParserTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Integration/Mail/MarkdownParserTest.php b/tests/Integration/Mail/MarkdownParserTest.php index ec22a693176c..ace33dd3afeb 100644 --- a/tests/Integration/Mail/MarkdownParserTest.php +++ b/tests/Integration/Mail/MarkdownParserTest.php @@ -19,7 +19,7 @@ public function testItCanParseMarkdownString($given, $expected) $this->assertInstanceOf(HtmlString::class, $html); $this->assertInstanceOf(DeferringDisplayableValue::class, $html); - $this->assertStringEqualsStringIgnoringLineEndings($expected, (string) $html); + $this->assertStringEqualsStringIgnoringLineEndings($expected.PHP_EOL, (string) $html); $this->assertSame((string) $html, $html->toHtml()); }); } @@ -32,7 +32,7 @@ public function testItCanParseMarkdownEncodedString($given, $expected) $this->assertInstanceOf(HtmlString::class, $html); $this->assertInstanceOf(DeferringDisplayableValue::class, $html); - $this->assertStringEqualsStringIgnoringLineEndings($expected, e($html)); + $this->assertStringEqualsStringIgnoringLineEndings($expected.PHP_EOL, e($html)); }); } From e54d8a0b1b62aab885f5cca0b85d34f97e8464e1 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 21 Feb 2025 09:44:21 +0800 Subject: [PATCH 07/43] wip Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Mail/Markdown.php | 15 +-------------- src/Illuminate/Mail/MarkdownString.php | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/Illuminate/Mail/Markdown.php b/src/Illuminate/Mail/Markdown.php index 19649b71bc2f..910ed522e6f9 100644 --- a/src/Illuminate/Mail/Markdown.php +++ b/src/Illuminate/Mail/Markdown.php @@ -5,10 +5,6 @@ use Illuminate\Contracts\View\Factory as ViewFactory; use Illuminate\Support\HtmlString; use Illuminate\Support\Str; -use League\CommonMark\Environment\Environment; -use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension; -use League\CommonMark\Extension\Table\TableExtension; -use League\CommonMark\MarkdownConverter; use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles; class Markdown @@ -105,16 +101,7 @@ public function renderText($view, array $data = []) */ public static function parse($text) { - $environment = new Environment([ - 'allow_unsafe_links' => false, - ]); - - $environment->addExtension(new CommonMarkCoreExtension); - $environment->addExtension(new TableExtension); - - $converter = new MarkdownConverter($environment); - - return new MarkdownString($text, $converter); + return new MarkdownString($text); } /** diff --git a/src/Illuminate/Mail/MarkdownString.php b/src/Illuminate/Mail/MarkdownString.php index 174a22276145..13f9eddb2e72 100644 --- a/src/Illuminate/Mail/MarkdownString.php +++ b/src/Illuminate/Mail/MarkdownString.php @@ -4,6 +4,9 @@ use Illuminate\Contracts\Support\DeferringDisplayableValue; use Illuminate\Support\HtmlString; +use League\CommonMark\Environment\Environment; +use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension; +use League\CommonMark\Extension\Table\TableExtension; use League\CommonMark\MarkdownConverter; class MarkdownString extends HtmlString implements DeferringDisplayableValue @@ -19,14 +22,20 @@ class MarkdownString extends HtmlString implements DeferringDisplayableValue * Create a new HTML string instance. * * @param string $html - * @param \League\CommonMark\MarkdownConverter $converter * @return void */ - public function __construct($html, MarkdownConverter $converter) + public function __construct($html = '') { parent::__construct($html); - $this->converter = $converter; + $environment = new Environment([ + 'allow_unsafe_links' => false, + ]); + + $environment->addExtension(new CommonMarkCoreExtension); + $environment->addExtension(new TableExtension); + + $this->converter = new MarkdownConverter($environment); } /** From ca869ce478e8983aa2d29c27e37442b142448610 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 21 Feb 2025 10:30:03 +0800 Subject: [PATCH 08/43] wip Signed-off-by: Mior Muhammad Zaki --- tests/Integration/Mail/MarkdownParserTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Integration/Mail/MarkdownParserTest.php b/tests/Integration/Mail/MarkdownParserTest.php index ace33dd3afeb..06603a7cfdfb 100644 --- a/tests/Integration/Mail/MarkdownParserTest.php +++ b/tests/Integration/Mail/MarkdownParserTest.php @@ -43,6 +43,7 @@ public static function markdownDataProvider() yield ['![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', '

Welcome to Laravel

']; yield ['!\[Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', '

![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)

']; yield ['Visit https://laravel.com/docs to browse the documentation', '

Visit https://laravel.com/docs to browse the documentation

']; + yield ['Visit to browse the documentation', '

Visit https://laravel.com/docs to browse the documentation

']; } public static function markdownEncodedDataProvider() @@ -50,5 +51,6 @@ public static function markdownEncodedDataProvider() yield ['[Laravel](https://laravel.com)', '<p>[Laravel](https://laravel.com)</p>']; yield ['![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', '<p>![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)</p>']; yield ['Visit https://laravel.com/docs to browse the documentation', '<p>Visit https://laravel.com/docs to browse the documentation</p>']; + yield ['Visit to browse the documentation', '<p>Visit <a href="https://laravel.com/docs">https://laravel.com/docs</a> to browse the documentation</p>']; } } From f76996ff0cad7c526046e71a61e9c71825d28368 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 21 Feb 2025 12:02:52 +0800 Subject: [PATCH 09/43] wip Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Mail/MarkdownString.php | 51 ++++++++----------- tests/Integration/Mail/MarkdownParserTest.php | 8 +-- 2 files changed, 26 insertions(+), 33 deletions(-) diff --git a/src/Illuminate/Mail/MarkdownString.php b/src/Illuminate/Mail/MarkdownString.php index 13f9eddb2e72..8f9b83a6a32e 100644 --- a/src/Illuminate/Mail/MarkdownString.php +++ b/src/Illuminate/Mail/MarkdownString.php @@ -11,33 +11,6 @@ class MarkdownString extends HtmlString implements DeferringDisplayableValue { - /** - * The Markdown Converter implementation. - * - * @var \League\CommonMark\MarkdownConverter - */ - protected $converter; - - /** - * Create a new HTML string instance. - * - * @param string $html - * @return void - */ - public function __construct($html = '') - { - parent::__construct($html); - - $environment = new Environment([ - 'allow_unsafe_links' => false, - ]); - - $environment->addExtension(new CommonMarkCoreExtension); - $environment->addExtension(new TableExtension); - - $this->converter = new MarkdownConverter($environment); - } - /** * Get the HTML string. * @@ -46,7 +19,7 @@ public function __construct($html = '') #[\Override] public function toHtml() { - return $this->converter->convert($this->html)->getContent(); + return $this->converter()->convert($this->html)->getContent(); } /** @@ -62,6 +35,26 @@ public function resolveDisplayableValue() $html = str_replace(array_keys($replacements), array_values($replacements), $this->html); - return $this->converter->convert($html)->getContent(); + return new HtmlString($this->converter([ + 'html_input' => 'escape', + ])->convert($html)->getContent()); + } + + /** + * Resolve the Markdown Converter. + * + * @param array $config + * @return \League\CommonMark\MarkdownConverter + */ + protected function converter(array $config = []) + { + $environment = new Environment(array_merge([ + ['allow_unsafe_links' => false], + ], $config)); + + $environment->addExtension(new CommonMarkCoreExtension); + $environment->addExtension(new TableExtension); + + return new MarkdownConverter($environment); } } diff --git a/tests/Integration/Mail/MarkdownParserTest.php b/tests/Integration/Mail/MarkdownParserTest.php index 06603a7cfdfb..13ce4660746c 100644 --- a/tests/Integration/Mail/MarkdownParserTest.php +++ b/tests/Integration/Mail/MarkdownParserTest.php @@ -48,9 +48,9 @@ public static function markdownDataProvider() public static function markdownEncodedDataProvider() { - yield ['[Laravel](https://laravel.com)', '<p>[Laravel](https://laravel.com)</p>']; - yield ['![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', '<p>![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)</p>']; - yield ['Visit https://laravel.com/docs to browse the documentation', '<p>Visit https://laravel.com/docs to browse the documentation</p>']; - yield ['Visit to browse the documentation', '<p>Visit <a href="https://laravel.com/docs">https://laravel.com/docs</a> to browse the documentation</p>']; + yield ['[Laravel](https://laravel.com)', '

[Laravel](https://laravel.com)

']; + yield ['![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', '

![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)

']; + yield ['Visit https://laravel.com/docs to browse the documentation', '

Visit https://laravel.com/docs to browse the documentation

']; + yield ['Visit to browse the documentation', '

Visit https://laravel.com/docs to browse the documentation

']; } } From 208256f42a4c62fbf96cbecef44d78f113a4e39c Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 21 Feb 2025 12:20:16 +0800 Subject: [PATCH 10/43] wip Signed-off-by: Mior Muhammad Zaki --- tests/Integration/Mail/MarkdownParserTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Integration/Mail/MarkdownParserTest.php b/tests/Integration/Mail/MarkdownParserTest.php index 13ce4660746c..8d58eeaea574 100644 --- a/tests/Integration/Mail/MarkdownParserTest.php +++ b/tests/Integration/Mail/MarkdownParserTest.php @@ -44,6 +44,7 @@ public static function markdownDataProvider() yield ['!\[Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', '

![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)

']; yield ['Visit https://laravel.com/docs to browse the documentation', '

Visit https://laravel.com/docs to browse the documentation

']; yield ['Visit to browse the documentation', '

Visit https://laravel.com/docs to browse the documentation

']; + yield ['Visit https://laravel.com/docs to browse the documentation', '

Visit https://laravel.com/docs to browse the documentation

']; } public static function markdownEncodedDataProvider() @@ -52,5 +53,6 @@ public static function markdownEncodedDataProvider() yield ['![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', '

![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)

']; yield ['Visit https://laravel.com/docs to browse the documentation', '

Visit https://laravel.com/docs to browse the documentation

']; yield ['Visit to browse the documentation', '

Visit https://laravel.com/docs to browse the documentation

']; + yield ['Visit https://laravel.com/docs to browse the documentation', '

Visit <span>https://laravel.com/docs</span> to browse the documentation

']; } } From cdca9732bcf13e4d6fad0f5e86bffc5d9b0c2764 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 21 Feb 2025 12:25:24 +0800 Subject: [PATCH 11/43] wip Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Mail/MarkdownString.php | 1 + tests/Integration/Mail/MarkdownParserTest.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Mail/MarkdownString.php b/src/Illuminate/Mail/MarkdownString.php index 8f9b83a6a32e..f3150692adec 100644 --- a/src/Illuminate/Mail/MarkdownString.php +++ b/src/Illuminate/Mail/MarkdownString.php @@ -31,6 +31,7 @@ public function resolveDisplayableValue() { $replacements = [ '[' => '\[', + '<' => '\<', ]; $html = str_replace(array_keys($replacements), array_values($replacements), $this->html); diff --git a/tests/Integration/Mail/MarkdownParserTest.php b/tests/Integration/Mail/MarkdownParserTest.php index 8d58eeaea574..d7a9ca948d63 100644 --- a/tests/Integration/Mail/MarkdownParserTest.php +++ b/tests/Integration/Mail/MarkdownParserTest.php @@ -52,7 +52,7 @@ public static function markdownEncodedDataProvider() yield ['[Laravel](https://laravel.com)', '

[Laravel](https://laravel.com)

']; yield ['![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', '

![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)

']; yield ['Visit https://laravel.com/docs to browse the documentation', '

Visit https://laravel.com/docs to browse the documentation

']; - yield ['Visit to browse the documentation', '

Visit https://laravel.com/docs to browse the documentation

']; + yield ['Visit to browse the documentation', '

Visit <https://laravel.com/docs> to browse the documentation

']; yield ['Visit https://laravel.com/docs to browse the documentation', '

Visit <span>https://laravel.com/docs</span> to browse the documentation

']; } } From d4055511cd2eb4061c2f824ea0134d27cb5f490b Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 21 Feb 2025 12:28:05 +0800 Subject: [PATCH 12/43] wip Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Mail/MarkdownString.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Mail/MarkdownString.php b/src/Illuminate/Mail/MarkdownString.php index f3150692adec..d6ea5b7cbf09 100644 --- a/src/Illuminate/Mail/MarkdownString.php +++ b/src/Illuminate/Mail/MarkdownString.php @@ -50,7 +50,7 @@ public function resolveDisplayableValue() protected function converter(array $config = []) { $environment = new Environment(array_merge([ - ['allow_unsafe_links' => false], + 'allow_unsafe_links' => false, ], $config)); $environment->addExtension(new CommonMarkCoreExtension); From 1f0889f1550dac9f5a627aa19dd86c18534d8be3 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 21 Feb 2025 14:39:09 +0800 Subject: [PATCH 13/43] wip Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Mail/MarkdownString.php | 38 +++++++++++++++---- .../resources/views/html/footer.blade.php | 2 +- .../resources/views/html/layout.blade.php | 2 +- .../Mail/resources/views/html/panel.blade.php | 2 +- .../resources/views/html/subcopy.blade.php | 2 +- .../Mail/resources/views/html/table.blade.php | 2 +- 6 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/Illuminate/Mail/MarkdownString.php b/src/Illuminate/Mail/MarkdownString.php index d6ea5b7cbf09..073990540802 100644 --- a/src/Illuminate/Mail/MarkdownString.php +++ b/src/Illuminate/Mail/MarkdownString.php @@ -12,22 +12,23 @@ class MarkdownString extends HtmlString implements DeferringDisplayableValue { /** - * Get the HTML string. + * Convert markdown to instance of HtmlString. * - * @return string + * @return \Illuminate\Contracts\Support\Htmlable */ - #[\Override] - public function toHtml() + public function convertMarkdownToHtml() { - return $this->converter()->convert($this->html)->getContent(); + return new HtmlString( + $this->converter()->convert($this->html)->getContent() + ); } /** - * Resolve the displayable value that the class is deferring. + * Convert encoded markdown to instance of HtmlString. * - * @return \Illuminate\Contracts\Support\Htmlable|string + * @return \Illuminate\Contracts\Support\Htmlable */ - public function resolveDisplayableValue() + public function convertEncodedMarkdownToHtml() { $replacements = [ '[' => '\[', @@ -41,6 +42,27 @@ public function resolveDisplayableValue() ])->convert($html)->getContent()); } + /** + * Resolve the displayable value that the class is deferring. + * + * @return \Illuminate\Contracts\Support\Htmlable + */ + public function resolveDisplayableValue() + { + return $this->convertEncodedMarkdownToHtml(); + } + + /** + * Get the HTML string. + * + * @return string + */ + #[\Override] + public function toHtml() + { + return $this->convertMarkdownToHtml()->toHtml(); + } + /** * Resolve the Markdown Converter. * diff --git a/src/Illuminate/Mail/resources/views/html/footer.blade.php b/src/Illuminate/Mail/resources/views/html/footer.blade.php index 3ff41f89cb90..778ada233e7c 100644 --- a/src/Illuminate/Mail/resources/views/html/footer.blade.php +++ b/src/Illuminate/Mail/resources/views/html/footer.blade.php @@ -3,7 +3,7 @@ diff --git a/src/Illuminate/Mail/resources/views/html/layout.blade.php b/src/Illuminate/Mail/resources/views/html/layout.blade.php index d31a01de8630..87b88e4d836c 100644 --- a/src/Illuminate/Mail/resources/views/html/layout.blade.php +++ b/src/Illuminate/Mail/resources/views/html/layout.blade.php @@ -40,7 +40,7 @@ -{{ Illuminate\Mail\Markdown::parse($slot) }} +{{ Illuminate\Mail\Markdown::parse($slot)->convertMarkdownToHtml() }} {{ $subcopy ?? '' }} diff --git a/src/Illuminate/Mail/resources/views/html/panel.blade.php b/src/Illuminate/Mail/resources/views/html/panel.blade.php index 2975a60a021e..a5d3a624b899 100644 --- a/src/Illuminate/Mail/resources/views/html/panel.blade.php +++ b/src/Illuminate/Mail/resources/views/html/panel.blade.php @@ -4,7 +4,7 @@
-{{ Illuminate\Mail\Markdown::parse($slot) }} +{{ Illuminate\Mail\Markdown::parse($slot)->convertMarkdownToHtml() }}
diff --git a/src/Illuminate/Mail/resources/views/html/subcopy.blade.php b/src/Illuminate/Mail/resources/views/html/subcopy.blade.php index 790ce6c2498a..f1ef07450da4 100644 --- a/src/Illuminate/Mail/resources/views/html/subcopy.blade.php +++ b/src/Illuminate/Mail/resources/views/html/subcopy.blade.php @@ -1,7 +1,7 @@ diff --git a/src/Illuminate/Mail/resources/views/html/table.blade.php b/src/Illuminate/Mail/resources/views/html/table.blade.php index a5f3348b233a..b58e0732d946 100644 --- a/src/Illuminate/Mail/resources/views/html/table.blade.php +++ b/src/Illuminate/Mail/resources/views/html/table.blade.php @@ -1,3 +1,3 @@
-{{ Illuminate\Mail\Markdown::parse($slot) }} +{{ Illuminate\Mail\Markdown::parse($slot)->convertMarkdownToHtml() }}
From bbd61dc67cf7c5fdd9111a223e8e8dffbb83a206 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Mon, 24 Feb 2025 09:35:32 +0800 Subject: [PATCH 14/43] wip Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Mail/MarkdownString.php | 65 +++++++------------ src/Illuminate/Support/EncodedHtmlString.php | 58 +++++++++++++++++ src/Illuminate/Support/helpers.php | 5 +- tests/Integration/Mail/MarkdownParserTest.php | 37 ++++++++--- tests/Support/SupportHelpersTest.php | 16 +++-- 5 files changed, 122 insertions(+), 59 deletions(-) create mode 100644 src/Illuminate/Support/EncodedHtmlString.php diff --git a/src/Illuminate/Mail/MarkdownString.php b/src/Illuminate/Mail/MarkdownString.php index 073990540802..3208c4e339d6 100644 --- a/src/Illuminate/Mail/MarkdownString.php +++ b/src/Illuminate/Mail/MarkdownString.php @@ -3,55 +3,15 @@ namespace Illuminate\Mail; use Illuminate\Contracts\Support\DeferringDisplayableValue; +use Illuminate\Support\EncodedHtmlString; use Illuminate\Support\HtmlString; use League\CommonMark\Environment\Environment; use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension; use League\CommonMark\Extension\Table\TableExtension; use League\CommonMark\MarkdownConverter; -class MarkdownString extends HtmlString implements DeferringDisplayableValue +class MarkdownString extends HtmlString { - /** - * Convert markdown to instance of HtmlString. - * - * @return \Illuminate\Contracts\Support\Htmlable - */ - public function convertMarkdownToHtml() - { - return new HtmlString( - $this->converter()->convert($this->html)->getContent() - ); - } - - /** - * Convert encoded markdown to instance of HtmlString. - * - * @return \Illuminate\Contracts\Support\Htmlable - */ - public function convertEncodedMarkdownToHtml() - { - $replacements = [ - '[' => '\[', - '<' => '\<', - ]; - - $html = str_replace(array_keys($replacements), array_values($replacements), $this->html); - - return new HtmlString($this->converter([ - 'html_input' => 'escape', - ])->convert($html)->getContent()); - } - - /** - * Resolve the displayable value that the class is deferring. - * - * @return \Illuminate\Contracts\Support\Htmlable - */ - public function resolveDisplayableValue() - { - return $this->convertEncodedMarkdownToHtml(); - } - /** * Get the HTML string. * @@ -60,7 +20,26 @@ public function resolveDisplayableValue() #[\Override] public function toHtml() { - return $this->convertMarkdownToHtml()->toHtml(); + EncodedHtmlString::encodeUsing(function ($value) { + $replacements = [ + '[' => '\[', + '<' => '\<', + ]; + + $html = str_replace(array_keys($replacements), array_values($replacements), $value); + + return $this->converter([ + 'html_input' => 'escape', + ])->convert($html)->getContent(); + }); + + try { + $html = $this->converter()->convert($this->html)->getContent(); + } finally { + EncodedHtmlString::flushState(); + } + + return new HtmlString($html ?? ''); } /** diff --git a/src/Illuminate/Support/EncodedHtmlString.php b/src/Illuminate/Support/EncodedHtmlString.php new file mode 100644 index 000000000000..47db90ea32d0 --- /dev/null +++ b/src/Illuminate/Support/EncodedHtmlString.php @@ -0,0 +1,58 @@ +html, $this->doubleEncode); + } + + /** + * Set the callable that will be used to encode the html strings. + * + * @param callable|null $factory + * @return void + */ + public static function encodeUsing(?callable $factory = null) + { + static::$encodeUsingFactory = $factory; + } + + /** + * Flush the class's global state. + * + * @return void + */ + public static function flushState() + { + static::$encodeUsingFactory = null; + } +} diff --git a/src/Illuminate/Support/helpers.php b/src/Illuminate/Support/helpers.php index 8ecb1eb3cffe..fc05dbc44127 100644 --- a/src/Illuminate/Support/helpers.php +++ b/src/Illuminate/Support/helpers.php @@ -4,6 +4,7 @@ use Illuminate\Contracts\Support\Htmlable; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Arr; +use Illuminate\Support\EncodedHtmlString; use Illuminate\Support\Env; use Illuminate\Support\Fluent; use Illuminate\Support\HigherOrderTapProxy; @@ -122,7 +123,7 @@ function class_uses_recursive($class) * * @param \Illuminate\Contracts\Support\DeferringDisplayableValue|\Illuminate\Contracts\Support\Htmlable|\BackedEnum|string|int|float|null $value * @param bool $doubleEncode - * @return string + * @return \Illuminate\Support\EncodedHtmlString */ function e($value, $doubleEncode = true) { @@ -138,7 +139,7 @@ function e($value, $doubleEncode = true) $value = $value->value; } - return htmlspecialchars($value ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', $doubleEncode); + return new EncodedHtmlString($value ?? '', $doubleEncode); } } diff --git a/tests/Integration/Mail/MarkdownParserTest.php b/tests/Integration/Mail/MarkdownParserTest.php index d7a9ca948d63..d3d97d8230c2 100644 --- a/tests/Integration/Mail/MarkdownParserTest.php +++ b/tests/Integration/Mail/MarkdownParserTest.php @@ -17,10 +17,9 @@ public function testItCanParseMarkdownString($given, $expected) tap(Markdown::parse($given), function ($html) use ($expected) { $this->assertInstanceOf(MarkdownString::class, $html); $this->assertInstanceOf(HtmlString::class, $html); - $this->assertInstanceOf(DeferringDisplayableValue::class, $html); $this->assertStringEqualsStringIgnoringLineEndings($expected.PHP_EOL, (string) $html); - $this->assertSame((string) $html, $html->toHtml()); + $this->assertSame((string) $html, (string) $html->toHtml()); }); } @@ -30,9 +29,8 @@ public function testItCanParseMarkdownEncodedString($given, $expected) tap(Markdown::parse($given), function ($html) use ($expected) { $this->assertInstanceOf(MarkdownString::class, $html); $this->assertInstanceOf(HtmlString::class, $html); - $this->assertInstanceOf(DeferringDisplayableValue::class, $html); - $this->assertStringEqualsStringIgnoringLineEndings($expected.PHP_EOL, e($html)); + $this->assertStringEqualsStringIgnoringLineEndings($expected.PHP_EOL, (string) $html); }); } @@ -49,10 +47,31 @@ public static function markdownDataProvider() public static function markdownEncodedDataProvider() { - yield ['[Laravel](https://laravel.com)', '

[Laravel](https://laravel.com)

']; - yield ['![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', '

![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)

']; - yield ['Visit https://laravel.com/docs to browse the documentation', '

Visit https://laravel.com/docs to browse the documentation

']; - yield ['Visit to browse the documentation', '

Visit <https://laravel.com/docs> to browse the documentation

']; - yield ['Visit https://laravel.com/docs to browse the documentation', '

Visit <span>https://laravel.com/docs</span> to browse the documentation

']; + yield [e('[Laravel](https://laravel.com)'), '

[Laravel](https://laravel.com)

']; + + yield [ + e('![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)'), + '

![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)

' + ]; + + yield [ + e('Visit https://laravel.com/docs to browse the documentation'), + '

Visit https://laravel.com/docs to browse the documentation

' + ]; + + yield [ + e('Visit to browse the documentation'), + '

Visit <https://laravel.com/docs> to browse the documentation

' + ]; + + yield [ + e('Visit https://laravel.com/docs to browse the documentation'), + '

Visit <span>https://laravel.com/docs</span> to browse the documentation

' + ]; + + yield [ + '![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)
'.e('Visit https://laravel.com/docs to browse the documentation'), + '

Welcome to Laravel
Visit <span>https://laravel.com/docs</span> to browse the documentation

' + ]; } } diff --git a/tests/Support/SupportHelpersTest.php b/tests/Support/SupportHelpersTest.php index e23877584968..415465d294d3 100644 --- a/tests/Support/SupportHelpersTest.php +++ b/tests/Support/SupportHelpersTest.php @@ -8,6 +8,7 @@ use Error; use Illuminate\Contracts\Support\Htmlable; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\EncodedHtmlString; use Illuminate\Support\Env; use Illuminate\Support\Optional; use Illuminate\Support\Sleep; @@ -21,8 +22,8 @@ use PHPUnit\Framework\TestCase; use ReflectionClass; use RuntimeException; -use stdClass; use Traversable; +use stdClass; class SupportHelpersTest extends TestCase { @@ -36,26 +37,31 @@ protected function tearDown(): void public function testE() { $str = 'A \'quote\' is bold'; - $this->assertSame('A 'quote' is <b>bold</b>', e($str)); + tap(e($str), function ($html) use ($str) { + $this->assertInstanceOf(EncodedHtmlString::class, $html); + $this->assertEquals('A 'quote' is <b>bold</b>', $html->toHtml()); + $this->assertEquals('A 'quote' is <b>bold</b>', (string) $html); + }); $html = m::mock(Htmlable::class); $html->shouldReceive('toHtml')->andReturn($str); + $this->assertEquals($str, e($html)); } public function testEWithInvalidCodePoints() { $str = mb_convert_encoding('føø bar', 'ISO-8859-1', 'UTF-8'); - $this->assertEquals('f�� bar', e($str)); + $this->assertEquals('f�� bar', (string) e($str)); } public function testEWithEnums() { $enumValue = StringBackedEnum::ADMIN_LABEL; - $this->assertSame('I am 'admin'', e($enumValue)); + $this->assertSame('I am 'admin'', (string) e($enumValue)); $enumValue = IntBackedEnum::ROLE_ADMIN; - $this->assertSame('1', e($enumValue)); + $this->assertSame('1', (string) e($enumValue)); } public function testBlank() From 1859539e2d7f76a4325ed6b7ee051843d04887a4 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Mon, 24 Feb 2025 01:35:55 +0000 Subject: [PATCH 15/43] Apply fixes from StyleCI --- src/Illuminate/Mail/MarkdownString.php | 1 - tests/Integration/Mail/MarkdownParserTest.php | 11 +++++------ tests/Support/SupportHelpersTest.php | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Illuminate/Mail/MarkdownString.php b/src/Illuminate/Mail/MarkdownString.php index 3208c4e339d6..ea7a7ea871c1 100644 --- a/src/Illuminate/Mail/MarkdownString.php +++ b/src/Illuminate/Mail/MarkdownString.php @@ -2,7 +2,6 @@ namespace Illuminate\Mail; -use Illuminate\Contracts\Support\DeferringDisplayableValue; use Illuminate\Support\EncodedHtmlString; use Illuminate\Support\HtmlString; use League\CommonMark\Environment\Environment; diff --git a/tests/Integration/Mail/MarkdownParserTest.php b/tests/Integration/Mail/MarkdownParserTest.php index d3d97d8230c2..5ca9bc717105 100644 --- a/tests/Integration/Mail/MarkdownParserTest.php +++ b/tests/Integration/Mail/MarkdownParserTest.php @@ -2,7 +2,6 @@ namespace Illuminate\Tests\Integration\Mail; -use Illuminate\Contracts\Support\DeferringDisplayableValue; use Illuminate\Mail\Markdown; use Illuminate\Mail\MarkdownString; use Illuminate\Support\HtmlString; @@ -51,27 +50,27 @@ public static function markdownEncodedDataProvider() yield [ e('![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)'), - '

![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)

' + '

![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)

', ]; yield [ e('Visit https://laravel.com/docs to browse the documentation'), - '

Visit https://laravel.com/docs to browse the documentation

' + '

Visit https://laravel.com/docs to browse the documentation

', ]; yield [ e('Visit to browse the documentation'), - '

Visit <https://laravel.com/docs> to browse the documentation

' + '

Visit <https://laravel.com/docs> to browse the documentation

', ]; yield [ e('Visit https://laravel.com/docs to browse the documentation'), - '

Visit <span>https://laravel.com/docs</span> to browse the documentation

' + '

Visit <span>https://laravel.com/docs</span> to browse the documentation

', ]; yield [ '![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)
'.e('Visit https://laravel.com/docs to browse the documentation'), - '

Welcome to Laravel
Visit <span>https://laravel.com/docs</span> to browse the documentation

' + '

Welcome to Laravel
Visit <span>https://laravel.com/docs</span> to browse the documentation

', ]; } } diff --git a/tests/Support/SupportHelpersTest.php b/tests/Support/SupportHelpersTest.php index 415465d294d3..a2cba4afa20d 100644 --- a/tests/Support/SupportHelpersTest.php +++ b/tests/Support/SupportHelpersTest.php @@ -22,8 +22,8 @@ use PHPUnit\Framework\TestCase; use ReflectionClass; use RuntimeException; -use Traversable; use stdClass; +use Traversable; class SupportHelpersTest extends TestCase { @@ -37,7 +37,7 @@ protected function tearDown(): void public function testE() { $str = 'A \'quote\' is bold'; - tap(e($str), function ($html) use ($str) { + tap(e($str), function ($html) { $this->assertInstanceOf(EncodedHtmlString::class, $html); $this->assertEquals('A 'quote' is <b>bold</b>', $html->toHtml()); $this->assertEquals('A 'quote' is <b>bold</b>', (string) $html); From e1b431503d3e746910f53223c3cff61e410934f0 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Mon, 24 Feb 2025 09:36:39 +0800 Subject: [PATCH 16/43] wip Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Mail/resources/views/html/footer.blade.php | 2 +- src/Illuminate/Mail/resources/views/html/layout.blade.php | 2 +- src/Illuminate/Mail/resources/views/html/panel.blade.php | 2 +- src/Illuminate/Mail/resources/views/html/subcopy.blade.php | 2 +- src/Illuminate/Mail/resources/views/html/table.blade.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Illuminate/Mail/resources/views/html/footer.blade.php b/src/Illuminate/Mail/resources/views/html/footer.blade.php index 778ada233e7c..3ff41f89cb90 100644 --- a/src/Illuminate/Mail/resources/views/html/footer.blade.php +++ b/src/Illuminate/Mail/resources/views/html/footer.blade.php @@ -3,7 +3,7 @@ diff --git a/src/Illuminate/Mail/resources/views/html/layout.blade.php b/src/Illuminate/Mail/resources/views/html/layout.blade.php index 87b88e4d836c..d31a01de8630 100644 --- a/src/Illuminate/Mail/resources/views/html/layout.blade.php +++ b/src/Illuminate/Mail/resources/views/html/layout.blade.php @@ -40,7 +40,7 @@ -{{ Illuminate\Mail\Markdown::parse($slot)->convertMarkdownToHtml() }} +{{ Illuminate\Mail\Markdown::parse($slot) }} {{ $subcopy ?? '' }} diff --git a/src/Illuminate/Mail/resources/views/html/panel.blade.php b/src/Illuminate/Mail/resources/views/html/panel.blade.php index a5d3a624b899..2975a60a021e 100644 --- a/src/Illuminate/Mail/resources/views/html/panel.blade.php +++ b/src/Illuminate/Mail/resources/views/html/panel.blade.php @@ -4,7 +4,7 @@
-{{ Illuminate\Mail\Markdown::parse($slot)->convertMarkdownToHtml() }} +{{ Illuminate\Mail\Markdown::parse($slot) }}
diff --git a/src/Illuminate/Mail/resources/views/html/subcopy.blade.php b/src/Illuminate/Mail/resources/views/html/subcopy.blade.php index f1ef07450da4..790ce6c2498a 100644 --- a/src/Illuminate/Mail/resources/views/html/subcopy.blade.php +++ b/src/Illuminate/Mail/resources/views/html/subcopy.blade.php @@ -1,7 +1,7 @@ diff --git a/src/Illuminate/Mail/resources/views/html/table.blade.php b/src/Illuminate/Mail/resources/views/html/table.blade.php index b58e0732d946..a5f3348b233a 100644 --- a/src/Illuminate/Mail/resources/views/html/table.blade.php +++ b/src/Illuminate/Mail/resources/views/html/table.blade.php @@ -1,3 +1,3 @@
-{{ Illuminate\Mail\Markdown::parse($slot)->convertMarkdownToHtml() }} +{{ Illuminate\Mail\Markdown::parse($slot) }}
From dc4e4bac37969958fae54cd96c891ff48ff61bd6 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Mon, 24 Feb 2025 09:38:16 +0800 Subject: [PATCH 17/43] wip Signed-off-by: Mior Muhammad Zaki --- tests/Support/SupportHelpersTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Support/SupportHelpersTest.php b/tests/Support/SupportHelpersTest.php index a2cba4afa20d..99aa80c9edc5 100644 --- a/tests/Support/SupportHelpersTest.php +++ b/tests/Support/SupportHelpersTest.php @@ -45,7 +45,6 @@ public function testE() $html = m::mock(Htmlable::class); $html->shouldReceive('toHtml')->andReturn($str); - $this->assertEquals($str, e($html)); } From 4dfa33c9932007a821555a240c2b3f653ba02690 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Mon, 24 Feb 2025 09:44:17 +0800 Subject: [PATCH 18/43] wip Signed-off-by: Mior Muhammad Zaki --- .../Testing/Concerns/InteractsWithTestCaseLifecycle.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php index 4be085daa39c..4b81d3c4d45d 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php +++ b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php @@ -26,6 +26,7 @@ use Illuminate\Queue\Console\WorkCommand; use Illuminate\Queue\Queue; use Illuminate\Support\Carbon; +use Illuminate\Support\EncodedHtmlString; use Illuminate\Support\Facades\Facade; use Illuminate\Support\Facades\ParallelTesting; use Illuminate\Support\Once; @@ -171,6 +172,7 @@ protected function tearDownTheTestEnvironment(): void Component::forgetFactory(); ConvertEmptyStringsToNull::flushState(); Factory::flushState(); + EncodedHtmlString::flushState(); EncryptCookies::flushState(); HandleExceptions::flushState(); Migrator::withoutMigrations([]); From 93d84181095d7b49dc15906739a881ef2bb83ae5 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Mon, 24 Feb 2025 09:47:56 +0800 Subject: [PATCH 19/43] wip Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Mail/Markdown.php | 46 ++++++++++++++++++- tests/Integration/Mail/MarkdownParserTest.php | 2 - 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/Illuminate/Mail/Markdown.php b/src/Illuminate/Mail/Markdown.php index 910ed522e6f9..d223d1a4cd76 100644 --- a/src/Illuminate/Mail/Markdown.php +++ b/src/Illuminate/Mail/Markdown.php @@ -3,8 +3,13 @@ namespace Illuminate\Mail; use Illuminate\Contracts\View\Factory as ViewFactory; +use Illuminate\Support\EncodedHtmlString; use Illuminate\Support\HtmlString; use Illuminate\Support\Str; +use League\CommonMark\Environment\Environment; +use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension; +use League\CommonMark\Extension\Table\TableExtension; +use League\CommonMark\MarkdownConverter; use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles; class Markdown @@ -97,11 +102,30 @@ public function renderText($view, array $data = []) * Parse the given Markdown text into HTML. * * @param string $text - * @return \Illuminate\Mail\MarkdownString + * @return \Illuminate\Support\HtmlString */ public static function parse($text) { - return new MarkdownString($text); + EncodedHtmlString::encodeUsing(function ($value) { + $replacements = [ + '[' => '\[', + '<' => '\<', + ]; + + $html = str_replace(array_keys($replacements), array_values($replacements), $value); + + return static::converter([ + 'html_input' => 'escape', + ])->convert($html)->getContent(); + }); + + try { + $html = static::converter()->convert($text)->getContent(); + } finally { + EncodedHtmlString::flushState(); + } + + return new HtmlString($html ?? ''); } /** @@ -173,4 +197,22 @@ public function getTheme() { return $this->theme; } + + /** + * Resolve the Markdown Converter. + * + * @param array $config + * @return \League\CommonMark\MarkdownConverter + */ + public static function converter(array $config = []) + { + $environment = new Environment(array_merge([ + 'allow_unsafe_links' => false, + ], $config)); + + $environment->addExtension(new CommonMarkCoreExtension); + $environment->addExtension(new TableExtension); + + return new MarkdownConverter($environment); + } } diff --git a/tests/Integration/Mail/MarkdownParserTest.php b/tests/Integration/Mail/MarkdownParserTest.php index 5ca9bc717105..3d074504c338 100644 --- a/tests/Integration/Mail/MarkdownParserTest.php +++ b/tests/Integration/Mail/MarkdownParserTest.php @@ -14,7 +14,6 @@ class MarkdownParserTest extends TestCase public function testItCanParseMarkdownString($given, $expected) { tap(Markdown::parse($given), function ($html) use ($expected) { - $this->assertInstanceOf(MarkdownString::class, $html); $this->assertInstanceOf(HtmlString::class, $html); $this->assertStringEqualsStringIgnoringLineEndings($expected.PHP_EOL, (string) $html); @@ -26,7 +25,6 @@ public function testItCanParseMarkdownString($given, $expected) public function testItCanParseMarkdownEncodedString($given, $expected) { tap(Markdown::parse($given), function ($html) use ($expected) { - $this->assertInstanceOf(MarkdownString::class, $html); $this->assertInstanceOf(HtmlString::class, $html); $this->assertStringEqualsStringIgnoringLineEndings($expected.PHP_EOL, (string) $html); From cd647d12fe796830645cdf9eae3dbc7fe1c93ef4 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Mon, 24 Feb 2025 01:48:10 +0000 Subject: [PATCH 20/43] Apply fixes from StyleCI --- tests/Integration/Mail/MarkdownParserTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Integration/Mail/MarkdownParserTest.php b/tests/Integration/Mail/MarkdownParserTest.php index 3d074504c338..16b24bcd4398 100644 --- a/tests/Integration/Mail/MarkdownParserTest.php +++ b/tests/Integration/Mail/MarkdownParserTest.php @@ -3,7 +3,6 @@ namespace Illuminate\Tests\Integration\Mail; use Illuminate\Mail\Markdown; -use Illuminate\Mail\MarkdownString; use Illuminate\Support\HtmlString; use Orchestra\Testbench\TestCase; use PHPUnit\Framework\Attributes\DataProvider; From 4bf64bb9eae1517049605773eb16692a03d7c30a Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Mon, 24 Feb 2025 09:48:13 +0800 Subject: [PATCH 21/43] wip Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Mail/MarkdownString.php | 61 -------------------------- 1 file changed, 61 deletions(-) delete mode 100644 src/Illuminate/Mail/MarkdownString.php diff --git a/src/Illuminate/Mail/MarkdownString.php b/src/Illuminate/Mail/MarkdownString.php deleted file mode 100644 index ea7a7ea871c1..000000000000 --- a/src/Illuminate/Mail/MarkdownString.php +++ /dev/null @@ -1,61 +0,0 @@ - '\[', - '<' => '\<', - ]; - - $html = str_replace(array_keys($replacements), array_values($replacements), $value); - - return $this->converter([ - 'html_input' => 'escape', - ])->convert($html)->getContent(); - }); - - try { - $html = $this->converter()->convert($this->html)->getContent(); - } finally { - EncodedHtmlString::flushState(); - } - - return new HtmlString($html ?? ''); - } - - /** - * Resolve the Markdown Converter. - * - * @param array $config - * @return \League\CommonMark\MarkdownConverter - */ - protected function converter(array $config = []) - { - $environment = new Environment(array_merge([ - 'allow_unsafe_links' => false, - ], $config)); - - $environment->addExtension(new CommonMarkCoreExtension); - $environment->addExtension(new TableExtension); - - return new MarkdownConverter($environment); - } -} From ab4353434ac48407f77eff18e2a5fee5bc55a678 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Mon, 24 Feb 2025 10:15:02 +0800 Subject: [PATCH 22/43] wip Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Mail/Markdown.php | 4 +++- src/Illuminate/Support/EncodedHtmlString.php | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Mail/Markdown.php b/src/Illuminate/Mail/Markdown.php index d223d1a4cd76..c57bc5281625 100644 --- a/src/Illuminate/Mail/Markdown.php +++ b/src/Illuminate/Mail/Markdown.php @@ -119,13 +119,15 @@ public static function parse($text) ])->convert($html)->getContent(); }); + $html = ''; + try { $html = static::converter()->convert($text)->getContent(); } finally { EncodedHtmlString::flushState(); } - return new HtmlString($html ?? ''); + return new HtmlString($html); } /** diff --git a/src/Illuminate/Support/EncodedHtmlString.php b/src/Illuminate/Support/EncodedHtmlString.php index 47db90ea32d0..5ce9baac6ca9 100644 --- a/src/Illuminate/Support/EncodedHtmlString.php +++ b/src/Illuminate/Support/EncodedHtmlString.php @@ -30,7 +30,7 @@ public function __construct($html = '', protected bool $doubleEncode = true) #[\Override] public function toHtml() { - return (static::$encodeUsingFactory ?? function (string $value, bool $doubleEncode) { + return (static::$encodeUsingFactory ?? function ($value, bool $doubleEncode) { return htmlspecialchars($value ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', $doubleEncode); })($this->html, $this->doubleEncode); } From 823571a3ffd2989646b46164ef3cae98b99b2908 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Mon, 24 Feb 2025 14:14:30 +0800 Subject: [PATCH 23/43] Update EncodedHtmlString.php --- src/Illuminate/Support/EncodedHtmlString.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Illuminate/Support/EncodedHtmlString.php b/src/Illuminate/Support/EncodedHtmlString.php index 5ce9baac6ca9..df82413fb1b3 100644 --- a/src/Illuminate/Support/EncodedHtmlString.php +++ b/src/Illuminate/Support/EncodedHtmlString.php @@ -15,6 +15,7 @@ class EncodedHtmlString extends HtmlString * Create a new Encoded HTML string instance. * * @param string $html + * @param bool $doubleEncode * @return void */ public function __construct($html = '', protected bool $doubleEncode = true) From 1c578fe43d5d1070bcb64acce924799bfe17908f Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 28 Feb 2025 12:25:24 +0800 Subject: [PATCH 24/43] wip Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Mail/Mailable.php | 15 ++++++++++++--- src/Illuminate/Support/EncodedHtmlString.php | 16 +++++++++++++++- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/Illuminate/Mail/Mailable.php b/src/Illuminate/Mail/Mailable.php index f517b803dce6..fad121bb6cd5 100644 --- a/src/Illuminate/Mail/Mailable.php +++ b/src/Illuminate/Mail/Mailable.php @@ -13,6 +13,7 @@ use Illuminate\Contracts\Support\Renderable; use Illuminate\Contracts\Translation\HasLocalePreference; use Illuminate\Support\Collection; +use Illuminate\Support\EncodedHtmlString; use Illuminate\Support\HtmlString; use Illuminate\Support\Str; use Illuminate\Support\Traits\Conditionable; @@ -1371,7 +1372,9 @@ public function assertHasSubject($subject) */ public function assertSeeInHtml($string, $escape = true) { - $string = $escape ? e($string) : $string; + $flag = $this->markdown ? ENT_NOQUOTES : ENT_QUOTES; + + $string = $escape ? EncodedHtmlString::convert($string, flag: $flag | ENT_SUBSTITUTE) : $string; [$html, $text] = $this->renderForAssertions(); @@ -1393,7 +1396,9 @@ public function assertSeeInHtml($string, $escape = true) */ public function assertDontSeeInHtml($string, $escape = true) { - $string = $escape ? e($string) : $string; + $flag = $this->markdown ? ENT_NOQUOTES : ENT_QUOTES; + + $string = $escape ? EncodedHtmlString::convert($string, flag: $flag | ENT_SUBSTITUTE) : $string; [$html, $text] = $this->renderForAssertions(); @@ -1415,7 +1420,11 @@ public function assertDontSeeInHtml($string, $escape = true) */ public function assertSeeInOrderInHtml($strings, $escape = true) { - $strings = $escape ? array_map('e', $strings) : $strings; + $flag = $this->markdown ? ENT_NOQUOTES : ENT_QUOTES; + + $strings = $escape ? array_map(function ($string) use ($flag) { + return EncodedHtmlString::convert($string, $flag); + }, $strings) : $strings; [$html, $text] = $this->renderForAssertions(); diff --git a/src/Illuminate/Support/EncodedHtmlString.php b/src/Illuminate/Support/EncodedHtmlString.php index df82413fb1b3..6f2e2c59a371 100644 --- a/src/Illuminate/Support/EncodedHtmlString.php +++ b/src/Illuminate/Support/EncodedHtmlString.php @@ -23,6 +23,20 @@ public function __construct($html = '', protected bool $doubleEncode = true) parent::__construct($html); } + /** + * Convert using default encoding. + * + * @param string $value + * @param int $flag + * @param string $encoding + * @param bool $doubleEncode + * @return string + */ + public static function convert($value, int $flag = ENT_QUOTES | ENT_SUBSTITUTE, string $encoding = 'UTF-8', bool $doubleEncode = true) + { + return htmlspecialchars($value ?? '', $flag, $encoding, $doubleEncode); + } + /** * Get the HTML string. * @@ -32,7 +46,7 @@ public function __construct($html = '', protected bool $doubleEncode = true) public function toHtml() { return (static::$encodeUsingFactory ?? function ($value, bool $doubleEncode) { - return htmlspecialchars($value ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', $doubleEncode); + return static::convert($value, doubleEncode: $doubleEncode); })($this->html, $this->doubleEncode); } From 8b9fe3ec5da55f63ea1ca496b024c7fa73d372ab Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 28 Feb 2025 12:26:16 +0800 Subject: [PATCH 25/43] wip Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Mail/Mailable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Mail/Mailable.php b/src/Illuminate/Mail/Mailable.php index fad121bb6cd5..628f4795298b 100644 --- a/src/Illuminate/Mail/Mailable.php +++ b/src/Illuminate/Mail/Mailable.php @@ -1423,7 +1423,7 @@ public function assertSeeInOrderInHtml($strings, $escape = true) $flag = $this->markdown ? ENT_NOQUOTES : ENT_QUOTES; $strings = $escape ? array_map(function ($string) use ($flag) { - return EncodedHtmlString::convert($string, $flag); + return EncodedHtmlString::convert($string, flag: $flag | ENT_SUBSTITUTE); }, $strings) : $strings; [$html, $text] = $this->renderForAssertions(); From 20b8503b9c2686137e37834135d09a328cf49dbe Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 28 Feb 2025 12:32:16 +0800 Subject: [PATCH 26/43] wip Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Support/EncodedHtmlString.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Support/EncodedHtmlString.php b/src/Illuminate/Support/EncodedHtmlString.php index 6f2e2c59a371..8e43678bba3c 100644 --- a/src/Illuminate/Support/EncodedHtmlString.php +++ b/src/Illuminate/Support/EncodedHtmlString.php @@ -26,7 +26,7 @@ public function __construct($html = '', protected bool $doubleEncode = true) /** * Convert using default encoding. * - * @param string $value + * @param string|null $value * @param int $flag * @param string $encoding * @param bool $doubleEncode From 7eac8437205227e6f7a8253d9e6ac5d81a3bb141 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 28 Feb 2025 15:00:11 +0800 Subject: [PATCH 27/43] wip Signed-off-by: Mior Muhammad Zaki --- tests/Integration/Mail/MailableTest.php | 43 +++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 tests/Integration/Mail/MailableTest.php diff --git a/tests/Integration/Mail/MailableTest.php b/tests/Integration/Mail/MailableTest.php new file mode 100644 index 000000000000..8ea539760117 --- /dev/null +++ b/tests/Integration/Mail/MailableTest.php @@ -0,0 +1,43 @@ +addLocation(__DIR__.'/Fixtures'); + } + + public function testItCanAssertMarkdownEncodedString() + { + $mailable = new class extends Mailable { + public $message = "