diff --git a/lib/DigestSender.php b/lib/DigestSender.php
index 0dfe4d380..963afa996 100644
--- a/lib/DigestSender.php
+++ b/lib/DigestSender.php
@@ -220,27 +220,6 @@ public function sendDigestForUser(IUser $user, int $now, string $timezone, strin
* @return string
*/
protected function getHTMLSubject(IEvent $event): string {
- if ($event->getRichSubject() === '') {
- return htmlspecialchars($event->getParsedSubject());
- }
-
- $placeholders = $replacements = [];
- foreach ($event->getRichSubjectParameters() as $placeholder => $parameter) {
- $placeholders[] = '{' . $placeholder . '}';
-
- if ($parameter['type'] === 'file') {
- $replacement = (string)$parameter['path'];
- } else {
- $replacement = (string)$parameter['name'];
- }
-
- if (isset($parameter['link'])) {
- $replacements[] = '' . htmlspecialchars($replacement) . '';
- } else {
- $replacements[] = '' . htmlspecialchars($replacement) . '';
- }
- }
-
- return str_replace($placeholders, $replacements, $event->getRichSubject());
+ return HTMLSubjectFormatter::format($event);
}
}
diff --git a/lib/HTMLSubjectFormatter.php b/lib/HTMLSubjectFormatter.php
new file mode 100644
index 000000000..b9c480b7d
--- /dev/null
+++ b/lib/HTMLSubjectFormatter.php
@@ -0,0 +1,37 @@
+getRichSubject() === '') {
+ return htmlspecialchars($event->getParsedSubject());
+ }
+
+ $placeholders = $replacements = [];
+ foreach ($event->getRichSubjectParameters() as $placeholder => $parameter) {
+ $placeholders[] = '{' . $placeholder . '}';
+
+ $replacement = $parameter['type'] === 'file'
+ ? (string)$parameter['path']
+ : (string)$parameter['name'];
+
+ if (isset($parameter['link'])) {
+ $replacements[] = '' . htmlspecialchars($replacement) . '';
+ } else {
+ $replacements[] = '' . htmlspecialchars($replacement) . '';
+ }
+ }
+
+ return str_replace($placeholders, $replacements, $event->getRichSubject());
+ }
+}
diff --git a/lib/MailQueueHandler.php b/lib/MailQueueHandler.php
index 46be003e4..b089f0c48 100644
--- a/lib/MailQueueHandler.php
+++ b/lib/MailQueueHandler.php
@@ -379,28 +379,7 @@ function ($event) use ($timezone, $l) {
}
protected function getHTMLSubject(IEvent $event): string {
- if ($event->getRichSubject() === '') {
- return htmlspecialchars($event->getParsedSubject());
- }
-
- $placeholders = $replacements = [];
- foreach ($event->getRichSubjectParameters() as $placeholder => $parameter) {
- $placeholders[] = '{' . $placeholder . '}';
-
- if ($parameter['type'] === 'file') {
- $replacement = (string)$parameter['path'];
- } else {
- $replacement = (string)$parameter['name'];
- }
-
- if (isset($parameter['link'])) {
- $replacements[] = '' . htmlspecialchars($replacement) . '';
- } else {
- $replacements[] = '' . htmlspecialchars($replacement) . '';
- }
- }
-
- return str_replace($placeholders, $replacements, $event->getRichSubject());
+ return HTMLSubjectFormatter::format($event);
}
/**
diff --git a/tests/HTMLSubjectFormatterTest.php b/tests/HTMLSubjectFormatterTest.php
new file mode 100644
index 000000000..321e0c0cc
--- /dev/null
+++ b/tests/HTMLSubjectFormatterTest.php
@@ -0,0 +1,56 @@
+createMock(IEvent::class);
+ $event->method('getRichSubject')->willReturn($richSubject);
+ $event->method('getRichSubjectParameters')->willReturn($richParams);
+ $event->method('getParsedSubject')->willReturn($parsedSubject);
+ return $event;
+ }
+
+ public function testNoRichSubjectFallsToParsed(): void {
+ $event = $this->getEvent('', [], 'Plain ');
+ $this->assertSame('Plain <subject>', HTMLSubjectFormatter::format($event));
+ }
+
+ public function testFileParameterUsesPath(): void {
+ $event = $this->getEvent(
+ 'File {file} was changed',
+ ['file' => ['type' => 'file', 'path' => '/my/file.txt', 'name' => 'file.txt']],
+ '',
+ );
+ $this->assertSame('File /my/file.txt was changed', HTMLSubjectFormatter::format($event));
+ }
+
+ public function testNonFileParameterUsesName(): void {
+ $event = $this->getEvent(
+ '{user} shared',
+ ['user' => ['type' => 'user', 'name' => 'Alice & Bob']],
+ '',
+ );
+ $this->assertSame('Alice & Bob shared', HTMLSubjectFormatter::format($event));
+ }
+
+ public function testParameterWithLinkRendersAnchor(): void {
+ $event = $this->getEvent(
+ 'See {file}',
+ ['file' => ['type' => 'file', 'path' => 'report.pdf', 'name' => 'report.pdf', 'link' => 'https://example.com/report.pdf']],
+ '',
+ );
+ $this->assertSame('See report.pdf', HTMLSubjectFormatter::format($event));
+ }
+}