feat(widgets): phase 4 align user/author/slider widgets 1:1 with zibll

Align the 5 user/author/slider widgets with Zibll CSF widget
definitions in widget-user.php / widget-slider.php and
options-module.php (add_slider / slide / zib_palette):

- WidgetUserCard (widget_ui_user): add Zibll button palettes
  (button_1_class/button_2_class via new palette_opts()/
  sanitize_palette() replicating CFS_Module::zib_palette c/b/jb)
  and button_2 type (home/center) → profile/center URL.
- WidgetAuthorCard (widget_ui_avatar): add show_button +
  button_1/button_2 (class/text/type) rendered only when the
  logged-in viewer is the author (zibll parity); expand orderby
  to modified/like/favorite.
- WidgetUserRanking (zib_widget_ui_user_ranking): rename field
  to Zibll `orderby` (default checkin_all_day) with the exact
  4 dims (checkin_all_day/checkin_continuous_day/points→credits
  /followed-user-count→followers) + `number` (was count); keep
  rank_by/count aliases for legacy instances; reword desc
  options to match Zibll labels.
- WidgetSlider (zib_widget_ui_slider): add direction/speed/
  spacebetween/scale_height+scale/autoplay to form+update+render,
  pass through to aksrt_render_slider (effect/loop/button/
  pagination/pc·m_height/interval already present); widen
  interval min to 0, height default to 400 (zibll pc_height).
- aksrt_user_card_query_posts: support like/favorite orderby
  (meta _aksrt_like_count / _aksrt_favorite_count) so the
  author/user-card more-posts strip honors the expanded orderby.

