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: 博客, 双栏, 响应式, 自定义背景, 自定义颜色, 自定义图标, 自定义菜单, 特色图像, 收录优化, 邮件中心, 主题选项, 小工具, 文章置顶, 无障碍友好