diff --git a/templates/email/html/default.php b/templates/email/html/default.php index 70a6cd4492..47421fde07 100644 --- a/templates/email/html/default.php +++ b/templates/email/html/default.php @@ -15,8 +15,11 @@ * @var string $content */ -$lines = explode("\n", $content); - -foreach ($lines as $line) : - echo '

' . $line . "

\n"; -endforeach; +// $content is plain text: blank lines separate paragraphs and single line breaks become
. +// (*BSR_ANYCRLF) limits \R to CR and LF; without /u it would also match the byte 0x85 inside UTF-8 characters such as "Å". +?> + + +

', array_map(h(...), preg_split('/(*BSR_ANYCRLF)\R/', $paragraph) ?: [])) ?>

+ + diff --git a/tests/TestCase/Mailer/DefaultTemplateTest.php b/tests/TestCase/Mailer/DefaultTemplateTest.php new file mode 100644 index 0000000000..7a543d6e70 --- /dev/null +++ b/tests/TestCase/Mailer/DefaultTemplateTest.php @@ -0,0 +1,92 @@ +viewBuilder() + ->setTemplate('default') + ->disableAutoLayout(); + + return $renderer->render($content, [Message::MESSAGE_HTML])[Message::MESSAGE_HTML]; + } + + /** + * Test blank lines separate paragraphs and single line breaks become
. + * + * @return void + */ + public function testHtmlParagraphs(): void + { + $html = $this->renderHtml("First line\r\nsecond line\r\n\r\n\r\nNext paragraph\n"); + + $this->assertSame( + '

First line
second line

Next paragraph

', + preg_replace('/>\s+<', trim($html)), + ); + } + + /** + * Test the content is escaped. + * + * @return void + */ + public function testHtmlEscapesContent(): void + { + $html = $this->renderHtml(' & more'); + + $this->assertStringContainsString('<script>alert("x")</script> & more', $html); + $this->assertStringNotContainsString('