v3.2.13 图片选项全部自带默认图,新装站点不再出现空洞/破图
根本原因:kratos_option() 运行时只读数据库,`std` 仅在后台表单预填和 「恢复默认」时生效,前台根本读不到。因此新装站点所有图片项都是空值。 另外 isset() 挡不住「用户清空」——of_sanitize_upload() 对空输入返回 '', 键存在且值为空串,调用方传的兜底值被绕过。 同时不能把绝对 URL 写进 std:它会随保存一起写库,主题目录改名/换 CDN/ 迁站后永久 404。本站 s_wechat_url / g_donate_alipay / g_donate_wechat 三项就指向已删除的 kratos-master 目录,一直在破图(这三项是 hidden 字段, 后台界面上看不到,所以坏了很久没人发现)。 做法: - 新增 inc/theme-option-defaults.php,图片默认值注册表(11 项), 默认图一律运行时给出,不写数据库。 - yang_option_image() 三层判断:空/纯空白 → 默认图;指向已不存在的主题 目录 → 默认图;文件不存在则不输出地址(避免换来一个 404)。 - 陈旧判断用 get_template()(主题 slug)而非 basename(目录名),避免本地 开发目录名与 slug 不一致时误伤当前主题自己的图。 - 模板统一走取值函数:header(logo/favicon/banner)、footer(微信码/国旗)、 404、index、article 缩略图、widgets 头像、page-toolbar、theme-core 赞赏码。 - 国旗与 favicon 的渲染抽成 yang_flag_block() / yang_favicon_tag(), 空值时不输出空 src / 空 href。 - 后台 9 处 std 改为 yang_options_default_img(),与前台默认图同源; 该包装函数自带 function_exists 防护,单独加载 theme-options.php 不会 Fatal。 新增图片(本地生成,合计 291KB):default-404 / default-share / default-empty / default-avatar / default-banner / default-flag / favicon.ico。 favicon 为 WordPress 风格自绘(品牌蓝圆底 + 白色 W),非站长个人专用那张。 验证:run_tests 146 条(新增第 4 组图片默认值 22+ 条);falsify 35 条注入 全部能被抓住(新增 10 条);verify_all.sh 14 组全绿。
@@ -12,12 +12,12 @@ get_header(); ?>
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 page404">
|
<div class="col-12 page404">
|
||||||
<div class="thumbnail" style="background-image: url(<?php
|
<div class="thumbnail" style="background-image: url(<?php
|
||||||
if(!kratos_option('g_404')){
|
/* 统一走默认图注册表:用户填了就用他的,空着就用包内默认图。
|
||||||
$img = ASSET_PATH . '/assets/img/404.jpg';
|
原来那段 if/else 用 !kratos_option() 判真假,而空字符串是
|
||||||
} else {
|
falsy、'' 却又让 isset 成立,两种写法混在一起时行为很难预期;
|
||||||
$img = kratos_option('g_404', ASSET_PATH . '/assets/img/404.jpg');
|
现在只判一次"最终拿到什么"。
|
||||||
}
|
URL 里有括号/引号的可能,用 esc_url 兜一层再进 CSS。 */
|
||||||
echo $img; ?>">
|
echo esc_url(yang_option_image('g_404')); ?>">
|
||||||
<div class="overlay"></div>
|
<div class="overlay"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content text-center">
|
<div class="content text-center">
|
||||||
|
|||||||
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 743 B |
@@ -17,7 +17,7 @@
|
|||||||
<div class="wechat">
|
<div class="wechat">
|
||||||
<span class="kicon i-wechat"></span>
|
<span class="kicon i-wechat"></span>
|
||||||
<div class="wechat-pic">
|
<div class="wechat-pic">
|
||||||
<img src="<?php echo kratos_option('s_wechat_url', ASSET_PATH . '/assets/img/wechat.png'); ?>">
|
<img src="<?php echo esc_url(yang_option_image('s_wechat_url')); ?>" alt="<?php esc_attr_e('微信二维码', 'kratos'); ?>">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
@@ -53,7 +53,11 @@
|
|||||||
echo '<p><a href="' . kratos_option('s_gov_link', '#') . '" target="_blank" rel="nofollow" ><i class="police-ico"></i>' . kratos_option('s_gov') . '</a></p>';
|
echo '<p><a href="' . kratos_option('s_gov_link', '#') . '" target="_blank" rel="nofollow" ><i class="police-ico"></i>' . kratos_option('s_gov') . '</a></p>';
|
||||||
}
|
}
|
||||||
if (kratos_option('seo_statistical')) {echo '<p>' . kratos_option('seo_statistical') . '</p>';}
|
if (kratos_option('seo_statistical')) {echo '<p>' . kratos_option('seo_statistical') . '</p>';}
|
||||||
if (kratos_option('g_flag')) {echo '<div id="flag"><img style="width:50px" src="' . kratos_option('g_flag_url') . '"><div>';}
|
/* 国旗:勾了「是否添加国旗」才显示。以前 src 直接取选项,
|
||||||
|
用户没上传地址就得到 <img src="">,页面上是一个破图图标。
|
||||||
|
逻辑抽到 yang_flag_block() 里,测试能直接测同一个函数,
|
||||||
|
不必"照着抄一遍"(抄件永远是对的,模板改坏了也发现不了)。 */
|
||||||
|
echo yang_flag_block();
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ require get_template_directory() . '/inc/update-checker/autoload.php';
|
|||||||
// 核心配置
|
// 核心配置
|
||||||
require get_template_directory() . '/inc/theme-core.php';
|
require get_template_directory() . '/inc/theme-core.php';
|
||||||
|
|
||||||
|
// 图片类选项的默认值(Logo/favicon/404/分享图… 空值时落到包内默认图)。
|
||||||
|
// 必须排在 theme-core.php 之后:它要用 ASSET_PATH 与 kratos_option。
|
||||||
|
require get_template_directory() . '/inc/theme-option-defaults.php';
|
||||||
|
|
||||||
// 站点配置
|
// 站点配置
|
||||||
require get_template_directory() . '/inc/theme-setting.php';
|
require get_template_directory() . '/inc/theme-setting.php';
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,11 @@
|
|||||||
<meta name="keywords" content="<?php echo keywords(); ?>">
|
<meta name="keywords" content="<?php echo keywords(); ?>">
|
||||||
<meta name="description" itemprop="description" content="<?php echo description(); ?>">
|
<meta name="description" itemprop="description" content="<?php echo description(); ?>">
|
||||||
<meta name="theme-color" content="<?php echo kratos_option('g_chrome', '#282a2c'); ?>">
|
<meta name="theme-color" content="<?php echo kratos_option('g_chrome', '#282a2c'); ?>">
|
||||||
<link rel="shortcut icon" href="<?php echo kratos_option('g_icon'); ?>">
|
<?php
|
||||||
|
/* favicon:空值时落到主题包内的默认图标,**绝不输出空 href**。
|
||||||
|
标签由 yang_favicon_tag() 生成,测试直接测那个函数。 */
|
||||||
|
echo yang_favicon_tag();
|
||||||
|
?>
|
||||||
<?php wp_head(); wp_print_scripts('jquery'); ?>
|
<?php wp_head(); wp_print_scripts('jquery'); ?>
|
||||||
</head>
|
</head>
|
||||||
<?php flush(); ?>
|
<?php flush(); ?>
|
||||||
@@ -35,9 +39,15 @@ wp_body_open();
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="navbar-brand" href="<?php echo get_option('home'); ?>">
|
<a class="navbar-brand" href="<?php echo get_option('home'); ?>">
|
||||||
<?php
|
<?php
|
||||||
if (kratos_option('g_logo')){
|
/* Logo 空着时不塞通用图,回落到站点名称文字 —— 一张人人
|
||||||
echo '<img src="' . kratos_option('g_logo') . '">';
|
共用的占位 logo 会让每个站看起来一模一样,比文字更难看。
|
||||||
}else{
|
但不能用 kratos_option('g_logo') 直接判真假:用户在后台
|
||||||
|
把那一格清空后值是 ''、键却还在,isset 仍成立。
|
||||||
|
用 yang_option_image() 统一按"空串也算空"判断。 */
|
||||||
|
$yang_logo = yang_option_image('g_logo');
|
||||||
|
if ($yang_logo !== '') {
|
||||||
|
echo '<img src="' . esc_url($yang_logo) . '" alt="' . esc_attr(get_bloginfo('name')) . '">';
|
||||||
|
} else {
|
||||||
bloginfo('name');
|
bloginfo('name');
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
@@ -66,13 +76,11 @@ wp_body_open();
|
|||||||
<?php if(kratos_option('top_select', 'banner') == 'banner'){ ?>
|
<?php if(kratos_option('top_select', 'banner') == 'banner'){ ?>
|
||||||
<div class="banner">
|
<div class="banner">
|
||||||
<div class="overlay"></div>
|
<div class="overlay"></div>
|
||||||
<div class="content text-center" style="background-image: url(<?php
|
<div class="content text-center" style="background-image: url(<?php
|
||||||
if(!kratos_option('top_img')){
|
/* 顶部图片导航的背景图。空值(含用户清空那一格)落到包内默认图。
|
||||||
$img = ASSET_PATH . '/assets/img/background.png';
|
CSS 是 height:300px + background-size:cover,默认图已按
|
||||||
} else {
|
1920x640 压好(见 _tooling 里的生成脚本)。 */
|
||||||
$img = kratos_option('top_img', ASSET_PATH . '/assets/img/background.png');
|
echo esc_url(yang_option_image('top_img')); ?>);">
|
||||||
}
|
|
||||||
echo $img; ?>);">
|
|
||||||
<div class="introduce animated fadeInUp">
|
<div class="introduce animated fadeInUp">
|
||||||
<?php
|
<?php
|
||||||
if (is_category() || is_tag()) {
|
if (is_category() || is_tag()) {
|
||||||
|
|||||||
@@ -121,14 +121,14 @@ function post_thumbnail()
|
|||||||
$img_val = $img_src[$img_count];
|
$img_val = $img_src[$img_count];
|
||||||
}
|
}
|
||||||
if (!empty($img_val)) {
|
if (!empty($img_val)) {
|
||||||
echo '<img src="' . $img_val . '" />';
|
echo '<img src="' . esc_url($img_val) . '" />';
|
||||||
} else {
|
} else {
|
||||||
if (!kratos_option('g_postthumbnail')) {
|
/* 正文里没图、也没设特色图时用默认特色图。
|
||||||
$img = ASSET_PATH . '/assets/img/default.jpg';
|
空值(用户清过那一格)同样落到包内默认图。 */
|
||||||
} else {
|
$img = yang_option_image('g_postthumbnail');
|
||||||
$img = kratos_option('g_postthumbnail', ASSET_PATH . '/assets/img/default.jpg');
|
if ($img !== '') {
|
||||||
|
echo '<img src="' . esc_url($img) . '" />';
|
||||||
}
|
}
|
||||||
echo '<img src="' . $img . '" />';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,8 +94,8 @@ function theme_autoload()
|
|||||||
$data = array(
|
$data = array(
|
||||||
'site' => home_url(),
|
'site' => home_url(),
|
||||||
'directory' => get_stylesheet_directory_uri(),
|
'directory' => get_stylesheet_directory_uri(),
|
||||||
'alipay' => kratos_option('g_donate_alipay', ASSET_PATH . '/assets/img/donate.png'),
|
'alipay' => yang_option_image('g_donate_alipay'),
|
||||||
'wechat' => kratos_option('g_donate_wechat', ASSET_PATH . '/assets/img/donate.png'),
|
'wechat' => yang_option_image('g_donate_wechat'),
|
||||||
'repeat' => __('您已经赞过了', 'kratos'),
|
'repeat' => __('您已经赞过了', 'kratos'),
|
||||||
'thanks' => __('感谢您的支持', 'kratos'),
|
'thanks' => __('感谢您的支持', 'kratos'),
|
||||||
'donate' => __('打赏作者', 'kratos'),
|
'donate' => __('打赏作者', 'kratos'),
|
||||||
|
|||||||
@@ -0,0 +1,307 @@
|
|||||||
|
<?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',
|
||||||
|
|
||||||
|
// 下面三个主题包里本来就有图,只是这里显式登记,
|
||||||
|
// 让"所有图片字段都有默认"这件事可被一处查全。
|
||||||
|
's_wechat_url' => '/assets/img/wechat.png',
|
||||||
|
'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;
|
||||||
@@ -20,6 +20,29 @@ function getrobots()
|
|||||||
return $robots;
|
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)
|
||||||
|
: '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function kratos_options()
|
function kratos_options()
|
||||||
{
|
{
|
||||||
$sitename = get_bloginfo('name');
|
$sitename = get_bloginfo('name');
|
||||||
@@ -55,12 +78,24 @@ function kratos_options()
|
|||||||
'desc' => __('不选择图片则显示文字标题', 'kratos'),
|
'desc' => __('不选择图片则显示文字标题', 'kratos'),
|
||||||
'id' => 'g_logo',
|
'id' => 'g_logo',
|
||||||
'type' => 'upload',
|
'type' => 'upload',
|
||||||
|
/* 有意不设 std:这里空着时显示站点名称文字,比塞一张通用占位
|
||||||
|
logo 好看。其它图片字段见 inc/theme-option-defaults.php。 */
|
||||||
);
|
);
|
||||||
|
|
||||||
$options[] = array(
|
$options[] = array(
|
||||||
'name' => __('Favicon 图片', 'kratos'),
|
'name' => __('Favicon 图片', 'kratos'),
|
||||||
'desc' => __('浏览器收藏夹和地址栏中显示的图标', 'kratos'),
|
'desc' => __('浏览器收藏夹和地址栏中显示的图标。<b>留空则自动使用主题自带的默认图标</b>,不会出现空白。', 'kratos'),
|
||||||
'id' => 'g_icon',
|
'id' => 'g_icon',
|
||||||
|
/* 有意不写 std。
|
||||||
|
*
|
||||||
|
* std 里存的是**绝对 URL**,一旦用户保存表单就会被写进数据库。
|
||||||
|
* 数据库里躺着一个写死的主题目录地址是隐患:主题目录改名、
|
||||||
|
* 换 CDN、或站点迁移域名之后,那条记录就指向不存在的文件
|
||||||
|
* (线上 s_wechat_url 指向早已删除的 kratos-master 目录,就是这么坏掉的)。
|
||||||
|
*
|
||||||
|
* 所以默认值一律由运行时提供(yang_option_image()),库里保持空。
|
||||||
|
* 这样默认图地址永远由当前请求现算,改目录也不用改数据。
|
||||||
|
*/
|
||||||
'type' => 'upload',
|
'type' => 'upload',
|
||||||
);
|
);
|
||||||
$options[] = array(
|
$options[] = array(
|
||||||
@@ -72,8 +107,9 @@ $options[] = array(
|
|||||||
);
|
);
|
||||||
$options[] = array(
|
$options[] = array(
|
||||||
'name' => __('国旗地址', 'kratos'),
|
'name' => __('国旗地址', 'kratos'),
|
||||||
'desc' => __('选择国旗图片地址', 'kratos'),
|
'desc' => __('选择国旗图片地址。<b>留空则自动使用主题自带的默认国旗</b>。', 'kratos'),
|
||||||
'id' => 'g_flag_url',
|
'id' => 'g_flag_url',
|
||||||
|
/* 不写 std —— 理由同 g_icon:默认值由运行时给,库里不存绝对地址。 */
|
||||||
'type' => 'upload',
|
'type' => 'upload',
|
||||||
);
|
);
|
||||||
$options[] = array(
|
$options[] = array(
|
||||||
@@ -158,16 +194,19 @@ $options[] = array(
|
|||||||
|
|
||||||
$options[] = array(
|
$options[] = array(
|
||||||
'id' => 's_wechat_url',
|
'id' => 's_wechat_url',
|
||||||
'std' => ASSET_PATH . '/assets/img/wechat.png',
|
/* 后台显示的初始值必须与前台实际用的默认图**同一份来源**,
|
||||||
|
否则用户在后台看到 A 图、前台显示 B 图,一保存又把 A 写进库,
|
||||||
|
前台跟着退回 A —— 前后台像两个产品。
|
||||||
|
所以这里统一从 yang_image_defaults() 取,不在两处各写一遍路径。 */
|
||||||
|
'std' => yang_options_default_img('s_wechat_url'),
|
||||||
'type' => 'upload',
|
'type' => 'upload',
|
||||||
'class' => 'hidden',
|
'class' => 'hidden',
|
||||||
'type' => 'upload',
|
|
||||||
);
|
);
|
||||||
|
|
||||||
$options[] = array(
|
$options[] = array(
|
||||||
'name' => __('404 页面图片', 'kratos'),
|
'name' => __('404 页面图片', 'kratos'),
|
||||||
'id' => 'g_404',
|
'id' => 'g_404',
|
||||||
'std' => ASSET_PATH . '/assets/img/404.jpg',
|
'std' => yang_options_default_img('g_404'),
|
||||||
'type' => 'upload',
|
'type' => 'upload',
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -180,7 +219,7 @@ $options[] = array(
|
|||||||
'name' => __('分享图片', 'kratos'),
|
'name' => __('分享图片', 'kratos'),
|
||||||
'desc' => __('搜索引擎或者社交工具分享首页时抓取的图片', 'kratos'),
|
'desc' => __('搜索引擎或者社交工具分享首页时抓取的图片', 'kratos'),
|
||||||
'id' => 'seo_shareimg',
|
'id' => 'seo_shareimg',
|
||||||
'std' => ASSET_PATH . '/assets/img/default.jpg',
|
'std' => yang_options_default_img('seo_shareimg'),
|
||||||
'type' => 'upload',
|
'type' => 'upload',
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -314,7 +353,7 @@ $options[] = array(
|
|||||||
'desc' => __('当文章中没有图片并且没有设置特色图时在首页显示', 'kratos'),
|
'desc' => __('当文章中没有图片并且没有设置特色图时在首页显示', 'kratos'),
|
||||||
'id' => 'g_postthumbnail',
|
'id' => 'g_postthumbnail',
|
||||||
'class' => 'hidden',
|
'class' => 'hidden',
|
||||||
'std' => ASSET_PATH . '/assets/img/default.jpg',
|
'std' => yang_options_default_img('g_postthumbnail'),
|
||||||
'type' => 'upload',
|
'type' => 'upload',
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -322,7 +361,7 @@ $options[] = array(
|
|||||||
'name' => __('无内容图片', 'kratos'),
|
'name' => __('无内容图片', 'kratos'),
|
||||||
'desc' => __('当搜索不到文章或文章分类中没有文章时显示', 'kratos'),
|
'desc' => __('当搜索不到文章或文章分类中没有文章时显示', 'kratos'),
|
||||||
'id' => 'g_nothing',
|
'id' => 'g_nothing',
|
||||||
'std' => ASSET_PATH . '/assets/img/nothing.svg',
|
'std' => yang_options_default_img('g_nothing'),
|
||||||
'type' => 'upload',
|
'type' => 'upload',
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -342,7 +381,7 @@ $options[] = array(
|
|||||||
$options[] = array(
|
$options[] = array(
|
||||||
'name' => __('支付宝二维码', 'kratos'),
|
'name' => __('支付宝二维码', 'kratos'),
|
||||||
'id' => 'g_donate_alipay',
|
'id' => 'g_donate_alipay',
|
||||||
'std' => ASSET_PATH . '/assets/img/donate.png',
|
'std' => yang_options_default_img('g_donate_alipay'),
|
||||||
'type' => 'upload',
|
'type' => 'upload',
|
||||||
'class' => 'hidden',
|
'class' => 'hidden',
|
||||||
'type' => 'upload',
|
'type' => 'upload',
|
||||||
@@ -351,7 +390,7 @@ $options[] = array(
|
|||||||
$options[] = array(
|
$options[] = array(
|
||||||
'name' => __('微信二维码', 'kratos'),
|
'name' => __('微信二维码', 'kratos'),
|
||||||
'id' => 'g_donate_wechat',
|
'id' => 'g_donate_wechat',
|
||||||
'std' => ASSET_PATH . '/assets/img/donate.png',
|
'std' => yang_options_default_img('g_donate_wechat'),
|
||||||
'type' => 'upload',
|
'type' => 'upload',
|
||||||
'class' => 'hidden',
|
'class' => 'hidden',
|
||||||
'type' => 'upload',
|
'type' => 'upload',
|
||||||
@@ -376,7 +415,7 @@ $options[] = array(
|
|||||||
$options[] = array(
|
$options[] = array(
|
||||||
'name' => __('个人头像', 'kratos'),
|
'name' => __('个人头像', 'kratos'),
|
||||||
'id' => 'a_gravatar',
|
'id' => 'a_gravatar',
|
||||||
'std' => ASSET_PATH . '/assets/img/gravatar.png',
|
'std' => yang_options_default_img('a_gravatar'),
|
||||||
'type' => 'upload',
|
'type' => 'upload',
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -477,7 +516,7 @@ $options[] = array(
|
|||||||
$options[] = array(
|
$options[] = array(
|
||||||
'name' => __('图片导航', 'kratos'),
|
'name' => __('图片导航', 'kratos'),
|
||||||
'id' => 'top_img',
|
'id' => 'top_img',
|
||||||
'std' => ASSET_PATH . '/assets/img/background.png',
|
'std' => yang_options_default_img('top_img'),
|
||||||
'class' => 'hidden',
|
'class' => 'hidden',
|
||||||
'type' => 'upload',
|
'type' => 'upload',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ class widget_about extends WP_Widget
|
|||||||
{
|
{
|
||||||
$introduce = kratos_option('a_about', __('保持饥渴的专注,追求最佳的品质', 'kratos'));
|
$introduce = kratos_option('a_about', __('保持饥渴的专注,追求最佳的品质', 'kratos'));
|
||||||
$username = kratos_option('a_nickname', __('Kratos', 'kratos'));
|
$username = kratos_option('a_nickname', __('Kratos', 'kratos'));
|
||||||
$avatar = kratos_option('a_gravatar', ASSET_PATH . '/assets/img/gravatar.png');
|
$avatar = yang_option_image('a_gravatar');
|
||||||
$background = !empty($instance['background']) ? $instance['background'] : ASSET_PATH . '/assets/img/about-background.png';
|
$background = !empty($instance['background']) ? $instance['background'] : ASSET_PATH . '/assets/img/about-background.png';
|
||||||
|
|
||||||
echo '<div class="widget w-about">';
|
echo '<div class="widget w-about">';
|
||||||
|
|||||||
@@ -24,13 +24,12 @@ get_header(); ?>
|
|||||||
}else{ ?>
|
}else{ ?>
|
||||||
<div class="article-panel">
|
<div class="article-panel">
|
||||||
<div class="nothing">
|
<div class="nothing">
|
||||||
<img src="<?php
|
<img src="<?php
|
||||||
if(!kratos_option('g_nothing')){
|
/* 无内容插画:空值落到包内默认图(原来固定回落到
|
||||||
$img = ASSET_PATH . '/assets/img/nothing.svg';
|
nothing.svg,用户清空后仍会绕过 —— 见
|
||||||
} else {
|
theme-option-defaults.php 里的说明)。 */
|
||||||
$img = kratos_option('g_nothing', ASSET_PATH . '/assets/img/nothing.svg');
|
echo esc_url(yang_option_image('g_nothing')); ?>"
|
||||||
}
|
alt="<?php esc_attr_e('没有找到内容', 'kratos'); ?>">
|
||||||
echo $img; ?>">
|
|
||||||
<div class="sorry"><?php _e('很抱歉,没有找到任何内容', 'kratos'); ?></div>
|
<div class="sorry"><?php _e('很抱歉,没有找到任何内容', 'kratos'); ?></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<p class="name"><?php echo get_the_author_meta('display_name'); ?></p>
|
<p class="name"><?php echo get_the_author_meta('display_name'); ?></p>
|
||||||
<p class="motto mb-0"><?php echo get_the_author_meta('description'); ?></p>
|
<p class="motto mb-0"><?php echo get_the_author_meta('description'); ?></p>
|
||||||
<?php } else { ?>
|
<?php } else { ?>
|
||||||
<img src="<?php echo kratos_option('a_gravatar', ASSET_PATH . '/assets/img/gravatar.png'); ?>">
|
<img src="<?php echo esc_url(yang_option_image('a_gravatar')); ?>" alt="<?php echo esc_attr(kratos_option('a_nickname', 'Kratos')); ?>">
|
||||||
<p class="name"><?php echo kratos_option('a_nickname','Kratos'); ?></p>
|
<p class="name"><?php echo kratos_option('a_nickname','Kratos'); ?></p>
|
||||||
<p class="motto mb-0"><?php echo kratos_option('a_about', __('保持饥渴的专注,追求最佳的品质', 'kratos')); ?></p>
|
<p class="motto mb-0"><?php echo kratos_option('a_about', __('保持饥渴的专注,追求最佳的品质', 'kratos')); ?></p>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Theme Name: Kratos_Yang
|
Theme Name: Kratos_Yang
|
||||||
Text Domain: kratos_Yang
|
Text Domain: kratos_Yang
|
||||||
Version: 3.2.12
|
Version: 3.2.13
|
||||||
Requires PHP: 7.0
|
Requires PHP: 7.0
|
||||||
Description: 这是一个Kratos主题的分支版本,用来增加一些定制化功能。
|
Description: 这是一个Kratos主题的分支版本,用来增加一些定制化功能。
|
||||||
Tags: 博客, 双栏, 响应式, 自定义背景, 自定义颜色, 自定义图标, 自定义菜单, 特色图像, 收录优化, 邮件中心, 主题选项, 小工具, 文章置顶, 无障碍友好
|
Tags: 博客, 双栏, 响应式, 自定义背景, 自定义颜色, 自定义图标, 自定义菜单, 特色图像, 收录优化, 邮件中心, 主题选项, 小工具, 文章置顶, 无障碍友好
|
||||||
|
|||||||