WordPress 用户评论回复邮件通知

899次阅读
没有评论

本文提供用户评论回复邮件通知的代码片段,说明其功能、使用方法、已发现的问题与推荐修复/改进措施,帮助你安全可靠地在 WordPress 站点中启用评论通知邮件。

作者已编写插件实现此功能,点击前往:https://yanqs.me/wp-comment-notify-plugin/

WordPress 用户评论回复邮件通知

将以下代码粘贴入主题 functions.php 即可启用

话不多说,我们先贴上代码。

代码

<?php
// 自定义评论回复通知邮件
function custom_comment_mail_notify($comment_id)
{
    $admin_notify = '1'; // admin 要不要收回复通知 ( '1'=要 ; '0'=不要 )
    $admin_email = get_bloginfo('admin_email');
    $blogname = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);
    $comment = get_comment($comment_id);
    $parent_id = $comment->comment_parent ? $comment->comment_parent : '';
    $spam_confirmed = $comment->comment_approved;
    global $wpdb;
    $comments_waiting = $wpdb->get_var("SELECT count(comment_ID) FROM $wpdb->comments WHERE comment_approved = '0'");
    if (($parent_id != '') && ($spam_confirmed != 'spam') && ($to != $admin_email)) {
        $wp_email = 'no-reply@' . preg_replace('#^www\.#', '', strtolower($_SERVER['SERVER_NAME']));
        $to = trim(get_comment($parent_id)->comment_author_email);
        $subject = '您在 [' . $blogname . ']' . ' 中的留言有了新回复!';
        $message = '
                 <div style="background-color:white;border-left: 2px solid #555555;box-shadow:0 1px 3px #AAAAAA;line-height:180%;padding:0 15px 12px;width:500px;margin:50px auto;color:#555555;font-family:"Source Sans Pro","Hiragino Sans GB","Microsoft Yahei",SimSun,Helvetica,Arial,Sans-serif,monospace;font-size:14px;">
                        <h2 style="border-bottom:1px solid #DDD;font-size:14px;font-weight:normal;padding:13px 0 10px 8px;">
                                    <span style="color: #409eff;font-weight: bold;">&gt; </span>
                                    您在 <a style="text-decoration:none; color:#409eff;font-weight:600;" href="' . home_url() . '">' . $blogname . '</a> 的留言有回复啦!
                          </h2>
                          <div style="font-size: 14px; color: #777; padding: 0 10px; margin-top: 18px;">

<p><b>' . trim(get_comment($parent_id)->comment_author) . '</b> 您曾在文章<b>《' . get_the_title($comment->comment_post_ID) . '》</b>上发表评论:</p>
                                    <p style="background: #F5F5F5; padding: 10px 15px; margin: 18px 0;">' . nl2br(strip_tags(get_comment($parent_id)->comment_content)) . '</p>

<p>' . '<b>' . trim($comment->comment_author) . '</b>' . ' 给您的回复如下:</p>
                                    <p style="background: #F5F5F5; padding: 10px 15px; margin: 18px 0;">' . nl2br(strip_tags($comment->comment_content)) . '</p>

<p>您可以点击 <a style="text-decoration:none; color:#409eff" href="' . htmlspecialchars(get_comment_link($parent_id)) . '">查看完整的回复內容</a>,也欢迎再次光临 <a style="text-decoration:none; color:#409eff"
                                    href="' . home_url() . '">' . $blogname . '</a>。祝您生活愉快!</p>
                                    <p style="padding-bottom: 15px;">(此邮件由系统自动发出,请勿直接回复!)</p>
                          </div>
                 </div>';
        $from = "From: \"" . get_option('blogname') . "\" <$wp_email>";
        $headers = "$from\nContent-Type: text/html; charset=" . get_option('blog_charset') . "\n";
        wp_mail($to, $subject, $message, $headers);
    }
    //文章有新评论时通知管理员
    if ($parent_id == '' && (trim($comment->comment_author_email) != trim($admin_email)) && ($spam_confirmed != 'spam') && ($comment->comment_approved != 0)) {
        $wp_email = '';
        $subject = '在「' . $blogname . '」的文章《' . get_the_title($comment->comment_post_ID) . '》一文有新的评论';
        $message = '
            <div style="background-color:white;border-left: 2px solid #555555;box-shadow:0 1px 3px #AAAAAA;line-height:180%;padding:0 15px 12px;width:500px;margin:50px auto;color:#555555;font-family:"Source Sans Pro","Hiragino Sans GB","Microsoft Yahei",SimSun,Helvetica,Arial,Sans-serif,monospace;font-size:14px;">
                    <h2 style="border-bottom:1px solid #DDD;font-size:14px;font-weight:normal;padding:13px 0 10px 8px;">
                             <span style="color: #409eff;font-weight: bold;">&gt; </span>
                             <a style="text-decoration:none;color: #409eff;" href="' . home_url() . '">' . $blogname . '</a> 博客有新的评论啦!
                    </h2>
                    <div style="padding:0 12px 0 12px;margin-top:18px;">

<p><b>' . $comment->comment_author . '</b> 在文章<b>《' . get_the_title($comment->comment_post_ID) . '》</b>上发表评论:</p>
                             <p style="background-color: #f5f5f5;border: 0px solid #DDD;padding: 10px 15px;margin:18px 0;">' . $comment->comment_content . '</p>

<p>您可以点击 <a style="text-decoration:none; color:#409eff" href="' . htmlspecialchars(get_comment_link($parent_id)) . '">查看完整的回复內容</a>,也欢迎再次光临 <a style="text-decoration:none; color:#409eff" href="' . home_url() . '">' . $blogname . '</a>。祝您生活愉快!</p>
                    </div>
            </div>';
        $headers = "Content-Type: text/html; charset=" . get_option('blog_charset') . "\n";
        wp_mail($admin_email, $subject, $message, $headers);
    }
    //评论需要审核时通知
    if ($parent_id == '' && (trim($comment->comment_author_email) != trim($admin_email)) && ($spam_confirmed != 'spam') && ($spam_confirmed != 'trash') && ($comment->comment_approved == 0)) {
        $wp_email = '';
        $subject = '在「' . $blogname . '」的文章《' . get_the_title($comment->comment_post_ID) . '》中有新的评论需要审核';
        $message = '
            <div style="background-color:white;border-left: 2px solid #555555;box-shadow:0 1px 3px #AAAAAA;line-height:180%;padding:0 15px 12px;width:500px;margin:50px auto;color:#555555;font-family:"Source Sans Pro","Hiragino Sans GB","Microsoft Yahei",SimSun,Helvetica,Arial,Sans-serif,monospace;font-size:14px;">
                    <h2 style="border-bottom:1px solid #DDD;font-size:14px;font-weight:normal;padding:13px 0 10px 8px;">
                             <span style="color: #409eff;font-weight: bold;">&gt; 「 </span>
                             <a style="text-decoration:none;color: #409eff;" href="' . home_url() . '">' . $blogname . '」</a> 中有一条评论等待您的审核
                    </h2>
                    <div style="padding:0 12px 0 12px;margin-top:18px;">

<p><b>' . $comment->comment_author . '</b> 在文章<b><a style="text-decoration:none;color: #409eff;" href="' . get_permalink($comment->comment_post_ID) . '">《' . get_the_title($comment->comment_post_ID) . '》</a></b>上发表评论:</p>
                             <p style="background-color: #f5f5f5;border: 0px solid #DDD;padding: 10px 15px;margin:18px 0;">' . $comment->comment_content . '</p>

<p><a style="text-decoration:none;color: #007017;" href="' . admin_url("comment.php?action=approve&c={$comment_id}#wpbody-content") . '">[批准评论]</a> | <a style="text-decoration:none;color: #b32d2e;" href="' . admin_url("comment.php?action=trash&c={$comment_id}#wpbody-content") . '">[移至回收站]</a>。您还可以:<a style="text-decoration:none; color:#b32d2e" href="' . admin_url("comment.php?action=delete&c={$comment_id}#wpbody-content") . '">永久删除评论</a> | <a style="text-decoration:none;color: #b32d2e;" href="' . admin_url("comment.php?action=spam&c={$comment_id}#wpbody-content") . '">标记为垃圾评论</a>

<p>当前有 ' . $comments_waiting . ' 条评论等待审核。请移步<a style="text-decoration:none;color: #409eff;" href="' . admin_url('edit-comments.php?comment_status=moderated#wpbody-content') . '">审核页面</a>来查看。</p>也欢迎再次光临 <a style="text-decoration:none; color:#409eff" href="' . home_url() . '">' . $blogname . '</a>。祝您生活愉快!</p>
                    </div>
            </div>';
        $headers = "Content-Type: text/html; charset=" . get_option('blog_charset') . "\n";
        wp_mail($admin_email, $subject, $message, $headers);
    }
}
add_action('comment_post', 'custom_comment_mail_notify');

