Files
wordpress-kratos-yang/inc/theme-option-defaults.php
laoyang 0799268661 3.2.16: 删三项无用功能、选项重排为 10 组、修移动端提示条偏左
删除
- 微信二维码:配置项 s_wechat / 隐藏上传 s_wechat_url、footer 浮动块、
  默认图注册表条目、CSS 规则、wechat.png
- Font Awesome:选项 g_fontawesome、theme-core 入队、58KB 样式、
  assets/fonts/webfonts 全套字体(2.8MB,CSS 删后零引用)
- 多人模式:选项 multiusers、page-content / page-toolbar 两处分支

选项重排(9 组 → 10 组)
全站配置 / 顶部导航 / 首页配置 / 文章配置 / 站长信息 /
页脚配置 / 哀悼模式 / 收录与统计 / 邮件通知 / 自定义代码
哀悼 5 个字段从「首页配置」独立成「哀悼模式」——它不是首页专属功能。
g_404/g_chrome/g_animate/g_gravatar 等归「全站配置」,s_copyright 归「页脚配置」。

修复
- footer.php 版权行此前无条件硬编码,后台 s_copyright 填了不生效
- 附加日期命中时 yang_mourning_today_name() 返回空串,提示条退化成
  只说「今天是纪念日」而不给名字
- 小屏哀悼提示条 padding 写成 `7px 40px 7px 12px`(右宽左窄给关闭按钮
  让位),内容盒中心 181 vs 视口中心 195,整体偏左 14px。关闭按钮本就
  absolute 不占内部流,改为左右等宽 + 缩小按钮。桌面端同类错误 3.2.14 已修,
  此处漏了同一个断点。

加载顺序
theme-options.php 可能早于 theme-mourning.php / theme-custom-code.php 加载,
新增 yang_opt_* 四个 function_exists 包装,避免裸调用导致后台白屏。

验证:静态断言 62/0 · 后台面板 14/0 · 反向验证 7/7 与 44/0 ·
提示条 4 视口几何 20/0(注入旧 bug 精确复现 -14px)· 线上 3 视口 33/0
2026-10-23 22:56:10 +08:00

