feat: answer received form submissions by mail

This commit is contained in:
fromme committed 2026-09-24 16:35:31 +02:00
1 parent 19364df2db
commit aa728ddc99
22 files changed
+974 -9

No files matched your search

+34 -1
View File
@@ -29,10 +29,24 @@ class EmailTextRenderer
*/
private const ESCAPE_FLAGS = ENT_COMPAT | ENT_SUBSTITUTE;
/**
* The token for the booking link, and why it is not handled like the other placeholders:
* a placeholder value is substituted into the *rendered HTML* and escaped there, which is
* what makes a teamer's own words harmless - but it also means such a value can never be a
* link target, because CommonMark has already percent-encoded the braces by then
* ("%7BbookingUrl%7D"). This one is substituted into the Markdown *before* parsing instead,
* so the admin can write it as a proper link and CommonMark still gets to vet the URL
* through allow_unsafe_links. That is only defensible because the value comes from the
* deployment's own environment, exactly like the wording it is substituted into - never
* from a teamer or an applicant. Do not extend this to anything a user can influence.
*/
private const BOOKING_URL_TOKEN = '{bookingUrl}';
public function __construct(
private readonly EmailTextCatalog $catalog,
private readonly EmailTextRepository $emailTextRepository,
private readonly MailBodyRenderer $mailBodyRenderer,
private readonly string $bookingUrl = '',
) {
}
@@ -86,10 +100,29 @@ class EmailTextRenderer
return new RenderedEmailTextDto(
strtr($subject ?? '', $rawTokens),
$this->renderHeadline($headline ?? '', $htmlTokens),
$this->mailBodyRenderer->render($body ?? '', $rawTokens),
$this->mailBodyRenderer->render($this->insertBookingUrl($body ?? ''), $rawTokens),
);
}
/**
* Refuses rather than sending a broken link: an unconfigured APPLICATION_FORM_BOOKING_URL
* would otherwise render as <a href=""> and invite an applicant to book a call they cannot
* reach. The caller decides what that means - the endpoint logs it and answers the
* submission anyway, a reviewer sending by hand gets to see it.
*/
private function insertBookingUrl(string $body): string
{
if (false === str_contains($body, self::BOOKING_URL_TOKEN)) {
return $body;
}
if ('' === trim($this->bookingUrl)) {
throw new \RuntimeException('The mail wording uses {bookingUrl}, but APPLICATION_FORM_BOOKING_URL is not configured.');
}
return str_replace(self::BOOKING_URL_TOKEN, trim($this->bookingUrl), $body);
}
/**
* The headline is set as the mail's own <h1>, so it deliberately takes no Markdown:
* emphasis inside an already bold heading says nothing, and a stray "-" or "#" at the