From f5d48dacd157f237d21f959b1819e368698801dd Mon Sep 17 00:00:00 2001 From: hyzen Date: Fri, 31 Jul 2026 02:28:38 +0200 Subject: [PATCH] Fix: email notifications for new comments showing wrong post url --- comments.php | 114 ++++++++++++++++++++++++++------------------------- 1 file changed, 58 insertions(+), 56 deletions(-) diff --git a/comments.php b/comments.php index 8379e01..80d1612 100644 --- a/comments.php +++ b/comments.php @@ -104,71 +104,66 @@ function start_session(): void { * $type 'new_comment' | 'new_reply' * $actor username of the person who wrote the comment/reply * $body the comment/reply text - * $post_id the post slug/path (used as human-readable context) + * $post_url the post's current live URL + * $post_title the post's title, used as the link text * $notify_user ['username' => ..., 'email' => ...] | null — commenter being replied to */ -function send_notification(string $type, string $actor, string $body, string $post_id, ?array $notify_user): void { +function send_notification(string $type, string $actor, string $body, string $post_url, string $post_title, ?array $notify_user): void { $from = 'no-reply@freedoms4.org'; $headers = implode("\r\n", [ 'From: freedoms4.org <' . $from . '>', 'Reply-To: ' . $from, 'X-Mailer: PHP/' . PHP_VERSION, 'MIME-Version: 1.0', - 'Content-Type: text/plain; charset=UTF-8', + 'Content-Type: text/html; charset=UTF-8', ]); - $post_url = 'https://freedoms4.org' . trim($post_id, '"\''); + $safe_actor = htmlspecialchars($actor, ENT_QUOTES, 'UTF-8'); + $safe_body = nl2br(htmlspecialchars($body, ENT_QUOTES, 'UTF-8')); + $safe_title = htmlspecialchars($post_title !== '' ? $post_title : $post_url, ENT_QUOTES, 'UTF-8'); + $safe_url = htmlspecialchars($post_url, ENT_QUOTES, 'UTF-8'); + $link = "{$safe_title}"; - if ($type === 'new_reply') { - // ── Reply notification ── - // Always notify hyzen (unless hyzen is the one replying) - $reply_subject = "You have a new reply from {$actor}"; - $reply_body = - "You have a new reply from {$actor}:\n\n" . - "{$body}\n\n" . - "Post: {$post_url}\n\n" . - "freedoms4.org"; + $actor_is_hyzen = $actor === 'hyzen'; - // Notify the commenter being replied to (their registered email + @freedoms4.org), - // unless they are hyzen (handled separately below) - if ($notify_user && $notify_user['username'] !== 'hyzen') { - // Send to registered email - if (!empty($notify_user['email'])) { - @mail($notify_user['email'], $reply_subject, $reply_body, $headers); - } - // Send to their @freedoms4.org address - $site_email = $notify_user['username'] . '@freedoms4.org'; - @mail($site_email, $reply_subject, $reply_body, $headers); - } - - // Notify hyzen for all replies (unless hyzen is the replier) - if ($actor !== 'hyzen') { - // If hyzen is being replied to, use the reply subject; otherwise use new-comment subject - if ($notify_user && $notify_user['username'] === 'hyzen') { - @mail('hyzen@freedoms4.org', $reply_subject, $reply_body, $headers); - } else { - // hyzen gets a "new reply" notice even when it's not on their own comment - $hyzen_subject = "A new comment from {$actor}"; - $hyzen_body = - "A new comment from {$actor}:\n\n" . - "{$body}\n\n" . - "Post: {$post_url}\n\n" . - "freedoms4.org"; - @mail('hyzen@freedoms4.org', $hyzen_subject, $hyzen_body, $headers); - } - } - - } else { - // ── New top-level comment notification (hyzen only) ── - if ($actor === 'hyzen') return; // hyzen commenting on their own site — skip - $subject = "A new comment from {$actor}"; - $msg = - "A new comment from {$actor}:\n\n" . - "{$body}\n\n" . - "Post: {$post_url}\n\n" . - "freedoms4.org"; + if ($type === 'new_comment') { + // Top-level comment: hyzen gets notified + if ($actor_is_hyzen) return; + $subject = "A new comment from {$actor}"; + $msg = render_notification_email("A new comment from {$safe_actor}:", $safe_body, $link); @mail('hyzen@freedoms4.org', $subject, $msg, $headers); + return; } + + // $type === 'new_reply': two independent recipients — the person replied to and hyzen. Either, both, or neither may end up receiving an email + $parent_is_hyzen = $notify_user && $notify_user['username'] === 'hyzen'; + $replying_to_self = $notify_user && $notify_user['username'] === $actor; + + $reply_subject = "You have a new reply from {$actor}"; + $reply_body = render_notification_email("You have a new reply from {$safe_actor}:", $safe_body, $link); + + // Recipient 1: person being replied to. Skipped when: replying to own comment. Replying to hyzen is handled by Recipient 2 next + if ($notify_user && !$parent_is_hyzen && !$replying_to_self) { + if (!empty($notify_user['email'])) { + @mail($notify_user['email'], $reply_subject, $reply_body, $headers); + } + @mail($notify_user['username'] . '@freedoms4.org', $reply_subject, $reply_body, $headers); + } + + // Recipient 2: hyzen. Skipped when: hyzen sent the reply + if (!$actor_is_hyzen) { + if ($parent_is_hyzen) { + @mail('hyzen@freedoms4.org', $reply_subject, $reply_body, $headers); + } else { + $hyzen_subject = "A new comment from {$actor}"; + $hyzen_body = render_notification_email("A new comment from {$safe_actor}:", $safe_body, $link); + @mail('hyzen@freedoms4.org', $hyzen_subject, $hyzen_body, $headers); + } + } +} + +function render_notification_email(string $heading, string $safe_body, string $link): string { + return "

{$heading}

{$safe_body}

Post: {$link}

freedoms4.org

"; } function logged_in_user(): ?array { @@ -269,9 +264,11 @@ $action = $body['action'] ?? ''; // ── POST: add comment or reply ── if ($action === 'post' || $action === 'reply') { - $post_id = trim($body['post_id'] ?? ''); - $text = trim($body['body'] ?? ''); - $parent_id = isset($body['parent_id']) ? (int)$body['parent_id'] : null; + $post_id = trim($body['post_id'] ?? ''); + $post_url = trim($body['post_url'] ?? ''); + $post_title = trim($body['post_title'] ?? ''); + $text = trim($body['body'] ?? ''); + $parent_id = isset($body['parent_id']) ? (int)$body['parent_id'] : null; if ($post_id === '') { json_out(['success' => false, 'message' => 'post_id is required.']); @@ -282,6 +279,11 @@ if ($action === 'post' || $action === 'reply') { if (!preg_match('#^/[a-zA-Z0-9_/-]{1,200}/$#', $post_id)) { json_out(['success' => false, 'message' => 'Invalid post_id.']); } + // post_url used only for notification links + if (!preg_match('#^https://freedoms4\.org/[a-zA-Z0-9_/-]{1,200}/$#', $post_url)) { + $post_url = 'https://freedoms4.org' . $post_id; + } + $post_title = substr($post_title, 0, 300); if ($text === '') { json_out(['success' => false, 'message' => 'Comment cannot be empty.']); } @@ -336,10 +338,10 @@ if ($action === 'post' || $action === 'reply') { ); $parent_stmt->execute([':pid' => $parent_id]); $parent_author = $parent_stmt->fetch() ?: null; - send_notification('new_reply', $user['username'], $text, $post_id, $parent_author); + send_notification('new_reply', $user['username'], $text, $post_url, $post_title, $parent_author); } else { // Top-level comment - send_notification('new_comment', $user['username'], $text, $post_id, null); + send_notification('new_comment', $user['username'], $text, $post_url, $post_title, null); } json_out(['success' => true, 'id' => $row['id'], 'created_at' => $row['created_at']]);