diff --git a/inc/theme-avatar.php b/inc/theme-avatar.php
index 252e655..b0cfaeb 100644
--- a/inc/theme-avatar.php
+++ b/inc/theme-avatar.php
@@ -471,9 +471,10 @@ class Yang_Avatar_Prng
/**
* 收录的风格清单
*
- * 只收 CC0 1.0 与 MIT。CC BY 4.0 那些(adventurer / avataaars / bottts /
- * croodles / micah / toon-head 等)虽然更好看,但要求保留署名 ——
- * 主题是给用户装的,用户不会去维护署名,等于把合规义务转嫁给用户。
+ * 收录的 18 套全部可自由使用与商用,且都不要求署名(许可证明细见文件头)。
+ * CC BY 4.0 那 13 套(adventurer / big-ears / croodles / micah /
+ * toon-head 等)虽然更好看,但要求保留署名 —— 主题是给用户装的,
+ * 用户不会去维护署名,等于把合规义务转嫁给用户。
*
* 'label' 用中文,因为后台是给中文站长看的。
* 'size' 是模板文件体积,用于后台提示「这套会占多少」。
@@ -644,9 +645,13 @@ function yang_avatar_palette()
'f8961e', 'f9c74f', '4d908e', '577590', 'f94144', 'f3722c',
'a8dadc', '457b9d',
),
- // 肤色:从浅到深,覆盖常见人种
+ // 肤色:只留偏黄/偏浅的区间,不出现深棕与黑色
+ // ⚠️ 缩短这个数组是**安全的**:Prng::pick() 用
+ // integer(0, count-1) 取索引,消耗的随机数与长度无关,
+ // 所以其它组件(发型、衣着、表情)抽到的结果一点不变,
+ // 只有肤色这一项跟着变。别在这里加逻辑,改长度就够了。
'skinColor' => array(
- 'f2d3b8', 'e8b98f', 'd9a066', 'c68642', '8d5524', 'ffdbac', 'f1c27d',
+ 'ffdbac', 'f2d3b8', 'f1c27d', 'e8b98f',
),
// 发色
'hairColor' => array(
@@ -678,10 +683,13 @@ function yang_avatar_style_palette($style)
? $style['defaults'] : array();
// 字段名 → 调色板类别
+ // ⚠️ 这里只放**真的是皮肤**的字段。`baseColor` 不算 ——
+ // 它在 bottts 里是机器人的外壳色(19 种彩色),实测把它映射成
+ // 肤色后,机器人外壳会被涂成肉色 e8b98f / f1c27d,非常难看。
+ // 「base」这个名字像是底色,但各家风格语义并不统一,别按名字猜。
$map = array(
'backgroundColor' => 'backgroundColor',
'skinColor' => 'skinColor',
- 'baseColor' => 'skinColor',
'hairColor' => 'hairColor',
'topColor' => 'hairColor',
'beardColor' => 'hairColor',
@@ -768,14 +776,16 @@ function yang_avatar_data_uri($seed, $style = '', $size = 96)
* 响应状态码)都要发一次外网请求,而 gravatar.com 在国内全部超时,
* 这条路根本走不通。
*
- * 所以这里退一步,用「src 指向谁」做判据:
+ * 所以这里退一步,用「src 指向谁」+「邮箱属不属于本站用户」做判据:
* · src 是 data: → 说明别的插件/主题已经给过头像了,放行;
- * · src 主机属于 Gravatar 家族 → 替换成生成图;
+ * · src 主机属于 Gravatar 家族,**且**这个邮箱不是本站注册用户的
+ * → 替换成生成图;
+ * · 邮箱属于本站注册用户 → 原样放行(用他自己的头像);
* · 其它(媒体库图片、外链头像)→ 放行。
*
- * 代价:如果站长开了「Gravatar 加速」镜像、并且访客确实有 Gravatar
- * 头像,这里也会替换掉。这是有意的取舍 —— 相比"偶尔盖掉一个真实
- * 头像","多数访客是一张破图/灰头像"更伤。选项说明里写清了这一点。
+ * 代价:如果站长开了「Gravatar 加速」镜像、并且匿名访客确实有 Gravatar
+ * 头像,这里仍会替换掉。这是有意的取舍 —— 相比"偶尔盖掉一个匿名访客
+ * 的真实头像","多数访客是一张破图/灰头像"更伤。选项说明里写清了这一点。
*
* ── 为什么不再重新拼整个
────────────────────────────────────
* 早先的实现直接用 $args 里的 class / extra_attr 重新拼一个标签。
@@ -827,6 +837,22 @@ function yang_avatar_filter($avatar, $id_or_email, $size, $default, $alt, $args
return $avatar;
}
+ /* ── 本站注册用户:原样返回,一个字都不改 ──────────────────────
+ 用户诉求:邮箱能在用户表里查到,说明这个人是本站的人,用他自己的
+ 头像;查不到的(匿名评论者)才给他生成一张。
+
+ 这一步必须放在**拼 data: URI 之前**,否则白白算一遍 SVG 才丢弃。
+
+ ⚠️ 这里有个服务端无法回避的限制:WP 的 get_avatar() 从不检查
+ Gravatar 上到底有没有这张图,它只拼 URL 交给浏览器。所以
+ 「注册用户自己也没上传头像、Gravatar 上其实是默认灰人像」
+ 这种情况,**不联网就分辨不出来**。本功能坚持零外网调用
+ (发布检查会断言不含 curl/wp_remote_get),因此这里选择
+ 「只要是注册用户就不动」—— 宁可留一张灰人像,也不去猜。 */
+ if (yang_avatar_keep_member() && yang_avatar_is_registered_email($email)) {
+ return $avatar;
+ }
+
// 尺寸取实际要显示的那个,别用 96 蒙一把
$px = (int) $size;
if ($px <= 0) {
@@ -959,6 +985,83 @@ function yang_avatar_email_of($id_or_email)
return '';
}
+/**
+ * 这个邮箱是不是本站**注册用户**的邮箱
+ *
+ * ── 为什么要有这一层 ──────────────────────────────────────────────
+ * 早先的实现只判断「src 是不是 Gravatar 家族」,只要是就换成本地生成的
+ * 头像。结果是**连本站注册用户自己的头像也被盖掉** —— 站长看后台、
+ * 作者看文章列表,全是同一套卡通脸,很别扭。
+ *
+ * 用户的判据是:邮箱能在用户表里查到 ⇒ 这个人属于本站,用他自己的;
+ * 查不到(匿名评论者)⇒ 才给他生成一张。
+ *
+ * ── 查表方式 ────────────────────────────────────────────────────
+ * 优先 get_user_by('email'):WP 内部会把结果缓存到 object cache,
+ * 同一请求内多次调用同一邮箱不会重复打库。评论列表一页 20 条也只
+ * 打有限几次。
+ *
+ * ⚠️ 邮箱比较必须**先规范化**(trim + 小写)。WP 建用户时邮箱理论上
+ * 已小写,但导入用户、插件写库、老数据都可能出现大小写混用;
+ * 不规范化会漏判,表现成"明明是注册用户却还是被换了头像"。
+ *
+ * @param string $email
+ * @return bool
+ */
+function yang_avatar_is_registered_email($email)
+{
+ $email = trim((string) $email);
+ if ($email === '') {
+ return false;
+ }
+ // 大小写不敏感:WP 的 user_email 列在大多数排序规则下也是不敏感的,
+ // 但函数调用前先规范化,避免依赖数据库排序规则
+ $email = strtolower($email);
+
+ /* 请求内缓存
+ ────────────────────────────────────────────────────────────
+ 评论列表一页 20 条,同一个人常有 2~3 条;不缓存就是同一个邮箱
+ 反复打库。WP 的 get_user_by 内部虽有 object cache,但那是**按
+ user ID 缓存**的,第一次仍要走一次邮箱查询。
+ 这里再压一层,保证「同一请求内同一邮箱只查一次」。
+ 静态变量在请求结束即销毁,不会跨请求污染。 */
+ static $memo = array();
+ if (array_key_exists($email, $memo)) {
+ return $memo[$email];
+ }
+
+ $hit = false;
+ if (function_exists('get_user_by')) {
+ $u = get_user_by('email', $email);
+ if ($u && isset($u->ID) && (int) $u->ID > 0) {
+ $hit = true;
+ }
+ }
+ $memo[$email] = $hit;
+ return $hit;
+}
+
+/**
+ * 是否「只有匿名访客才换头像」
+ *
+ * 开关存库;默认 '1'(开)—— 这正是用户提需求时的原话:
+ * 「邮箱在我们博客里面属于这个博客的用户的,就用他自己的头像」。
+ *
+ * 关掉这个开关等于回到 3.2.38 的行为:所有 Gravatar 家族的 src 都换。
+ *
+ * @return bool
+ */
+function yang_avatar_keep_member()
+{
+ if (!yang_avatar_active()) {
+ return false;
+ }
+ if (function_exists('kratos_option')) {
+ return kratos_option('a_auto_keep_member', '1') !== '0';
+ }
+ return true;
+}
+
/* ══════════════════════════════════════════════════════════════════
* 渲染器本体
* ══════════════════════════════════════════════════════════════════ */
@@ -1822,8 +1925,11 @@ function yang_avatar_admin_assets($hook)
.of-avatar-preview-cell figcaption{font-size:10px;color:#646970;margin-top:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.of-avatar-preview-note{margin:10px 0 0;font-size:12px;color:#646970}
/* 开关没打开时把风格那一行藏起来 —— 用 body class 而不是 JS 内联样式,
- 这样保存后页面重载也不会闪一下 */
-body.yang-avatar-off #section-a_auto_style{display:none}
+ 这样保存后页面重载也不会闪一下。
+ ⚠️ 新增的「注册用户保留原头像」也必须一起藏:它同样只有在
+ 自动头像启用后才有意义,否则是一个悬空选项。 */
+body.yang-avatar-off #section-a_auto_style,
+body.yang-avatar-off #section-a_auto_keep_member{display:none}
';
wp_register_style('yang-avatar-admin', false, array(), null);
wp_enqueue_style('yang-avatar-admin');
diff --git a/inc/theme-options.php b/inc/theme-options.php
index 2b1fe8b..a0cbc16 100644
--- a/inc/theme-options.php
+++ b/inc/theme-options.php
@@ -286,6 +286,20 @@ function kratos_options()
'type' => 'checkbox',
);
+ /* ⚠️ 这个开关必须排在「头像风格」**之前**。
+ 风格列表是 18 张带缩略图的卡片,实测高约 2100px;
+ 排在它后面的控件实际落到 y≈2327px,而设置页视口约 1000px
+ —— 用户不滚三屏根本看不到。3.2.36 就是栽在「塞在长列表末尾」
+ 这个坑里,别再犯。 */
+ $options[] = array(
+ 'name' => __('注册用户保留原头像', 'kratos'),
+ 'desc' => __('建议开启。开启后只给「邮箱在用户表里查不到」的匿名访客生成头像;本站注册用户一律用他自己的头像,不会被替换。
关掉则所有 Gravatar 头像都会被换掉 —— 站内作者、管理员也会变成卡通脸。
说明:注册用户若自己也没上传过头像,Gravatar 上就是一张默认灰人像。要分辨这种情况必须向 gravatar.com 发请求,而本功能坚持不访问任何外部服务,所以这类用户会保持灰人像,不会自动生成。', 'kratos'),
+ 'std' => '1',
+ 'id' => 'a_auto_keep_member',
+ 'type' => 'checkbox',
+ 'requires' => 'a_auto_enable',
+ );
+
$options[] = array(
'name' => __('头像风格', 'kratos'),
'desc' => __('勾选上面的「启用自动头像」后,这里才会出现风格列表。所有风格都可自由使用与商用,且不要求署名。点一下即时预览,保存后生效。', 'kratos'),
diff --git a/style.css b/style.css
index 1b687e5..c7888ac 100644
--- a/style.css
+++ b/style.css
@@ -1,7 +1,7 @@
/*
Theme Name: Kratos_Yang
Text Domain: kratos_Yang
-Version: 3.2.38
+Version: 3.2.39
Requires PHP: 7.0
Description: 这是一个Kratos主题的分支版本,用来增加一些定制化功能。
Tags: 博客, 双栏, 响应式, 自定义背景, 自定义颜色, 自定义图标, 自定义菜单, 特色图像, 收录优化, 邮件中心, 主题选项, 小工具, 文章置顶, 无障碍友好