Files
wordpress-kratos-yang/inc/theme-mail-core.php
T
laoyang ec815d4282 评论通知邮件无样式:接管核心通知正文 #39
卸载 wpDiscuz 后,评论通知回落到核心 wp_notify_postauthor /
wp_notify_moderator。theme-mail.php 只接管了通道(SMTP、From),
并把 Content-Type 强改成 text/html,而正文仍是核心拼的纯文本,
客户端按 HTML 渲染 ⇒ 换行丢、链接变裸地址。

新增 inc/theme-mail-core.php,挂 comment_notification_text /
comment_moderation_text 两个过滤器,把正文换成主题模板。这是核心
自己提供的扩展点,不改核心文件,删掉即回退。

模板新增 links 参数,把 通过/回收站/垃圾 渲染成一行文字链接
(并排三按钮在手机上必然换行、高度不齐)。

版本 4.2.1 -> 4.2.2
2026-11-06 20:11:24 +08:00

248 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 接管 WordPress 核心的评论通知正文
*
* ── 要解决的问题 ──────────────────────────────────────────────
* 站点卸载 wpDiscuz 之后,评论通知回落到核心:
*
* wp_notify_postauthor() 有新评论 → 通知文章作者
* wp_notify_moderator() 有待审评论 → 通知管理员
*
* 主题的 theme-mail.php 只接管了**通道**(SMTP、From、Content-Type),
* 但正文仍然是核心拼出来的那一坨纯文本:
*
* 您的文章《留言本》有新评论
* 作者:某某(IP 地址:1.2.3.4)
* 评论:……
* 移至回收站:https://...
* 标记为垃圾评论:https://...
*
* 而 yang_mail_normalize_headers() 把 Content-Type 强改成了
* `text/html; charset=UTF-8`(这一步本身是对的:不改成 HTML 中文会乱码,
* 也没法塞模板)。两件事叠加的结果是:**一封被声明成 HTML 的邮件,
* 里面装的是没有标签的纯文本**。客户端按 HTML 渲染 ⇒ 换行全丢、
* 链接变成裸地址。用户看到的就是「一点样式都没有了」。
*
* ── 为什么之前没暴露 ──────────────────────────────────────────
* theme-mail.php 里有这条规则:
*
* 「没有插件接管 → 核心通知是唯一的顶层评论通知渠道,必须放行」
*
* 放行是对的,但只放行了「发不发」,没管「长什么样」。装着 wpDiscuz 时,
* 正文由插件的 wpdiscuz_email_content 过滤器重渲染,核心那段根本走不到;
* 插件一卸,这条路径才第一次暴露出来。
*
* ── 怎么修(不动核心)─────────────────────────────────────────
* 用核心自己提供的过滤器,在 wp_mail 之前把正文换掉:
*
* comment_notification_text ($notify_message, $comment_id)
* comment_moderation_text ($notify_message, $comment_id)
*
* 这是**扩展点**,不是改核心文件。wp-includes/pluggable.php 里那两行
* apply_filters() 是核心自己写的,我们只是挂上去;删掉本文件立刻回到
* 核心原始行为,完全可逆,也不会在升级时留下残留。
*
* ── 边界 ──────────────────────────────────────────────────────
* · 只在主题 SMTP 配置完整时接管。没配 SMTP 说明用户可能用别的邮件
* 插件,这时贸然换成 HTML 会和那边的模板打架。
* · trackback / pingback 不接管。它们不是评论,字段结构也不同,
* 套模板反而会丢信息(比如 trackback 摘要)。
*
* @author Yongquan Yang <yangyq.net>
* @license MIT License
*/
if (!defined('ABSPATH')) {
exit;
}
/**
* 取评论对应的文章标题
*
* 文章被删掉时 get_the_title() 返回空串,邮件里就会出现《》,
* 比直接说「文章已删除」更难懂。
*/
function yang_mail_core_post_title($post_id)
{
$post = get_post($post_id);
if (!$post) {
return __('(文章已删除)', 'kratos');
}
$t = get_the_title($post);
return $t !== '' ? $t : __('(无标题)', 'kratos');
}
/**
* 后台评论操作链接
*
* 核心给的 approve / spam / trash 是三个裸 URL,这里做成模板里的一行
* 文字链接。为什么不做成三个并排按钮:邮件里并排三个按钮在手机上
* 必然换行、高度不齐,看起来像渲染出错;而且这些是后台管理动作,
* 不该和主按钮抢视觉权重。
*
* @param int $comment_id
* @param bool $with_approve 已发布的评论不需要「通过」
* @return array
*/
function yang_mail_core_action_links($comment_id, $with_approve = false)
{
$id = (int) $comment_id;
$links = array();
if ($with_approve) {
$links[] = array(
'text' => __('通过审核', 'kratos'),
'url' => admin_url("comment.php?action=approve&c={$id}#wpbody-content"),
);
}
if (EMPTY_TRASH_DAYS) {
$links[] = array(
'text' => __('移至回收站', 'kratos'),
'url' => admin_url("comment.php?action=trash&c={$id}#wpbody-content"),
);
} else {
$links[] = array(
'text' => __('永久删除', 'kratos'),
'url' => admin_url("comment.php?action=delete&c={$id}#wpbody-content"),
);
}
$links[] = array(
'text' => __('标记为垃圾', 'kratos'),
'url' => admin_url("comment.php?action=spam&c={$id}#wpbody-content"),
);
return $links;
}
/**
* 把评论通知(有新评论 → 文章作者)渲染成主题模板
*
* @param string $text 核心生成的纯文本(仅在参数非法时原样返回)
* @param string|int $comment_id
* @return string
*/
function yang_mail_core_render_comment_notify($text, $comment_id)
{
if (!yang_mail_smtp_ready()) {
return $text;
}
$comment = get_comment($comment_id);
if (!($comment instanceof WP_Comment)) {
return $text;
}
// 引用 / Pingback 不是评论,字段结构不同,不套模板
if ($comment->comment_type === 'trackback' || $comment->comment_type === 'pingback') {
return $text;
}
$title = yang_mail_core_post_title($comment->comment_post_ID);
$author = $comment->comment_author !== '' ? $comment->comment_author : __('匿名访客', 'kratos');
$rows = array(
array('label' => __('文章', 'kratos'), 'value' => $title),
array('label' => __('评论者', 'kratos'), 'value' => $author, 'strong' => true),
);
if ($comment->comment_author_email !== '') {
$rows[] = array('label' => __('邮箱', 'kratos'), 'value' => $comment->comment_author_email);
}
if ($comment->comment_author_url !== '') {
$rows[] = array('label' => __('网址', 'kratos'), 'value' => $comment->comment_author_url);
}
if ($comment->comment_author_IP !== '') {
$rows[] = array('label' => __('IP 地址', 'kratos'), 'value' => $comment->comment_author_IP);
}
$rows[] = array('label' => __('时间', 'kratos'), 'value' => mysql2date('Y-m-d H:i', $comment->comment_date));
return yang_mail_render(array(
'title' => sprintf(__('%s · 新评论', 'kratos'), get_bloginfo('name')),
'preheader' => sprintf(__('%s 在《%s》下发表了评论。', 'kratos'), $author, $title),
'badge' => __('新评论', 'kratos'),
'heading' => __('文章有新评论', 'kratos'),
'intro' => sprintf(__('《%s》收到了一条新评论。', 'kratos'), $title),
'quotes' => array(
array(
'label' => sprintf(__('%s 说', 'kratos'), $author),
'content' => yang_mail_text($comment->comment_content, 1200),
'meta' => mysql2date('Y-m-d H:i', $comment->comment_date),
),
),
'rows' => $rows,
'cta_text' => __('查看评论', 'kratos'),
'cta_url' => get_comment_link($comment),
'links' => yang_mail_core_action_links($comment->comment_ID, false),
'note' => __('你收到这封邮件,是因为你是这篇文章的作者。', 'kratos'),
));
}
add_filter('comment_notification_text', 'yang_mail_core_render_comment_notify', 20, 2);
/**
* 把审核通知(有待审评论 → 管理员)渲染成主题模板
*
* 内容与上面高度重合,差别只有:徽标是「待审核」、主按钮去审核面板、
* 操作行多一个「通过审核」。合并成一个函数要靠一堆 if 分叉,
* 不如分开写清楚。
*
* @param string $text
* @param string|int $comment_id
* @return string
*/
function yang_mail_core_render_moderation($text, $comment_id)
{
if (!yang_mail_smtp_ready()) {
return $text;
}
$comment = get_comment($comment_id);
if (!($comment instanceof WP_Comment)) {
return $text;
}
if ($comment->comment_type === 'trackback' || $comment->comment_type === 'pingback') {
return $text;
}
$title = yang_mail_core_post_title($comment->comment_post_ID);
$author = $comment->comment_author !== '' ? $comment->comment_author : __('匿名访客', 'kratos');
$rows = array(
array('label' => __('文章', 'kratos'), 'value' => $title),
array('label' => __('评论者', 'kratos'), 'value' => $author, 'strong' => true),
);
if ($comment->comment_author_email !== '') {
$rows[] = array('label' => __('邮箱', 'kratos'), 'value' => $comment->comment_author_email);
}
if ($comment->comment_author_url !== '') {
$rows[] = array('label' => __('网址', 'kratos'), 'value' => $comment->comment_author_url);
}
if ($comment->comment_author_IP !== '') {
$rows[] = array('label' => __('IP 地址', 'kratos'), 'value' => $comment->comment_author_IP);
}
$rows[] = array('label' => __('时间', 'kratos'), 'value' => mysql2date('Y-m-d H:i', $comment->comment_date));
return yang_mail_render(array(
'title' => sprintf(__('%s · 待审核评论', 'kratos'), get_bloginfo('name')),
'preheader' => sprintf(__('%s 在《%s》下的评论等待审核。', 'kratos'), $author, $title),
'badge' => __('待审核', 'kratos'),
'heading' => __('有一条评论等待审核', 'kratos'),
'intro' => sprintf(__('《%s》收到了新评论,需要你确认后才公开显示。', 'kratos'), $title),
'quotes' => array(
array(
'label' => sprintf(__('%s 说', 'kratos'), $author),
'content' => yang_mail_text($comment->comment_content, 1200),
'meta' => mysql2date('Y-m-d H:i', $comment->comment_date),
),
),
'rows' => $rows,
'cta_text' => __('去后台审核', 'kratos'),
'cta_url' => admin_url('edit-comments.php?comment_status=moderated#wpbody-content'),
'links' => yang_mail_core_action_links($comment->comment_ID, true),
'note' => __('这是评论审核提醒,处理完即可忽略。', 'kratos'),
));
}
add_filter('comment_moderation_text', 'yang_mail_core_render_moderation', 20, 2);