Files
wordpress-kratos-yang/inc/theme-options.php
T
laoyang 5c7a9e2c1c 3.2.37 把「自动头像」提成独立标签页,排在全站配置之后 #6
3.2.36 上线后反馈「后台主题设置没有看到头像这个选项」。逐项排查确认
功能是好的:服务器文件已更新(style.css 3.2.36、theme-options.php md5
与本地一致、avatar-styles 14 份齐全),真实后台 HTML 里 a_auto_enable
出现 5 次、of-avatar-style 97 次,CSS/JS 全部下发,无头浏览器点开开关
后 14 张卡片、14 个单选、缩略图全部出图。

真正的原因是位置:它排在「全站配置」的第 9 项,纵坐标 1058px,而浏览器
视口 1000px 高,前面 8 项(其中「404 页面图片」一项就占 301px)把它顶到
首屏之外,不往下滚看不见;左侧 12 个标签页也没有任何「头像」字样,光扫
标签栏同样摸不着。

改动:
- 把两个选项从「全站配置」里搬出来,改成独立 heading 分组,插在
  「全站配置」之后、「顶部导航」之前,成为第 2 个标签页
- 开关显示名从「自动头像」改为「启用自动头像」,避免与标签页同名、
  「勾选上面的自动头像」这类指代说不清
- 分组注释序号全部重排为 1..13 连续

现在标签栏第 2 个就是「自动头像」,进去第一项就是开关(组内前面 0 个
控件,纵坐标 134px)。