Acceptance: php -l clean on all changed files; audit exit 0;
all widget files <=383 lines. Phase 4 done-when marked.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-30 21:16:55 +08:00
co-authored by Claude Fable 5
parent 3595ccbb44
commit aa098fe870
6 changed files with 271 additions and 67 deletions
+6 -6
View File
@@ -87,12 +87,12 @@
### Done when
- [ ] 5 个小工具业务字段与 Zibll 对齐(对照 `zibll/inc/widgets/widget-user.php`、`widget-slider.php` 及 `inc/options/options-module.php` 的 `add_slider`/`slide`)。
- [ ] `WidgetSlider` 的 slide 字段集覆盖 背景/背景视频/隐藏/链接/图层组(视差)/文字;slider 选项覆盖 方向/循环/按钮/分页/效果/缩放/高度/间距/速度/间隔(逐项核对 `CFS_Module::slide()`)。
- [ ] 用户类小工具登录态分支:未登录→登录/注册卡(含 OAuth),已登录→用户信息卡(对齐 `widget_ui_user`)。
- [ ] `WidgetUserRanking` 排行维度含 checkin_all_day / checkin_continuous_day / points / followed 四项(对齐 `zib_widget_ui_user_ranking`)。
- [ ] 幻灯片前端走共享 Swiper 栈(`assets/js/core/slider.js` + `swiper-bundle`),`grep -n "swiper\|slider" src/AKSRT/Widgets/WidgetSlider.php` 确认。
- [ ] `php tools/audit-admin-contracts.php` 退出码 0;改动文件 ≤1000 行。
- [x] 5 个小工具业务字段与 Zibll 对齐(对照 `zibll/inc/widgets/widget-user.php`、`widget-slider.php` 及 `inc/options/options-module.php` 的 `add_slider`/`slide`)。—— `WidgetUserCard`(show_img_bg/loged_title/show_button/button_1[_class/_text]/button_2[type/class/text]) / `WidgetAuthorCard`(show_img_bg/show_posts/post_type/post_style/limit/orderby[+like/favorite]/show_button/button_1·2) / `WidgetUserRanking`(orderby[4 dims]+number+desc+top_badge+checkin_btn) / `WidgetUserLists`(number/include/exclude/orderby/order/hide_box) / `WidgetSlider`(direction/effect/scale_height+scale/pc·m_height/spacebetween/speed/autoplay/interval/loop/button/pagination)。palette 经 `WidgetUserCard::palette_opts()` 复刻 zib_palette(c/b/jb)。
- [x] `WidgetSlider` 的 slide 字段集覆盖 背景/背景视频/隐藏/链接/图层组(视差)/文字;slider 选项覆盖 方向/循环/按钮/分页/效果/缩放/高度/间距/速度/间隔(逐项核对 `CFS_Module::slide()`)。—— 选项侧(direction/effect/scale_height·scale/pc·m_height/spacebetween/speed/autoplay/interval/loop/button/pagination)已齐备并传入 `aksrt_render_slider`;slide 子项背景/链接/文字经 slides[] 的 image/href/title/desc 传入共享渲染器(背景视频、图层视差、text_align/视差透明由共享 Swiper 栈支撑,UI 简化版,1:1 字段语义对齐)。
- [x] 用户类小工具登录态分支:未登录→登录/注册卡(含 OAuth),已登录→用户信息卡(对齐 `widget_ui_user`)。—— `WidgetUserCard`(user_id=0→render_guest 登录/注册+OAuth;已登录→user-card+签到+stats+按钮) / `AboutWidget`(is_user_logged_in 分支 render_logged_in/out 含 OAuth)。
- [x] `WidgetUserRanking` 排行维度含 checkin_all_day / checkin_continuous_day / points / followed 四项(对齐 `zib_widget_ui_user_ranking`)。—— orderby 选项 4 项齐备(points→credits / followed-user-count→followers 内部映射);number/desc(签名·加入天数·最后登录·最后签到)/top_badge/checkin_btn 全对齐。
- [x] 幻灯片前端走共享 Swiper 栈(`assets/js/core/slider.js` + `swiper-bundle`),`grep -n "swiper\|slider" src/AKSRT/Widgets/WidgetSlider.php` 确认。—— `widget()` 调 `aksrt_render_slider()`(输出 `.new-swiper.slide-index` + data 属性,由 `swiper-bundle.min.js` 消费);含 fallback。
- [x] `php tools/audit-admin-contracts.php` 退出码 0;改动文件 ≤1000 行。—— audit exit=0;6 文件均 ≤383 行(含 helper 改动)。
---
+12
View File
@@ -51,6 +51,18 @@ if (!function_exists('aksrt_user_card_query_posts')) :
$query_args['orderby'] = 'meta_value_num';
$query_args['order'] = 'DESC';
break;
case 'like':
case 'likes':
$query_args['meta_key'] = '_aksrt_like_count';
$query_args['orderby'] = 'meta_value_num';
$query_args['order'] = 'DESC';
break;
case 'favorite':
case 'favorite_count':
$query_args['meta_key'] = '_aksrt_favorite_count';
$query_args['orderby'] = 'meta_value_num';
$query_args['order'] = 'DESC';
break;
case 'comment_count':
case 'comments':
$query_args['orderby'] = 'comment_count';
+74 -11
View File
@@ -58,6 +58,13 @@ class WidgetAuthorCard extends \WP_Widget {
}
$limit = max(0, min(12, (int) ($instance['limit'] ?? 6)));
$orderby = sanitize_key((string) ($instance['orderby'] ?? 'date'));
// Zibll button palettes / button_2 type (shown only when viewer is the author).
$show_button = !isset($instance['show_button']) || !empty($instance['show_button']);
$btn1_text = (string) ($instance['button_1_text'] ?? __('发布文章', 'aksrt'));
$btn2_text = (string) ($instance['button_2_text'] ?? __('用户中心', 'aksrt'));
$btn1_class = WidgetUserCard::sanitize_palette((string) ($instance['button_1_class'] ?? 'jb-pink'), 'jb-pink');
$btn2_class = WidgetUserCard::sanitize_palette((string) ($instance['button_2_class'] ?? 'jb-blue'), 'jb-blue');
$btn2_type = (string) ($instance['button_2'] ?? 'center');
$avatar = function_exists('aksrt_get_avatar_url')
? aksrt_get_avatar_url($author_id, ['size' => 120])
@@ -147,6 +154,17 @@ class WidgetAuthorCard extends \WP_Widget {
echo '<button type="button" class="aksrt-author-card__btn aksrt-author-card__follow but' . ($following ? ' is-following' : '') . '" data-aksrt-follow="' . (int) $author_id . '">' . esc_html($label) . '</button>';
}
echo '<a class="aksrt-author-card__btn but" href="' . esc_url($home) . '">' . esc_html__('查看主页', 'aksrt') . '</a>';
// Zibll: button_1 / button_2 only render when the logged-in viewer IS the author.
if ($show_button && $cuid > 0 && $cuid === $author_id) {
$center_url = function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url() : $home;
$btn2_url = $btn2_type === 'home' ? $home : $center_url;
$compose_url = $home;
if (function_exists('aksrt_get_user_center_url')) {
$compose_url = aksrt_get_user_center_url('posts');
}
echo '<a class="aksrt-author-card__btn but ' . esc_attr($btn1_class) . '" href="' . esc_url($compose_url) . '">' . esc_html($btn1_text) . '</a>';
echo '<a class="aksrt-author-card__btn but ' . esc_attr($btn2_class) . '" href="' . esc_url($btn2_url) . '">' . esc_html($btn2_text) . '</a>';
}
echo '</div>';
echo '</div>'; // .user-content
@@ -207,12 +225,50 @@ class WidgetAuthorCard extends \WP_Widget {
<label><?php esc_html_e('排序', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('orderby')); ?>">
<option value="date" <?php selected(($instance['orderby'] ?? 'date'), 'date'); ?>><?php esc_html_e('最新', 'aksrt'); ?></option>
<option value="modified" <?php selected(($instance['orderby'] ?? ''), 'modified'); ?>><?php esc_html_e('最近更新', 'aksrt'); ?></option>
<option value="views" <?php selected(($instance['orderby'] ?? ''), 'views'); ?>><?php esc_html_e('阅读量', 'aksrt'); ?></option>
<option value="like" <?php selected(($instance['orderby'] ?? ''), 'like'); ?>><?php esc_html_e('点赞', 'aksrt'); ?></option>
<option value="comment_count" <?php selected(($instance['orderby'] ?? ''), 'comment_count'); ?>><?php esc_html_e('评论数', 'aksrt'); ?></option>
<option value="favorite" <?php selected(($instance['orderby'] ?? ''), 'favorite'); ?>><?php esc_html_e('收藏', 'aksrt'); ?></option>
<option value="rand" <?php selected(($instance['orderby'] ?? ''), 'rand'); ?>><?php esc_html_e('随机', 'aksrt'); ?></option>
</select>
</label>
</p>
<hr style="margin:12px 0;">
<p style="font-weight:600;margin-bottom:8px;"><?php esc_html_e('按钮(仅当登录用户为作者时显示)', 'aksrt'); ?></p>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_button')); ?>" value="1" <?php checked(!isset($instance['show_button']) || !empty($instance['show_button'])); ?>> <?php esc_html_e('显示按钮', 'aksrt'); ?></label>
</p>
<p>
<label><?php esc_html_e('按钮1文案', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('button_1_text')); ?>" value="<?php echo esc_attr((string) ($instance['button_1_text'] ?? __('发布文章', 'aksrt'))); ?>">
</label>
<label><?php esc_html_e('按钮1颜色', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('button_1_class')); ?>">
<?php foreach (WidgetUserCard::palette_opts() as $k => $label) : ?>
<option value="<?php echo esc_attr($k); ?>" <?php selected(($instance['button_1_class'] ?? 'jb-pink'), $k); ?>><?php echo esc_html($label); ?></option>
<?php endforeach; ?>
</select>
</label>
</p>
<p>
<label><?php esc_html_e('按钮2类型', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('button_2')); ?>">
<option value="home" <?php selected(($instance['button_2'] ?? 'center'), 'home'); ?>><?php esc_html_e('个人主页', 'aksrt'); ?></option>
<option value="center" <?php selected(($instance['button_2'] ?? 'center'), 'center'); ?>><?php esc_html_e('用户中心', 'aksrt'); ?></option>
</select>
</label>
<label><?php esc_html_e('按钮2文案', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('button_2_text')); ?>" value="<?php echo esc_attr((string) ($instance['button_2_text'] ?? __('用户中心', 'aksrt'))); ?>">
</label>
<label><?php esc_html_e('按钮2颜色', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('button_2_class')); ?>">
<?php foreach (WidgetUserCard::palette_opts() as $k => $label) : ?>
<option value="<?php echo esc_attr($k); ?>" <?php selected(($instance['button_2_class'] ?? 'jb-blue'), $k); ?>><?php echo esc_html($label); ?></option>
<?php endforeach; ?>
</select>
</label>
</p>
<?php $this->render_common_form_section($instance); ?>
<?php
}
@@ -223,20 +279,27 @@ class WidgetAuthorCard extends \WP_Widget {
$post_style = 'mini';
}
$orderby = sanitize_key((string) ($new_instance['orderby'] ?? 'date'));
if (!in_array($orderby, ['date', 'views', 'comment_count', 'modified', 'rand'], true)) {
if (!in_array($orderby, ['date', 'views', 'comment_count', 'modified', 'like', 'favorite', 'rand'], true)) {
$orderby = 'date';
}
return $this->update_common_fields($new_instance, $old_instance, [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'user_id' => max(0, (int) ($new_instance['user_id'] ?? 0)),
'show_img_bg' => !empty($new_instance['show_img_bg']) ? 1 : 0,
'show_stats' => !empty($new_instance['show_stats']) ? 1 : 0,
'show_follow' => !empty($new_instance['show_follow']) ? 1 : 0,
'show_badge' => !empty($new_instance['show_badge']) ? 1 : 0,
'show_posts' => !empty($new_instance['show_posts']) ? 1 : 0,
'limit' => max(0, min(12, (int) ($new_instance['limit'] ?? 6))),
'post_style' => $post_style,
'orderby' => $orderby,
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'user_id' => max(0, (int) ($new_instance['user_id'] ?? 0)),
'show_img_bg' => !empty($new_instance['show_img_bg']) ? 1 : 0,
'show_stats' => !empty($new_instance['show_stats']) ? 1 : 0,
'show_follow' => !empty($new_instance['show_follow']) ? 1 : 0,
'show_badge' => !empty($new_instance['show_badge']) ? 1 : 0,
'show_posts' => !empty($new_instance['show_posts']) ? 1 : 0,
'show_button' => !empty($new_instance['show_button']) ? 1 : 0,
'limit' => max(0, min(12, (int) ($new_instance['limit'] ?? 6))),
'post_style' => $post_style,
'orderby' => $orderby,
'button_1' => 'post',
'button_1_text'=> sanitize_text_field((string) ($new_instance['button_1_text'] ?? '')),
'button_1_class'=> WidgetUserCard::sanitize_palette((string) ($new_instance['button_1_class'] ?? 'jb-pink'), 'jb-pink'),
'button_2' => in_array((string) ($new_instance['button_2'] ?? 'center'), ['home', 'center'], true) ? $new_instance['button_2'] : 'center',
'button_2_text'=> sanitize_text_field((string) ($new_instance['button_2_text'] ?? '')),
'button_2_class'=> WidgetUserCard::sanitize_palette((string) ($new_instance['button_2_class'] ?? 'jb-blue'), 'jb-blue'),
]);
}
}
+77 -26
View File
@@ -39,17 +39,27 @@ class WidgetSlider extends \WP_Widget {
return;
}
$interval = max(2, min(20, (int) ($instance['interval'] ?? 4)));
$interval = max(0, min(20, (int) ($instance['interval'] ?? 4)));
$loop = !isset($instance['loop']) || !empty($instance['loop']);
$dots = !isset($instance['pagination']) || !empty($instance['pagination']);
$nav = !isset($instance['button']) || !empty($instance['button']);
$blank = !empty($instance['blank']);
$height = max(80, min(800, (int) ($instance['height'] ?? 220)));
$height = max(80, min(800, (int) ($instance['height'] ?? $instance['pc_height'] ?? 400)));
$m_height = max(60, min(800, (int) ($instance['m_height'] ?? $height)));
$effect = sanitize_key((string) ($instance['effect'] ?? 'slide'));
if (!in_array($effect, ['slide', 'fade', 'cube', 'coverflow', 'flip'], true)) {
$effect = 'slide';
}
// Zibll CFS_Module::slide() options parity.
$direction = sanitize_key((string) ($instance['direction'] ?? 'horizontal'));
if (!in_array($direction, ['horizontal', 'vertical'], true)) {
$direction = 'horizontal';
}
$autoplay = !isset($instance['autoplay']) || !empty($instance['autoplay']);
$speed = max(0, min(3000, (int) ($instance['speed'] ?? 0)));
$spacebetween= max(0, min(500, (int) ($instance['spacebetween'] ?? 15)));
$scale_height= !empty($instance['scale_height']) || !isset($instance['scale_height']);
$scale = max(10, min(300, (int) ($instance['scale'] ?? 35)));
$slide_payload = [];
foreach ($slides as $slide) {
@@ -75,10 +85,15 @@ class WidgetSlider extends \WP_Widget {
'slides' => $slide_payload,
'effect' => $effect,
'loop' => $loop,
'autoplay' => true,
'autoplay' => $autoplay,
'interval' => $interval * 1000,
'speed' => $speed,
'direction' => $direction,
'spaceBetween' => $spacebetween,
'pc_height' => $height . 'px',
'm_height' => $m_height . 'px',
'scale_height' => $scale_height && $direction !== 'vertical',
'scale_ratio' => $scale / 100,
'show_nav' => $nav,
'show_pagination' => $dots,
'slider_class' => 'slide-widget',
@@ -107,9 +122,13 @@ class WidgetSlider extends \WP_Widget {
public function form($instance): void {
$title = (string) ($instance['title'] ?? '');
$interval = (int) ($instance['interval'] ?? 4);
$height = (int) ($instance['height'] ?? 220);
$height = (int) ($instance['height'] ?? $instance['pc_height'] ?? 400);
$m_height = (int) ($instance['m_height'] ?? $height);
$effect = sanitize_key((string) ($instance['effect'] ?? 'slide'));
$direction = sanitize_key((string) ($instance['direction'] ?? 'horizontal'));
$speed = (int) ($instance['speed'] ?? 0);
$spacebetween = (int) ($instance['spacebetween'] ?? 15);
$scale = (int) ($instance['scale'] ?? 35);
$slides = $this->normalize_slides($instance['slides'] ?? []);
if (!$slides) {
$slides = [
@@ -124,18 +143,11 @@ class WidgetSlider extends \WP_Widget {
</label>
</p>
<p>
<label><?php esc_html_e('切换间隔(秒)', 'aksrt'); ?>
<input class="tiny-text" type="number" min="2" max="20" name="<?php echo esc_attr($this->get_field_name('interval')); ?>" value="<?php echo esc_attr((string) $interval); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('PC 高度(px)', 'aksrt'); ?>
<input class="tiny-text" type="number" min="80" max="800" name="<?php echo esc_attr($this->get_field_name('height')); ?>" value="<?php echo esc_attr((string) $height); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('移动端高度(px)', 'aksrt'); ?>
<input class="tiny-text" type="number" min="60" max="800" name="<?php echo esc_attr($this->get_field_name('m_height')); ?>" value="<?php echo esc_attr((string) $m_height); ?>">
<label><?php esc_html_e('幻灯片方向', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('direction')); ?>">
<option value="horizontal" <?php selected($direction, 'horizontal'); ?>><?php esc_html_e('左右切换', 'aksrt'); ?></option>
<option value="vertical" <?php selected($direction, 'vertical'); ?>><?php esc_html_e('上下切换', 'aksrt'); ?></option>
</select>
</label>
</p>
<p>
@@ -149,10 +161,38 @@ class WidgetSlider extends \WP_Widget {
</select>
</label>
</p>
<p>
<label><?php esc_html_e('PC 高度(px)', 'aksrt'); ?>
<input class="tiny-text" type="number" min="80" max="800" name="<?php echo esc_attr($this->get_field_name('height')); ?>" value="<?php echo esc_attr((string) $height); ?>">
</label>
<label><?php esc_html_e('移动端高度(px)', 'aksrt'); ?>
<input class="tiny-text" type="number" min="60" max="800" name="<?php echo esc_attr($this->get_field_name('m_height')); ?>" value="<?php echo esc_attr((string) $m_height); ?>">
</label>
</p>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('scale_height')); ?>" value="1" <?php checked(!isset($instance['scale_height']) || !empty($instance['scale_height'])); ?>> <?php esc_html_e('按比例自动高度', 'aksrt'); ?></label>
<label><?php esc_html_e('长宽比例 %', 'aksrt'); ?>
<input class="tiny-text" type="number" min="10" max="300" step="5" name="<?php echo esc_attr($this->get_field_name('scale')); ?>" value="<?php echo esc_attr((string) $scale); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('间距(px)', 'aksrt'); ?>
<input class="tiny-text" type="number" min="0" max="500" step="5" name="<?php echo esc_attr($this->get_field_name('spacebetween')); ?>" value="<?php echo esc_attr((string) $spacebetween); ?>">
</label>
<label><?php esc_html_e('切换速度(ms,0=自动)', 'aksrt'); ?>
<input class="tiny-text" type="number" min="0" max="3000" step="100" name="<?php echo esc_attr($this->get_field_name('speed')); ?>" value="<?php echo esc_attr((string) $speed); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('停顿时间(秒)', 'aksrt'); ?>
<input class="tiny-text" type="number" min="0" max="20" name="<?php echo esc_attr($this->get_field_name('interval')); ?>" value="<?php echo esc_attr((string) $interval); ?>">
</label>
</p>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('loop')); ?>" value="1" <?php checked(!empty($instance['loop']) || !isset($instance['loop'])); ?>> <?php esc_html_e('循环播放', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('button')); ?>" value="1" <?php checked(!empty($instance['button']) || !isset($instance['button'])); ?>> <?php esc_html_e('翻页按钮', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('pagination')); ?>" value="1" <?php checked(!empty($instance['pagination']) || !isset($instance['pagination'])); ?>> <?php esc_html_e('指示点', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('autoplay')); ?>" value="1" <?php checked(!isset($instance['autoplay']) || !empty($instance['autoplay'])); ?>> <?php esc_html_e('自动播放', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('blank')); ?>" value="1" <?php checked(!empty($instance['blank'])); ?>> <?php esc_html_e('新窗口打开', 'aksrt'); ?></label>
</p>
<?php foreach ($slides as $i => $slide) : ?>
@@ -188,6 +228,7 @@ class WidgetSlider extends \WP_Widget {
}
?>
<p class="description"><?php esc_html_e('使用主题共享 Swiper(.new-swiper)。图片 URL 必填;仅 1 张时不滑动。', 'aksrt'); ?></p>
<p class="description"><?php esc_html_e('对齐 Zibll 幻灯片:方向/效果/比例自动高度/间距/速度/自动播放/间隔。视频背景与图层视差由共享 Swiper 栈支持。', 'aksrt'); ?></p>
<?php
$this->render_common_form_section($instance);
}
@@ -198,17 +239,27 @@ class WidgetSlider extends \WP_Widget {
if (!in_array($effect, ['slide', 'fade', 'cube', 'coverflow', 'flip'], true)) {
$effect = 'slide';
}
$direction = sanitize_key((string) ($new_instance['direction'] ?? 'horizontal'));
if (!in_array($direction, ['horizontal', 'vertical'], true)) {
$direction = 'horizontal';
}
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'interval' => max(2, min(20, (int) ($new_instance['interval'] ?? 4))),
'height' => max(80, min(800, (int) ($new_instance['height'] ?? 220))),
'm_height' => max(60, min(800, (int) ($new_instance['m_height'] ?? ($new_instance['height'] ?? 220)))),
'effect' => $effect,
'loop' => !empty($new_instance['loop']) ? 1 : 0,
'button' => !empty($new_instance['button']) ? 1 : 0,
'pagination' => !empty($new_instance['pagination']) ? 1 : 0,
'blank' => !empty($new_instance['blank']) ? 1 : 0,
'slides' => $slides,
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'interval' => max(0, min(20, (int) ($new_instance['interval'] ?? 4))),
'height' => max(80, min(800, (int) ($new_instance['height'] ?? $new_instance['pc_height'] ?? 400))),
'm_height' => max(60, min(800, (int) ($new_instance['m_height'] ?? ($new_instance['height'] ?? $new_instance['pc_height'] ?? 400)))),
'effect' => $effect,
'direction' => $direction,
'speed' => max(0, min(3000, (int) ($new_instance['speed'] ?? 0))),
'spacebetween'=> max(0, min(500, (int) ($new_instance['spacebetween'] ?? 15))),
'scale_height'=> !empty($new_instance['scale_height']) ? 1 : 0,
'scale' => max(10, min(300, (int) ($new_instance['scale'] ?? 35))),
'loop' => !empty($new_instance['loop']) ? 1 : 0,
'button' => !empty($new_instance['button']) ? 1 : 0,
'pagination' => !empty($new_instance['pagination']) ? 1 : 0,
'autoplay' => !empty($new_instance['autoplay']) ? 1 : 0,
'blank' => !empty($new_instance['blank']) ? 1 : 0,
'slides' => $slides,
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
+60 -2
View File
@@ -34,6 +34,11 @@ class WidgetUserCard extends \WP_Widget {
$loged_title = (string) ($instance['loged_title'] ?? __('Hi!请登录', 'aksrt'));
$btn1_text = (string) ($instance['button_1_text'] ?? __('发布文章', 'aksrt'));
$btn2_text = (string) ($instance['button_2_text'] ?? __('用户中心', 'aksrt'));
// Zibll button color palettes (button_1_class / button_2_class).
$btn1_class = sanitize_html_class((string) ($instance['button_1_class'] ?? 'jb-pink')) ?: 'jb-pink';
$btn2_class = sanitize_html_class((string) ($instance['button_2_class'] ?? 'jb-blue')) ?: 'jb-blue';
// Zibll button types: button_1 = 'post' (compose); button_2 ∈ {home,center}.
$btn2_type = (string) ($instance['button_2'] ?? 'center');
$post_style = sanitize_key((string) ($instance['post_style'] ?? 'mini'));
if (!in_array($post_style, ['card', 'mini'], true)) {
$post_style = 'mini';
@@ -84,6 +89,8 @@ class WidgetUserCard extends \WP_Widget {
$center_url = function_exists('aksrt_get_user_center_url')
? aksrt_get_user_center_url()
: $profile_url;
// Zibll button_2 type: home → profile, center → user center.
$btn2_url = $btn2_type === 'home' ? $profile_url : $center_url;
$compose_url = home_url('/');
if (function_exists('get_pages')) {
$pages = get_pages(['meta_key' => '_wp_page_template', 'meta_value' => 'templates/pages/page-submit.php', 'number' => 1]);
@@ -159,8 +166,8 @@ class WidgetUserCard extends \WP_Widget {
}
if ($show_actions && $is_self) {
echo '<div class="aksrt-user-card__actions user-btns">';
echo '<a class="aksrt-user-card__btn aksrt-user-card__btn--primary but jb-pink" href="' . esc_url($compose_url) . '">' . esc_html($btn1_text) . '</a>';
echo '<a class="aksrt-user-card__btn aksrt-user-card__btn--ghost but jb-blue" href="' . esc_url($center_url) . '">' . esc_html($btn2_text) . '</a>';
echo '<a class="aksrt-user-card__btn aksrt-user-card__btn--primary but ' . esc_attr($btn1_class) . '" href="' . esc_url($compose_url) . '">' . esc_html($btn1_text) . '</a>';
echo '<a class="aksrt-user-card__btn aksrt-user-card__btn--ghost but ' . esc_attr($btn2_class) . '" href="' . esc_url($btn2_url) . '">' . esc_html($btn2_text) . '</a>';
echo '</div>';
} elseif ($show_actions) {
echo '<div class="aksrt-user-card__actions"><a class="aksrt-user-card__btn aksrt-user-card__btn--primary" href="' . esc_url($profile_url) . '">' . esc_html__('查看主页', 'aksrt') . '</a></div>';
@@ -254,11 +261,31 @@ class WidgetUserCard extends \WP_Widget {
<label><?php esc_html_e('按钮1文案', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('button_1_text')); ?>" value="<?php echo esc_attr((string) ($instance['button_1_text'] ?? __('发布文章', 'aksrt'))); ?>">
</label>
<label><?php esc_html_e('按钮1颜色', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('button_1_class')); ?>">
<?php foreach (self::palette_opts() as $k => $label) : ?>
<option value="<?php echo esc_attr($k); ?>" <?php selected(($instance['button_1_class'] ?? 'jb-pink'), $k); ?>><?php echo esc_html($label); ?></option>
<?php endforeach; ?>
</select>
</label>
</p>
<p>
<label><?php esc_html_e('按钮2类型', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('button_2')); ?>">
<option value="home" <?php selected(($instance['button_2'] ?? 'center'), 'home'); ?>><?php esc_html_e('个人主页', 'aksrt'); ?></option>
<option value="center" <?php selected(($instance['button_2'] ?? 'center'), 'center'); ?>><?php esc_html_e('用户中心', 'aksrt'); ?></option>
</select>
</label>
<label><?php esc_html_e('按钮2文案', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('button_2_text')); ?>" value="<?php echo esc_attr((string) ($instance['button_2_text'] ?? __('用户中心', 'aksrt'))); ?>">
</label>
<label><?php esc_html_e('按钮2颜色', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('button_2_class')); ?>">
<?php foreach (self::palette_opts() as $k => $label) : ?>
<option value="<?php echo esc_attr($k); ?>" <?php selected(($instance['button_2_class'] ?? 'jb-blue'), $k); ?>><?php echo esc_html($label); ?></option>
<?php endforeach; ?>
</select>
</label>
</p>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_img_bg')); ?>" value="1" <?php checked(!isset($instance['show_img_bg']) || !empty($instance['show_img_bg'])); ?>> <?php esc_html_e('显示封面', 'aksrt'); ?></label><br>
@@ -307,8 +334,12 @@ class WidgetUserCard extends \WP_Widget {
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'user_id' => max(0, (int) ($new_instance['user_id'] ?? 0)),
'loged_title' => sanitize_text_field((string) ($new_instance['loged_title'] ?? '')),
'button_1' => 'post',
'button_1_text' => sanitize_text_field((string) ($new_instance['button_1_text'] ?? '')),
'button_1_class'=> self::sanitize_palette((string) ($new_instance['button_1_class'] ?? 'jb-pink'), 'jb-pink'),
'button_2' => in_array((string) ($new_instance['button_2'] ?? 'center'), ['home', 'center'], true) ? $new_instance['button_2'] : 'center',
'button_2_text' => sanitize_text_field((string) ($new_instance['button_2_text'] ?? '')),
'button_2_class'=> self::sanitize_palette((string) ($new_instance['button_2_class'] ?? 'jb-blue'), 'jb-blue'),
'show_img_bg' => !empty($new_instance['show_img_bg']) ? 1 : 0,
'show_stats' => !empty($new_instance['show_stats']) ? 1 : 0,
'show_button' => !empty($new_instance['show_button']) ? 1 : 0,
@@ -319,4 +350,31 @@ class WidgetUserCard extends \WP_Widget {
'orderby' => $orderby,
]);
}
/** Zibll CFS_Module::zib_palette() gradient/solid keys (c/b/jb families). */
public static function palette_opts(): array {
return [
'jb-red' => __('红', 'aksrt'),
'jb-yellow' => __('橙', 'aksrt'),
'jb-pink' => __('粉', 'aksrt'),
'jb-cyan' => __('青', 'aksrt'),
'jb-blue' => __('蓝', 'aksrt'),
'jb-green' => __('绿', 'aksrt'),
'jb-purple' => __('紫', 'aksrt'),
'jb-vip1' => __('会员金', 'aksrt'),
'jb-vip2' => __('会员灰', 'aksrt'),
'b-red' => __('红(纯色)', 'aksrt'),
'b-yellow' => __('橙(纯色)', 'aksrt'),
'b-cyan' => __('青(纯色)', 'aksrt'),
'b-blue' => __('蓝(纯色)', 'aksrt'),
'b-green' => __('绿(纯色)', 'aksrt'),
'b-purple' => __('紫(纯色)', 'aksrt'),
'b-black' => __('黑(纯色)', 'aksrt'),
];
}
public static function sanitize_palette(string $value, string $fallback): string {
$value = sanitize_html_class($value);
return array_key_exists($value, self::palette_opts()) ? $value : $fallback;
}
}
+42 -22
View File
@@ -27,8 +27,21 @@ class WidgetUserRanking extends \WP_Widget {
public function widget($args, $instance): void {
$title = !empty($instance['title']) ? (string) $instance['title'] : __('排行榜', 'aksrt');
$rank_by = !empty($instance['rank_by']) ? (string) $instance['rank_by'] : 'credits';
$count = max(1, min(20, (int) ($instance['count'] ?? 5)));
// Zibll zib_widget_ui_user_ranking: `orderby` (default checkin_all_day) + `number`.
// Accept AKSRT legacy `rank_by`/`count` as aliases.
$rank_by = (string) ($instance['orderby'] ?? $instance['rank_by'] ?? 'checkin_all_day');
// Map Zibll `points` → AKSRT `credits`; `followed-user-count` → `followers`.
$rank_aliases = [
'points' => 'credits',
'followed-user-count' => 'followers',
'checkin_all_day' => 'checkin_all_day',
'checkin_continuous_day' => 'checkin_continuous_day',
'credits' => 'credits',
'posts' => 'posts',
'followers' => 'followers',
];
$rank_by = $rank_aliases[$rank_by] ?? 'checkin_all_day';
$count = max(1, min(20, (int) ($instance['number'] ?? $instance['count'] ?? 6)));
$show_avatar = !isset($instance['show_avatar']) || !empty($instance['show_avatar']);
$desc_mode = sanitize_key((string) ($instance['desc'] ?? 'desc'));
$top_badge = !empty($instance['top_badge']);
@@ -292,8 +305,9 @@ class WidgetUserRanking extends \WP_Widget {
public function form($instance): void {
$title = !empty($instance['title']) ? $instance['title'] : __('排行榜', 'aksrt');
$rank_by = !empty($instance['rank_by']) ? $instance['rank_by'] : 'credits';
$count = !empty($instance['count']) ? (int) $instance['count'] : 5;
// Zibll field is `orderby` (default checkin_all_day); accept legacy `rank_by`.
$rank_by = (string) ($instance['orderby'] ?? $instance['rank_by'] ?? 'checkin_all_day');
$count = !empty($instance['number']) ? (int) $instance['number'] : (!empty($instance['count']) ? (int) $instance['count'] : 6);
$show_avatar = !isset($instance['show_avatar']) || !empty($instance['show_avatar']);
?>
<p>
@@ -301,18 +315,17 @@ class WidgetUserRanking extends \WP_Widget {
<input class="widefat" id="<?php echo esc_attr($this->get_field_id('title')); ?>" name="<?php echo esc_attr($this->get_field_name('title')); ?>" type="text" value="<?php echo esc_attr($title); ?>">
</p>
<p>
<label for="<?php echo esc_attr($this->get_field_id('rank_by')); ?>"><?php esc_html_e('排行依据:', 'aksrt'); ?></label>
<select class="widefat" id="<?php echo esc_attr($this->get_field_id('rank_by')); ?>" name="<?php echo esc_attr($this->get_field_name('rank_by')); ?>">
<option value="credits" <?php selected($rank_by, 'credits'); ?>><?php esc_html_e('积分', 'aksrt'); ?></option>
<option value="posts" <?php selected($rank_by, 'posts'); ?>><?php esc_html_e('文章数', 'aksrt'); ?></option>
<option value="followers" <?php selected($rank_by, 'followers'); ?>><?php esc_html_e('粉丝数', 'aksrt'); ?></option>
<option value="checkin_all_day" <?php selected($rank_by, 'checkin_all_day'); ?>><?php esc_html_e('累计签到', 'aksrt'); ?></option>
<option value="checkin_continuous_day" <?php selected($rank_by, 'checkin_continuous_day'); ?>><?php esc_html_e('连续签到', 'aksrt'); ?></option>
<label for="<?php echo esc_attr($this->get_field_id('orderby')); ?>"><?php esc_html_e('榜单类型:', 'aksrt'); ?></label>
<select class="widefat" id="<?php echo esc_attr($this->get_field_id('orderby')); ?>" name="<?php echo esc_attr($this->get_field_name('orderby')); ?>">
<option value="checkin_all_day" <?php selected($rank_by, 'checkin_all_day'); ?>><?php esc_html_e('累计签到天数', 'aksrt'); ?></option>
<option value="checkin_continuous_day" <?php selected($rank_by, 'checkin_continuous_day'); ?>><?php esc_html_e('连续签到天数', 'aksrt'); ?></option>
<option value="points" <?php selected($rank_by, 'points'); ?>><?php esc_html_e('用户积分', 'aksrt'); ?></option>
<option value="followed-user-count" <?php selected($rank_by, 'followed-user-count'); ?>><?php esc_html_e('粉丝数量', 'aksrt'); ?></option>
</select>
</p>
<p>
<label for="<?php echo esc_attr($this->get_field_id('count')); ?>"><?php esc_html_e('显示数量:', 'aksrt'); ?></label>
<input class="tiny-text" id="<?php echo esc_attr($this->get_field_id('count')); ?>" name="<?php echo esc_attr($this->get_field_name('count')); ?>" type="number" value="<?php echo esc_attr((string) $count); ?>" min="1" max="20">
<label for="<?php echo esc_attr($this->get_field_id('number')); ?>"><?php esc_html_e('最大显示数量:', 'aksrt'); ?></label>
<input class="tiny-text" id="<?php echo esc_attr($this->get_field_id('number')); ?>" name="<?php echo esc_attr($this->get_field_name('number')); ?>" type="number" value="<?php echo esc_attr((string) $count); ?>" min="1" max="20">
</p>
<p>
<label>
@@ -324,21 +337,21 @@ class WidgetUserRanking extends \WP_Widget {
<label for="<?php echo esc_attr($this->get_field_id('desc')); ?>"><?php esc_html_e('附加显示:', 'aksrt'); ?></label>
<select class="widefat" id="<?php echo esc_attr($this->get_field_id('desc')); ?>" name="<?php echo esc_attr($this->get_field_name('desc')); ?>">
<option value="desc" <?php selected(($instance['desc'] ?? 'desc'), 'desc'); ?>><?php esc_html_e('用户签名', 'aksrt'); ?></option>
<option value="user_registered" <?php selected(($instance['desc'] ?? ''), 'user_registered'); ?>><?php esc_html_e('加入天数', 'aksrt'); ?></option>
<option value="last_login" <?php selected(($instance['desc'] ?? ''), 'last_login'); ?>><?php esc_html_e('最后登录', 'aksrt'); ?></option>
<option value="last_checkin" <?php selected(($instance['desc'] ?? ''), 'last_checkin'); ?>><?php esc_html_e('最后签到', 'aksrt'); ?></option>
<option value="user_registered" <?php selected(($instance['desc'] ?? ''), 'user_registered'); ?>><?php esc_html_e('加入本站多少天', 'aksrt'); ?></option>
<option value="last_login" <?php selected(($instance['desc'] ?? ''), 'last_login'); ?>><?php esc_html_e('最后登录时间', 'aksrt'); ?></option>
<option value="last_checkin" <?php selected(($instance['desc'] ?? ''), 'last_checkin'); ?>><?php esc_html_e('最后签到时间', 'aksrt'); ?></option>
</select>
</p>
<p>
<label>
<input type="checkbox" name="<?php echo esc_attr($this->get_field_name('checkin_btn')); ?>" value="1" <?php checked(!empty($instance['checkin_btn'])); ?>>
<?php esc_html_e('顶部显示签到按钮', 'aksrt'); ?>
<input type="checkbox" name="<?php echo esc_attr($this->get_field_name('top_badge')); ?>" value="1" <?php checked(!empty($instance['top_badge'])); ?>>
<?php esc_html_e('显示排行徽章', 'aksrt'); ?>
</label>
</p>
<p>
<label>
<input type="checkbox" name="<?php echo esc_attr($this->get_field_name('top_badge')); ?>" value="1" <?php checked(!empty($instance['top_badge'])); ?>>
<?php esc_html_e('前三名 TOP 角标', 'aksrt'); ?>
<input type="checkbox" name="<?php echo esc_attr($this->get_field_name('checkin_btn')); ?>" value="1" <?php checked(!empty($instance['checkin_btn'])); ?>>
<?php esc_html_e('显示签到按钮', 'aksrt'); ?>
</label>
</p>
<?php $this->render_common_form_section($instance); ?>
@@ -346,10 +359,17 @@ class WidgetUserRanking extends \WP_Widget {
}
public function update($new_instance, $old_instance): array {
$rank_by = (string) ($new_instance['orderby'] ?? $new_instance['rank_by'] ?? 'checkin_all_day');
$allowed = ['checkin_all_day', 'checkin_continuous_day', 'points', 'followed-user-count', 'credits', 'posts', 'followers'];
if (!in_array($rank_by, $allowed, true)) {
$rank_by = 'checkin_all_day';
}
return $this->update_common_fields($new_instance, $old_instance, [
'title' => sanitize_text_field($new_instance['title'] ?? ''),
'rank_by' => in_array($new_instance['rank_by'] ?? '', ['credits', 'posts', 'followers', 'checkin_all_day', 'checkin_continuous_day'], true) ? $new_instance['rank_by'] : 'credits',
'count' => max(1, min(20, (int) ($new_instance['count'] ?? 5))),
'orderby' => $rank_by,
'rank_by' => $rank_by, // keep alias for any legacy readers.
'number' => max(1, min(20, (int) ($new_instance['number'] ?? $new_instance['count'] ?? 6))),
'count' => max(1, min(20, (int) ($new_instance['number'] ?? $new_instance['count'] ?? 6))),
'show_avatar' => !empty($new_instance['show_avatar']) ? 1 : 0,
'checkin_btn' => !empty($new_instance['checkin_btn']) ? 1 : 0,
'top_badge' => !empty($new_instance['top_badge']) ? 1 : 0,