概述

上述代码提供了一个函数 pk_comment_mail_notify($comment_id),并通过 add_action('comment_post', 'pk_comment_mail_notify') 将其挂载到 comment_post 钩子上。函数实现了三类通知:

  • 当某条评论是对已有评论的回复时,向被回复的评论作者发送回复通知(邮件 HTML 模板)。
  • 当某篇文章有新评论时,向管理员发送通知(仅当该评论不是来自管理员本人且未标记为垃圾且已通过审核)。
  • 当某条评论需要人工审核(未通过自动审核)时,向管理员发送审核提醒邮件。

邮件内容使用 HTML 模版拼接,包含文章标题、原评论与回复内容,并提供跳转链接(如查看回复、审核评论等)。

如何使用

  1. 将上述代码放入你主题或插件的合适位置(例如主题的 functions.php 或单独插件文件中)。
  2. 确保文件被加载(通过 require/include 或在插件中激活)。
  3. 根据需要修改 From 地址、样式或文案。

示例:把上述代码保存为 WordPress_Mail.php 放在主题目录并在 functions.php 中引入:

require get_template_directory() . '/WordPress_Mail.php';

已知问题与安全建议(重要)

下面列出阅读 WordPress_Mail.php 源码时应注意的问题,并给出修复建议:

  1. 条件中使用了未定义变量 $to
  • 代码片段中有如下判断:
