版本号 3.2.5 -> 3.2.6(统一邮件出口 + 邮件模板 + 纪念日自动哀悼 + 去掉广告位)
评论邮件重复的根因是发信源不止一个,分两层处理:一层管「谁发」,一层管「发几封」。 只做前者的话邮件照样重复,只是都从同一个 SMTP 出去。 邮件治理(新增 inc/theme-mail.php) - phpmailer_init 优先级 9999 接管通道,保证主题配置优先于其它 SMTP 插件; 接管前先重置可能被别的插件设过的字段,避免残留半套配置。 - From 强制等于 SMTP 登录账号。QQ / 163 会以「发件人不符」拒收。 - wp_mail 优先级 99 统一 From 与 Content-Type;调用方自带的 From 与 Content-Type 被剥离后重建,自定义 header 保留。 - wp_mail 优先级 100 做短期去重:同一收件人 + 同主题 + 同正文,90 秒内只发一次。 用 transient 是因为评论通知跨请求(wpDiscuz 走 AJAX、审核通知走后台请求), 静态变量跨不过去。90 秒足够宽(一次提交引发的通知都在同一秒)也足够窄 (手动重发通常隔几分钟,不该被吞)。 - 被去重的那次把 to 置空并由 pre_wp_mail 返回 true,而不是 return null: 返回 null 会改变 wp_mail 返回值语义,有的插件据此判定失败并重试,反而更多封。 - 用 filter 关掉核心的 notify_postauthor / notify_moderator,不改进数据库里的 comments_notify / moderation_notify。改用户设置属于越权,而且用户下次进后台 一勾又回来了;filter 是可逆的。只拦通知,不影响评论审核流程。 - 线上站点 m_smtp=1 但 m_host 为空时判定为「未就绪」并完全放手。只勾开关不填 主机时若强行接管,所有邮件会静默失败,比回退到 mail() 更糟。 邮件模板(新增 inc/theme-mail-template.php) - 表格布局 + 全内联样式:Outlook 2007-2019 用 Word 引擎,不认 flex / grid; Gmail 会剥掉 <head> 里的 style 块(含 media query)。 - 不用外部图片、不用 CSS 变量、不用 ::before。按钮的 padding 放在 td 上, 因为 Outlook 忽略 a 的 padding。 - 宽度用混合写法:HTML 属性 width=600 给 Outlook,CSS 的 width:100% + max-width:600px 让手机窄屏自动收窄。只写死 600px 会让手机横向滚动。 - badge 背景用预算的实色而非 rgba。Outlook 不支持 rgba,会当成非法值丢掉, 半透明背景变成全透明,badge 直接消失。 - 主色下沿那条 3px 深色带必须 height 同时写成 HTML 属性和 CSS, 否则 Outlook 把它撑成一行文字高,比例就毁了。 - 用 WCAG 相对亮度判断主色深浅来决定叠在上面的文字用白还是深色。 简单取平均会把 #00ff00 判成中等亮度(人眼对绿最敏感、蓝最不敏感)。 - 次要文字统一调到 #5f6b78。原来的浅灰在白底上只有 1.84:1,看着"高级"但读不出来; 现在最低 4.62:1,全部过 AA。 theme-smtp.php - 删掉 comment_notify / comment_approved 两个钩子及其硬编码模板。这两个钩子与 wpDiscuz 的 notifyCommentSubscribers 完全重复,是重复收信的直接来源。 纪念日自动哀悼(新增 inc/theme-mourning.php) - 法定纪念日三个默认勾选:09-03 抗战胜利纪念日、09-30 烈士纪念日、 12-13 南京大屠杀死难者国家公祭日。依据是 2014 年全国人大常委会两次决定, 已在代码注释里记下来源。 - 民间通行做法(05-12、09-18 等)默认不勾,由用户自己决定。替用户做价值判断不合适。 - 一次性全国哀悼日(2020-04-04 等)支持 YYYY-MM-DD 写法,只在当年生效。 不能当周年循环,否则明年 4 月 4 日又自动变黑白。 - 按站点时区判断(current_time),不按服务器时区。服务器在 UTC 时会晚 8 小时生效。 - 只挂 wp_head 而不限定 is_home():老代码限定首页,详情页不黑白,那是 bug。 - 打印时取消灰度,否则打印出来是灰的。 - 保留旧的 g_rip 手动常开开关。不能因为升级把原本黑白中的站点变回彩色。 options-framework - 补 multicheck 的渲染分支与 of_sanitize_multicheck。框架原来只在保存侧处理了 这个类型,没有渲染分支,所以这个类型此前根本不可用。 - 值存成 array(键 => 1|0) 而不是「勾选了的键组成的列表」:列表无法区分 「用户明确全部取消」和「从未配置」,会导致取消勾选后静默回退到默认值。 - 读取侧用 array_key_exists 判断是否保存过,而不是判断值是否为空。 - 配套补上 of-multicheck 样式,没有样式时 9 个选项会挤成一行流水文字。 去掉广告配置 - 删除主题选项里的广告配置段,以及 single.php 里上下两处广告位输出。 验证 - 全主题 68 个 PHP 文件语法检查通过。 - 行为测试 82 条、multicheck 闭环 25 条、邮件几何 15 条、设计审计 23 条。 - 反向验证 16 个注入 bug 全部被断言抓住(去重失效、不写标记、to 未清空、 From 未剥离、亮度算法退化、时区改用 date()、全不勾回退默认等)。 - 邮件最低文字对比度 4.62:1;320px 视口下卡片自动收窄到 296px 不溢出。
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
/**
|
||||
* 纪念日自动哀悼
|
||||
*
|
||||
* ── 依据 ─────────────────────────────────────────────────────
|
||||
* 以下日期分三类,可信度不同,代码里也分开对待:
|
||||
*
|
||||
* A. 法定纪念日(全国人大常委会决定设立,每年国家举行纪念活动)
|
||||
* · 09-03 中国人民抗日战争胜利纪念日
|
||||
* 2014-02-27 十二届全国人大常委会第七次会议决定设立
|
||||
* · 09-30 烈士纪念日
|
||||
* 2014-08-31 十二届全国人大常委会第十次会议决定设立
|
||||
* · 12-13 南京大屠杀死难者国家公祭日
|
||||
* 2014-02-27 同一会议决定设立
|
||||
* 依据:全国人大网《铭记历史坐标 凝聚前行力量》
|
||||
* http://www.npc.gov.cn/c2/c30834/202510/t20251020_448426.html
|
||||
* http://npc.gov.cn/c2/c12435/c12487/201905/t20190521_220992.html
|
||||
*
|
||||
* B. 民间广泛采用(非法定,但主流网站普遍黑白)
|
||||
* · 05-12 汶川地震纪念(2008-05-12 14:28)
|
||||
* · 09-18 九一八事变纪念(1931-09-18)
|
||||
* 这两个日期黑白是网络惯例,但不是法定「哀悼日」,所以默认**不勾选**,
|
||||
* 由用户自己决定。硬编码成默认开启会替用户做价值判断,不合适。
|
||||
*
|
||||
* C. 历史全国哀悼日(一次性事件,非每年)
|
||||
* · 2008-05-19~21 汶川地震(国务院决定)
|
||||
* · 2010-04-21 玉树地震(国务院决定)
|
||||
* · 2010-08-15 舟曲泥石流(国务院决定)
|
||||
* · 2020-04-04 新冠疫情牺牲烈士和逝世同胞(国务院决定)
|
||||
* 这些是**特定年份的特定日期**,不是周年循环。所以代码里不把它们
|
||||
* 当成"每年重复",而是保留为可自定义的具体日期,避免明年 4 月 4 日
|
||||
* 又自动变黑白(那是错的)。
|
||||
*
|
||||
* ── 设计取舍 ──────────────────────────────────────────────────
|
||||
* 1. **按站点时区判断**,不按服务器时区。用 current_time('md'),
|
||||
* 它走 WordPress 设置的时区。否则服务器在 UTC,北京时间 9 月 18 日
|
||||
* 早上 8 点前还算 17 日,哀悼会晚 8 小时生效。
|
||||
*
|
||||
* 2. **只判断月日,不判断年份**。纪念日是周年性质,每年的同一天都要生效。
|
||||
*
|
||||
* 3. **兼容旧的 g_rip 开关**。老版本是纯手动「一直黑白」。
|
||||
* 若用户勾着旧的 g_rip,仍然一直黑白;新逻辑是叠加在上面的。
|
||||
* 不能因为升级就让原本黑白中的站点突然恢复彩色。
|
||||
*
|
||||
* @author Yongquan Yang <yangyq.net>
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 内置纪念日清单
|
||||
*
|
||||
* 返回 array( 键 => array(月份, 日, 名称, 说明, 分类, 是否默认勾选) )
|
||||
*
|
||||
* 键名用 `m-d` 形式(补零),与 current_time('m') / current_time('d') 直接可比。
|
||||
*/
|
||||
function yang_mourning_presets()
|
||||
{
|
||||
return array(
|
||||
// ── A 类:法定纪念日 ──
|
||||
'09-03' => array(
|
||||
'name' => __('中国人民抗日战争胜利纪念日', 'kratos'),
|
||||
'note' => __('1945 年 9 月 2 日日本签署投降书,9 月 3 日定为胜利纪念日', 'kratos'),
|
||||
'group' => 'legal',
|
||||
'default' => 1,
|
||||
),
|
||||
'09-30' => array(
|
||||
'name' => __('烈士纪念日', 'kratos'),
|
||||
'note' => __('向人民英雄敬献花篮仪式在天安门广场举行', 'kratos'),
|
||||
'group' => 'legal',
|
||||
'default' => 1,
|
||||
),
|
||||
'12-13' => array(
|
||||
'name' => __('南京大屠杀死难者国家公祭日', 'kratos'),
|
||||
'note' => __('1937 年 12 月 13 日南京沦陷,30 万同胞遇难', 'kratos'),
|
||||
'group' => 'legal',
|
||||
'default' => 1,
|
||||
),
|
||||
|
||||
// ── B 类:民间惯例,默认不勾 ──
|
||||
'05-12' => array(
|
||||
'name' => __('汶川地震纪念日', 'kratos'),
|
||||
'note' => __('2008 年 5 月 12 日 14 时 28 分,汶川发生 8.0 级地震', 'kratos'),
|
||||
'group' => 'custom',
|
||||
'default' => 0,
|
||||
),
|
||||
'09-18' => array(
|
||||
'name' => __('九一八事变纪念日', 'kratos'),
|
||||
'note' => __('1931 年 9 月 18 日,日本关东军发动九一八事变', 'kratos'),
|
||||
'group' => 'custom',
|
||||
'default' => 0,
|
||||
),
|
||||
'07-07' => array(
|
||||
'name' => __('七七事变纪念日', 'kratos'),
|
||||
'note' => __('1937 年 7 月 7 日,卢沟桥事变,全民族抗战爆发', 'kratos'),
|
||||
'group' => 'custom',
|
||||
'default' => 0,
|
||||
),
|
||||
'12-12' => array(
|
||||
'name' => __('西安事变纪念日', 'kratos'),
|
||||
'note' => __('1936 年 12 月 12 日,张学良、杨虎城发动兵谏', 'kratos'),
|
||||
'group' => 'custom',
|
||||
'default' => 0,
|
||||
),
|
||||
'08-15' => array(
|
||||
'name' => __('日本宣布无条件投降纪念日', 'kratos'),
|
||||
'note' => __('1945 年 8 月 15 日,日本天皇宣布接受波茨坦公告', 'kratos'),
|
||||
'group' => 'custom',
|
||||
'default' => 0,
|
||||
),
|
||||
'04-04' => array(
|
||||
'name' => __('清明节', 'kratos'),
|
||||
'note' => __('节气日期每年浮动,这里固定为 4 月 4 日,仅作近似', 'kratos'),
|
||||
'group' => 'custom',
|
||||
'default' => 0,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取用户勾选的纪念日
|
||||
*
|
||||
* 老站点升级上来时这个选项还不存在,此时返回 presets 里 default=1 的那些。
|
||||
* 注意**不能**简单用 `kratos_option('g_rip_days', 默认数组)`:kratos_option
|
||||
* 对未设置的键返回默认值,但如果用户曾经保存过空数组(全部取消勾选),
|
||||
* 存进去的就是空数组,此时必须尊重「用户就是要关掉」,不能再回退到默认。
|
||||
* 所以这里用「option 数组里有没有这个键」来判断,而不是判断值是否为空。
|
||||
*/
|
||||
function yang_mourning_days()
|
||||
{
|
||||
$presets = yang_mourning_presets();
|
||||
$opts = get_option('kratos');
|
||||
$has = is_array($opts) && array_key_exists('g_rip_days', $opts);
|
||||
|
||||
if (!$has) {
|
||||
// 从未保存过:用内置默认
|
||||
$out = array();
|
||||
foreach ($presets as $md => $info) {
|
||||
if (!empty($info['default'])) {
|
||||
$out[] = $md;
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
$saved = $opts['g_rip_days'];
|
||||
if (!is_array($saved)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// 只保留在 presets 里存在、且值为真的日期(防止脏数据)
|
||||
$out = array();
|
||||
foreach ($saved as $md => $v) {
|
||||
if ($v && isset($presets[$md])) {
|
||||
$out[] = $md;
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 今天是否应当哀悼
|
||||
*/
|
||||
function yang_is_mourning_today()
|
||||
{
|
||||
// 旧的手动开关:开了就一直黑白(保持向后兼容,不能让升级把黑白站点变回彩色)
|
||||
if (kratos_option('g_rip', false)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!kratos_option('g_rip_auto', false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 走站点时区,不是服务器时区
|
||||
$md = current_time('m') . '-' . current_time('d');
|
||||
$days = yang_mourning_days();
|
||||
|
||||
// 自定义附加日期(用户自己填的,如特定年份的哀悼日)
|
||||
if (kratos_option('g_rip_extra', '')) {
|
||||
$extra = kratos_option('g_rip_extra', '');
|
||||
foreach (preg_split('/[\s,,、;;]+/u', (string) $extra) as $one) {
|
||||
$one = trim($one);
|
||||
if ($one === '') {
|
||||
continue;
|
||||
}
|
||||
// 支持 MM-DD 与 YYYY-MM-DD 两种写法
|
||||
if (preg_match('/^\d{2}-\d{2}$/', $one)) {
|
||||
$days[] = $one;
|
||||
} elseif (preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $one, $m)) {
|
||||
// 带年份的只在当年生效,不是周年循环
|
||||
if ((int) $m[1] === (int) current_time('Y')) {
|
||||
$days[] = $m[2] . '-' . $m[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return in_array($md, $days, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出黑白样式
|
||||
*
|
||||
* 几个细节:
|
||||
* 1. `html { filter: grayscale(1) }` 会给整页建一个合成层,fixed 定位的元素
|
||||
* 在某些旧版 Chrome 上会错位。所以额外加 `background-attachment: fixed`
|
||||
* 的补丁样式不写;这里改用给 body 和 .k-header 分别处理更稳。
|
||||
* —— 实际上实测最稳的是给 html 加 filter 并用 transform: translateZ(0)
|
||||
* 在 body 上兜住,避免 iOS Safari 的渲染错位。这里保留 html 方案,
|
||||
* 因为它在用户当前主题结构下工作正常(老功能的写法),只是补一条
|
||||
* iOS 的兼容声明。
|
||||
* 2. 不能只挂 is_home():详情页也应该黑白。老代码限定首页,那是 bug。
|
||||
* 3. 只在真正哀悼的日子输出,避免给所有页面增加一条无用样式。
|
||||
*/
|
||||
function yang_mourning_style()
|
||||
{
|
||||
if (is_admin() || !yang_is_mourning_today()) {
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<style id="yang-mourning">'
|
||||
. "\nhtml{filter:grayscale(100%);-webkit-filter:grayscale(100%);}"
|
||||
. "\nhtml body{-webkit-transform:translateZ(0);}"
|
||||
// 打印时不灰度,否则打印出来是灰的
|
||||
. "\n@media print{html{filter:none!important;-webkit-filter:none!important;}}"
|
||||
. "\n</style>\n";
|
||||
}
|
||||
add_action('wp_head', 'yang_mourning_style', 1);
|
||||
|
||||
/**
|
||||
* 后台显示今天的哀悼状态,便于确认配置是否生效
|
||||
*/
|
||||
function yang_mourning_status_text()
|
||||
{
|
||||
if (kratos_option('g_rip', false)) {
|
||||
return __('当前为手动常开模式:站点一直黑白,与日期无关。', 'kratos');
|
||||
}
|
||||
if (!kratos_option('g_rip_auto', false)) {
|
||||
return __('自动哀悼未开启。', 'kratos');
|
||||
}
|
||||
$days = yang_mourning_days();
|
||||
if (empty($days)) {
|
||||
return __('自动哀悼已开启,但一个日期都没勾选,因此不会生效。', 'kratos');
|
||||
}
|
||||
sort($days);
|
||||
$today = current_time('m') . '-' . current_time('d');
|
||||
$is = in_array($today, $days, true);
|
||||
return sprintf(
|
||||
/* translators: 1: 今天的月日, 2: 是否处于哀悼, 3: 勾选的日期列表 */
|
||||
__('今天是 %1$s,%2$s。已启用:%3$s', 'kratos'),
|
||||
$today,
|
||||
$is ? __('正在哀悼,站点已黑白', 'kratos') : __('不在哀悼日', 'kratos'),
|
||||
implode('、', $days)
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user