306 lines
12 KiB
PHP
Raw Permalink 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
/**
* 图片类选项的默认值
*
* ── 为什么需要这个文件 ───────────────────────────────────────────────
* 主题设置里有一批图片字段(Logo、favicon、国旗、404 配图、分享图、
* 默认特色图、无内容图、头像、背景图、二维码…)。新装一个站点时它们
* 全是空的,页面上就会出现:`<link href="">` 的空 favicon、
* `<img src="">` 的破图、一片空白的 404 配图。
*
* ── 原来的 std 为什么不够 ───────────────────────────────────────────
* 选项数组里的 `std` 只在两处起作用:
* ① 后台表单首次打开时的预填值;
* ② 点「恢复默认」按钮时写进库的那批值。
* 而运行时的 kratos_option() **完全不看 std** —— 它只查库,查不到才用
* 调用方传进来的第二个参数。所以:
* · 新装站点(库里没有该键)→ 模板若没写第二参数,就是空;
* · 用户保存过表单但把那格删空了 → 键存在且值是 '',
* kratos_option() 用 isset() 判断,'' 也算"已设置",
* 连调用方传的默认值都会被绕过。
* 两个口子合起来,才导致"明明填了 std 却还是空的"。
*
* ── 本文件的职责 ───────────────────────────────────────────────────
* 用一张集中的「字段 → 默认图」注册表 + 一个访问函数,把上面两个口子
* 一起堵住:
* · 值是空字符串 / 只有空白 → 用默认图;
* · 默认图本身若不存在(有人删了包内文件)→ 返回空,绝不输出假路径。
* 图片文件都在 assets/img/ 下,体积已按用途压到合计约 300KB。
*
* ── 有意不放默认图的字段 ───────────────────────────────────────────
* `g_logo`(图片 Logo)不做默认图。它空着时模板回落到站点名称文字,
* 那比给所有站点塞同一张通用 logo 图好看得多 —— 一张共用的占位 logo
* 会让每个站看起来都一样,反而更难看。
*
* @author Yongquan Yang <yangyq.net>
* @license MIT License
*/
if (!function_exists('yang_image_defaults')):
/**
* 字段 => 主题包内的相对路径
*
* 只登记**确实需要**默认图的字段。新增字段时加一行即可,
* 模板里用 yang_option_image('xxx') 读取。
*/
function yang_image_defaults()
{
return array(
// 站点图标。不用站点所有者自己的那枚(那是他专属的),
// 用一枚中性的 WordPress 风格图标(品牌蓝圆 + 白 W)。
'g_icon' => '/assets/img/favicon.ico',
// 页脚国旗。页面上固定 width:50px,100px 宽足够 2x 屏。
'g_flag_url' => '/assets/img/default-flag.png',
// 404 配图。整块背景,1000px 宽够用。
'g_404' => '/assets/img/default-404.jpg',
// 分享图与默认特色图:同一类用途(社交卡片 / 列表缩略),
// 共用一张,避免包里放两份几乎一样的图。
'seo_shareimg' => '/assets/img/default-share.jpg',
'g_postthumbnail' => '/assets/img/default-share.jpg',
// 搜不到内容时的插画。
'g_nothing' => '/assets/img/default-empty.jpg',
// 站长头像。
'a_gravatar' => '/assets/img/default-avatar.jpg',
// 顶部图片导航的背景。CSS 是 height:300px + cover。
'top_img' => '/assets/img/default-banner.jpg',
// 下面两个主题包里本来就有图,只是这里显式登记,
// 让"所有图片字段都有默认"这件事可被一处查全。
'g_donate_alipay' => '/assets/img/donate.png',
'g_donate_wechat' => '/assets/img/donate.png',
);
}
endif;
if (!function_exists('yang_image_default')):
/**
* 取某个字段的默认图 URL;没有登记或文件不存在则返回 ''。
*
* 为什么要探文件存在:包被精简、或有人手工删了图片时,
* 返回一个 404 的地址比返回空更糟 —— 页面能打开但满屏破图。
* 探存在略有一点 IO 开销,但 WP 对象缓存 + 文件系统缓存下可忽略,
* 且本函数只在拼 HTML 时调用,不在循环里大批量跑。
*/
function yang_image_default($id)
{
$map = yang_image_defaults();
if (!isset($map[$id])) {
return '';
}
$rel = $map[$id];
// ASSET_PATH 由 theme-core.php 定义(可能是 CDN 前缀)。
// 它没定义时退回主题目录,避免在极早期调用时炸掉。
$base = defined('ASSET_PATH') ? ASSET_PATH : get_template_directory_uri();
// 文件存在性只能查本地目录;CDN 模式下本地也有一份,照查不误。
if (!file_exists(get_template_directory() . $rel)) {
return '';
}
return $base . $rel;
}
endif;
/* ── 给后台选项数组用的安全包装 ─────────────────────────────
*
* inc/theme-options.php 只在 admin_init 之后才被 require,正常时序下
* yang_image_default() 一定已存在。但那个文件在某些自定义场景
* (别处的脚本直接 include 它、或子主题换了加载顺序)可能先被读到,
* 那时调用未定义函数会 **Fatal**,整个后台白屏。
*
* 这个包装在函数缺失时返回空串,让后台退化成"没有预填默认值",
* 而不是白屏 —— 降级要降得能继续用,不能降成打不开。
*/
if (!function_exists('yang_options_default_img')):
function yang_options_default_img($id)
{
return function_exists('yang_image_default')
? yang_image_default($id)
: '';
}
endif;
if (!function_exists('yang_image_url_is_stale')):
/**
* 判断库里存的图片地址是不是"指向已不存在的主题目录"的陈旧地址。
*
* 为什么需要这个:
* options 表里可能躺着 `https://站点/wp-content/themes/<旧目录名>/assets/img/x.png`
* 这样的绝对地址(曾经的 std 被保存进库,或用户手动填过)。主题目录
* 一旦改名(kratos-master → kratos_yang),这些地址全部 404,页面上
* 就是破图。它们是"非空值",会绕过一切"空则用默认"的兜底,
* 所以必须单独识别。
*
* 判据刻意保守:只有当路径里出现 /wp-content/themes/<某目录>/ 且
* 那个目录**不是**当前主题目录时,才认定陈旧。用户把图传到
* uploads 目录、或填外链,都不受影响。
*/
function yang_image_url_is_stale($url)
{
if (!is_string($url) || $url === '') {
return false;
}
// 只关心主题目录下的资源;uploads / 外链一律不碰
if (strpos($url, '/wp-content/themes/') === false) {
return false;
}
/* 当前主题的目录名(slug)。
*
* 用 get_template() 而不是 basename(get_stylesheet_directory()):
* 后者取的是**文件系统目录名**,而 URL 里的那段是**主题 slug**。
* 正常安装时两者相同,但本地开发(仓库目录叫 wordpress-kratos-yang)
* 或符号链接部署时就会不一致,导致把"当前主题自己的地址"
* 误判成陈旧地址 ⇒ 用户明明填对了图却被换成默认图。
* get_template() 返回的就是 WP 认定的主题 slug,与 URL 同源。 */
$current = function_exists('get_template') ? get_template() : '';
if ($current === '') {
$current = basename(get_stylesheet_directory());
}
// 取出 /wp-content/themes/ 之后的第一段
$tail = substr($url, strpos($url, '/wp-content/themes/') + strlen('/wp-content/themes/'));
$slash = strpos($tail, '/');
$dir = $slash === false ? $tail : substr($tail, 0, $slash);
$dir = trim($dir);
if ($dir === '' || $dir === $current) {
return false;
}
return true;
}
endif;
if (!function_exists('yang_option_image')):
/**
* 读一个图片类选项,空值自动落到默认图。
*
* 这是模板里应该用的函数,取代直接调 kratos_option() ——
* 后者对空字符串不做兜底。
*
* @param string $id 选项 id
* @return string 图片 URL,可能为空(表示"确实没有可用图")
*/
function yang_option_image($id)
{
$val = kratos_option($id, '');
if (is_string($val)) {
$val = trim($val);
}
// 空值 → 用默认图
if ($val === '' || $val === false || $val === null) {
return yang_image_default($id);
}
/* 非空但指向已删除的主题目录(例如早先被写进库的
* .../themes/kratos-master/...)→ 同样落到默认图。
* 这种值不识别的话会一直是破图,而且比"空着"更难发现:
* 后台看着是填了图的。 */
if (yang_image_url_is_stale($val)) {
return yang_image_default($id);
}
return $val;
}
endif;
if (!function_exists('yang_flag_block')):
/**
* 页脚国旗那一块 HTML。
*
* ── 为什么抽成函数而不是写在 footer.php 里 ──────────────────
* 原来这段是内联的 if/else。内联写法有个测试上的致命问题:
* 断言只能"照着这段逻辑再抄一遍"来测,于是测的是抄件、
* 不是模板本身 —— 模板里真改坏了,测试照样全绿(自己测自己)。
* 抽成函数后,测试直接调它,调的就是线上跑的那份代码。
*
* ── 三种情况 ────────────────────────────────────────────
* 1. 没勾「是否添加国旗」 → 输出空(用户就是不想显示)
* 2. 勾了、但拿不到任何图片地址 → 输出空(**绝不输出 <img src="">**)
* 3. 勾了、有地址 → 输出国旗
*
* 第 2 种是老代码漏掉的:src 直接取选项,空值就成了破图图标。
* 破图比不显示更难看,也更容易被误以为"站坏了"。
*/
function yang_flag_block()
{
if (!kratos_option('g_flag')) {
return '';
}
$url = yang_option_image('g_flag_url');
if ($url === '') {
return '';
}
return '<div id="flag"><img style="width:50px" src="'
. esc_url($url) . '" alt=""><div>';
}
endif;
if (!function_exists('yang_favicon_tag')):
/**
* favicon 的 <link> 标签;拿不到地址时返回空(不输出空 href)。
*
* 空 href 的害处:浏览器会退而请求当前页面当作图标,
* 白跑一次请求、图标还是错的(显示成一个空白方块)。
*/
function yang_favicon_tag()
{
$url = yang_option_image('g_icon');
if ($url === '') {
return '';
}
return '<link rel="shortcut icon" href="' . esc_url($url) . '">';
}
endif;
if (!function_exists('yang_option_image_or')):
/**
* 同 yang_option_image(),但允许调用方指定一个"就地兜底"的回退值。
*
* 用在语义上不该共用默认图的场合:例如 Logo 空着时该显示站点名称,
* 而不是某张图。
*
* @param string $id 选项 id
* @param string $fallback 连默认图都没有时用的值
* @return string
*/
function yang_option_image_or($id, $fallback = '')
{
$img = yang_option_image($id);
return $img !== '' ? $img : $fallback;
}
endif;