if (($parent_id != '') && ($spam_confirmed != 'spam') && ($to != $admin_email)) {
    // ... 之后才设置 $to = trim(get_comment($parent_id)->comment_author_email);
}
  • 问题:此处 $to 在判断时尚未定义,可能导致未定义变量通知或逻辑错误(在某些 PHP 配置下会被视为空串),应改为直接比较父评论作者邮箱或先定义 $to

  • 推荐修复(把 $to 的定义提前并比较邮箱):

$parent_author_email = trim(get_comment($parent_id)->comment_author_email);
if (($parent_id != '') && ($spam_confirmed != 'spam') && ($parent_author_email != $admin_email)) {
    $to = $parent_author_email;
    // ...
}
  1. 头部换行与头注入风险
  • 当前代码构造邮件头使用 “\n”(单 LF)或字符串拼接:
$headers = "$from\nContent-Type: text/html; charset=" . get_option('blog_charset') . "\n";
  • 建议:使用 \r\n 作为头部行结束符;更安全的做法是使用 wp_mail() 的 headers 参数传入数组或使用 WordPress 的过滤器 wp_mail_from / wp_mail_from_name 来设置发件人,避免直接拼接来自外部的值。

  • 另外,任何直接来自用户输入的头部字段(如 FromSubject)都应清洗换行符以防止 header 注入攻击:

function safe_mail_header($value) {
    return str_replace(array("\r", "\n"), '', $value);
}

$from_name = safe_mail_header(get_option('blogname'));
$wp_email = 'no-reply@' . preg_replace('#^www\\.#', '', strtolower($_SERVER['SERVER_NAME']));
$from = sprintf('From: "%s" <%s>', $from_name, $wp_email);
  1. HTML 内容需转义与严格控制用户输入
  • 邮件中插入了评论内容($comment->comment_content)和作者名。即使要显示 HTML,也应使用 wp_kses_post()esc_html() / esc_attr() 进行过滤,避免邮件中包含恶意脚本或反射式内容。示例:
