* @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 = 'robots.txt'; $seoreading = '' . __('设置-阅读-对搜索引擎的可见性', 'kratos') . ''; $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' => __('浏览器收藏夹和地址栏中显示的图标。留空则自动使用主题自带的默认图标,不会出现空白。', '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', ); /* ══════════════════════════════════════════════════════════ * 二、顶部导航 —— 页头那一条 * ══════════════════════════════════════════════════════════ */ $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' => __('顶部样式选「图片导航」时使用。留空则自动使用主题自带的默认图片。', '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' => __('文章既没有正文图片、也没有设置特色图时,用这张顶上。留空则自动使用主题自带的默认图片。', 'kratos'), 'id' => 'g_postthumbnail', 'class' => 'hidden', 'std' => yang_options_default_img('g_postthumbnail'), 'type' => 'upload', ); $options[] = array( 'name' => __('无内容图片', 'kratos'), 'desc' => __('搜索不到结果、或分类下还没有文章时显示的插画。留空则自动使用主题自带的默认图片。', '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' => __('留空则自动使用主题自带的默认图片。', 'kratos'), 'id' => 'g_donate_alipay', 'std' => yang_options_default_img('g_donate_alipay'), 'type' => 'upload', 'class' => 'hidden', ); $options[] = array( 'name' => __('微信收款码', 'kratos'), 'desc' => __('留空则自动使用主题自带的默认图片。', '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 只渲染成一行
** ——
写 会被静默剥掉,页面上只剩裸文字。所以这里用 强调。 */
__('变黑白时,页面顶部会出现一条提示,点进去是一个说明页面(彩色),讲清楚为什么变黑白、哪几天会变。
这里可以改那个页面的路径。留空默认 memory,即 %s
注意:不要和已有文章或页面的路径重名。', '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' => __('搜索引擎或者社交工具分享首页时抓取的图片。留空则自动使用主题自带的默认图片。', '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 自带的「有新评论通知站长」,避免和插件重复发信。只有当插件在场时勾这一项才有意义;插件不在场时主题一定会保留自带通知,不会让你收不到信。', 'kratos'),
'id' => 'm_keep_core_notify',
'std' => false,
'type' => 'checkbox',
);
/* ══════════════════════════════════════════════════════════
* 十二、CDN 加速 —— 三个输入框,不多加
*
* 用户明确要求「一共就这 3 个选项,不要弄得太复杂」。
* 所以这里没有开关、没有自动开关、没有服务商选择,只有:
* CDN 地址 / 白名单 / 黑名单
* 地址留空 = 功能关闭。这比再加一个「启用」勾选框更省事,
* 也不会出现「勾了启用但没填地址」这种半配置状态。
*
* 匹配逻辑(写进说明里,让用户看得到):
* 先看黑名单 → 命中就保持原地址
* 黑名单没命中 → 再看白名单
* 黑名单优先级高于白名单
* ══════════════════════════════════════════════════════════ */
$options[] = array(
'name' => __('CDN 加速', 'kratos'),
'type' => 'heading',
);
$options[] = array(
'name' => __('CDN 地址', 'kratos'),
'desc' => __('资源的加速前缀,例如 https://cdn.example.com。可以不带协议(自动按 https 补全)。留空即关闭整个功能,站点行为与不装这项功能完全一致。', 'kratos'),
'id' => 'cdn_url',
'std' => '',
'type' => 'text',
'placeholder' => 'https://cdn.example.com',
);
$options[] = array(
'name' => __('白名单', 'kratos'),
'desc' => __('命中这些规则的文件才走 CDN。一行一条,# 或 ; 开头的行是注释。留空 = 所有静态文件都走 CDN。', 'kratos'),
'id' => 'cdn_include',
'std' => '',
'type' => 'code',
'settings' => array('rows' => 10),
);
$options[] = array(
'name' => __('白名单写法', 'kratos'),
'desc' => __('按路径匹配(正则规则除外,它对完整地址匹配)。下面每种写法都可以直接抄:', 'kratos')
. '
* —— 全部静态文件'
. '
jpg 或 .jpg 或 *.jpg —— 扩展名'
. '
jpg,png,webp 或 .jpg .png —— 一串扩展名,命中任一'
. '
/wp-content/uploads/ —— 以这个路径开头(以斜杠开头、不含星号)'
. '
*/uploads/* —— 通配符,* 可以跨斜杠'
. '
uploads —— 路径里含这几个字(子串)'
. '
~\/20\d\d\/~ —— 正则,对完整地址匹配',
'type' => 'info',
);
$options[] = array(
'name' => __('黑名单', 'kratos'),
'desc' => __('命中的文件即使符合白名单也不走 CDN,保持原地址。一行一条,写法与白名单完全相同。黑名单优先于白名单:两边都命中时按黑名单处理。', 'kratos'),
'id' => 'cdn_exclude',
'std' => '',
'type' => 'code',
'settings' => array('rows' => 10),
);
$options[] = array(
'name' => __('这些地址永远不走 CDN', 'kratos'),
'desc' => __('下面这些不受两套名单影响,是硬性排除 —— 把它们改到 CDN 会让站点直接坏掉,所以不给开关:'
. '
· 以斜杠结尾的地址(页面、栏目页、导航链接):改了会让访客跳去 CDN 域名,全站 404'
. '
· 页面与动态后缀:php、html、htm、asp 等'
. '
· 没有后缀的地址:通常是接口、短链、伪静态页'
. '
· 后台与接口路径:wp-admin、wp-login.php、wp-json、wp-cron.php、xmlrpc.php、/feed/'
. '
· 非本站域名:外链图片、别人家的 CDN'
. '
· 非 http(s) 协议:data:、mailto:、tel:、javascript:', 'kratos'),
'type' => 'info',
);
$options[] = array(
'name' => __('后台与接口里不改写', 'kratos'),
'desc' => __('为了不影响编辑与调试,下面这些地方保持原地址:后台页面、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,这里留空即可。保存后立即生效,随主题升级保留。', 'kratos'),
'id' => 'g_custom_css',
'std' => yang_opt_default_custom_css(),
'type' => 'code',
'settings' => array('rows' => 14),
);
$options[] = array(
'name' => __('自定义 JS', 'kratos'),
'desc' => __('附加脚本,在页面底部加载,不需要写 <script> 标签。', 'kratos'),
'id' => 'g_custom_js',
'std' => '',
'type' => 'code',
'settings' => array('rows' => 14),
);
return $options;
}