diff --git a/inc/options-framework/includes/class-options-framework-admin.php b/inc/options-framework/includes/class-options-framework-admin.php
index 07f89dc..0e215a1 100644
--- a/inc/options-framework/includes/class-options-framework-admin.php
+++ b/inc/options-framework/includes/class-options-framework-admin.php
@@ -63,6 +63,17 @@ class Options_Framework_Admin {
add_action( 'optionsframework_after_validate', array( $this, 'save_options_notice' ) );
add_action( 'optionsframework_after_sendmail', array( $this, 'send_mail_notice' ) );
+
+ /*
+ * 进设置页就检查 CDN 配置是否"勾了但没填地址"。
+ *
+ * 挂在 admin_init 上、在 options_page() 渲染之前跑,
+ * 这样 add_settings_error 的结果会被页面里的 settings_errors() 显示。
+ * 用 current_user_can 兜一层:虽然本类整体已经在
+ * optionsframework_init 里判过权限,但这个钩子单独挂,
+ * 以后若有人把它挪出去也不会漏权限判断。
+ */
+ add_action( 'admin_init', array( $this, 'yang_cdn_config_notice' ), 21 );
}
public function add_top_options_page()
@@ -284,6 +295,65 @@ class Options_Framework_Admin {
}
}
+ /*
+ * ★ CDN 配置校验(3.2.35)
+ *
+ * 「勾了启用但地址为空」是**非法配置**,不是"关闭"。用户原话:
+ * 「如果点了这个复选框,但是 CDN 地址不填,那就不行,得提示他
+ * 说这个东西必须填。」所以这里拦住整个保存。
+ *
+ * ── 怎么做到"拒绝保存" ──────────────────────────────────
+ * 本函数是 register_setting() 的 sanitize_callback,WP 会把
+ * 它的返回值直接写进 options 表。所以**返回旧值就等于拒绝本次
+ * 保存**(新值一个字段都不落库),不需要额外抛异常或 unset。
+ *
+ * 为什么是"整单拒绝"而不是"只把开关取消":用户明确选了这条路
+ * (选项一「整个保存被拒绝」)。附带效果是同一次里改的其它设置
+ * 也会一起作废 —— 这是有意的:提交了一个不合法的表单,就不该
+ * 让其中一部分悄悄生效,否则用户会以为"我保存成功了"。
+ *
+ * ── 为什么用 $_POST 而不是 $input ──────────────────────
+ * $input 已经过 WP 的 wp_unslash,但复选框"没勾"时这个键
+ * **根本不会出现在 POST 里**(浏览器不提交未勾选的 checkbox)。
+ * 而这里的 $input 是同名过滤器链上拿到的数组,语义一致;
+ * 用 $input 就够,且不依赖具体表单字段名。$clean 里已经有
+ * 经过 of_sanitize_checkbox() 的值,直接用它最准。
+ */
+ if ( function_exists( 'yang_cdn_check_config' ) ) {
+ $cdn_enable = isset( $clean['cdn_enable'] ) ? $clean['cdn_enable'] : false;
+ $cdn_url = isset( $clean['cdn_url'] ) ? $clean['cdn_url'] : '';
+ $cdn_err = yang_cdn_check_config( $cdn_enable, $cdn_url );
+
+ if ( $cdn_err !== '' ) {
+ /*
+ * ⚠️ 这里**绝对不能**调 update_option() 把旧值写回去。
+ * 本函数就在 sanitize_option_kratos 过滤器上,
+ * update_option() 会再触发一次同名过滤器 ⇒ 无限递归
+ * ⇒ 吃爆内存(3.2.11 的事故,报错落在区块解析器上,
+ * 看那个报错会完全找错方向)。见下面 sendmail 分支的注释。
+ *
+ * 正确做法就是"返回旧值":WP 自己会把它写进库,
+ * 结果与"没保存"等价。
+ */
+ add_settings_error(
+ 'options-framework',
+ 'cdn_needs_url',
+ __( '未保存:', 'kratos' ) . $cdn_err,
+ 'error'
+ );
+ /*
+ * 「保存成功」那条提示不用手动清:本函数在校验分支里
+ * 直接 return,后面的 do_action('optionsframework_after_validate')
+ * 根本不会执行,所以它不会被加进 settings_errors。
+ * 若改成"清一下",反而是多余动作,会让人误以为
+ * 两条提示都会出现、需要手工压掉。
+ */
+
+ $current = get_option( 'kratos' );
+ return is_array( $current ) ? $current : array();
+ }
+ }
+
if ( isset( $_POST['sendmail'] ) ) {
/*
* 测试发信要让「这一批新配置」立刻生效,但**绝对不能**
@@ -340,6 +410,34 @@ class Options_Framework_Admin {
$GLOBALS['__yang_sendmail_result'] = null;
}
+ /**
+ * 进入设置页时的状态提示
+ *
+ * ── 为什么需要在"进页面"时就提示 ────────────────────────────
+ * 保存时的拦截只在用户点保存那一刻生效。而"勾了启用但地址空"
+ * 这个状态还有另一种来源:**老站点从 3.2.34 升级上来**。
+ * 3.2.34 没有这个复选框,升级后如果用户清空过地址、又一直没动
+ * 过这个开关,他就是"看起来已启用、实际没生效"的状态。
+ * 这种人不会点保存(他没想改设置),所以只靠保存拦截永远救不到他。
+ *
+ * 提示放在 options_page() 渲染之前(本函数挂在 admin_init),
+ * add_settings_error 会随 settings_errors() 一起显示。
+ */
+ function yang_cdn_config_notice() {
+ if ( ! function_exists( 'yang_cdn_missing_url' ) ) {
+ return;
+ }
+ if ( ! yang_cdn_missing_url() ) {
+ return;
+ }
+ add_settings_error(
+ 'options-framework',
+ 'cdn_missing_url',
+ __( '「启用 CDN 加速」是勾选状态,但 CDN 地址是空的,功能当前没有生效(前台与关闭时完全一样)。请填写 CDN 地址,或者取消勾选这个开关。', 'kratos' ),
+ 'error'
+ );
+ }
+
/**
* Display message when options have been saved
*/
diff --git a/inc/theme-cdn.php b/inc/theme-cdn.php
index 587bb3f..d264b96 100644
--- a/inc/theme-cdn.php
+++ b/inc/theme-cdn.php
@@ -26,18 +26,102 @@
* @license MIT License
*/
+/**
+ * 预制白名单(写死在主题代码里,同时作为后台输入框的默认内容)
+ *
+ * ── 为什么是这些扩展名 ──────────────────────────────────────
+ * 只收「内容不变、可以放心放到第三方节点上」的静态资源。
+ * 判据是三条:
+ * ① 按扩展名就能确定是静态文件,不需要服务端参与
+ * ② 不含用户私有内容(所以没有 .xml —— 那可能是 sitemap,
+ * 也可能是 RSS 导出,还可能含未公开数据)
+ * ③ 同一个文件在 CDN 上被读一千次也不会产生任何副作用
+ *
+ * ── 为什么带点写 ────────────────────────────────────────────
+ * 规则引擎里单个纯字母词会**同时**按「扩展名」和「路径子串」匹配
+ * (这是为了救 `uploads` 那条目录规则,见 yang_cdn_compile_rule)。
+ * 不带点的 `avif` 会命中 `/wp-content/uploads/avif-optimized/a.jpg`
+ * 这种路径;带点之后子串是 `.avif`,不会误伤。
+ *
+ * 这份清单会**预填进后台的白名单输入框**,用户可以随意增删改;
+ * 保存之后以用户的内容为准,主题不会再去覆盖它。
+ * 想恢复成这份默认内容,点后台的「恢复默认」即可。
+ */
+function yang_cdn_preset_include()
+{
+ return array(
+ '.avif', '.css', '.gif', '.jpeg', '.jpg', '.js', '.json',
+ '.mp3', '.mp4', '.pdf', '.png', '.svg', '.webp',
+ );
+}
+
+/**
+ * 预制黑名单
+ *
+ * ── 为什么这份清单有必要 ────────────────────────────────────
+ * 硬性排除(yang_cdn_should_rewrite 的 ① )已经挡住了 .php / .html
+ * 一类会直接改坏站点的后缀。但那只是「改了站点会坏」的下限,不是
+ * 「改了会出事」的全部:`.env`、`.sql`、`.bak`、`.log` 这些一旦
+ * 被推到第三方 CDN 节点上,等于把配置、数据库备份、错误日志公开了。
+ * 它们不在硬性排除里(因为主题没法预知用户装了哪些插件、
+ * 备份文件放在哪个目录),所以必须在默认配置里先拦住。
+ *
+ * 同样预填进后台的黑名单输入框,用户可以改。黑名单优先于白名单,
+ * 所以即使把白名单写成 `*`,这份黑名单里的后缀也不会走 CDN。
+ */
+function yang_cdn_preset_exclude()
+{
+ return array(
+ '.php', '.php3', '.php4', '.php5', '.phtml', '.phar',
+ '.env', '.sql', '.bak', '.log', '.ini', '.sh', '.htaccess',
+ );
+}
+
+/** 预制清单 → 一行一条的文本(输入框与默认值都用这个形态) */
+function yang_cdn_preset_text($list)
+{
+ return implode("\n", $list);
+}
+
+/**
+ * 「启用」复选框的默认值
+ *
+ * ── 默认值为什么这么算 ──────────────────────────────────────
+ * 3.2.34 的语义是「填了 CDN 地址就生效」,没有开关。本版加了开关,
+ * 如果把它默认成「不勾」,那么升级上来的站点(地址早就填好、正在
+ * 正常工作)会在升级那一刻**静默失去加速**,而且用户不会去查
+ * 一个从没动过的复选框。
+ *
+ * 所以默认值取「地址非空就当作已启用」:
+ * · 老站点填过地址 → 默认勾选 → 升级后行为不变
+ * · 老站点没填地址 → 默认不勾 → 点保存不会被「必须填地址」拦住
+ * · 用户亲手保存过一次之后,库里就有明确的值,按用户的来
+ *
+ * ⚠️ 这里**不能**走 yang_cdn_opt():那个函数会先取 yang_cdn_defaults(),
+ * 而 defaults 又调本函数,会无限递归。必须直接问 kratos_option。
+ */
+function yang_cdn_enable_default()
+{
+ $u = kratos_option('cdn_url', '');
+ return is_string($u) && trim($u) !== '';
+}
+
/**
* 运行时默认值
*
* 与 3.2.32 的教训一致:`std` 只在后台预填和恢复默认时有用,
* **运行时取不到**。所以这里单独给一份,供 kratos_option() 回落。
+ *
+ * 两个输入框的默认值就是预制清单 —— 这样「从未保存过配置」的站点
+ * 装好主题就有一套能用的规则(而不是"白名单为空 = 全部加速")。
*/
function yang_cdn_defaults()
{
return array(
+ 'cdn_enable' => yang_cdn_enable_default(),
'cdn_url' => '',
- 'cdn_include' => '',
- 'cdn_exclude' => '',
+ 'cdn_include' => yang_cdn_preset_text(yang_cdn_preset_include()),
+ 'cdn_exclude' => yang_cdn_preset_text(yang_cdn_preset_exclude()),
);
}
@@ -55,6 +139,104 @@ function yang_cdn_opt($key)
return is_string($v) ? $v : '';
}
+/**
+ * 把一个「复选框的值」判成真/假
+ *
+ * 抽出来是因为有三个地方要判:运行时(yang_cdn_enabled)、
+ * 保存校验(yang_cdn_check_config)、以及后台提示。
+ * 三处各写一份 `=== '1'` 的话,只要有一处写窄了就会表现成
+ * "勾了但功能不开",而且极难查。
+ */
+function yang_cdn_truthy($v)
+{
+ if (is_string($v)) {
+ $v = trim($v);
+ return $v !== '' && $v !== '0';
+ }
+ return (bool) $v;
+}
+
+/**
+ * 「启用 CDN 加速」复选框当前是不是勾着的
+ *
+ * ── 为什么真值判断要写得这么宽 ──────────────────────────────
+ * 这个值经过 Options Framework 的 of_sanitize_checkbox(),勾了存字符串
+ * '1'、没勾存 false。但历史数据里可能是 1 / '1' / true / 'on',
+ * 也可能压根没有这个键(3.2.34 之前的站点)。所以一律按「真值」判,
+ * 不做 `=== '1'` 这种字面量比较 —— 那会让老站点全部变成"未启用"。
+ */
+function yang_cdn_enabled()
+{
+ $d = yang_cdn_defaults();
+ return yang_cdn_truthy(kratos_option('cdn_enable', $d['cdn_enable']));
+}
+
+/**
+ * 功能此刻到底生不生效
+ *
+ * 三个条件同时满足才算生效:勾了开关、地址非空、地址能解析。
+ * 任何一条不满足都直接返回 false —— 这时前台行为与没装这个功能完全一致
+ * (不是"少改一点",是**一个字节都不改**)。
+ *
+ * 把这四个判断收在一个函数里,是为了让「哪里算关闭」只有一处定义:
+ * 输出缓冲(yang_cdn_buffer_start)、整页改写(yang_cdn_rewrite_html)、
+ * 后台诊断(yang_cdn_test_url)三处都调它,不会出现
+ * "缓冲没接管但后台说已命中"这种自相矛盾。
+ */
+function yang_cdn_active()
+{
+ if (!yang_cdn_enabled()) {
+ return false;
+ }
+ return yang_cdn_parse_url(yang_cdn_opt('cdn_url')) !== null;
+}
+
+/**
+ * 保存前的配置校验(★ 只在后台保存链路里用)
+ *
+ * ── 为什么参数要显式传进来,而不是直接读库 ──────────────────
+ * 本函数是在 register_setting 的 sanitize 回调里跑的。那一刻用户
+ * 刚提交的新值**还没写库**,直接调 yang_cdn_opt() / yang_cdn_enabled()
+ * 读到的是**上一次**的配置 —— 于是「勾了但没填地址」永远判不出来
+ * (上一次可能是合法的),而「上次勾了、这次取消勾选」会被误判成
+ * 非法并拒绝保存。这类"校验的是旧数据"的 bug 极难自查:表现成
+ * "提示时有时无",取决于上一次存的是什么。
+ *
+ * 所以这里只接受调用方传进来的、**这一次提交的** $enable 与 $url。
+ *
+ * 返回 '' 表示没问题;返回非空字符串是给用户看的原因。
+ */
+function yang_cdn_check_config($enable, $url)
+{
+ if (!yang_cdn_truthy($enable)) {
+ return '';
+ }
+ $url = is_string($url) ? trim($url) : '';
+ if ($url === '') {
+ return '「启用 CDN 加速」已勾选,但 CDN 地址是空的。请填写 CDN 地址,或者取消勾选这个开关。';
+ }
+ if (yang_cdn_parse_url($url) === null) {
+ return 'CDN 地址「' . $url . '」无法识别,请填写形如 https://cdn.example.com 的地址。';
+ }
+ return '';
+}
+
+/**
+ * 「勾了开关却没填地址」这种非法状态 —— 供后台提示用
+ *
+ * ── 为什么不在运行时判断,也不影响前台 ──────────────────────
+ * 这个状态在前台**等同于关闭**(yang_cdn_active() 会返回 false),
+ * 所以前台不需要它。它只是后台的一个提示条件:
+ * 用户下次进入设置页时,如果看到自己勾了但没填地址,应该被提醒。
+ *
+ * 判据必须与 yang_cdn_check_config() 的口径完全一致(勾了 + 空地址),
+ * 否则会出现"保存时拦住、进页面却不提示"这种前后矛盾。
+ */
+function yang_cdn_missing_url()
+{
+ return yang_cdn_enabled() && trim(yang_cdn_opt('cdn_url')) === '';
+}
+
/**
* 解析 CDN 地址
*
@@ -83,15 +265,66 @@ function yang_cdn_parse_url($raw)
return null;
}
+ /*
+ * ★ 主机名必须自己校验一遍 —— parse_url 对主机极其宽容。
+ *
+ * 实测(PHP 7.4 / 8.4 都一样):
+ * parse_url('https://not a url at all') → host = 'not a url at all'
+ * parse_url('https://exa mple.com/x') → host = 'exa mple.com'
+ * parse_url('https://cdn..example.com') → host = 'cdn..example.com'
+ *
+ * 只判 `empty($p['host'])` 挡不住这些。后果不是"少加速一点",而是
+ * **全站静态资源一起变成畸形地址**:用户把地址栏里的整串复制进来、
+ * 或者手打了空格,页面上每个 css/js/图都会拼成
+ * `https://not a url at all/wp-content/...` ⇒ 全部加载失败,
+ * 站点直接没有样式。而用户在后台看到的只是一个"填了地址"的框。
+ *
+ * 所以这里显式拒掉:空白、控制字符、分隔符、首尾点、连续点,
+ * 以及不属于「字母/数字/点/横线/下划线」的字符。
+ * 国际化域名(中文域名)放行 —— \p{L} 覆盖各类文字。
+ */
+ $host = $p['host'];
+ $is_ipv6 = (substr($host, 0, 1) === '[' && substr($host, -1) === ']');
+ if (!$is_ipv6) {
+ /*
+ * ⚠️ 这里**只留两条**检查,刻意不写「空白字符单独判一条」。
+ *
+ * 空白(含 \t \n \r 与控制字符)以及 / ? # @ : \ 这些分隔符,
+ * 全都不在下面的字符集里 ⇒ 已经被字符集检查挡住了。
+ * 单独再写一条 `preg_match('/[\s\x00-\x1f\x7f]/')` 看着更"严密",
+ * 实际是**恒被下一条覆盖的空转断言** —— 反向验证时拆掉它
+ * 探针照样全绿,谁也不知道它其实不起作用。
+ * 宁可少一条真的,不要多一条假的(判据要能证明自己有用)。
+ */
+ if (substr($host, 0, 1) === '.' || substr($host, -1) === '.'
+ || strpos($host, '..') !== false) {
+ return null;
+ }
+ if (!preg_match('/^(?:[A-Za-z0-9._\-]|\p{L})+\z/u', $host)) {
+ return null;
+ }
+ }
+
$scheme = !empty($p['scheme']) ? strtolower($p['scheme']) : 'https';
if ($scheme !== 'http' && $scheme !== 'https') {
return null;
}
+ /*
+ * 子路径(CDN 地址可以是 `cdn.example.com/static`)同理:
+ * 路径里出现空白或控制字符,拼出来的资源地址就是非法的
+ * (`https://cdn.example.com/sta tic/a.css` 浏览器会当断点处理)。
+ * 合法的编码形式 `%20` 不受影响 —— 它不含真正的空白字符。
+ */
+ $path = isset($p['path']) ? rtrim($p['path'], '/') : '';
+ if (preg_match('/[\s\x00-\x1f\x7f]/', $path)) {
+ return null;
+ }
+
return array(
'scheme' => $scheme,
- 'host' => strtolower($p['host']),
- 'path' => isset($p['path']) ? rtrim($p['path'], '/') : '',
+ 'host' => strtolower($host),
+ 'path' => $path,
);
}
@@ -284,6 +517,18 @@ function yang_cdn_rules()
*/
function yang_cdn_should_rewrite($url, $path)
{
+ /*
+ * 功能没开 ⇒ 任何地址都不走 CDN。
+ *
+ * 这一条必须放在最前面,且不能只放在调用方(buffer_start / rewrite_html):
+ * 本函数是「规则判定」的唯一出口,后台诊断、自测脚本、以及将来可能
+ * 新增的调用方都从它拿结论。只在入口挡的话,只要有人新加一个调用点
+ * 就会漏判,而漏判的表现是"关掉了还在加速"。
+ */
+ if (!yang_cdn_active()) {
+ return false;
+ }
+
$rules = yang_cdn_rules();
$include_cache = $rules['include'];
$exclude_cache = $rules['exclude'];
@@ -481,10 +726,17 @@ function yang_cdn_site_base()
*/
function yang_cdn_rewrite_html($html)
{
- $cdn = yang_cdn_parse_url(yang_cdn_opt('cdn_url'));
- if ($cdn === null) {
+ /*
+ * ⚠️ 这里必须**先查开关再查地址**,不能只靠 yang_cdn_parse_url() 返回 null。
+ *
+ * 3.2.34 只有「地址空了 = 关闭」一个条件;本版加了显式开关,
+ * 如果这里还只看地址,那么用户取消勾选后地址仍然躺在库里,
+ * 前台会继续改写 —— 开关等于没用,而且现象是"我关了它还在动"。
+ */
+ if (!yang_cdn_active()) {
return $html;
}
+ $cdn = yang_cdn_parse_url(yang_cdn_opt('cdn_url'));
if (strpos($html, 'http') === false && strpos($html, 'src=') === false
&& strpos($html, 'href=') === false && strpos($html, 'url(') === false) {
return $html;
@@ -651,8 +903,8 @@ function yang_cdn_buffer_start()
if (isset($_GET['customize_changeset_uuid']) || isset($_GET['customize_preview'])) {
return;
}
- // 站点还没配 CDN 地址 —— 直接不接管,零开销
- if (yang_cdn_parse_url(yang_cdn_opt('cdn_url')) === null) {
+ // 功能没开(开关没勾 / 地址没填 / 地址解析不了)—— 直接不接管,零开销
+ if (!yang_cdn_active()) {
return;
}
@@ -697,6 +949,16 @@ function yang_cdn_test_url($url)
'reason' => '',
);
+ /*
+ * 诊断入口要**说清楚是哪个条件没满足**,而不是笼统说"被排除"。
+ * 后台用户看到「地址未填写」和「未命中白名单」是两种完全不同的
+ * 修法,混成一句话就等于没提示。
+ */
+ if (!yang_cdn_enabled()) {
+ $r['reason'] = '「启用 CDN 加速」没有勾选,功能已关闭';
+ return $r;
+ }
+
if ($cdn === null) {
$r['reason'] = 'CDN 地址未填写或无法解析';
return $r;
diff --git a/inc/theme-options.php b/inc/theme-options.php
index 2851b68..8ae932c 100644
--- a/inc/theme-options.php
+++ b/inc/theme-options.php
@@ -872,15 +872,27 @@ function kratos_options()
);
/* ══════════════════════════════════════════════════════════
- * 十二、CDN 加速 —— 三个输入框,不多加
+ * 十二、CDN 加速 —— 一个开关 + 两个名单
*
- * 用户明确要求「一共就这 3 个选项,不要弄得太复杂」。
- * 所以这里没有开关、没有自动开关、没有服务商选择,只有:
- * CDN 地址 / 白名单 / 黑名单
- * 地址留空 = 功能关闭。这比再加一个「启用」勾选框更省事,
- * 也不会出现「勾了启用但没填地址」这种半配置状态。
+ * 3.2.34 原设计是「地址留空即关闭」,没有开关。本版(3.2.35)
+ * 按用户要求补了三件事:
+ * ① 「启用 CDN 加速」复选框,作为总开关
+ * ② 白名单预填 13 类静态资源后缀(用户可改)
+ * ③ 黑名单预填安全相关后缀(用户可改)
+ *
+ * ── 预制内容为什么放在输入框里而不是硬编码 ──────────────────
+ * 用户明确选了这条路:「预制的这些就放在白名单的输入框里,
+ * 这样用户就可以直接修改;修改完之后,我们再把它保存下来,
+ * 按照用户修改的来执行。」
+ *
+ * 所以预制内容写在 std 里 —— 它只服务两件事:
+ * · 后台首次渲染时预填
+ * · 点「恢复默认」时还原成这份内容
+ * 一旦用户保存过,库里就有明确的值,运行时读的是库里的值,
+ * 主题**不会**再去覆盖用户改过的内容。
*
* 匹配逻辑(写进说明里,让用户看得到):
+ * 开关没勾 → 整个功能关闭,两套名单都不参与
* 先看黑名单 → 命中就保持原地址
* 黑名单没命中 → 再看白名单
* 黑名单优先级高于白名单
@@ -890,20 +902,49 @@ function kratos_options()
'type' => 'heading',
);
+ /*
+ * 开关必须是本段的第一个控件。
+ *
+ * std 用 yang_cdn_enable_default()(地址非空 ⇒ 默认勾选),
+ * 目的是**老站点升级不丢加速**:3.2.34 的站点只要填过地址就
+ * 一直在加速,升级后若默认不勾,它会静默失去加速,而用户不会
+ * 去查一个从没动过的复选框。
+ */
+ $options[] = array(
+ 'name' => __('启用 CDN 加速', 'kratos'),
+ 'desc' => __('勾选后功能才生效。没勾时,下面填什么都不产生作用,前台与不装这个功能完全一致。'
+ . '
勾了但 CDN 地址留空是不允许的:点保存会被拦下来并提示必须填地址。', 'kratos'),
+ 'id' => 'cdn_enable',
+ 'std' => function_exists('yang_cdn_enable_default') ? yang_cdn_enable_default() : false,
+ 'type' => 'checkbox',
+ );
+
$options[] = array(
'name' => __('CDN 地址', 'kratos'),
- 'desc' => __('资源的加速前缀,例如 https://cdn.example.com。可以不带协议(自动按 https 补全)。留空即关闭整个功能,站点行为与不装这项功能完全一致。', 'kratos'),
+ 'desc' => __('资源的加速前缀,例如 https://cdn.example.com。可以不带协议(自动按 https 补全)。'
+ . '勾选了上面的开关,这一项就必须填。', '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' => __('命中这些规则的文件才走 CDN。一行一条,# 或 ; 开头的行是注释。留空 = 所有静态文件都走 CDN。', 'kratos'),
+ 'desc' => __('命中这些规则的文件才走 CDN。一行一条,# 或 ; 开头的行是注释。'
+ . '
这里已经预填了 13 类常用静态资源,你可以直接改、加、删,保存后按你改的执行。'
+ . '
⚠️ 把它全部删空 = 所有静态文件都走 CDN(不只是下面这 13 类)。'
+ . '不想让字体、图标字体之类的走 CDN,就把 * 换成具体后缀。', 'kratos'),
'id' => 'cdn_include',
- 'std' => '',
+ 'std' => function_exists('yang_cdn_preset_text') ? yang_cdn_preset_text(yang_cdn_preset_include()) : '',
'type' => 'code',
'settings' => array('rows' => 10),
);
@@ -917,15 +958,20 @@ function kratos_options()
. '
/wp-content/uploads/ —— 以这个路径开头(以斜杠开头、不含星号)'
. '
*/uploads/* —— 通配符,* 可以跨斜杠'
. '
uploads —— 路径里含这几个字(子串)'
- . '
~\/20\d\d\/~ —— 正则,对完整地址匹配',
+ . '
~\/20\d\d\/~ —— 正则,对完整地址匹配'
+ . '
扩展到字体也很常见:加一行 .woff2 .woff .ttf .eot .otf 即可。',
'type' => 'info',
);
$options[] = array(
'name' => __('黑名单', 'kratos'),
- 'desc' => __('命中的文件即使符合白名单也不走 CDN,保持原地址。一行一条,写法与白名单完全相同。黑名单优先于白名单:两边都命中时按黑名单处理。', 'kratos'),
+ 'desc' => __('命中的文件即使符合白名单也不走 CDN,保持原地址。一行一条,写法与白名单完全相同。'
+ . '黑名单优先于白名单:两边都命中时按黑名单处理。'
+ . '
这里已经预填了安全相关的后缀(.php .env .sql .bak .log 等)—— '
+ . '它们一旦被推到第三方 CDN 节点上,等于把配置、数据库备份、错误日志公开了。'
+ . '建议保留这行,即使白名单写的是 *。', 'kratos'),
'id' => 'cdn_exclude',
- 'std' => '',
+ 'std' => function_exists('yang_cdn_preset_text') ? yang_cdn_preset_text(yang_cdn_preset_exclude()) : '',
'type' => 'code',
'settings' => array('rows' => 10),
);
diff --git a/style.css b/style.css
index 82bd3d3..df25db4 100644
--- a/style.css
+++ b/style.css
@@ -1,7 +1,7 @@
/*
Theme Name: Kratos_Yang
Text Domain: kratos_Yang
-Version: 3.2.34
+Version: 3.2.35
Requires PHP: 7.0
Description: 这是一个Kratos主题的分支版本,用来增加一些定制化功能。
Tags: 博客, 双栏, 响应式, 自定义背景, 自定义颜色, 自定义图标, 自定义菜单, 特色图像, 收录优化, 邮件中心, 主题选项, 小工具, 文章置顶, 无障碍友好