From ebf48b07073f9d60d87453190a0cb31814e74613 Mon Sep 17 00:00:00 2001 From: Patrick Niebeling Date: Wed, 30 Sep 2026 11:05:22 +0200 Subject: [PATCH] Link the card title instead of an extra button in the mail The plain-text part gets the card URL on its own line. Bumps to 35.2.2. --- appinfo/info.xml | 2 +- lib/Service/NotificationMailer.php | 35 ++++++++++++++++++++---------- package.json | 2 +- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index f7e79e6..0632728 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -24,7 +24,7 @@ This app lets every user configure Deck automation workflows for the boards they A background job periodically evaluates all enabled workflows and moves cards whose due date has passed, using Deck's own internal PHP classes only (no HTTP/OCS calls). ]]> - 35.2.1 + 35.2.2 agpl Patrick Niebeling WorkflowDeckAutomation diff --git a/lib/Service/NotificationMailer.php b/lib/Service/NotificationMailer.php index 572f279..ea3e790 100644 --- a/lib/Service/NotificationMailer.php +++ b/lib/Service/NotificationMailer.php @@ -98,21 +98,34 @@ class NotificationMailer { * @param array{board: string, sourceStack: string, targetStack: string} $targets */ private function addCard(IEMailTemplate $template, Workflow $workflow, Card $card, array $targets): void { - // Single-argument addBodyText() runs htmlspecialchars() itself, so - // these card-, workflow-, board- and stack-titles (user input) are - // safe as-is. The card description and the workflow's name are - // deliberately left out — the mail only says what moved where. - $template->addBodyText(sprintf( - '„%1$s“ (Board „%2$s“) ist überfällig und wurde von „%3$s“ nach „%4$s“ verschoben.', + // The card description and the workflow's name are deliberately left + // out — the mail only says what moved where. The card title itself is + // the link to the card. + $sentence = '„%1$s“ (Board „%2$s“) ist überfällig und wurde von „%3$s“ nach „%4$s“ verschoben.'; + $url = $this->urlGenerator->getAbsoluteURL('/apps/deck/board/' . $workflow->getBoardId() . '/card/' . $card->getId()); + + // With both parts passed, addBodyText() escapes nothing: every title + // is user input and has to be escaped here for the HTML part. + $html = sprintf( + $sentence, + '' . self::escape($card->getTitle()) . '', + self::escape($targets['board']), + self::escape($targets['sourceStack']), + self::escape($targets['targetStack']), + ); + $plain = sprintf( + $sentence, $card->getTitle(), $targets['board'], $targets['sourceStack'], $targets['targetStack'], - )); - $template->addBodyButton( - 'Karte öffnen', - $this->urlGenerator->getAbsoluteURL('/apps/deck/board/' . $workflow->getBoardId() . '/card/' . $card->getId()), - ); + ) . PHP_EOL . $url; + + $template->addBodyText($html, $plain); + } + + private static function escape(string $text): string { + return htmlspecialchars($text, ENT_QUOTES, 'UTF-8'); } /** diff --git a/package.json b/package.json index 7d6ecf6..b1d9db5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "workflow_deck_automation", - "version": "35.2.1", + "version": "35.2.2", "private": true, "type": "module", "description": "Automates moving overdue Deck cards between stacks, configurable per user.",