$safe_parent_content = wp_kses_post(get_comment($parent_id)->comment_content);
$safe_reply_content = wp_kses_post($comment->comment_content);
  1. 使用 WordPress 推荐方式发送 HTML 邮件
  • 若需发送 HTML,推荐通过 wp_mail() 并在发送前临时添加过滤器设置内容类型:
add_filter('wp_mail_content_type', function() { return 'text/html'; });
wp_mail($to, $subject, $message, $headers_array);
remove_filter('wp_mail_content_type', '...');

或者将 headers 作为数组传入:$headers = array('From: ...', 'Content-Type: text/html; charset=UTF-8');

  1. 建议使用 wp_mail() 的返回值与错误记录
  • wp_mail() 返回 true/false,建议在发送失败时记录日志(使用 error_log() 或 WP 的日志机制)以便排查。

推荐改进与增强

  • 使用 wp_mail_fromwp_mail_from_name 过滤器统一设置发件人,或在主题/插件初始化处设置而不是在函数内部硬编码。
  • 对邮件头使用数组模式,避免行结束符差异。
  • 对 HTML 邮件使用 wp_mail_content_type 过滤器。
  • 将样式模板抽出为可维护的部分(如使用 ob_start() + 模板文件输出),便于修改与本地化。
  • 更进一步,考虑使用 SMTP 插件(如 WP Mail SMTP 或使用 PHPMailer 自行配置 OAuth/TLS),以提升送达率。

建议修补(完整示例片段)

下面给出一个合并了上面建议的简化示例片段,你可以把它替换 WordPress_Mail.php 中对应的逻辑:

// 取得父评论作者邮箱并检查
$parent_author_email = $parent_id ? trim(get_comment($parent_id)->comment_author_email) : '';
if ($parent_id && $spam_confirmed !== 'spam' && $parent_author_email && $parent_author_email !== $admin_email) {
    $to = $parent_author_email;

    $from_name = str_replace(array("\r", "\n"), '', get_option('blogname'));
    $wp_email = 'no-reply@' . preg_replace('#^www\\.#', '', strtolower($_SERVER['SERVER_NAME']));
    $headers = array();
    $headers[] = sprintf('From: %s <%s>', $from_name, $wp_email);
    // 使用 wp_mail 时通过 filter 设置 content type 为 HTML
    add_filter('wp_mail_content_type', function() { return 'text/html'; });

    // 内容使用过滤以防注入或不当 HTML
    $safe_parent_author = esc_html(trim(get_comment($parent_id)->comment_author));
    $safe_parent_content = wp_kses_post(get_comment($parent_id)->comment_content);
    $safe_reply_author = esc_html(trim($comment->comment_author));
    $safe_reply_content = wp_kses_post($comment->comment_content);

    $subject = sprintf('您在 [%s] 的留言有了新回复!', wp_specialchars_decode(get_option('blogname'), ENT_QUOTES));
    // 这里可用模板文件输出 HTML 内容
    $message = '
<p>原评论者: <strong>' . $safe_parent_author . '</strong></p>' .
               '
<p>' . nl2br($safe_parent_content) . '</p>' .
               '
<hr>' .
               '
<p>回复者: <strong>' . $safe_reply_author . '</strong></p>' .
               '
<p>' . nl2br($safe_reply_content) . '</p>';

    if (! wp_mail($to, $subject, $message, $headers)) {
        error_log('评论回复通知邮件发送失败, comment_id: ' . $comment_id);
    }
    remove_filter('wp_mail_content_type', '...');
}

注意:上面示例里的 remove_filter 应使用与 add_filter 相同的回调引用(若使用匿名函数需另行处理)。生产代码中建议使用具名函数来便于移除。

测试说明

  • 在本地或开发环境测试邮件功能,推荐使用 MailHog / Mailtrap 捕获并查看邮件输出,而不是直接对真实用户发送。
  • 检查 wp_mail() 返回值并查看服务器邮件日志(如使用 sendmail/postfix 时的 /var/log/mail.log)。

正文完
 2
评论(没有评论)

YanQS's Blog