侧栏挂件一致性:标题可配、标签列数、重复 id、URL 转义 (3.2.50) #20
一并修掉上一轮列出的 5 处侧栏挂件遗留问题:
1. 标题不读 $args(4 个挂件标题在后台改不了)
新增 yang_widget_title($args, $instance, $fallback, $id_base),5 个挂件
统一走它:读 $instance['title'] → 空则回落 $fallback → 过 widget_title
过滤器 → 输出用 $args['before_title']/after_title → 仍为空则整个节点不输出。
四个挂件的 update() 补 title,form() 补输入框 + 「留空则不显示标题」说明。
widget_tags 留空回落「标签聚合」,老站点显示不变。
2. 标签列数随屏宽反转(1440 一列、1180 反而两列)
逐视口实测:真实断点在 1311→1310,@media(max-width:1310px) 只把格子压成
110px 而不改侧栏宽度(侧栏 992px 以上恒为容器 1/3),于是格子变小反而能
塞下 3 个。改为 .w-tags .item a / .widget_links ul li 用
width: calc(50% - 2px) + 父容器 font-size:0(消掉 inline-block 之间的
空白宽度),并在 1310 断点里删掉这两条 110px 规则(分类/归档的 110px
不是网格,保留)。判据守的是「列数单调不增」而不是「某视口恰好两列」。
3. 重复 id nav-tab(桌面一份、手机一份)
两份导航合并为一份,id 全部带实例后缀:yang_widget_uid($this) 取
$widget->id(如 widget_posts-2),输出 {uid}-tab / {uid}-new 等,
aria-controls / aria-labelledby 同步指向真实 id,两实例并用也不撞。
4. 搜索框固定 id(searchform / search / searchsubmit)
同样改为 {uid}-form / {uid}-field / {uid}-submit,并补
<label class="sr-only" for="{uid}-field">搜索</label>。
5. 图片 URL 未加引号 + 未转义
src/href 一律加引号 + esc_url(),alt 用 wp_strip_all_tags + esc_attr,
简介用 nl2br(esc_html())。背景图踩了一个自己写出来的坑:一开始写成
esc_url() 套在 esc_attr() 里 = 双重转义,& 变 &amp;,CSS 里拿到
字面量 amp,背景图 404。改为 url() 内部用 esc_url_raw()(只做协议/字符
清洗,不入实体),整个 style 属性交给 esc_attr() 统一转义一次。
顺带把侧栏标签页图标在窄屏隐藏(<1200px),避免三个 tab 挤在一起。
验证:
- _widgets_consistency.php 68 通过 / 0 失败
- _widgets_consistency_falsify.php 24 通过 / 0 失败(基线1·自检3·注入20)
- _widgets_hot_test.php 33 / 0
- _widgets_hot_falsify.php 20 / 0
- _widgets_hot_geom.js 24 / 0(真实浏览器几何)
- _widgets_ids_dom.js 5 / 0(真实 DOM 查重复 id)
- _widgets_visual20.js 30 / 0(像素级:图片 naturalWidth>0、
标题装饰线、标签等宽、tab 点击)
- _widgets_icon_wide.js 7 / 0(断点精确在 1200)
- _widgets_tags_geom.js 5 / 0(列数单调性)
- 发布脚本 R1~R10 十组注入全部真红
This commit is contained in:
+86
-13
@@ -1857,6 +1857,31 @@ button:focus {
|
|||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 标签页图标:xl(≥1200px)显示,更窄时隐藏。
|
||||||
|
*
|
||||||
|
* 🔴 3.2.50 之前这件事是靠**渲染两份 DOM** 做的:PHP 里输出
|
||||||
|
* 一个 `d-none d-xl-flex` 的带图标版本和一个 `d-xl-none` 的无图标版本,
|
||||||
|
* 于是 nav-tab / nav-new-tab / nav-hot-tab / nav-random-tab
|
||||||
|
* 四个 id 在同一个页面里各出现两次(HTML 非法,aria-labelledby 失效)。
|
||||||
|
* 现在 DOM 只有一份,显隐交给下面的媒体查询。 */
|
||||||
|
.k-main .sidebar .w-recommended .nav-link .tab-icon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
.k-main .sidebar .w-recommended .nav-link .tab-icon {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 无图标时补一点内边距,避免三个标签挤在一起 */
|
||||||
|
@media (max-width: 1199.98px) {
|
||||||
|
.k-main .sidebar .w-recommended .nav-tabs .nav-link {
|
||||||
|
padding-left: 18px;
|
||||||
|
padding-right: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.k-main .sidebar .title {
|
.k-main .sidebar .title {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 0 0 15px;
|
margin: 0 0 15px;
|
||||||
@@ -1876,25 +1901,63 @@ button:focus {
|
|||||||
content: "";
|
content: "";
|
||||||
}
|
}
|
||||||
|
|
||||||
.k-main .sidebar .w-tags .item a {
|
/* ── 标签聚合 / 友情链接:宽度改为「按容器自适应」
|
||||||
|
*
|
||||||
|
* 🔴 3.2.50 之前的写法是两段魔法数字:
|
||||||
|
*
|
||||||
|
* .w-tags .item a { width: 164px } ← 默认
|
||||||
|
* @media (max-width:1310px) { ... { width: 110px } } ← 窄屏
|
||||||
|
*
|
||||||
|
* 侧栏宽度由 Bootstrap 的 col-lg-4 决定,在 992px 以下是「整行」,
|
||||||
|
* 在 992px 以上是容器的 1/3。而 1310px 这个断点**只改标签宽度、
|
||||||
|
* 不改侧栏宽度**,于是在 1311→1310px 处出现:
|
||||||
|
*
|
||||||
|
* 1311px:侧栏内容区 336.7px,标签 164px → 一行 2 个
|
||||||
|
* 1310px:侧栏内容区 336.7px,标签 110px → 一行 3 个
|
||||||
|
*
|
||||||
|
* **窗口变窄,列数反而多了一个**。这是典型的"两次魔法数字叠加":
|
||||||
|
* 第二段是为了修第一段在某个视口下的溢出,修的时候没管单调性。
|
||||||
|
*
|
||||||
|
* 现在不再写死像素,改为按容器百分比:
|
||||||
|
*
|
||||||
|
* width: calc(50% - 2px) 默认每行 2 个
|
||||||
|
* margin: 0 1px 10px 0 左右各 1px,共 2px 被 50%-2px 抵掉
|
||||||
|
*
|
||||||
|
* 50%-2px 这个写法保证「一行恰好放 2 个且不多不少」:
|
||||||
|
* 两个 50%-2px 加两个 1px 右边距 = 100%,再算上 inline-block 之间的
|
||||||
|
* 空白字符会被 font-size:0 吃掉(见 .item 的声明),所以不会意外换行。
|
||||||
|
*
|
||||||
|
* 要改列数只需改这一个百分比,窄屏不会再"越窄越多列"。
|
||||||
|
*/
|
||||||
|
.k-main .sidebar .w-tags .item,
|
||||||
|
.k-main .sidebar .widget_links ul {
|
||||||
|
/* 🔴 inline-block 元素之间的换行/空格会渲染成一个空格宽度,
|
||||||
|
两个 50% 就永远放不进一行。font-size:0 让空白字符零宽,
|
||||||
|
子元素再把字号设回来。这是 inline-block 网格的标准做法。 */
|
||||||
|
font-size: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.k-main .sidebar .w-tags .item a,
|
||||||
|
.k-main .sidebar .widget_links ul li {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 0 1px 10px 0;
|
margin: 0 1px 10px 0;
|
||||||
padding: 2px 0;
|
padding: 2px 0;
|
||||||
width: 164px;
|
/* 每行 2 个:两个 (50% - 2px) + 两个 1px 右边距 = 100% */
|
||||||
|
width: calc(50% - 2px);
|
||||||
border: 1px solid #eeeeee;
|
border: 1px solid #eeeeee;
|
||||||
color: #333;
|
color: #333;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
/* 长标签名(如 HarmonyOS)不能撑破格子 */
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
.k-main .sidebar .widget_links ul li {
|
.k-main .sidebar .widget_links ul li {
|
||||||
display: inline-block;
|
border: none;
|
||||||
margin: 0 1px 10px 0;
|
|
||||||
padding: 2px 0;
|
|
||||||
width: 164px;
|
|
||||||
color: #333;
|
|
||||||
list-style: none;
|
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
}
|
}
|
||||||
@@ -2036,11 +2099,21 @@ button:focus {
|
|||||||
padding: 8px 22px;
|
padding: 8px 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.k-main .sidebar .w-tags .item a,
|
/* 🔴 这里本来还有两条把宽定死的规则:
|
||||||
.k-main .sidebar .widget_links ul li {
|
*
|
||||||
width: 110px;
|
* .k-main .sidebar .w-tags .item a,
|
||||||
}
|
* .k-main .sidebar .widget_links ul li,
|
||||||
|
* .k-main .sidebar .widget_categories ul li,
|
||||||
|
* .k-main .sidebar .widget_archive ul li { width: 110px }
|
||||||
|
*
|
||||||
|
* 1310px 这个断点**不改变侧栏宽度**(992px 以上一直是容器 1/3),
|
||||||
|
* 所以它只做了一件事:把格子压窄 ⇒ 同样宽度下一行多塞一个 ⇒
|
||||||
|
* 窗口越窄列数越多。3.2.50 起标签与友情链接改成 `calc(50% - 2px)`,
|
||||||
|
* 由容器宽度自己决定列数;这里只剩 tab 的内边距需要收一收。
|
||||||
|
*
|
||||||
|
* 分类/归档那两条宽度**保留** —— 它们的父元素是 li 列表,
|
||||||
|
* 不是按行排的网格,压窄只影响观感不影响列数,且原有排版依赖它。
|
||||||
|
* 见下方规则。 */
|
||||||
.k-main .sidebar .widget_categories ul li,
|
.k-main .sidebar .widget_categories ul li,
|
||||||
.k-main .sidebar .widget_archive ul li {
|
.k-main .sidebar .widget_archive ul li {
|
||||||
width: 110px;
|
width: 110px;
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+240
-38
@@ -35,6 +35,114 @@ function widgets_init()
|
|||||||
}
|
}
|
||||||
add_action('widgets_init', 'widgets_init');
|
add_action('widgets_init', 'widgets_init');
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
* 挂件标题与外框(3.2.50)
|
||||||
|
*
|
||||||
|
* ── 为什么要有这两个函数 ──────────────────────────────────────────────
|
||||||
|
* register_sidebar() 里已经声明了版式:
|
||||||
|
*
|
||||||
|
* 'before_title' => '<div class="title">',
|
||||||
|
* 'after_title' => '</div>',
|
||||||
|
*
|
||||||
|
* 但主题自己的挂件**全都绕开了它**:`widget_tags` 把「标签聚合」硬编码在
|
||||||
|
* 代码里,`widget_ad` / `widget_about` / `widget_posts` 干脆没有标题字段,
|
||||||
|
* `widget_search` 有字段却自己手写了一个 `<div class="title">`。
|
||||||
|
*
|
||||||
|
* 结果是:在后台改侧栏版式(换标题标签、加装饰)对这四个挂件无效,
|
||||||
|
* 而且用户根本没地方给它们起标题。这两个函数把「标题」收敛到一处,
|
||||||
|
* 保证所有挂件走同一条路径。
|
||||||
|
*
|
||||||
|
* 🔴 空标题必须**不输出任何节点**,而不是输出空的 `<div class="title"></div>` ——
|
||||||
|
* 后者会留下一条只有内边距的空白条,视觉上像"多了一块间距"。
|
||||||
|
* ====================================================================== */
|
||||||
|
|
||||||
|
if (!function_exists('yang_widget_title')):
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输出挂件标题(走 sidebar 注册的 before_title / after_title)。
|
||||||
|
*
|
||||||
|
* @param array $args widget() 收到的 $args
|
||||||
|
* @param string $instance 实例里的 title
|
||||||
|
* @param string $fallback 没设置标题时的默认值;传 '' 表示默认不显示标题
|
||||||
|
* @param string $id_base 挂件 id_base,仅用于 widget_title 过滤器
|
||||||
|
*/
|
||||||
|
function yang_widget_title($args, $instance, $fallback = '', $id_base = '')
|
||||||
|
{
|
||||||
|
$title = isset($instance['title']) ? trim((string) $instance['title']) : '';
|
||||||
|
if ($title === '') {
|
||||||
|
$title = $fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 兼容 WP 惯例:主题/插件可以用 widget_title 过滤器改标题。
|
||||||
|
* 这里保留它,否则装过「改挂件标题」类插件的站点行为会变。 */
|
||||||
|
if (function_exists('apply_filters')) {
|
||||||
|
$title = apply_filters('widget_title', $title, (array) $instance, $id_base);
|
||||||
|
$title = trim((string) $title);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($title === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$before = isset($args['before_title']) ? $args['before_title'] : '<div class="title">';
|
||||||
|
$after = isset($args['after_title']) ? $args['after_title'] : '</div>';
|
||||||
|
|
||||||
|
echo $before . esc_html($title) . $after;
|
||||||
|
}
|
||||||
|
|
||||||
|
endif;
|
||||||
|
|
||||||
|
if (!function_exists('yang_widget_uid')):
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成挂件内 HTML id 的唯一前缀。
|
||||||
|
*
|
||||||
|
* 🔴 为什么不能用写死的 id:挂件可以添加多个实例,同一页面上
|
||||||
|
* 两个「文章聚合」就会产生两套重复 id(HTML 非法,
|
||||||
|
* aria-labelledby 也只能指到第一个)。
|
||||||
|
* `$this->id` 由 WP_Widget 分配(形如 `widget_posts-2`),
|
||||||
|
* 同一请求内唯一;但在单元测试/旧版环境里可能不存在,所以做兜底。
|
||||||
|
*
|
||||||
|
* @param object $widget 挂件实例
|
||||||
|
* @return string 形如 `yang-widget_posts-2`
|
||||||
|
*/
|
||||||
|
function yang_widget_uid($widget)
|
||||||
|
{
|
||||||
|
$id = '';
|
||||||
|
if (is_object($widget) && isset($widget->id) && is_string($widget->id) && $widget->id !== '') {
|
||||||
|
$id = $widget->id;
|
||||||
|
} elseif (is_object($widget) && isset($widget->id_base) && is_string($widget->id_base) && $widget->id_base !== '') {
|
||||||
|
$id = $widget->id_base;
|
||||||
|
} else {
|
||||||
|
$id = 'widget';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'yang-' . preg_replace('/[^a-z0-9_-]+/i', '-', $id);
|
||||||
|
}
|
||||||
|
|
||||||
|
endif;
|
||||||
|
|
||||||
|
if (!function_exists('yang_widget_open')):
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 挂件外框起始标签。
|
||||||
|
*
|
||||||
|
* 侧栏注册的 before_widget 是 `<div class="widget %2$s">`,其中 %2$s 是
|
||||||
|
* 挂件类名(classname)。这里故意**不用**它,而是自己拼类名:
|
||||||
|
* 主题的挂件样式全部写成 `.k-main .sidebar .w-about` 这种以 `w-` 开头的
|
||||||
|
* 短类名,用 classname 会变成 `widget_widget_about` 之类,样式一条都不生效。
|
||||||
|
* 保留 `widget` 这个基类(主题里 `.k-main .sidebar .widget` 提供白底与内边距)。
|
||||||
|
*
|
||||||
|
* @param string $modifier 形如 'w-about'
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function yang_widget_class($modifier)
|
||||||
|
{
|
||||||
|
return 'widget ' . $modifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
endif;
|
||||||
|
|
||||||
/* ==========================================================================
|
/* ==========================================================================
|
||||||
* 热度算法(3.2.49)
|
* 热度算法(3.2.49)
|
||||||
*
|
*
|
||||||
@@ -208,15 +316,29 @@ class widget_search extends WP_Widget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function widget( $args, $instance ) {
|
public function widget( $args, $instance ) {
|
||||||
$title = ! empty( $instance['title'] ) ? $instance['title'] : '';
|
/* 🔴 id 必须带实例后缀。
|
||||||
$title = apply_filters( 'widget_title', $title, $instance, $this->id_base );
|
* 原来写死 id="searchform" / id="search" / id="searchsubmit",
|
||||||
|
* 挂两个搜索挂件时全撞;主题别处或核心再放一个搜索表单也会撞。 */
|
||||||
|
$uid = yang_widget_uid($this);
|
||||||
|
|
||||||
echo '<div class="widget w-search">';
|
echo '<div class="' . esc_attr(yang_widget_class('w-search')) . '">';
|
||||||
if ( $title ) {
|
yang_widget_title($args, $instance, '', $this->id_base);
|
||||||
echo '<div class="title">'. $title .'</div>';
|
?>
|
||||||
}
|
<div class="item">
|
||||||
echo '<div class="item"> <form role="search" method="get" id="searchform" class="searchform" action="'. home_url('/') .'"> <div class="input-group mt-2 mb-2"> <input type="text" name="s" id="search" class="form-control" placeholder="'. __('搜点什么呢?', 'kratos') .'"> <div class="input-group-append"> <button class="btn btn-primary btn-search" type="submit" id="searchsubmit">'. __('搜索', 'kratos') .'</button> </div> </div> </form>';
|
<form role="search" method="get" id="<?php echo esc_attr($uid); ?>-form"
|
||||||
echo '</div></div>';
|
class="searchform" action="<?php echo esc_url(home_url('/')); ?>">
|
||||||
|
<div class="input-group mt-2 mb-2">
|
||||||
|
<label class="sr-only" for="<?php echo esc_attr($uid); ?>-field"><?php _e('搜索', 'kratos'); ?></label>
|
||||||
|
<input type="text" name="s" id="<?php echo esc_attr($uid); ?>-field" class="form-control"
|
||||||
|
placeholder="<?php esc_attr_e('搜点什么呢?', 'kratos'); ?>">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button class="btn btn-primary btn-search" type="submit" id="<?php echo esc_attr($uid); ?>-submit"><?php _e('搜索', 'kratos'); ?></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
echo '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function form( $instance ) {
|
public function form( $instance ) {
|
||||||
@@ -264,8 +386,21 @@ class widget_ad extends WP_Widget
|
|||||||
$image = !empty($instance['image']) ? $instance['image'] : '';
|
$image = !empty($instance['image']) ? $instance['image'] : '';
|
||||||
$url = !empty($instance['url']) ? $instance['url'] : '';
|
$url = !empty($instance['url']) ? $instance['url'] : '';
|
||||||
|
|
||||||
echo '<div class="widget w-ad">';
|
/* 🔴 原来这三处都是裸拼:
|
||||||
echo '<a href="' . $url . '" target="_blank" rel="noreferrer"><img src="' . $image . '"><div class="prompt">' . $subtitle . '</div></a>';
|
* '<a href="' . $url . '"...><img src="' . $image . '"><div class="prompt">' . $subtitle . '</div></a>'
|
||||||
|
* ① $url 没 esc_url —— 后台填个带引号的地址就能截断 href 属性;
|
||||||
|
* ② img 的 src 值**没有引号** —— 图片 URL 里只要有一个空格,
|
||||||
|
* 后半截就会被解析成另一个属性(常见于文件名带空格的媒体库图片);
|
||||||
|
* ③ $subtitle 没转义 —— 后台填 < 或 & 会破坏结构。
|
||||||
|
* 下面全部补齐;alt 取副标题,避免读屏软件只念文件名。 */
|
||||||
|
echo '<div class="' . esc_attr(yang_widget_class('w-ad')) . '">';
|
||||||
|
yang_widget_title($args, $instance, '', $this->id_base);
|
||||||
|
|
||||||
|
$imgAlt = wp_strip_all_tags($subtitle);
|
||||||
|
echo '<a href="' . esc_url($url) . '" target="_blank" rel="noreferrer noopener">'
|
||||||
|
. '<img src="' . esc_url($image) . '" alt="' . esc_attr($imgAlt) . '">'
|
||||||
|
. '<div class="prompt">' . esc_html($subtitle) . '</div>'
|
||||||
|
. '</a>';
|
||||||
echo '</div>';
|
echo '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,20 +408,27 @@ class widget_ad extends WP_Widget
|
|||||||
{
|
{
|
||||||
$instance = array();
|
$instance = array();
|
||||||
|
|
||||||
$instance['subtitle'] = (!empty($new_instance['subtitle'])) ? $new_instance['subtitle'] : '';
|
$instance['title'] = (!empty($new_instance['title'])) ? sanitize_text_field($new_instance['title']) : '';
|
||||||
$instance['image'] = (!empty($new_instance['image'])) ? $new_instance['image'] : '';
|
$instance['subtitle'] = (!empty($new_instance['subtitle'])) ? sanitize_text_field($new_instance['subtitle']) : '';
|
||||||
$instance['url'] = (!empty($new_instance['url'])) ? $new_instance['url'] : '';
|
$instance['image'] = (!empty($new_instance['image'])) ? esc_url_raw($new_instance['image']) : '';
|
||||||
|
$instance['url'] = (!empty($new_instance['url'])) ? esc_url_raw($new_instance['url']) : '';
|
||||||
|
|
||||||
return $instance;
|
return $instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function form($instance)
|
public function form($instance)
|
||||||
{
|
{
|
||||||
|
$title = !empty($instance['title']) ? $instance['title'] : '';
|
||||||
$subtitle = !empty($instance['subtitle']) ? $instance['subtitle'] : __('广告', 'kratos');
|
$subtitle = !empty($instance['subtitle']) ? $instance['subtitle'] : __('广告', 'kratos');
|
||||||
$image = !empty($instance['image']) ? $instance['image'] : '';
|
$image = !empty($instance['image']) ? $instance['image'] : '';
|
||||||
$url = !empty($instance['url']) ? $instance['url'] : '';
|
$url = !empty($instance['url']) ? $instance['url'] : '';
|
||||||
?>
|
?>
|
||||||
<div class="media-widget-control">
|
<div class="media-widget-control">
|
||||||
|
<p>
|
||||||
|
<label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('标题:', 'kratos');?></label>
|
||||||
|
<input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo esc_attr($title); ?>">
|
||||||
|
<span class="description"><?php _e('留空则不显示标题。', 'kratos');?></span>
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<label for="<?php echo $this->get_field_id('subtitle'); ?>"><?php _e('副标题:', 'kratos');?></label>
|
<label for="<?php echo $this->get_field_id('subtitle'); ?>"><?php _e('副标题:', 'kratos');?></label>
|
||||||
<input class="widefat" id="<?php echo $this->get_field_id('subtitle'); ?>" name="<?php echo $this->get_field_name('subtitle'); ?>" type="text" value="<?php echo esc_attr($subtitle); ?>">
|
<input class="widefat" id="<?php echo $this->get_field_id('subtitle'); ?>" name="<?php echo $this->get_field_name('subtitle'); ?>" type="text" value="<?php echo esc_attr($subtitle); ?>">
|
||||||
@@ -334,15 +476,44 @@ class widget_about extends WP_Widget
|
|||||||
$avatar = yang_option_image('a_gravatar');
|
$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">';
|
/* 🔴 原来写的是 `background:url(' . $background . ') no-repeat ...`
|
||||||
echo '<div class="background" style="background:url(' . $background . ') no-repeat center center;-webkit-background-size: cover; -moz-background-size: cover; -o-background-size: cover; background-size: cover;"></div><div class="wrapper text-center">';
|
* —— url() 里的地址**没有引号**。只要路径含空格、括号、单引号,
|
||||||
|
* CSS 解析器就会在那里截断,整条声明作废(背景变白)。
|
||||||
|
* 媒体库文件名带空格很常见。这里补引号。
|
||||||
|
*
|
||||||
|
* 🔴 这里**只拼一次转义**,而且拼完还要整体 esc_attr()。
|
||||||
|
* 第一版写的是 esc_url($background) 再套 esc_attr($bg) ——
|
||||||
|
* esc_url() 已经做过 HTML 实体化,esc_attr() 又来一次 ⇒
|
||||||
|
* `&` 被编成 `&amp;`,CSS 里拿到的是字面量 "amp",
|
||||||
|
* 背景图直接 404。测试里那条「URL 完整」判据抓到的就是它。
|
||||||
|
*
|
||||||
|
* 正确做法:URL 只做**协议/字符清洗**(esc_url_raw,不入实体),
|
||||||
|
* url() 内部用双引号包住,最后整个 style 属性交给 esc_attr()
|
||||||
|
* 统一转义一次。这样 `&` 只会被编成 `&`(正确),
|
||||||
|
* 浏览器解析回属性值后仍是原始的 `&`。
|
||||||
|
*
|
||||||
|
* 🔴 $introduce 来自主题选项 textarea,$username 来自 text,
|
||||||
|
* 原来是裸 echo。个人简介里写个 < 就把后面的结构吃掉了。
|
||||||
|
* 简介保留换行(textarea 的自然语义),其余全部转义。 */
|
||||||
|
$bg = 'background-image:url("' . esc_url_raw($background) . '");'
|
||||||
|
. 'background-repeat:no-repeat;'
|
||||||
|
. 'background-position:center center;'
|
||||||
|
. 'background-size:cover;';
|
||||||
|
|
||||||
|
echo '<div class="' . esc_attr(yang_widget_class('w-about')) . '">';
|
||||||
|
yang_widget_title($args, $instance, '', $this->id_base);
|
||||||
|
|
||||||
|
echo '<div class="background" style="' . esc_attr($bg) . '"></div><div class="wrapper text-center">';
|
||||||
if (current_user_can('manage_options')) {
|
if (current_user_can('manage_options')) {
|
||||||
echo '<a href="' . admin_url() . '">';
|
echo '<a href="' . esc_url(admin_url()) . '">';
|
||||||
} else {
|
} else {
|
||||||
echo '<a href="' . wp_login_url() . '">';
|
echo '<a href="' . esc_url(wp_login_url()) . '">';
|
||||||
}
|
}
|
||||||
echo '<img src="' . $avatar . '"></a>';
|
echo '<img src="' . esc_url($avatar) . '" alt="' . esc_attr(wp_strip_all_tags($username)) . '"></a>';
|
||||||
echo '</div><div class="textwidget text-center"><p class="username">'. $username .'</p><p class="about">' . $introduce . '</p></div>';
|
echo '</div><div class="textwidget text-center">'
|
||||||
|
. '<p class="username">' . esc_html($username) . '</p>'
|
||||||
|
. '<p class="about">' . nl2br(esc_html($introduce)) . '</p>'
|
||||||
|
. '</div>';
|
||||||
echo '</div>';
|
echo '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,16 +521,23 @@ class widget_about extends WP_Widget
|
|||||||
{
|
{
|
||||||
$instance = array();
|
$instance = array();
|
||||||
|
|
||||||
$instance['background'] = (!empty($new_instance['background'])) ? $new_instance['background'] : '';
|
$instance['title'] = (!empty($new_instance['title'])) ? sanitize_text_field($new_instance['title']) : '';
|
||||||
|
$instance['background'] = (!empty($new_instance['background'])) ? esc_url_raw($new_instance['background']) : '';
|
||||||
|
|
||||||
return $instance;
|
return $instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function form($instance)
|
public function form($instance)
|
||||||
{
|
{
|
||||||
|
$title = !empty($instance['title']) ? $instance['title'] : '';
|
||||||
$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';
|
||||||
?>
|
?>
|
||||||
<div class="media-widget-control">
|
<div class="media-widget-control">
|
||||||
|
<p>
|
||||||
|
<label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('标题:', 'kratos');?></label>
|
||||||
|
<input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo esc_attr($title); ?>">
|
||||||
|
<span class="description"><?php _e('留空则不显示标题。', 'kratos');?></span>
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<label for="<?php echo $this->get_field_id('background'); ?>"><?php _e('背景图片:', 'kratos');?></label>
|
<label for="<?php echo $this->get_field_id('background'); ?>"><?php _e('背景图片:', 'kratos');?></label>
|
||||||
<input class="widefat" id="<?php echo $this->get_field_id('background'); ?>" name="<?php echo $this->get_field_name('background'); ?>" type="text" value="<?php echo esc_attr($background); ?>">
|
<input class="widefat" id="<?php echo $this->get_field_id('background'); ?>" name="<?php echo $this->get_field_name('background'); ?>" type="text" value="<?php echo esc_attr($background); ?>">
|
||||||
@@ -397,8 +575,11 @@ class widget_tags extends WP_Widget
|
|||||||
'echo' => false,
|
'echo' => false,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
echo '<div class="widget w-tags">';
|
/* 🔴 标题原来是硬编码的 __('标签聚合'),后台没有输入框也改不了。
|
||||||
echo '<div class="title">' . __('标签聚合', 'kratos') . '</div>';
|
* 现在走实例的 title,没设才回落到「标签聚合」——
|
||||||
|
* 这样老站点的显示**一点不变**,但用户可以改了。 */
|
||||||
|
echo '<div class="' . esc_attr(yang_widget_class('w-tags')) . '">';
|
||||||
|
yang_widget_title($args, $instance, __('标签聚合', 'kratos'), $this->id_base);
|
||||||
echo '<div class="item">' . $tags . '</div>';
|
echo '<div class="item">' . $tags . '</div>';
|
||||||
echo '</div>';
|
echo '</div>';
|
||||||
}
|
}
|
||||||
@@ -407,8 +588,9 @@ class widget_tags extends WP_Widget
|
|||||||
{
|
{
|
||||||
$instance = array();
|
$instance = array();
|
||||||
|
|
||||||
$instance['number'] = (!empty($new_instance['number'])) ? $new_instance['number'] : '';
|
$instance['title'] = (!empty($new_instance['title'])) ? sanitize_text_field($new_instance['title']) : '';
|
||||||
$instance['order'] = (!empty($new_instance['order'])) ? $new_instance['order'] : '';
|
$instance['number'] = (!empty($new_instance['number'])) ? (int) $new_instance['number'] : 8;
|
||||||
|
$instance['order'] = (!empty($new_instance['order'])) ? $new_instance['order'] : 'RAND';
|
||||||
|
|
||||||
return $instance;
|
return $instance;
|
||||||
}
|
}
|
||||||
@@ -416,10 +598,16 @@ class widget_tags extends WP_Widget
|
|||||||
public function form($instance)
|
public function form($instance)
|
||||||
{
|
{
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
$title = !empty($instance['title']) ? $instance['title'] : '';
|
||||||
$number = !empty($instance['number']) ? $instance['number'] : '8';
|
$number = !empty($instance['number']) ? $instance['number'] : '8';
|
||||||
$order = !empty($instance['order']) ? $instance['order'] : 'RAND';
|
$order = !empty($instance['order']) ? $instance['order'] : 'RAND';
|
||||||
?>
|
?>
|
||||||
<div class="media-widget-control">
|
<div class="media-widget-control">
|
||||||
|
<p>
|
||||||
|
<label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('标题:', 'kratos');?></label>
|
||||||
|
<input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo esc_attr($title); ?>" placeholder="<?php echo esc_attr__('标签聚合', 'kratos'); ?>">
|
||||||
|
<span class="description"><?php _e('留空则仍显示「标签聚合」。', 'kratos');?></span>
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<label for="<?php echo $this->get_field_id('number'); ?>"><?php _e('显示数量:', 'kratos');?></label>
|
<label for="<?php echo $this->get_field_id('number'); ?>"><?php _e('显示数量:', 'kratos');?></label>
|
||||||
<input class="widefat" id="<?php echo $this->get_field_id('number'); ?>" name="<?php echo $this->get_field_name('number'); ?>" type="text" value="<?php echo esc_attr($number); ?>" />
|
<input class="widefat" id="<?php echo $this->get_field_id('number'); ?>" name="<?php echo $this->get_field_name('number'); ?>" type="text" value="<?php echo esc_attr($number); ?>" />
|
||||||
@@ -456,20 +644,27 @@ class widget_posts extends WP_Widget
|
|||||||
$number = 6;
|
$number = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
echo '<div class="widget w-recommended">';
|
/* 🔴 id 唯一化(原来全是写死的)。
|
||||||
|
* 老代码把同一套标签页导航写了两份 —— 桌面一份带图标(d-none d-xl-flex)、
|
||||||
|
* 手机一份不带图标(d-xl-none)—— 于是 nav-tab / nav-new-tab /
|
||||||
|
* nav-hot-tab / nav-random-tab **四个 id 各出现两次**,HTML 非法;
|
||||||
|
* `aria-labelledby="nav-new-tab"` 也只能指到第一份。
|
||||||
|
* 挂两个「文章聚合」实例时撞得更彻底。
|
||||||
|
*
|
||||||
|
* 现在只渲染**一份**导航:图标用 CSS 在窄屏隐藏(见 kratos.css 的
|
||||||
|
* `.w-recommended .tab-icon`)。这样 id 天然唯一,DOM 也少一半。 */
|
||||||
|
$uid = yang_widget_uid($this);
|
||||||
|
|
||||||
|
echo '<div class="' . esc_attr(yang_widget_class('w-recommended')) . '">';
|
||||||
|
yang_widget_title($args, $instance, '', $this->id_base);
|
||||||
?>
|
?>
|
||||||
<div class="nav nav-tabs d-none d-xl-flex" id="nav-tab" role="tablist">
|
<div class="nav nav-tabs" id="<?php echo esc_attr($uid); ?>-tab" role="tablist">
|
||||||
<a class="nav-item nav-link active" id="nav-new-tab" data-toggle="tab" href="#nav-new" role="tab" aria-controls="nav-new" aria-selected="false"><i class="kicon i-tabnew"></i><?php _e('最新', 'kratos');?></a>
|
<a class="nav-item nav-link active" id="<?php echo esc_attr($uid); ?>-new-tab" data-toggle="tab" href="#<?php echo esc_attr($uid); ?>-new" role="tab" aria-controls="<?php echo esc_attr($uid); ?>-new" aria-selected="true"><i class="kicon i-tabnew tab-icon"></i><?php _e('最新', 'kratos');?></a>
|
||||||
<a class="nav-item nav-link" id="nav-hot-tab" data-toggle="tab" href="#nav-hot" role="tab" aria-controls="nav-hot" aria-selected="true"><i class="kicon i-tabhot"></i><?php _e('热点', 'kratos');?></a>
|
<a class="nav-item nav-link" id="<?php echo esc_attr($uid); ?>-hot-tab" data-toggle="tab" href="#<?php echo esc_attr($uid); ?>-hot" role="tab" aria-controls="<?php echo esc_attr($uid); ?>-hot" aria-selected="false"><i class="kicon i-tabhot tab-icon"></i><?php _e('热点', 'kratos');?></a>
|
||||||
<a class="nav-item nav-link" id="nav-random-tab" data-toggle="tab" href="#nav-random" role="tab" aria-controls="nav-random" aria-selected="false"><i class="kicon i-tabrandom"></i><?php _e('随机', 'kratos');?></a>
|
<a class="nav-item nav-link" id="<?php echo esc_attr($uid); ?>-random-tab" data-toggle="tab" href="#<?php echo esc_attr($uid); ?>-random" role="tab" aria-controls="<?php echo esc_attr($uid); ?>-random" aria-selected="false"><i class="kicon i-tabrandom tab-icon"></i><?php _e('随机', 'kratos');?></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav nav-tabs d-xl-none" id="nav-tab" role="tablist">
|
<div class="tab-content" id="<?php echo esc_attr($uid); ?>-tabContent">
|
||||||
<a class="nav-item nav-link active" id="nav-new-tab" data-toggle="tab" href="#nav-new" role="tab" aria-controls="nav-new" aria-selected="false"><?php _e('最新', 'kratos');?></a>
|
<div class="tab-pane fade show active" id="<?php echo esc_attr($uid); ?>-new" role="tabpanel" aria-labelledby="<?php echo esc_attr($uid); ?>-new-tab">
|
||||||
<a class="nav-item nav-link" id="nav-hot-tab" data-toggle="tab" href="#nav-hot" role="tab" aria-controls="nav-hot" aria-selected="true"><?php _e('热点', 'kratos');?></a>
|
|
||||||
<a class="nav-item nav-link" id="nav-random-tab" data-toggle="tab" href="#nav-random" role="tab" aria-controls="nav-random" aria-selected="false"><?php _e('随机', 'kratos');?></a>
|
|
||||||
</div>
|
|
||||||
<div class="tab-content" id="nav-tabContent">
|
|
||||||
<div class="tab-pane fade show active" id="nav-new" role="tabpanel" aria-labelledby="nav-new-tab">
|
|
||||||
<?php
|
<?php
|
||||||
/* 🔴 原来写的是 `'numberposts=' . $number . ' & offset=0'` ——
|
/* 🔴 原来写的是 `'numberposts=' . $number . ' & offset=0'` ——
|
||||||
* 字符串里带空格,WP_Query 解析时参数名变成 `' offset'`(前面
|
* 字符串里带空格,WP_Query 解析时参数名变成 `' offset'`(前面
|
||||||
@@ -483,7 +678,7 @@ class widget_posts extends WP_Widget
|
|||||||
<a class="bookmark-item" title="<?php echo esc_attr($post->post_title); ?>" href="<?php echo esc_url(get_permalink($post->ID)); ?>" rel="bookmark"><i class="kicon i-book"></i><?php echo esc_html(wp_strip_all_tags($post->post_title)) ?></a>
|
<a class="bookmark-item" title="<?php echo esc_attr($post->post_title); ?>" href="<?php echo esc_url(get_permalink($post->ID)); ?>" rel="bookmark"><i class="kicon i-book"></i><?php echo esc_html(wp_strip_all_tags($post->post_title)) ?></a>
|
||||||
<?php endforeach;?>
|
<?php endforeach;?>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="nav-hot" role="tabpanel" aria-labelledby="nav-hot-tab">
|
<div class="tab-pane fade" id="<?php echo esc_attr($uid); ?>-hot" role="tabpanel" aria-labelledby="<?php echo esc_attr($uid); ?>-hot-tab">
|
||||||
<?php
|
<?php
|
||||||
/* 热点:全站累计热度(views/love/评论 三项 log 加权),
|
/* 热点:全站累计热度(views/love/评论 三项 log 加权),
|
||||||
* 不再受「最近 N 天发布」的时间窗限制。原因见 most_comm_posts()
|
* 不再受「最近 N 天发布」的时间窗限制。原因见 most_comm_posts()
|
||||||
@@ -492,7 +687,7 @@ class widget_posts extends WP_Widget
|
|||||||
most_comm_posts(0, $number);
|
most_comm_posts(0, $number);
|
||||||
} ?>
|
} ?>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="nav-random" role="tabpanel" aria-labelledby="nav-random-tab">
|
<div class="tab-pane fade" id="<?php echo esc_attr($uid); ?>-random" role="tabpanel" aria-labelledby="<?php echo esc_attr($uid); ?>-random-tab">
|
||||||
<?php
|
<?php
|
||||||
$myposts = get_posts(array(
|
$myposts = get_posts(array(
|
||||||
'numberposts' => $number,
|
'numberposts' => $number,
|
||||||
@@ -511,6 +706,7 @@ class widget_posts extends WP_Widget
|
|||||||
{
|
{
|
||||||
$instance = array();
|
$instance = array();
|
||||||
|
|
||||||
|
$instance['title'] = (!empty($new_instance['title'])) ? sanitize_text_field($new_instance['title']) : '';
|
||||||
$instance['number'] = (!empty($new_instance['number'])) ? (int) $new_instance['number'] : 6;
|
$instance['number'] = (!empty($new_instance['number'])) ? (int) $new_instance['number'] : 6;
|
||||||
|
|
||||||
/* `days` 已废弃:旧实现拿它当「最近 N 天发布」的时间窗,
|
/* `days` 已废弃:旧实现拿它当「最近 N 天发布」的时间窗,
|
||||||
@@ -520,9 +716,15 @@ class widget_posts extends WP_Widget
|
|||||||
}
|
}
|
||||||
public function form($instance)
|
public function form($instance)
|
||||||
{
|
{
|
||||||
|
$title = !empty($instance['title']) ? $instance['title'] : '';
|
||||||
$number = !empty($instance['number']) ? $instance['number'] : '6';
|
$number = !empty($instance['number']) ? $instance['number'] : '6';
|
||||||
?>
|
?>
|
||||||
<div class="media-widget-control">
|
<div class="media-widget-control">
|
||||||
|
<p>
|
||||||
|
<label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('标题:', 'kratos');?></label>
|
||||||
|
<input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo esc_attr($title); ?>">
|
||||||
|
<span class="description"><?php _e('留空则不显示标题。', 'kratos');?></span>
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<label for="<?php echo $this->get_field_id('number'); ?>"><?php _e('展示数量:', 'kratos');?></label>
|
<label for="<?php echo $this->get_field_id('number'); ?>"><?php _e('展示数量:', 'kratos');?></label>
|
||||||
<input class="widefat" id="<?php echo $this->get_field_id('number'); ?>" name="<?php echo $this->get_field_name('number'); ?>" type="text" value="<?php echo esc_attr($number); ?>" />
|
<input class="widefat" id="<?php echo $this->get_field_id('number'); ?>" name="<?php echo $this->get_field_name('number'); ?>" type="text" value="<?php echo esc_attr($number); ?>" />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Theme Name: Kratos_Yang
|
Theme Name: Kratos_Yang
|
||||||
Text Domain: kratos_Yang
|
Text Domain: kratos_Yang
|
||||||
Version: 3.2.49
|
Version: 3.2.50
|
||||||
Requires PHP: 7.0
|
Requires PHP: 7.0
|
||||||
Description: 这是一个Kratos主题的分支版本,用来增加一些定制化功能。
|
Description: 这是一个Kratos主题的分支版本,用来增加一些定制化功能。
|
||||||
Tags: 博客, 双栏, 响应式, 自定义背景, 自定义颜色, 自定义图标, 自定义菜单, 特色图像, 收录优化, 邮件中心, 主题选项, 小工具, 文章置顶, 无障碍友好
|
Tags: 博客, 双栏, 响应式, 自定义背景, 自定义颜色, 自定义图标, 自定义菜单, 特色图像, 收录优化, 邮件中心, 主题选项, 小工具, 文章置顶, 无障碍友好
|
||||||
|
|||||||
Reference in New Issue
Block a user