theme-avatar.php 一行没动:它的显隐 CSS 与 JS 全部按 section id 定位
(#section-a_auto_style / getElementById('a_auto_enable')),不依赖分组
序号,搬位置不受影响。

验证:
- 分组结构 17/0、几何+交互 29/0(视口 1600x1000,正是上一版把它顶出
  首屏的高度)
- 几何反向 6 项被捕获(主判据用「开关是所在分组第 1 项」,与像素无关)
- 源码守卫 20/0,反向 12/12 全部被捕获
- 包内契约 30/0,反向 8/8 全部被捕获
- 逐字节渲染 84/0,无 mbstring(php -n)84/0
- 严格错误级别下 PHP 8.4.25 与 8.5.12(线上同版)各 43/0
- 回归:后台标签 88/0、3.2.36 头像守卫 55/0、3.2.36 包契约 24/0、
  CDN 56/0 与 77/0、3.2.28 13/0
- 89 个 php 文件 php -l 全通过
2026-10-25 19:18:21 +08:00

1074 lines
50 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
/**
* 主题选项
*
* ── 分类原则(3.2.15 重排)────────────────────────────────────
* 原来九个头部分类里有几处明显错位:
* · 哀悼功能挂在「首页配置」下面 —— 它影响的是整站(首页、文章页、说明页),
* 跟首页没有关系;
* · 国旗、备案、版权拆在「页脚」但「404 图片」在全站,都是零碎的;
* · 「收录配置」把 robots.txt 一起塞进来,其实那是另一件事。
*
* 现在的顺序按「访客看到的顺序」排:整站基础 → 顶部 → 首页 → 文章 →
* 站长 → 页脚 → 专项功能 → 收录 → 邮件 → 自定义代码。
* 每一项只出现在它真正生效的地方,不再有"在首页配置里改整站行为"这种事。
*
* @author Yongquan Yang <yangyq.net>
* @license MIT License
* @version 2020.09.21
*/
function getrobots()
{
$site_url = parse_url(site_url());
$path = (!empty($site_url['path'])) ? $site_url['path'] : '';
$robots = "User-agent: *\n\n";
$robots .= "Disallow: $path/wp-admin/\n";
$robots .= "Disallow: $path/wp-includes/\n";
$robots .= "Disallow: $path/wp-content/plugins/\n";
$robots .= "Disallow: $path/wp-content/themes/\n";
return $robots;
}
/**
* 图片字段的默认值(后台预填用)。
*
* ── 为什么不直接调 yang_image_default() ──────────────────────
* 本文件由 optionsframework_init() 在 init 钩子上 require,
* 而 yang_image_default() 来自 inc/theme-option-defaults.php。
* 正常时序下后者先加载,但**加载顺序被打乱时**(子主题、外部脚本
* 直接 include 本文件、插件在更早的钩子里触发本函数),
* 直接调用就会 Fatal —— 后台整个白屏,比"少一个预填值"严重得多。
*
* 这里先用 function_exists() 探一下,缺了就返回空串:
* 后台退化成没有预填默认值,但页面能打开、能保存、能干活。
* 降级必须降到"还能用",不能降到"打不开"。
*/
if (!function_exists('yang_options_default_img')) {
function yang_options_default_img($id)
{
return function_exists('yang_image_default')
? yang_image_default($id)
: '';
}
}
/**
* 哀悼相关辅助函数的安全访问
*
* 同样的道理:yang_mourning_presets() / yang_mourning_status_text() /
* yang_mourning_extra_raw() 都在 inc/theme-mourning.php 里,由
* functions.php 按顺序 require。只要有人(子主题、迁移脚本、别的插件)
* 在 theme-mourning.php 之前触发了 kratos_options(),这里就会 Fatal,
* 整个后台白屏,且报错指向本文件,很难联想到是加载顺序问题。
*
* 降级策略:缺函数时按"没有纪念日 / 没有状态说明"降级 ——
* 后台那几个控件会变空,但页面能打开、能保存、能继续用。
*/
if (!function_exists('yang_opt_mourning_presets')) {
function yang_opt_mourning_presets()
{
return function_exists('yang_mourning_presets')
? yang_mourning_presets()
: array();
}
}
if (!function_exists('yang_opt_mourning_status')) {
function yang_opt_mourning_status()
{
return function_exists('yang_mourning_status_text')
? yang_mourning_status_text()
: '';
}
}
if (!function_exists('yang_opt_mourning_extra_raw')) {
function yang_opt_mourning_extra_raw()
{
return function_exists('yang_mourning_extra_raw')
? yang_mourning_extra_raw()
: '';
}
}
/**
* 今年清明的月日,给后台说明文字用(如「4 月 5 日」)
*
* 换算不出来(超出年份表范围)时返回一句明确的说明,
* **不要**退化成「4 月 4 日」—— 那正是用户要求删掉的近似值。
*/
if (!function_exists('yang_opt_qingming_label')) {
function yang_opt_qingming_label()
{
if (!function_exists('yang_qingming_md')) {
return __('换算不可用', 'kratos');
}
$md = yang_qingming_md();
if ($md === null) {
return sprintf(
__('今年(%s 年)超出可换算范围,清明不会自动生效', 'kratos'),
current_time('Y')
);
}
list($mo, $da) = explode('-', $md);
return sprintf(__('%d 月 %d 日', 'kratos'), (int) $mo, (int) $da);
}
}
/**
* 自定义 CSS 的默认值(来自 inc/theme-custom-code.php)
*
* 同样是加载顺序问题:这个函数在 theme-custom-code.php 里,
* 而 kratos_options() 可能被更早触发。缺了就返回空串,
* 后台的「自定义 CSS」框是空的,但能打开、能填、能保存。
*
* 3.2.29 起该默认值恒为空:原来的那批规则(段落缩进、h2 蓝条、列表缩进)
* 已并入主题公共样式 assets/css/site.css,配置项只留给站长做临时覆盖。
*/
if (!function_exists('yang_opt_default_custom_css')) {
function yang_opt_default_custom_css()
{
return function_exists('yang_default_custom_css')
? yang_default_custom_css()
: '';
}
}
function kratos_options()
{
$sitename = get_bloginfo('name');
$imagepath = ASSET_PATH . '/assets/img/options/';
$seorobots = '<a href="' . home_url() . '/robots.txt" target="_blank">robots.txt</a>';
$seoreading = '<a href="' . admin_url('options-reading.php') . '" target="_blank">' . __('设置-阅读-对搜索引擎的可见性', 'kratos') . '</a>';
$top_array = array(
'banner' => __( '图片导航', 'kratos' ),
'color' => __( '颜色导航', 'kratos' ),
);
$options = array();
/* ══════════════════════════════════════════════════════════
* 一、全站配置 —— 影响每一个页面的东西
* ══════════════════════════════════════════════════════════ */
$options[] = array(
'name' => __('全站配置', 'kratos'),
'type' => 'heading',
);
$options[] = array(
'name' => __('图片 Logo', 'kratos'),
'desc' => __('不选择图片则显示文字标题', 'kratos'),
'id' => 'g_logo',
'type' => 'upload',
/* 有意不设 std:这里空着时显示站点名称文字,比塞一张通用占位
logo 好看。其它图片字段见 inc/theme-option-defaults.php。 */
);
$options[] = array(
'name' => __('Favicon 图片', 'kratos'),
'desc' => __('浏览器收藏夹和地址栏中显示的图标。<b>留空则自动使用主题自带的默认图标</b>,不会出现空白。', 'kratos'),
'id' => 'g_icon',
/* 有意不写 std。
*
* std 里存的是**绝对 URL**,一旦用户保存表单就会被写进数据库。
* 数据库里躺着一个写死的主题目录地址是隐患:主题目录改名、
* 换 CDN、或站点迁移域名之后,那条记录就指向不存在的文件,
* 页面上是破图,而用户在后台看到的仍是"已设置",很难自己发现。
*
* 所以默认值一律由运行时提供(yang_option_image()),库里保持空。
* 这样默认图地址永远由当前请求现算,改目录也不用改数据。
*/
'type' => 'upload',
);
$options[] = array(
'name' => __('背景颜色', 'kratos'),
'desc' => __('全站页面的背景颜色,需填写十六进制颜色码', 'kratos'),
'id' => 'g_background',
'std' => '#f5f5f5',
'type' => 'color',
);
$options[] = array(
'name' => __('Chrome 导航栏颜色', 'kratos'),
'desc' => __('Chrome 移动端浏览器地址栏的颜色', 'kratos'),
'id' => 'g_chrome',
'std' => '#282a2c',
'type' => 'color',
);
$options[] = array(
'name' => __('404 页面图片', 'kratos'),
'desc' => __('访客打开不存在的地址时显示的配图', 'kratos'),
'id' => 'g_404',
'std' => yang_options_default_img('g_404'),
'type' => 'upload',
);
$options[] = array(
'name' => __('前台管理员导航', 'kratos'),
'desc' => __('登录管理员后,页面顶部显示 WordPress 管理工具条', 'kratos'),
'std' => '1',
'id' => 'g_adminbar',
'type' => 'checkbox',
);
$options[] = array(
'name' => __('CSS 动画库', 'kratos'),
'desc' => __('开启 animate.css 效果。会增加一个约 60KB 的样式文件,不需要就别开。', 'kratos'),
'std' => '0',
'id' => 'g_animate',
'type' => 'checkbox',
);
$options[] = array(
'name' => __('Gravatar 加速', 'kratos'),
'desc' => __('把评论头像的请求转发到国内可访问的镜像,改善加载速度。若开启了下面的「自动头像」,未上传头像的访客不再需要这张图。', 'kratos'),
'std' => '0',
'id' => 'g_gravatar',
'type' => 'checkbox',
);
$options[] = array(
'name' => __('禁止生成缩略图', 'kratos'),
'desc' => __('上传图片时不再额外生成多种尺寸的副本,节省磁盘空间', 'kratos'),
'id' => 'g_removeimgsize',
'std' => '0',
'type' => 'checkbox',
);
$options[] = array(
'name' => __('Gutenberg 编辑器', 'kratos'),
'desc' => __('开启 WordPress 区块编辑器。关闭则使用经典编辑器。', 'kratos'),
'std' => '0',
'id' => 'g_gutenberg',
'type' => 'checkbox',
);
/* ══════════════════════════════════════════════════════════
* 二、自动头像 —— 独立一块
*
* 3.2.36 里这两项原本塞在「全站配置」内部(Gravatar 加速之后)。
* 上线后发现**根本找不到**:它排在第 9 位,纵坐标 1058px,而
* 浏览器视口才 1000px 高 —— 前 8 项(其中「404 页面图片」一项
* 就占 301px)把它顶到首屏之外,不往下滚看不见。左侧标签栏也没
* 有「头像」字样,光扫标签栏同样摸不着。
*
* 所以提成独立标签页:标签栏直接能看见,进去第一项就是开关。
* 这也更贴合原始需求「最上面的一个复选框,勾完之后出现下面这些
* 列表」——独立成页后,这个「上面/下面」的关系才真的成立。
*
* ── 为什么开关与风格拆成两个字段 ──────────────────────────
* 若用一个字段直接存风格名(留空=关闭),用户「主动关闭」和
* 「还没选过风格」在库里会变成同一种状态(都空),以后想恢复
* 默认值时分不清该填什么。两个字段各管一件事更清楚。
*
* ⚠️ std 只在后台预填和「恢复默认」时起作用,运行时 kratos_option()
* 完全不看它。所以运行时默认值必须同步给一份:
* 开关默认关('0'),风格默认 lorelei(见 yang_avatar_style())。
* ══════════════════════════════════════════════════════════ */
$options[] = array(
'name' => __('自动头像', 'kratos'),
'type' => 'heading',
);
$options[] = array(
'name' => __('启用自动头像', 'kratos'),
'desc' => __('访客没注册、也没上传头像时,按他的邮箱生成一张头像。<b>全程本地生成,不请求任何外部服务</b>,同一个邮箱永远得到同一张。若访客已经上传过真实头像,不受影响。', 'kratos'),
'std' => '0',
'id' => 'a_auto_enable',
'type' => 'checkbox',
);
$options[] = array(
'name' => __('头像风格', 'kratos'),
'desc' => __('勾选上面的「启用自动头像」后,这里才会出现风格列表。所有风格都是 CC0 或 MIT 许可,可自由使用与商用。点一下即时预览,保存后生效。', 'kratos'),
'std' => 'lorelei',
'id' => 'a_auto_style',
'type' => 'avatar_style',
/* 只有开关打开时才显示这个控件;关着的时候看不到它,
避免用户在关闭状态下改了风格却不知道为什么没生效。 */
'requires' => 'a_auto_enable',
);
/* ══════════════════════════════════════════════════════════
* 三、顶部导航 —— 页头那一条
* ══════════════════════════════════════════════════════════ */
$options[] = array(
'name' => __('顶部导航', 'kratos'),
'type' => 'heading',
);
$options[] = array(
'name' => __( '顶部样式', 'kratos' ),
'desc' => __('请选择顶部样式(图片导航或颜色导航)', 'kratos'),
'id' => 'top_select',
'std' => 'banner',
'type' => 'select',
'options' => $top_array
);
$options[] = array(
'name' => __('图片导航背景', 'kratos'),
'desc' => __('顶部样式选「图片导航」时使用。<b>留空则自动使用主题自带的默认图片</b>。', 'kratos'),
'id' => 'top_img',
'std' => yang_options_default_img('top_img'),
'class' => 'hidden',
'type' => 'upload',
);
$options[] = array(
'name' => __('颜色导航背景', 'kratos'),
'desc' => __('顶部样式选「颜色导航」时使用', 'kratos'),
'id' => 'top_color',
'std' => '#24292e',
'class' => 'hidden',
'type' => 'color',
);
$options[] = array(
'name' => __('副标题', 'kratos'),
'desc' => __('显示在站点标题旁边的一句短话', 'kratos'),
'id' => 'top_title',
'std' => 'Kratos',
'class' => 'hidden',
'type' => 'text',
);
$options[] = array(
'name' => __('标题描述', 'kratos'),
'std' => __('一款专注于用户阅读体验的响应式博客主题', 'kratos'),
'id' => 'top_describe',
'class' => 'hidden',
'type' => 'text',
);
/* ══════════════════════════════════════════════════════════
* 四、首页配置 —— 只管首页列表长什么样
* ══════════════════════════════════════════════════════════ */
$options[] = array(
'name' => __('首页配置', 'kratos'),
'type' => 'heading',
);
$options[] = array(
'name' => __('特色图片', 'kratos'),
'desc' => __('在首页文章列表的每篇标题上方显示一张配图', 'kratos'),
'std' => '1',
'id' => 'g_thumbnail',
'type' => 'checkbox',
);
$options[] = array(
'name' => __('默认特色图', 'kratos'),
'desc' => __('文章既没有正文图片、也没有设置特色图时,用这张顶上。<b>留空则自动使用主题自带的默认图片</b>。', 'kratos'),
'id' => 'g_postthumbnail',
'class' => 'hidden',
'std' => yang_options_default_img('g_postthumbnail'),
'type' => 'upload',
);
$options[] = array(
'name' => __('无内容图片', 'kratos'),
'desc' => __('搜索不到结果、或分类下还没有文章时显示的插画。<b>留空则自动使用主题自带的默认图片</b>。', 'kratos'),
'id' => 'g_nothing',
'std' => yang_options_default_img('g_nothing'),
'type' => 'upload',
);
/* ══════════════════════════════════════════════════════════
* 五、文章配置 —— 打开单篇文章才看得到的东西
* ══════════════════════════════════════════════════════════ */
$options[] = array(
'name' => __('文章配置', 'kratos'),
'type' => 'heading',
);
$options[] = array(
'name' => __('页面布局', 'kratos'),
'desc' => __('文章页面是否显示侧边栏。仅在文章详情页生效。', 'kratos'),
'id' => "g_article_widgets",
'std' => "two_side",
'type' => "images",
'options' => array(
'one_side' => $imagepath . 'col-12.png',
'two_side' => $imagepath . 'col-8.png')
);
$options[] = array(
'name' => __('文章打赏', 'kratos'),
'desc' => __('在文章底部显示打赏按钮,点开是收款二维码', 'kratos'),
'id' => 'g_donate',
'type' => 'checkbox',
);
$options[] = array(
'name' => __('支付宝收款码', 'kratos'),
'desc' => __('<b>留空则自动使用主题自带的默认图片</b>。', 'kratos'),
'id' => 'g_donate_alipay',
'std' => yang_options_default_img('g_donate_alipay'),
'type' => 'upload',
'class' => 'hidden',
);
$options[] = array(
'name' => __('微信收款码', 'kratos'),
'desc' => __('<b>留空则自动使用主题自带的默认图片</b>。', 'kratos'),
'id' => 'g_donate_wechat',
'std' => yang_options_default_img('g_donate_wechat'),
'type' => 'upload',
'class' => 'hidden',
);
/* ══════════════════════════════════════════════════════════
* 六、自动目录 —— 把长文切成能跳的章节
*
* 与「文章配置」分开成独立标签页,是刻意的:目录的选项有六项,
* 塞进文章配置里会把「页面布局 / 打赏」这些一眼要看到的东西挤下去。
*
* ⚠️ 这里的 std 必须与 inc/theme-toc.php 的 yang_toc_defaults() 一致。
* 前者只服务「后台预填」和「恢复默认」,运行时一律走
* kratos_option($id, 默认值) —— 两处不一致会出现
* 「页面照 A 渲染、后台框里显示 B」的错位,而且极难发现。
* ══════════════════════════════════════════════════════════ */
$options[] = array(
'name' => __('自动目录', 'kratos'),
'type' => 'heading',
);
/* ── 分组标题:正文里的那一份 ──────────────────────────────
*
* 用 info 类型而不是 heading:heading 会另起一个标签页
* (framework 里 `case "heading"` 会 $counter++ 并新建
* #options-group-N),info 只渲染成一行 <h4> + 说明文字,
* 既不进标签页菜单,也不生成表单字段(见
* class-options-interface.php 的 :24 / :59 / :91)。
*
* 加这两条小标题的原因:浮动目录和正文目录是两件互不相干的事,
* 但原来的选项排成一长列,「添加方式」「自动插入位置」跟在
* 「浮动显示」后面,看着像是两种目录共用的设置,容易让人以为
* 浮动目录也分手动/自动。 */
$options[] = array(
'name' => __('正文里的目录', 'kratos'),
'desc' => __('下面这几项只影响<b>正文流里</b>那一份目录 —— 它跟着文章一起排版,会占正文位置。', 'kratos'),
'id' => 'toc_group_inline',
'type' => 'info',
);
$options[] = array(
'name' => __('在正文中显示', 'kratos'),
'desc' => __('勾选后,在文章正文里插一份目录。<br>它和下面的「浮动显示」<b>互不影响</b>:两个都勾就同时出现两份(正文一份、边上浮一份),只勾一个就只出现一种。', 'kratos'),
'id' => 'toc_inline',
'std' => '1',
'type' => 'checkbox',
);
$options[] = array(
'name' => __('正文中的对齐', 'kratos'),
'desc' => __('正文里那份目录在<b>文章内容区域内</b>靠左还是靠右。这里不是「浮到页面边上」—— 目录仍然在正文流里,上下文的文字会绕开它,只是整体贴向一侧。<br>只在勾选「在正文中显示」时才有意义。', 'kratos'),
'id' => 'toc_inline_align',
'std' => 'left',
'type' => 'select',
'options' => array(
'left' => __('靠左', 'kratos'),
'right' => __('靠右', 'kratos'),
),
);
$options[] = array(
'name' => __('添加方式', 'kratos'),
'desc' => __('<b>手动</b>:不自动加,只有正文里手写了 <b>[TOC]</b> 的地方才显示(老文章的行为,升级后不会突然多出目录)。<br><b>自动</b>:由主题自己在「自动插入位置」处加一份,不用改任何文章。<br>本项<b>只管正文里的那一份</b>;浮动目录不受它影响(浮动目录没有地方写 <b>[TOC]</b>,勾了就一直在)。', 'kratos'),
'id' => 'toc_mode',
'std' => 'manual',
'type' => 'select',
'options' => array(
'manual' => __('手动(只在写 [TOC] 的地方显示)', 'kratos'),
'auto' => __('自动添加', 'kratos'),
),
);
$options[] = array(
'name' => __('自动插入位置', 'kratos'),
'desc' => __('只在「添加方式 = 自动」且勾选了「在正文中显示」时起作用;手动方式下插入点由你自己写的 <b>[TOC]</b> 决定。<br><b>第一个标题之前</b>取正文里最先出现的 h1 或 h2 —— 文章大标题在正文之外,不会被算进去;一个 h1/h2 都没有时退回插在文章开头。', 'kratos'),
'id' => 'toc_anchor',
'std' => 'first',
'type' => 'select',
'options' => array(
'start' => __('文章开头', 'kratos'),
'first' => __('第一个标题之前(H1 / H2)', 'kratos'),
'end' => __('文章结尾', 'kratos'),
),
);
$options[] = array(
'name' => __('浮动目录', 'kratos'),
'desc' => __('下面两项只影响<b>浮在页面边上</b>那一份 —— 它不占正文位置,和正文里的目录互不相干。', 'kratos'),
'id' => 'toc_group_float',
'type' => 'info',
);
$options[] = array(
'name' => __('浮动显示', 'kratos'),
'desc' => __('勾选后,目录变成贴在页面边缘的浮动面板,点边上的小标签滑出,<b>不占正文位置</b>。窄屏(手机)上是从边缘滑出的抽屉,不会挤占正文。<br>与「在正文中显示」互相独立,可以同时勾选。<b>它没有手动/自动之分</b>:勾了就一直在,不勾就不出现。', 'kratos'),
'id' => 'toc_float',
'std' => '',
'type' => 'checkbox',
);
$options[] = array(
'name' => __('浮动方向', 'kratos'),
'desc' => __('浮动面板贴页面左边还是右边。只在勾选「浮动显示」时才有意义。', 'kratos'),
'id' => 'toc_float_side',
'std' => 'left',
'type' => 'select',
'options' => array(
'left' => __('页面左侧', 'kratos'),
'right' => __('页面右侧', 'kratos'),
),
);
$options[] = array(
'name' => __('两份目录共用的设置', 'kratos'),
'desc' => __('下面几项对正文里和浮动的那一份<b>同时生效</b>。', 'kratos'),
'id' => 'toc_group_common',
'type' => 'info',
);
$options[] = array(
'name' => __('默认状态', 'kratos'),
'desc' => __('<b>默认显示</b>:进来就能看到全部条目。<b>默认隐藏</b>:先只露出标题栏(或侧边的标签),读者自己点开。<b>较长或标题很多的文章建议默认隐藏</b>,否则目录会占掉一屏。<br>注:这里只管「默认长什么样」,读者点开后仍然可以正常使用;正文里手写的 <b>[TOC]</b> 也可以用 <b>open="0"</b> 单独指定。', 'kratos'),
'id' => 'toc_default',
'std' => 'show',
'type' => 'select',
'options' => array(
'show' => __('默认显示', 'kratos'),
'hide' => __('默认隐藏', 'kratos'),
),
);
$options[] = array(
'name' => __('目录标题', 'kratos'),
'desc' => __('显示在目录顶部的那行字。留空则用「目录」。', 'kratos'),
'id' => 'toc_title',
'std' => __('目录', 'kratos'),
'type' => 'text',
);
$options[] = array(
'name' => __('收录标题级别', 'kratos'),
'desc' => __('勾选要进目录的标题级别。<b>正文里的 h1 是作者自己写的章节标题</b>(文章大标题在正文之外,不会被收进来),所以要用 h1 分章的文章也建议勾上 h1。<br>h4 以下通常是细节,勾上会让目录变得很长。', 'kratos'),
'id' => 'toc_levels',
'std' => array('1' => 1, '2' => 1, '3' => 1, '4' => 0),
'type' => 'multicheck',
'options' => array(
'1' => array('name' => 'H1', 'note' => __('一级标题', 'kratos')),
'2' => array('name' => 'H2', 'note' => __('二级标题', 'kratos')),
'3' => array('name' => 'H3', 'note' => __('三级标题', 'kratos')),
'4' => array('name' => 'H4', 'note' => __('四级标题', 'kratos')),
),
);
$options[] = array(
'name' => __('显示序号', 'kratos'),
'desc' => __('在条目前面加 1 / 1.1 / 1.1.1 这样的序号。', 'kratos'),
'id' => 'toc_numbering',
'std' => '0',
'type' => 'checkbox',
);
/* ══════════════════════════════════════════════════════════
* 七、站长信息 —— 文章页页脚那张名片
* ══════════════════════════════════════════════════════════ */
$options[] = array(
'name' => __('站长信息', 'kratos'),
'type' => 'heading',
);
$options[] = array(
'name' => __('个人头像', 'kratos'),
'desc' => __('显示在文章底部作者名片里。<b>留空则自动使用主题自带的默认头像</b>。', 'kratos'),
'id' => 'a_gravatar',
'std' => yang_options_default_img('a_gravatar'),
'type' => 'upload',
);
$options[] = array(
'name' => __('个人昵称', 'kratos'),
'id' => 'a_nickname',
'std' => 'Kratos',
'type' => 'text',
);
$options[] = array(
'name' => __('个人简介', 'kratos'),
'std' => __('保持饥渴的专注,追求最佳的品质', 'kratos'),
'id' => 'a_about',
'type' => 'textarea',
);
/* ══════════════════════════════════════════════════════════
* 八、页脚配置 —— 页面最底下那几行
* ══════════════════════════════════════════════════════════ */
$options[] = array(
'name' => __('页脚配置', 'kratos'),
'type' => 'heading',
);
$options[] = array(
'name' => __('版权信息', 'kratos'),
'desc' => __('页脚显示的那行版权声明。<b>留空则自动用「COPYRIGHT © 当前年份 站点名称 版权所有」</b>。', 'kratos'),
'id' => 's_copyright',
/* 有意不写 std:std 里写死的年份过一年就变成旧年份,
而且一旦保存就永久留在库里。改由 footer.php 每次现算。 */
'type' => 'textarea',
);
$options[] = array(
'name' => __('是否显示国旗', 'kratos'),
'desc' => __('在页脚显示一面国旗', 'kratos'),
'std' => '0',
'id' => 'g_flag',
'type' => 'checkbox',
);
$options[] = array(
'name' => __('国旗图片', 'kratos'),
'desc' => __('<b>留空则自动使用主题自带的默认国旗</b>。', 'kratos'),
'id' => 'g_flag_url',
/* 不写 std —— 理由同 g_icon:默认值由运行时给,库里不存绝对地址。 */
'type' => 'upload',
);
$options[] = array(
'name' => __('工信部备案信息', 'kratos'),
'id' => 's_icp',
'placeholder' => __('例如:京ICP证xxxxxx号', 'kratos'),
'type' => 'text',
);
$options[] = array(
'name' => __('公安网备案信息', 'kratos'),
'id' => 's_gov',
'placeholder' => __('例如:京公网安备 xxxxxxxxxxxx号', 'kratos'),
'type' => 'text',
);
$options[] = array(
'name' => __('公安网备案链接', 'kratos'),
'id' => 's_gov_link',
'placeholder' => __('例如:http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=xxxxx', 'kratos'),
'type' => 'text',
);
/* ══════════════════════════════════════════════════════════
* 九、哀悼模式 —— 整站级功能,与首页无关
*
* 这一组原来错放在「首页配置」下面。它影响的是整站配色
* (首页、文章页、说明页都变黑白),放首页下面会让人以为
* 只改首页,而且想找它的人第一反应不会去点「首页配置」。
* 单独成组,名字直说它是什么。
* ══════════════════════════════════════════════════════════ */
$options[] = array(
'name' => __('哀悼模式', 'kratos'),
'type' => 'heading',
);
$options[] = array(
'name' => __('手动常开', 'kratos'),
'desc' => __('勾上就一直黑白,与日期无关。这是老的开关,日常建议用下面的「按纪念日自动哀悼」。', 'kratos'),
'id' => 'g_rip',
'type' => 'checkbox',
);
$options[] = array(
'name' => __('按纪念日自动哀悼', 'kratos'),
'desc' => __('勾选后,只有在下表勾选的那些日期才会自动黑白,其余日子正常显示。日期按站点时区判断,只比对月和日,每年循环。', 'kratos'),
'id' => 'g_rip_auto',
'type' => 'checkbox',
);
// 把内置清单转成 multicheck 需要的格式
$rip_legal = array();
$rip_custom = array();
foreach (yang_opt_mourning_presets() as $key => $info) {
/* 键有两种:静态日期(`09-03`)和动态日期(`qingming`)。
动态日期的键是内部标识,不能直接摆在用户面前 ——
所以看 key_label,没有就回退到键本身(静态日期就是 MM-DD,
显示出来正好是用户认得的日期)。 */
$label = isset($info['key_label']) ? $info['key_label'] : $key;
$entry = array(
'name' => $label . ' ' . $info['name'],
'note' => $info['note'],
);
if (isset($info['group']) && $info['group'] === 'legal') {
$rip_legal[$key] = $entry;
} else {
$rip_custom[$key] = $entry;
}
}
$rip_default = array();
foreach (yang_opt_mourning_presets() as $key => $info) {
$rip_default[$key] = !empty($info['default']) ? 1 : 0;
}
$options[] = array(
'name' => __('纪念日清单', 'kratos'),
'desc' => sprintf(
__('勾选需要自动黑白的日期。<b>默认一个都不勾</b>,请按自己的意愿挑选。前三个是法定纪念日,后面几个是民间通行做法。日期按站点时区判断,只比对月和日,每年循环。<br><b>清明节</b>是节气,日期每年浮动(4 月 4~6 日之间),已脱离固定日期、按天文历逐年换算 —— 今年是 <b>%s</b>。', 'kratos'),
esc_html(yang_opt_qingming_label())
),
'id' => 'g_rip_days',
'std' => $rip_default,
'type' => 'multicheck',
'options' => array_merge($rip_legal, $rip_custom),
);
$options[] = array(
'name' => __('附加日期', 'kratos'),
'desc' => sprintf(
__('清单里没有的日期填这里。<br><b>%1$s</b> 表示每年循环(如 05-12);<b>%2$s</b> 表示只在当年生效(如 2020-04-04,用于历次全国哀悼日这类一次性事件)。<br>多个日期用空格或逗号分隔。当前填的是 <b>%3$s</b>。', 'kratos'),
'MM-DD',
'YYYY-MM-DD',
esc_html(yang_opt_mourning_extra_raw() === '' ? __('(空)', 'kratos') : yang_opt_mourning_extra_raw())
),
'id' => 'g_rip_extra',
'type' => 'text',
);
$options[] = array(
'name' => __('说明页路径', 'kratos'),
'desc' => sprintf(
/* 注意:desc 会过 wp_kses($allowedtags),那是 WordPress 的默认白名单,
只放行 a/abbr/b/br/em/i/strong 等,**不放行 <code>** ——
写 <code> 会被静默剥掉,页面上只剩裸文字。所以这里用 <b> 强调。 */
__('变黑白时,页面顶部会出现一条提示,点进去是一个说明页面(彩色),讲清楚为什么变黑白、哪几天会变。<br>这里可以改那个页面的路径。留空默认 <b>memory</b>,即 %s<br><b>注意:</b>不要和已有文章或页面的路径重名。', 'kratos'),
esc_url(home_url('/memory/'))
),
'id' => 'g_rip_page_slug',
'std' => 'memory',
'type' => 'text',
);
$options[] = array(
'desc' => yang_opt_mourning_status(),
'type' => 'info',
);
/* ══════════════════════════════════════════════════════════
* 十、收录与统计 —— 给搜索引擎看的
* ══════════════════════════════════════════════════════════ */
$options[] = array(
'name' => __('收录与统计', 'kratos'),
'type' => 'heading',
);
$options[] = array(
'name' => __('分享图片', 'kratos'),
'desc' => __('搜索引擎或者社交工具分享首页时抓取的图片。<b>留空则自动使用主题自带的默认图片</b>。', 'kratos'),
'id' => 'seo_shareimg',
'std' => yang_options_default_img('seo_shareimg'),
'type' => 'upload',
);
$options[] = array(
'name' => __('关键词', 'kratos'),
'desc' => __('每个关键词之间需要用「英文逗号」分割', 'kratos'),
'id' => 'seo_keywords',
'type' => 'text',
);
$options[] = array(
'name' => __('站点描述', 'kratos'),
'id' => 'seo_description',
'type' => 'textarea',
);
$options[] = array(
'name' => __('统计代码', 'kratos'),
'desc' => __('填在页脚的统计脚本(如站点访问量统计)。注意:输入 HTML/JS 代码时请注意辨别代码安全!', 'kratos'),
'id' => 'seo_statistical',
'type' => 'textarea',
);
$options[] = array(
'name' => __('robots.txt 内容', 'kratos'),
'desc' => __('- 需要 ', 'kratos') . $seoreading . __(' 是开启的状态,下面的配置才会生效', 'kratos'),
'type' => 'info',
);
$options[] = array(
'desc' => __('- 如果网站根目录下已经有 robots.txt 文件,下面的配置不会生效', 'kratos'),
'type' => 'info',
);
$options[] = array(
'desc' => __('- 点击 ', 'kratos') . $seorobots . __(' 查看配置是否生效,如果网站开启了 CDN,可能需要刷新缓存才会生效', 'kratos'),
'type' => 'info',
);
$options[] = array(
'id' => 'seo_robots',
'std' => getrobots(),
'type' => 'textarea',
);
/* ══════════════════════════════════════════════════════════
* 十一、邮件通知 —— SMTP 与评论提醒
* ══════════════════════════════════════════════════════════ */
$options[] = array(
'name' => __('邮件通知', 'kratos'),
'type' => 'heading',
);
$options[] = array(
'name' => __('SMTP 服务', 'kratos'),
'desc' => __('用第三方邮箱发信。不开启时走服务器本地发信,很多主机上会被判定为垃圾邮件。', 'kratos'),
'id' => 'm_smtp',
'type' => 'checkbox',
);
$options[] = array(
'name' => __('邮件服务器', 'kratos'),
'desc' => __('填写发件服务器地址', 'kratos'),
'id' => 'm_host',
'class' => 'hidden',
'type' => 'text',
);
$options[] = array(
'name' => __('服务器端口', 'kratos'),
'desc' => __('填写发件服务器端口', 'kratos'),
'id' => 'm_port',
'class' => 'hidden',
'type' => 'text',
);
$options[] = array(
'name' => __('授权方式', 'kratos'),
'desc' => __('填写登录鉴权的方式,ssl 或 tls', 'kratos'),
'id' => 'm_sec',
'class' => 'hidden',
'type' => 'text',
);
$options[] = array(
'name' => __('邮箱帐号', 'kratos'),
'desc' => __('填写邮箱账号', 'kratos'),
'id' => 'm_username',
'class' => 'hidden',
'type' => 'text',
);
$options[] = array(
'name' => __('邮箱密码', 'kratos'),
'desc' => __('填写邮箱密码或授权码', 'kratos'),
'id' => 'm_passwd',
'class' => 'hidden',
'type' => 'password',
);
$options[] = array(
'id' => 'm_sendmail',
'class' => 'hidden',
'type' => 'sendmail',
);
/* ── 评论提醒的开关 ──────────────────────────────────────
*
* ⚠️ 这三个开关原先**只有代码在读取、后台没有界面**:
* `m_comment_notify` —— 有人回复时通知被回复者
* `m_comment_approved_notify` —— 评论通过审核时通知评论者
* `m_keep_core_notify` —— 保留 WordPress 自带的作者通知
* 也就是说用户无法关掉它们,只能改代码。
* 静态契约检查(`_static_315.php`)会报「模板读取的选项都有界面」失败,
* 那一项正是靠这个抓出来的。
*
* 默认值取 true 与代码里的 `kratos_option('m_comment_notify', true)` 一致:
* 评论提醒默认开着(这是博客的基本礼貌),想关的人自己能关。
*/
$options[] = array(
'name' => __('有人回复时发邮件通知', 'kratos'),
'desc' => __('评论收到回复时,给被回复的人发一封提醒。需要先在下面配好发信方式,否则发不出去。', 'kratos'),
'id' => 'm_comment_notify',
'std' => true,
'type' => 'checkbox',
);
$options[] = array(
'name' => __('评论通过审核时通知评论者', 'kratos'),
'desc' => __('评论从「待审核」变为「已通过」时,告诉评论者他的评论已显示。适合开了先审后发的站点。', 'kratos'),
'id' => 'm_comment_approved_notify',
'std' => true,
'type' => 'checkbox',
);
$options[] = array(
'name' => __('保留 WordPress 自带的作者通知', 'kratos'),
'desc' => __('主题在装了评论插件时会关掉 WordPress 自带的「有新评论通知站长」,避免和插件重复发信。<b>只有当插件在场时勾这一项才有意义</b>;插件不在场时主题一定会保留自带通知,不会让你收不到信。', 'kratos'),
'id' => 'm_keep_core_notify',
'std' => false,
'type' => 'checkbox',
);
/* ══════════════════════════════════════════════════════════
* 十二、CDN 加速 —— 一个开关 + 两个名单
*
* 3.2.34 原设计是「地址留空即关闭」,没有开关。本版(3.2.35)
* 按用户要求补了三件事:
* ① 「启用 CDN 加速」复选框,作为总开关
* ② 白名单预填 13 类静态资源后缀(用户可改)
* ③ 黑名单预填安全相关后缀(用户可改)
*
* ── 预制内容为什么放在输入框里而不是硬编码 ──────────────────
* 用户明确选了这条路:「预制的这些就放在白名单的输入框里,
* 这样用户就可以直接修改;修改完之后,我们再把它保存下来,
* 按照用户修改的来执行。」
*
* 所以预制内容写在 std 里 —— 它只服务两件事:
* · 后台首次渲染时预填
* · 点「恢复默认」时还原成这份内容
* 一旦用户保存过,库里就有明确的值,运行时读的是库里的值,
* 主题**不会**再去覆盖用户改过的内容。
*
* 匹配逻辑(写进说明里,让用户看得到):
* 开关没勾 → 整个功能关闭,两套名单都不参与
* 先看黑名单 → 命中就保持原地址
* 黑名单没命中 → 再看白名单
* 黑名单优先级高于白名单
* ══════════════════════════════════════════════════════════ */
$options[] = array(
'name' => __('CDN 加速', 'kratos'),
'type' => 'heading',
);
/*
* 开关必须是本段的第一个控件。
*
* std 用 yang_cdn_enable_default()(地址非空 ⇒ 默认勾选),
* 目的是**老站点升级不丢加速**:3.2.34 的站点只要填过地址就
* 一直在加速,升级后若默认不勾,它会静默失去加速,而用户不会
* 去查一个从没动过的复选框。
*/
$options[] = array(
'name' => __('启用 CDN 加速', 'kratos'),
'desc' => __('勾选后功能才生效。<b>没勾时,下面填什么都不产生作用</b>,前台与不装这个功能完全一致。'
. '<br>勾了但 CDN 地址留空是<b>不允许</b>的:点保存会被拦下来并提示必须填地址。', 'kratos'),
'id' => 'cdn_enable',
'std' => function_exists('yang_cdn_enable_default') ? yang_cdn_enable_default() : false,
'type' => 'checkbox',
);
$options[] = array(
'name' => __('CDN 地址', 'kratos'),
'desc' => __('资源的加速前缀,例如 <code>https://cdn.example.com</code>。可以不带协议(自动按 https 补全)。'
. '<b>勾选了上面的开关,这一项就必须填</b>。', 'kratos'),
'id' => 'cdn_url',
'std' => '',
'type' => 'text',
'placeholder' => 'https://cdn.example.com',
);
/*
* 白名单的 std 就是预制清单。
*
* ⚠️ std 在运行时**完全不生效**(kratos_option 只查库),所以
* 这里只负责「后台预填」与「恢复默认」。运行时若库里没有这个键,
* 由 yang_cdn_defaults() 提供同一份内容 —— 两处必须一致,
* 否则会出现"后台显示 13 项、运行时按 0 项算"的错位。
*/
$options[] = array(
'name' => __('白名单', 'kratos'),
'desc' => __('命中这些规则的文件<b>才</b>走 CDN。一行一条,<code>#</code> 或 <code>;</code> 开头的行是注释。'
. '<br><b>这里已经预填了 13 类常用静态资源</b>,你可以直接改、加、删,保存后按你改的执行。'
. '<br>⚠️ 把它全部删空 = <b>所有静态文件都走 CDN</b>(不只是下面这 13 类)。'
. '不想让字体、图标字体之类的走 CDN,就把 <code>*</code> 换成具体后缀。', 'kratos'),
'id' => 'cdn_include',
'std' => function_exists('yang_cdn_preset_text') ? yang_cdn_preset_text(yang_cdn_preset_include()) : '',
'type' => 'code',
'settings' => array('rows' => 10),
);
$options[] = array(
'name' => __('白名单写法', 'kratos'),
'desc' => __('按<b>路径</b>匹配(正则规则除外,它对完整地址匹配)。下面每种写法都可以直接抄:', 'kratos')
. '<br><code>*</code> —— 全部静态文件'
. '<br><code>jpg</code> 或 <code>.jpg</code> 或 <code>*.jpg</code> —— 扩展名'
. '<br><code>jpg,png,webp</code> 或 <code>.jpg .png</code> —— 一串扩展名,命中任一'
. '<br><code>/wp-content/uploads/</code> —— 以这个路径开头(以斜杠开头、不含星号)'
. '<br><code>*/uploads/*</code> —— 通配符,<code>*</code> 可以跨斜杠'
. '<br><code>uploads</code> —— 路径里含这几个字(子串)'
. '<br><code>~\/20\d\d\/~</code> —— 正则,对完整地址匹配'
. '<br><br>扩展到字体也很常见:加一行 <code>.woff2 .woff .ttf .eot .otf</code> 即可。',
'type' => 'info',
);
$options[] = array(
'name' => __('黑名单', 'kratos'),
'desc' => __('命中的文件<b>即使符合白名单也不走 CDN</b>,保持原地址。一行一条,写法与白名单完全相同。'
. '<b>黑名单优先于白名单</b>:两边都命中时按黑名单处理。'
. '<br><b>这里已经预填了安全相关的后缀</b>(<code>.php</code> <code>.env</code> <code>.sql</code> <code>.bak</code> <code>.log</code> 等)—— '
. '它们一旦被推到第三方 CDN 节点上,等于把配置、数据库备份、错误日志公开了。'
. '<b>建议保留这行</b>,即使白名单写的是 <code>*</code>。', 'kratos'),
'id' => 'cdn_exclude',
'std' => function_exists('yang_cdn_preset_text') ? yang_cdn_preset_text(yang_cdn_preset_exclude()) : '',
'type' => 'code',
'settings' => array('rows' => 10),
);
$options[] = array(
'name' => __('这些地址永远不走 CDN', 'kratos'),
'desc' => __('下面这些不受两套名单影响,是硬性排除 —— 把它们改到 CDN 会让站点直接坏掉,所以不给开关:'
. '<br>· <b>以斜杠结尾</b>的地址(页面、栏目页、导航链接):改了会让访客跳去 CDN 域名,全站 404'
. '<br>· <b>页面与动态后缀</b>:<code>php</code>、<code>html</code>、<code>htm</code>、<code>asp</code> 等'
. '<br>· <b>没有后缀</b>的地址:通常是接口、短链、伪静态页'
. '<br>· <b>后台与接口路径</b>:<code>wp-admin</code>、<code>wp-login.php</code>、<code>wp-json</code>、<code>wp-cron.php</code>、<code>xmlrpc.php</code>、<code>/feed/</code>'
. '<br>· <b>非本站域名</b>:外链图片、别人家的 CDN'
. '<br>· <b>非 http(s) 协议</b>:<code>data:</code>、<code>mailto:</code>、<code>tel:</code>、<code>javascript:</code>', 'kratos'),
'type' => 'info',
);
$options[] = array(
'name' => __('后台与接口里不改写', 'kratos'),
'desc' => __('为了不影响编辑与调试,下面这些地方<b>保持原地址</b>:后台页面、REST 接口、AJAX 请求、feed 订阅、WP-Cron、定制器预览、命令行。只有访客看到的前台页面才改写。', 'kratos'),
'type' => 'info',
);
/* ══════════════════════════════════════════════════════════
* 十三、自定义代码 —— 兜底用,放最后
*
* 3.2.29 起这两项默认都为空:站点自己的通用样式
* (段落缩进、h2 蓝条、列表缩进)已并入 assets/css/site.css,
* 跟着主题走、升级不丢,也不再需要在这里重复一份。
* ══════════════════════════════════════════════════════════ */
$options[] = array(
'name' => __('自定义代码', 'kratos'),
'type' => 'heading',
);
$options[] = array(
'name' => __('自定义 CSS', 'kratos'),
'desc' => __('附加样式,排在主题公共样式之后加载,用于临时覆盖。站点通用样式已并入主题的 assets/css/site.css,这里<b>留空即可</b>。保存后立即生效,随主题升级保留。', 'kratos'),
'id' => 'g_custom_css',
'std' => yang_opt_default_custom_css(),
'type' => 'code',
'settings' => array('rows' => 14),
);
$options[] = array(
'name' => __('自定义 JS', 'kratos'),
'desc' => __('附加脚本,在页面底部加载,不需要写 &lt;script&gt; 标签。', 'kratos'),
'id' => 'g_custom_js',
'std' => '',
'type' => 'code',
'settings' => array('rows' => 14),
);
return $options;
}