feat(widgets): simplify locations and paid sidebar

Remove deprecated widget locations and widgets, standardize sidebar labels, and render a compact paid-post purchase card in the article sidebar.
This commit is contained in:
kate522 committed 2026-08-14 01:49:50 +08:00
1 parent 51d2ab3aa7
commit bd7d3046a5
43 files changed
+294 -3581

No files matched your search

-11
View File
@@ -380,17 +380,6 @@ trait ScriptsPart1c {
}
// Frontend translate (默认关)
$translate_js = get_template_directory() . '/assets/js/frontend-translate.js';
if (file_exists($translate_js)) {
wp_enqueue_script(
'aksrt-frontend-translate',
$base_uri . '/assets/js/frontend-translate.js',
[],
(string) filemtime($translate_js),
true
);
}
// Content protection (copy notice / right-click / F12)
$content_protect_js = get_template_directory() . '/assets/js/content-protect.js';
if (file_exists($content_protect_js)) {
+26 -57
View File
@@ -58,7 +58,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'all_footer',
'name' => __('全站页脚小工具', 'aksrt'),
'name' => __('全站 - 页脚小工具', 'aksrt'),
'description' => __('显示在页脚内部的统一小工具区域。', 'aksrt'),
'before_widget' => $card_wrapper_before,
'after_widget' => '</div>',
@@ -68,7 +68,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'mobile_nav_fluid',
'name' => __('移动端菜单底部', 'aksrt'),
'name' => __('移动端 - 菜单底部', 'aksrt'),
'description' => __('显示在移动端抽屉菜单底部。', 'aksrt'),
'before_widget' => $card_wrapper_before,
'after_widget' => '</div>',
@@ -118,7 +118,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'home_sidebar',
'name' => __('首页侧栏', 'aksrt'),
'name' => __('首页 - 侧边栏', 'aksrt'),
'description' => __('显示在首页右侧栏。', 'aksrt'),
'before_widget' => $card_wrapper_before,
'after_widget' => '</div>',
@@ -126,21 +126,10 @@ trait SetupPart1 {
'after_title' => $title_after,
]);
// 新前端使用的侧边栏 (sidebar.php)
register_sidebar([
'id' => 'sidebar-blog',
'name' => __('博客侧边栏', 'aksrt'),
'description' => __('显示在首页、归档等列表页右侧(新前端)。', 'aksrt'),
'before_widget' => $card_wrapper_before,
'after_widget' => '</div>',
'before_title' => $title_before,
'after_title' => $title_after,
]);
// 文章详情页侧栏
register_sidebar([
'id' => 'sidebar-article',
'name' => __('文章侧边栏', 'aksrt'),
'name' => __('文章页 - 侧边栏', 'aksrt'),
'description' => __('显示在文章详情页右侧。', 'aksrt'),
'before_widget' => $card_wrapper_before,
'after_widget' => '</div>',
@@ -151,7 +140,7 @@ trait SetupPart1 {
// ===== 文章页 Widget 区域(模板zibll 多位置) =====
register_sidebar([
'id' => 'single_top_fluid',
'name' => __('文章页 · 顶部全宽', 'aksrt'),
'name' => __('文章页 - 顶部全宽', 'aksrt'),
'description' => __('文章页顶部全宽区域(在封面/面包屑之上)。', 'aksrt'),
'before_widget' => $fluid_wrapper_before,
'after_widget' => '</div>',
@@ -161,7 +150,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'single_top_content',
'name' => __('文章页 · 内容上方', 'aksrt'),
'name' => __('文章页 - 内容上方', 'aksrt'),
'description' => __('文章主体卡片上方(在内容列内)。', 'aksrt'),
'before_widget' => $card_wrapper_before,
'after_widget' => '</div>',
@@ -171,7 +160,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'single_bottom_content',
'name' => __('文章页 · 内容下方', 'aksrt'),
'name' => __('文章页 - 内容下方', 'aksrt'),
'description' => __('评论区下方(在内容列内)。', 'aksrt'),
'before_widget' => $card_wrapper_before,
'after_widget' => '</div>',
@@ -181,7 +170,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'single_bottom_fluid',
'name' => __('文章页 · 底部全宽', 'aksrt'),
'name' => __('文章页 - 底部全宽', 'aksrt'),
'description' => __('文章页底部全宽区域(在页脚之上)。', 'aksrt'),
'before_widget' => $fluid_wrapper_before,
'after_widget' => '</div>',
@@ -189,30 +178,10 @@ trait SetupPart1 {
'after_title' => $title_after,
]);
register_sidebar([
'id' => 'sidebar-footer-1',
'name' => __('页脚栏 1', 'aksrt'),
'description' => '',
'before_widget' => '<div class="footer-widget %2$s">',
'after_widget' => '</div>',
'before_title' => '<h4 class="footer-widget-title">',
'after_title' => '</h4>',
]);
register_sidebar([
'id' => 'sidebar-footer-2',
'name' => __('页脚栏 2', 'aksrt'),
'description' => '',
'before_widget' => '<div class="footer-widget %2$s">',
'after_widget' => '</div>',
'before_title' => '<h4 class="footer-widget-title">',
'after_title' => '</h4>',
]);
// ===== 分类页 Widget 区域 =====
register_sidebar([
'id' => 'cat_top_fluid',
'name' => __('分类页 · 顶部全宽', 'aksrt'),
'name' => __('分类页 - 顶部全宽', 'aksrt'),
'description' => __('分类归档页顶部全宽区域。', 'aksrt'),
'before_widget' => $fluid_wrapper_before,
'after_widget' => '</div>',
@@ -222,7 +191,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'cat_top_content',
'name' => __('分类页 · 内容上方', 'aksrt'),
'name' => __('分类页 - 内容上方', 'aksrt'),
'description' => __('分类归档页内容区上方。', 'aksrt'),
'before_widget' => $card_wrapper_before,
'after_widget' => '</div>',
@@ -232,7 +201,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'cat_bottom_content',
'name' => __('分类页 · 内容下方', 'aksrt'),
'name' => __('分类页 - 内容下方', 'aksrt'),
'description' => __('分类归档页内容区下方。', 'aksrt'),
'before_widget' => $card_wrapper_before,
'after_widget' => '</div>',
@@ -242,7 +211,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'cat_bottom_fluid',
'name' => __('分类页 · 底部全宽', 'aksrt'),
'name' => __('分类页 - 底部全宽', 'aksrt'),
'description' => __('分类归档页底部全宽区域。', 'aksrt'),
'before_widget' => $fluid_wrapper_before,
'after_widget' => '</div>',
@@ -252,7 +221,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'cat_sidebar',
'name' => __('分类页 · 侧栏', 'aksrt'),
'name' => __('分类页 - 侧栏', 'aksrt'),
'description' => __('分类归档页侧栏区域。', 'aksrt'),
'before_widget' => $card_wrapper_before,
'after_widget' => '</div>',
@@ -263,7 +232,7 @@ trait SetupPart1 {
// ===== 标签页 Widget 区域 =====
register_sidebar([
'id' => 'tag_top_fluid',
'name' => __('标签页 · 顶部全宽', 'aksrt'),
'name' => __('标签页 - 顶部全宽', 'aksrt'),
'description' => __('标签归档页顶部全宽区域。', 'aksrt'),
'before_widget' => $fluid_wrapper_before,
'after_widget' => '</div>',
@@ -273,7 +242,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'tag_top_content',
'name' => __('标签页 · 内容上方', 'aksrt'),
'name' => __('标签页 - 内容上方', 'aksrt'),
'description' => __('标签归档页内容区上方。', 'aksrt'),
'before_widget' => $card_wrapper_before,
'after_widget' => '</div>',
@@ -283,7 +252,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'tag_bottom_content',
'name' => __('标签页 · 内容下方', 'aksrt'),
'name' => __('标签页 - 内容下方', 'aksrt'),
'description' => __('标签归档页内容区下方。', 'aksrt'),
'before_widget' => $card_wrapper_before,
'after_widget' => '</div>',
@@ -293,7 +262,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'tag_bottom_fluid',
'name' => __('标签页 · 底部全宽', 'aksrt'),
'name' => __('标签页 - 底部全宽', 'aksrt'),
'description' => __('标签归档页底部全宽区域。', 'aksrt'),
'before_widget' => $fluid_wrapper_before,
'after_widget' => '</div>',
@@ -303,7 +272,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'tag_sidebar',
'name' => __('标签页 · 侧栏', 'aksrt'),
'name' => __('标签页 - 侧栏', 'aksrt'),
'description' => __('标签归档页侧栏区域。', 'aksrt'),
'before_widget' => $card_wrapper_before,
'after_widget' => '</div>',
@@ -314,7 +283,7 @@ trait SetupPart1 {
// ===== 搜索页 Widget 区域 =====
register_sidebar([
'id' => 'search_top_fluid',
'name' => __('搜索页 · 顶部全宽', 'aksrt'),
'name' => __('搜索页 - 顶部全宽', 'aksrt'),
'description' => __('搜索结果页顶部全宽区域。', 'aksrt'),
'before_widget' => $fluid_wrapper_before,
'after_widget' => '</div>',
@@ -324,7 +293,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'search_top_content',
'name' => __('搜索页 · 内容上方', 'aksrt'),
'name' => __('搜索页 - 内容上方', 'aksrt'),
'description' => __('搜索结果页内容区上方。', 'aksrt'),
'before_widget' => $card_wrapper_before,
'after_widget' => '</div>',
@@ -334,7 +303,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'search_bottom_content',
'name' => __('搜索页 · 内容下方', 'aksrt'),
'name' => __('搜索页 - 内容下方', 'aksrt'),
'description' => __('搜索结果页内容区下方。', 'aksrt'),
'before_widget' => $card_wrapper_before,
'after_widget' => '</div>',
@@ -344,7 +313,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'search_bottom_fluid',
'name' => __('搜索页 · 底部全宽', 'aksrt'),
'name' => __('搜索页 - 底部全宽', 'aksrt'),
'description' => __('搜索结果页底部全宽区域。', 'aksrt'),
'before_widget' => $fluid_wrapper_before,
'after_widget' => '</div>',
@@ -354,7 +323,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'search_sidebar',
'name' => __('搜索页 · 侧栏', 'aksrt'),
'name' => __('搜索页 - 侧栏', 'aksrt'),
'description' => __('搜索结果页侧栏区域。', 'aksrt'),
'before_widget' => $card_wrapper_before,
'after_widget' => '</div>',
@@ -364,7 +333,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'archive_bottom_fluid',
'name' => __('归档页 · 底部全宽', 'aksrt'),
'name' => __('归档页 - 底部全宽', 'aksrt'),
'description' => __('分类/标签/归档页底部全宽区域。', 'aksrt'),
'before_widget' => $fluid_wrapper_before,
'after_widget' => '</div>',
@@ -374,7 +343,7 @@ trait SetupPart1 {
register_sidebar([
'id' => 'author-sidebar',
'name' => __('作者页 · 侧栏', 'aksrt'),
'name' => __('作者页 - 侧栏', 'aksrt'),
'description' => __('作者归档页侧栏区域。', 'aksrt'),
'before_widget' => $card_wrapper_before,
'after_widget' => '</div>',
@@ -441,7 +410,7 @@ trait SetupPart1 {
}
$id = 'page_' . $key . '_' . (int) $page->ID;
register_sidebar([
'name' => sprintf(__('[页面:%1$s]-%2$s', 'aksrt'), $name, $area_labels[$key]),
'name' => sprintf(__('[页面: %1$s] - %2$s', 'aksrt'), $name, $area_labels[$key]),
'id' => $id,
'description' => sprintf(__('显示在[页面:%1$s]的 %2$s 位置', 'aksrt'), $name, $area_labels[$key]),
'before_widget' => $key === 'sidebar' ? $card_wrapper_before : $fluid_wrapper_before,
-21
View File
@@ -27,7 +27,6 @@ class WidgetAjax {
$routes = [
'/widgets/posts' => 'rest_main_posts',
'/widgets/tab-posts' => 'rest_tab_posts',
'/widgets/paid-posts' => 'rest_paid_posts',
'/widgets/category-tabs' => 'rest_category_tabs',
'/widgets/category-block' => 'rest_category_block',
'/widgets/category-recommend' => 'rest_category_recommend',
@@ -123,26 +122,6 @@ class WidgetAjax {
], 200);
}
public function rest_paid_posts(\WP_REST_Request $request) {
if (!class_exists('\AKSRT\Widgets\WidgetPaidPosts')) {
return $this->fail();
}
$paginate = sanitize_key((string) ($request->get_param('paginate') ?? 'ajax'));
if (!in_array($paginate, ['none', 'ajax', 'number'], true)) {
$paginate = 'ajax';
}
$instance = [
'count' => max(1, min(20, (int) ($request->get_param('count') ?? 6))),
'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'date')),
'cat' => max(0, (int) ($request->get_param('cat') ?? 0)),
'limit_day' => max(0, (int) ($request->get_param('limit_day') ?? 0)),
'style' => sanitize_key((string) ($request->get_param('style') ?? 'mini')),
'paginate' => $paginate,
];
$page = max(1, (int) ($request->get_param('page') ?? $request->get_param('paged') ?? 1));
return $this->payload(\AKSRT\Widgets\WidgetPaidPosts::query_payload($instance, $page), $page);
}
public function rest_category_tabs(\WP_REST_Request $request) {
$cat = max(0, (int) ($request->get_param('cat') ?? 0));
$count = max(1, min(12, (int) ($request->get_param('count') ?? 6)));
-383
View File
@@ -1,383 +0,0 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class AboutWidget extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
'aksrt_about',
esc_html__('AKSRT 用户中心', 'aksrt'),
[
'description' => esc_html__('登录状态面板:未登录显示登录/注册按钮,已登录显示用户信息与快捷入口', 'aksrt'),
'customize_selective_refresh' => true,
]
);
}
/**
* 前端渲染
*/
public function widget($args, $instance): void {
echo $args['before_widget'];
$title = apply_filters('widget_title', $instance['title'] ?? '', $instance, $this->id_base);
if ($title) {
echo $args['before_title'] . esc_html($title) . $args['after_title'];
}
// 判断登录状态
if (is_user_logged_in()) {
$this->render_logged_in();
} else {
$this->render_logged_out($instance);
}
// 输出内联样式 & JS(仅在本 widget 实例加载时注入一次)
$this->inline_assets();
echo $args['after_widget'];
}
// ==========================================
// 已登录状态渲染(Zibll 风格用户卡片)
// ==========================================
private function render_logged_in(): void {
$u = wp_get_current_user();
$avatar = get_avatar_url($u->ID, ['size' => 160]);
$profile = aksrt_get_user_center_url();
$logout = aksrt_get_logout_url(home_url('/'));
// 封面图:用户在用户中心上传的背景图 > 默认背景图
$cover = get_user_meta($u->ID, 'aksrt_background_image', true);
if (! $cover) {
$cover = get_theme_file_uri('assets/img/person_background.png');
}
?>
<div class="aksrt-user-card">
<div class="aksrt-user-card__cover" style="padding-bottom: 50%;">
<img class="aksrt-user-card__cover-img aksrt-lazy lazyload" data-src="<?php echo esc_url($cover); ?>" src="<?php echo esc_url(get_template_directory_uri() . '/assets/img/placeholder.svg'); ?>" alt="" loading="lazy">
</div>
<div class="aksrt-user-card__content">
<div class="aksrt-user-card__avatar">
<img class="aksrt-lazy lazyload" data-src="<?php echo esc_url($avatar); ?>" src="<?php echo esc_url(get_template_directory_uri() . '/assets/img/placeholder.svg'); ?>" alt="" width="80" height="80" loading="lazy">
</div>
<div class="aksrt-user-card__info">
<div class="aksrt-user-card__name"><?php if (function_exists('aksrt_get_user_name')) { echo aksrt_get_user_name(['id' => (int) $u->ID, 'class' => 'flex ac', 'follow' => false, 'medal' => true, 'auth' => true, 'level' => true]); } else { echo esc_html($u->display_name); } ?></div>
<div class="aksrt-user-card__desc"><?php $bio = trim((string) get_user_meta($u->ID, 'description', true)); echo esc_html($bio !== '' ? $bio : $u->user_email); ?></div>
</div>
<div class="aksrt-user-card__btns">
<a class="aksrt-user-card__btn aksrt-user-card__btn--primary" href="<?php echo esc_url($profile); ?>">
<i class="fas fa-user" aria-hidden="true"></i> <?php esc_html_e('个人主页', 'aksrt'); ?>
</a>
<a class="aksrt-user-card__btn aksrt-user-card__btn--outline" href="<?php echo esc_url($logout); ?>">
<i class="fas fa-sign-out-alt" aria-hidden="true"></i> <?php esc_html_e('退出', 'aksrt'); ?>
</a>
</div>
</div>
</div>
<?php
}
// ==========================================
// 未登录状态渲染(Zibll 风格用户卡片)
// ==========================================
private function render_logged_out(array $instance = []): void {
$login_url = aksrt_get_login_url();
$register_url = aksrt_get_register_url();
// 封面图:后台配置 > 默认
$cover = ! empty($instance['cover']) ? $instance['cover'] : get_theme_file_uri('assets/img/widgets-original.png');
$avatar = get_avatar_url(0, ['size' => 160, 'default' => 'mystery']);
$oauth_html = '';
if (class_exists('\AKSRT\Auth\OAuth')) {
$oauth = \AKSRT\Auth\OAuth::get_instance();
if (method_exists($oauth, 'render_social_buttons')) {
$oauth_html = (string) $oauth->render_social_buttons(__('社交账号登录', 'aksrt'));
} elseif (method_exists($oauth, 'get_login_buttons')) {
$oauth_html = (string) $oauth->get_login_buttons();
}
}
?>
<div class="aksrt-user-card">
<div class="aksrt-user-card__cover" style="padding-bottom: 50%;">
<?php if ($cover) : ?>
<img class="aksrt-user-card__cover-img aksrt-lazy lazyload" data-src="<?php echo esc_url($cover); ?>" src="<?php echo esc_url(get_template_directory_uri() . '/assets/img/placeholder.svg'); ?>" alt="" loading="lazy">
<?php endif; ?>
</div>
<div class="aksrt-user-card__content">
<div class="aksrt-user-card__avatar">
<img class="aksrt-lazy lazyload" data-src="<?php echo esc_url($avatar); ?>" src="<?php echo esc_url(get_template_directory_uri() . '/assets/img/placeholder.svg'); ?>" alt="" width="80" height="80" loading="lazy">
</div>
<div class="aksrt-user-card__info">
<div class="aksrt-user-card__desc"><?php esc_html_e('Hi! 请登录', 'aksrt'); ?></div>
</div>
<div class="aksrt-user-card__btns">
<a class="aksrt-user-card__btn aksrt-user-card__btn--primary signin-loader" href="<?php echo esc_url($login_url); ?>"><?php esc_html_e('登 录', 'aksrt'); ?></a>
<?php if (get_option('users_can_register')) : ?>
<a class="aksrt-user-card__btn aksrt-user-card__btn--outline signup-loader" href="<?php echo esc_url($register_url); ?>"><?php esc_html_e('注 册', 'aksrt'); ?></a>
<?php endif; ?>
</div>
<?php if ($oauth_html) : ?>
<div class="aksrt-user-card__oauth">
<div class="aksrt-user-card__divider"><?php esc_html_e('社交账号登录', 'aksrt'); ?></div>
<div class="aksrt-user-card__social-icons">
<?php echo $oauth_html; ?>
</div>
</div>
<?php endif; ?>
</div>
</div>
<?php
}
// ==========================================
// 内联样式(Zibll 风格用户卡片,只输出一次)
// ==========================================
private function inline_assets(): void {
if (did_action('aksrt_user_card_assets')) {
return;
}
do_action('aksrt_user_card_assets');
?>
<style>
/* ========== AKSRT 用户卡片(Zibll 风格) ========== */
/*
* 消除"卡片套卡片":
* 外层 .widget-card 提供 background/shadow/border-radius/padding:15px
* 内层 .aksrt-user-card 用负 margin 把封面图扩展到外层边缘,
* 使封面图与外层卡片融为一体,不再有 15px 间距。
*/
.widget-card > .aksrt-user-card {
margin: -15px -15px 0;
overflow: hidden;
border-radius: var(--radius-card, 8px) var(--radius-card, 8px) 0 0;
}
/* 有标题时:取消标题下方的 margin-bottom,因为卡片已无额外间距 */
.widget.aksrt_about.widget-card > .widget-title + .aksrt-user-card {
margin-top: -5px;
}
/* ---- 封面图 ---- */
.aksrt-user-card__cover {
position: relative;
overflow: hidden;
background: linear-gradient(135deg, var(--color-primary), var(--color-primary-dark, #4f46e5));
}
.aksrt-user-card__cover-img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
/* ---- 内容区 — 悬浮在封面上方 ---- */
.aksrt-user-card__content {
padding: 15px;
margin-top: -55px;
position: relative;
text-align: center;
}
/* ---- 头像(绝对居中) ---- */
.aksrt-user-card__avatar {
text-align: center;
line-height: 0;
}
.aksrt-user-card__avatar img {
display: block;
width: 80px;
height: 80px;
margin: 0 auto;
border-radius: 50%;
border: 3px solid var(--color-primary);
object-fit: cover;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
/* ---- 用户信息 ---- */
.aksrt-user-card__info {
margin-top: 12px;
text-align: center;
}
.aksrt-user-card__name {
font-size: 1.1em;
font-weight: bold;
color: var(--color-ink, var(--color-text));
}
.aksrt-user-card__desc {
font-size: 13px;
color: var(--color-text-muted, #9ca3af);
margin-top: 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* ---- 按钮组 ---- */
.aksrt-user-card__btns {
display: flex;
gap: 10px;
justify-content: center;
margin-top: 16px;
}
.aksrt-user-card__btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
padding: 8px 20px;
border-radius: 20px;
font-size: 13px;
font-weight: 600;
text-decoration: none;
transition: opacity 0.2s ease, transform 0.15s ease;
flex: 1;
max-width: 140px;
}
.aksrt-user-card__btn:hover {
opacity: 0.88;
transform: translateY(-1px);
}
.aksrt-user-card__btn:active {
transform: translateY(0);
}
.aksrt-user-card__btn--primary {
background: var(--color-primary);
color: #fff;
}
.aksrt-user-card__btn--outline {
background: var(--color-primary-softer, rgba(136,171,218,0.05));
color: var(--color-primary);
border: 1px solid var(--color-primary-50, rgba(136,171,218,0.12));
}
/* ---- OAuth 社交登录 ---- */
.aksrt-user-card__oauth {
margin-top: 12px;
}
.aksrt-user-card__divider {
font-size: 12px;
color: var(--color-text-muted, #9ca3af);
margin-bottom: 10px;
position: relative;
}
.aksrt-user-card__divider::before,
.aksrt-user-card__divider::after {
content: '';
position: absolute;
top: 50%;
width: 28%;
height: 1px;
background: var(--color-border, #e5e7eb);
}
.aksrt-user-card__divider::before { left: 0; }
.aksrt-user-card__divider::after { right: 0; }
.aksrt-user-card__social-icons {
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: center;
}
.aksrt-user-card__social-icons a {
display: inline-flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
border-radius: 50%;
font-size: 15px;
color: var(--color-text-muted, #6b7280);
background: var(--color-bg-tertiary, #f3f4f6);
text-decoration: none;
transition: background 0.18s ease, color 0.18s ease;
}
.aksrt-user-card__social-icons a:hover {
background: var(--color-primary);
color: #fff;
}
/* ---- 暗色适配 ---- */
html.dark .aksrt-user-card__avatar img,
body.theme-dark .aksrt-user-card__avatar img {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
html.dark .aksrt-user-card__social-icons a,
body.theme-dark .aksrt-user-card__social-icons a {
background: rgba(255, 255, 255, 0.08);
}
html.dark .aksrt-user-card__social-icons a:hover,
body.theme-dark .aksrt-user-card__social-icons a:hover {
background: var(--color-primary);
}
/* ---- 移动端 ---- */
@media screen and (max-width: 480px) {
.aksrt-user-card__content {
margin-top: -45px;
}
.aksrt-user-card__avatar img {
width: 68px;
height: 68px;
}
}
</style>
<?php
}
// ==========================================
// 后台表单
// ==========================================
public function form($instance): void {
$title = $instance['title'] ?? '';
$cover = $instance['cover'] ?? '';
$default_cover = get_theme_file_uri('assets/img/widgets-original.png');
?>
<p>
<label for="<?php echo $this->get_field_id('title'); ?>"><?php esc_html_e('标题(可选):', 'aksrt'); ?></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); ?>">
</p>
<p>
<label for="<?php echo $this->get_field_id('cover'); ?>"><?php esc_html_e('未登录背景图 URL:', 'aksrt'); ?></label>
<input class="widefat" id="<?php echo $this->get_field_id('cover'); ?>"
name="<?php echo $this->get_field_name('cover'); ?>"
type="url" value="<?php echo esc_attr($cover); ?>"
placeholder="<?php echo esc_attr($default_cover); ?>">
</p>
<p style="font-size:12px;color:#666;">
<?php esc_html_e('留空则使用默认背景图。已登录用户可使用个人封面图。', 'aksrt'); ?>
</p>
<?php
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
$business = [
'title' => sanitize_text_field($new_instance['title'] ?? ''),
'cover' => esc_url_raw($new_instance['cover'] ?? ''),
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+1 -1
View File
@@ -18,7 +18,7 @@ class WidgetAnnouncement extends \WP_Widget {
public function __construct() {
parent::__construct(
'aksrt_announcement',
__('公告跑马灯', 'aksrt'),
__('AKSRT 公告跑马灯', 'aksrt'),
[
'description' => __('滚动显示公告/通知(共享垂直 Swiper)。', 'aksrt'),
'classname' => 'widget_aksrt_announcement',
+1 -1
View File
@@ -18,7 +18,7 @@ class WidgetCoverCards extends \WP_Widget {
public function __construct() {
parent::__construct(
'aksrt_cover_cards',
__('图文封面卡片', 'aksrt'),
__('AKSRT 图文封面卡片', 'aksrt'),
['description' => __('图片/视频封面 + 文字叠加的图文卡片(共享 graphic_card)。', 'aksrt')]
);
}
-230
View File
@@ -1,230 +0,0 @@
<?php
/**
* Feature intro with media tabs (zibll zib_widget_feature_intro lite).
*
* @package AKSRT
*/
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetFeatureIntro extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
'aksrt_feature_intro',
esc_html__('AKSRT 功能介绍', 'aksrt'),
[
'description' => esc_html__('左文右图/视频的 TAB 功能介绍模块。', 'aksrt'),
'classname' => 'widget_aksrt_feature_intro',
]
);
}
public function widget($args, $instance): void {
$slides = $this->normalize_slides($instance['slides'] ?? []);
if (!$slides) {
if (current_user_can('manage_options')) {
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo function_exists('aksrt_get_widget_null')
? aksrt_get_widget_null(__('请添加至少一个功能介绍面板', 'aksrt')) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
: '<p class="widget-empty">' . esc_html__('请添加至少一个功能介绍面板', 'aksrt') . '</p>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
return;
}
$heading = trim((string) ($instance['heading'] ?? ''));
$desc = trim((string) ($instance['description'] ?? ''));
$media_pos = (($instance['media_position'] ?? 'right') === 'left') ? 'left' : 'right';
$chrome = !empty($instance['browser_chrome']) || !isset($instance['browser_chrome']);
$ratio = max(30, min(100, (int) ($instance['media_ratio'] ?? 66)));
$show_stat = !empty($instance['show_stat']);
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$uid = 'aksrt-fi-' . esc_attr($this->id);
echo '<div class="aksrt-fi media-' . esc_attr($media_pos) . '" id="' . $uid . '" data-aksrt-feature-intro="1">';
echo '<div class="aksrt-fi__text">';
if ($heading !== '') {
echo '<h2 class="aksrt-fi__title">' . esc_html($heading) . '</h2>';
}
if ($desc !== '') {
echo '<div class="aksrt-fi__desc">' . wp_kses_post(wpautop($desc)) . '</div>';
}
if ($show_stat) {
$sn = sanitize_text_field((string) ($instance['stat_number'] ?? ''));
$ss = sanitize_text_field((string) ($instance['stat_suffix'] ?? '+'));
$sl = sanitize_text_field((string) ($instance['stat_label'] ?? ''));
if ($sn !== '') {
$num_attr = is_numeric($sn) ? ' data-aksrt-countup data-target="' . esc_attr((string) (float) $sn) . '" data-duration="1600"' : '';
echo '<div class="aksrt-fi__stat"><span class="aksrt-fi__stat-num"' . $num_attr . '>' . esc_html(is_numeric($sn) ? '0' : $sn) . '</span>';
if ($ss !== '') {
echo '<span class="aksrt-fi__stat-plus">' . esc_html($ss) . '</span>';
}
if ($sl !== '') {
echo '<span class="aksrt-fi__stat-label">' . esc_html($sl) . '</span>';
}
echo '</div>';
}
}
if (count($slides) > 1) {
echo '<div class="aksrt-fi__tabs" role="tablist">';
foreach ($slides as $i => $s) {
$cls = $i === 0 ? ' is-active' : '';
$label = $s['tab_label'] !== '' ? $s['tab_label'] : sprintf(__('面板 %d', 'aksrt'), $i + 1);
echo '<button type="button" class="aksrt-fi__tab' . esc_attr($cls) . '" data-index="' . (int) $i . '" role="tab">' . esc_html($label) . '</button>';
}
echo '</div>';
}
echo '</div>';
echo '<div class="aksrt-fi__media">';
foreach ($slides as $i => $s) {
$cls = $i === 0 ? ' is-active' : '';
echo '<div class="aksrt-fi__panel' . esc_attr($cls) . '" data-index="' . (int) $i . '">';
$box_cls = $chrome ? 'aksrt-fi__browser' : 'aksrt-fi__browser is-plain';
echo '<div class="' . esc_attr($box_cls) . '">';
if ($chrome) {
echo '<div class="aksrt-fi__browser-bar" aria-hidden="true"><span></span><span></span><span></span></div>';
}
echo '<div class="aksrt-fi__browser-body" style="padding-bottom:' . esc_attr((string) $ratio) . '%">';
if ($s['media_type'] === 'video' && $s['video_url'] !== '') {
$poster = $s['video_poster'] !== '' ? ' poster="' . esc_url($s['video_poster']) . '"' : '';
$auto = $i === 0 ? ' autoplay' : '';
if ($i === 0) {
echo '<video class="aksrt-fi__video" src="' . esc_url($s['video_url']) . '"' . $poster . $auto . ' muted loop playsinline></video>';
} else {
echo '<video class="aksrt-fi__video" data-src="' . esc_url($s['video_url']) . '"' . $poster . ' muted loop playsinline></video>';
}
} elseif ($s['image'] !== '') {
$ph = get_template_directory_uri() . '/assets/img/placeholder.svg';
echo '<img class="aksrt-fi__img aksrt-lazy lazyload" data-src="' . esc_url($s['image']) . '" src="' . esc_url($ph) . '" alt="' . esc_attr($s['tab_label']) . '" loading="lazy" />';
}
echo '</div></div></div>';
}
echo '</div></div>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* @param mixed $raw
* @return array<int, array<string,string>>
*/
private function normalize_slides($raw): array {
if (is_string($raw)) {
$decoded = json_decode($raw, true);
$raw = is_array($decoded) ? $decoded : [];
}
if (!is_array($raw)) {
return [];
}
$out = [];
foreach ($raw as $row) {
if (!is_array($row)) {
continue;
}
$type = sanitize_key((string) ($row['media_type'] ?? 'image'));
if ($type !== 'video') {
$type = 'image';
}
$image = esc_url_raw(trim((string) ($row['image'] ?? '')));
$video = esc_url_raw(trim((string) ($row['video_url'] ?? '')));
if ($type === 'video' && $video === '') {
continue;
}
if ($type === 'image' && $image === '') {
continue;
}
$out[] = [
'tab_label' => sanitize_text_field((string) ($row['tab_label'] ?? '')),
'media_type' => $type,
'image' => $image,
'video_url' => $video,
'video_poster' => esc_url_raw(trim((string) ($row['video_poster'] ?? ''))),
];
if (count($out) >= 8) {
break;
}
}
return $out;
}
public function form($instance): void {
$json = is_string($instance['slides'] ?? null)
? (string) $instance['slides']
: wp_json_encode($this->normalize_slides($instance['slides'] ?? []), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
?>
<p>
<label><?php esc_html_e('主标题', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('heading')); ?>" value="<?php echo esc_attr((string) ($instance['heading'] ?? '')); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('描述', 'aksrt'); ?>
<textarea class="widefat" rows="3" name="<?php echo esc_attr($this->get_field_name('description')); ?>"><?php echo esc_textarea((string) ($instance['description'] ?? '')); ?></textarea>
</label>
</p>
<p>
<label><?php esc_html_e('媒体 TAB JSON', 'aksrt'); ?>
<textarea class="widefat" rows="6" name="<?php echo esc_attr($this->get_field_name('slides')); ?>" placeholder='[{"tab_label":"面板1","media_type":"image","image":"https://..."}]'><?php echo esc_textarea((string) $json); ?></textarea>
</label>
</p>
<p>
<label><?php esc_html_e('媒体位置', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('media_position')); ?>">
<option value="right" <?php selected(($instance['media_position'] ?? 'right'), 'right'); ?>><?php esc_html_e('右侧', 'aksrt'); ?></option>
<option value="left" <?php selected(($instance['media_position'] ?? ''), 'left'); ?>><?php esc_html_e('左侧', 'aksrt'); ?></option>
</select>
</label>
</p>
<p>
<label><?php esc_html_e('媒体高度占比 %', 'aksrt'); ?>
<input class="tiny-text" type="number" min="30" max="100" name="<?php echo esc_attr($this->get_field_name('media_ratio')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['media_ratio'] ?? 66))); ?>">
</label>
</p>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('browser_chrome')); ?>" value="1" <?php checked(!empty($instance['browser_chrome']) || !isset($instance['browser_chrome'])); ?>> <?php esc_html_e('浏览器外框', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_stat')); ?>" value="1" <?php checked(!empty($instance['show_stat'])); ?>> <?php esc_html_e('显示统计数字', 'aksrt'); ?></label>
</p>
<p>
<label><?php esc_html_e('统计数字', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('stat_number')); ?>" value="<?php echo esc_attr((string) ($instance['stat_number'] ?? '')); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('统计后缀', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('stat_suffix')); ?>" value="<?php echo esc_attr((string) ($instance['stat_suffix'] ?? '+')); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('统计标签', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('stat_label')); ?>" value="<?php echo esc_attr((string) ($instance['stat_label'] ?? '')); ?>">
</label>
</p>
<?php
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
$business = [
'heading' => sanitize_text_field((string) ($new_instance['heading'] ?? '')),
'description' => wp_kses_post((string) ($new_instance['description'] ?? '')),
'slides' => $this->normalize_slides($new_instance['slides'] ?? []),
'media_position' => ($new_instance['media_position'] ?? '') === 'left' ? 'left' : 'right',
'media_ratio' => max(30, min(100, (int) ($new_instance['media_ratio'] ?? 66))),
'browser_chrome' => !empty($new_instance['browser_chrome']) ? 1 : 0,
'show_stat' => !empty($new_instance['show_stat']) ? 1 : 0,
'stat_number' => sanitize_text_field((string) ($new_instance['stat_number'] ?? '')),
'stat_suffix' => sanitize_text_field((string) ($new_instance['stat_suffix'] ?? '')),
'stat_label' => sanitize_text_field((string) ($new_instance['stat_label'] ?? '')),
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
-128
View File
@@ -1,128 +0,0 @@
<?php
/**
* Language switcher widget (Polylang / WPML / filter).
*
* @package AKSRT
*/
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetLangSwitch extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
'aksrt_lang_switch',
__('语言切换器', 'aksrt'),
[
'description' => __('在侧栏显示多语言切换按钮。', 'aksrt'),
'classname' => 'widget_aksrt_lang_switch',
]
);
}
/**
* @param array<string,mixed> $args
* @param array<string,mixed> $instance
*/
public function widget($args, $instance): void {
$title = !empty($instance['title']) ? (string) $instance['title'] : '';
$style = sanitize_key((string) ($instance['style'] ?? 'dropdown'));
if (!in_array($style, ['dropdown', 'flags', 'inline'], true)) {
$style = 'dropdown';
}
$show_name = !isset($instance['show_name']) || !empty($instance['show_name']);
$hide_current = !empty($instance['hide_current']);
if (!function_exists('aksrt_has_multilingual') || !aksrt_has_multilingual()) {
if (current_user_can('manage_options')) {
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '<div class="c-red muted-box mb10"><b>' . esc_html__('语言切换:', 'aksrt') . '</b>' .
esc_html__('未检测到多语言插件(Polylang/WPML)或可用语言不足。', 'aksrt') . '</div>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
return;
}
// inline style: treat as flags row without dropdown chrome.
$render_style = $style === 'inline' ? 'flags' : $style;
$html = function_exists('aksrt_render_language_switcher') ? aksrt_render_language_switcher($render_style) : '';
if ($html === '') {
if (current_user_can('manage_options')) {
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo function_exists('aksrt_get_widget_null')
? aksrt_get_widget_null(__('暂无可用语言', 'aksrt')) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
: '<p class="widget-empty">' . esc_html__('暂无可用语言', 'aksrt') . '</p>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
return;
}
// Optional modifiers via wrapper classes.
$wrap = 'aksrt-lang-switch-widget style-' . esc_attr($style);
if (!$show_name) {
$wrap .= ' hide-name';
}
if ($hide_current) {
$wrap .= ' hide-current';
}
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
if ($title !== '') {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
echo '<div class="' . esc_attr($wrap) . '">' . $html . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* @param array<string,mixed> $instance
*/
public function form($instance): void {
$title = !empty($instance['title']) ? (string) $instance['title'] : '';
$style = !empty($instance['style']) ? (string) $instance['style'] : 'dropdown';
?>
<p>
<label for="<?php echo esc_attr($this->get_field_id('title')); ?>"><?php esc_html_e('标题(可选):', 'aksrt'); ?></label>
<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('style')); ?>"><?php esc_html_e('样式:', 'aksrt'); ?></label>
<select class="widefat" id="<?php echo esc_attr($this->get_field_id('style')); ?>" name="<?php echo esc_attr($this->get_field_name('style')); ?>">
<option value="dropdown" <?php selected($style, 'dropdown'); ?>><?php esc_html_e('下拉菜单', 'aksrt'); ?></option>
<option value="flags" <?php selected($style, 'flags'); ?>><?php esc_html_e('国旗图标', 'aksrt'); ?></option>
<option value="inline" <?php selected($style, 'inline'); ?>><?php esc_html_e('横向排列', 'aksrt'); ?></option>
</select>
</p>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_name')); ?>" value="1" <?php checked(!isset($instance['show_name']) || !empty($instance['show_name'])); ?>> <?php esc_html_e('显示语言名', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('hide_current')); ?>" value="1" <?php checked(!empty($instance['hide_current'])); ?>> <?php esc_html_e('隐藏当前语言', 'aksrt'); ?></label>
</p>
<p><small><?php esc_html_e('需要安装 Polylang 或 WPML 等多语言插件才能生效。', 'aksrt'); ?></small></p>
<?php
$this->render_common_form_section($instance);
}
/**
* @param array<string,mixed> $new_instance
* @param array<string,mixed> $old_instance
* @return array<string,mixed>
*/
public function update($new_instance, $old_instance): array {
$style = sanitize_key((string) ($new_instance['style'] ?? 'dropdown'));
if (!in_array($style, ['dropdown', 'flags', 'inline'], true)) {
$style = 'dropdown';
}
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'style' => $style,
'show_name' => !empty($new_instance['show_name']) ? 1 : 0,
'hide_current' => !empty($new_instance['hide_current']) ? 1 : 0,
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
-273
View File
@@ -1,273 +0,0 @@
<?php
/**
* Paid posts list (zibll zib_widget_ui_posts_pay + ajax).
*
* @package AKSRT
*/
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetPaidPosts extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
'aksrt_paid_posts',
esc_html__('AKSRT 付费内容列表', 'aksrt'),
[
'description' => esc_html__('展示开启付费/积分可见的文章(支持 AJAX)。', 'aksrt'),
'classname' => 'widget_aksrt_paid_posts',
]
);
}
public static function build_query_args(array $instance, int $paged = 1): array {
$count = max(1, min(20, (int) ($instance['count'] ?? 6)));
$orderby = sanitize_key((string) ($instance['orderby'] ?? 'date'));
$query = [
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => $count,
'paged' => max(1, $paged),
'order' => 'DESC',
'meta_query' => [
'relation' => 'OR',
[
'key' => '_aksrt_pay_price',
'value' => 0,
'compare' => '>',
'type' => 'NUMERIC',
],
[
'key' => '_aksrt_credits_cost',
'value' => 0,
'compare' => '>',
'type' => 'NUMERIC',
],
[
'key' => '_aksrt_visibility',
'value' => ['pay', 'credits'],
'compare' => 'IN',
],
],
];
if ($orderby === 'views') {
$query['meta_key'] = 'aksrt_post_views';
$query['orderby'] = 'meta_value_num';
} elseif ($orderby === 'price') {
$query['meta_key'] = '_aksrt_pay_price';
$query['orderby'] = 'meta_value_num';
} else {
$query['orderby'] = 'date';
}
$cat = max(0, (int) ($instance['cat'] ?? 0));
if ($cat > 0) {
$query['cat'] = $cat;
}
$limit_day = max(0, (int) ($instance['limit_day'] ?? 0));
if ($limit_day > 0) {
$query['date_query'] = [['after' => $limit_day . ' days ago']];
}
return $query;
}
public static function render_items_html(\WP_Query $q, string $style = 'mini'): string {
if (!$q->have_posts()) {
return '';
}
$placeholder = get_template_directory_uri() . '/assets/img/placeholder.svg';
ob_start();
while ($q->have_posts()) {
$q->the_post();
$pid = get_the_ID();
$price = (float) get_post_meta($pid, '_aksrt_pay_price', true);
$credits = (int) get_post_meta($pid, '_aksrt_credits_cost', true);
$thumb = get_the_post_thumbnail_url($pid, 'thumbnail');
if ($price > 0) {
$badge = '¥' . number_format($price, 2, '.', '');
} elseif ($credits > 0) {
$badge = sprintf(__('%d 积分', 'aksrt'), $credits);
} else {
$badge = __('付费', 'aksrt');
}
$mode = in_array($style, ['list', 'card', 'mini'], true) ? $style : 'mini';
if (function_exists('aksrt_render_post')) {
$item = aksrt_render_post([
'mode' => $mode,
'context' => 'widget',
'show_thumb' => true,
'show_meta' => true,
'show_excerpt' => $mode === 'list',
'extra_class' => 'ajax-item aksrt-paid-posts__' . $mode,
]);
if ($item !== '') {
echo '<div class="aksrt-paid-posts__item is-' . esc_attr($mode) . ' ajax-item">';
echo $item; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '<span class="aksrt-paid-posts__price badge">' . esc_html($badge) . '</span>';
echo '</div>';
continue;
}
}
echo '<li class="aksrt-paid-posts__item ajax-item">';
if ($thumb) {
echo '<a class="aksrt-paid-posts__thumb" href="' . esc_url(get_permalink()) . '"><img class="aksrt-lazy lazyload" data-src="' . esc_url($thumb) . '" src="' . esc_url($placeholder) . '" alt="" loading="lazy" /></a>';
}
echo '<div class="aksrt-paid-posts__body">';
echo '<a class="aksrt-paid-posts__title" href="' . esc_url(get_permalink()) . '">' . esc_html(get_the_title()) . '</a>';
echo '<div class="aksrt-paid-posts__meta"><span class="aksrt-paid-posts__price">' . esc_html($badge) . '</span></div>';
echo '</div></li>';
}
wp_reset_postdata();
return (string) ob_get_clean();
}
public static function query_payload(array $instance, int $page = 1): array {
$page = max(1, $page);
$paginate = sanitize_key((string) ($instance['paginate'] ?? 'ajax'));
$count = max(1, min(20, (int) ($instance['count'] ?? 6)));
$q = new \WP_Query(self::build_query_args($instance, $page));
$style = sanitize_key((string) ($instance['style'] ?? 'mini'));
if (!in_array($style, ['mini', 'list', 'card'], true)) {
$style = 'mini';
}
$items = self::render_items_html($q, $style);
$wrap_tag = (strpos($items, 'posts-mini') !== false || strpos($items, 'aksrt-paid-posts__mini') !== false) ? 'div' : 'ul';
$html = $items !== '' ? '<' . $wrap_tag . ' class="aksrt-paid-posts style-' . esc_attr($style) . '">' . $items . '</' . $wrap_tag . '>' : '';
if ($html === '' && $page === 1) {
$html = function_exists('aksrt_get_ajax_null')
? aksrt_get_ajax_null(__('暂无付费内容', 'aksrt'))
: '<p class="widget-empty ajax-item">' . esc_html__('暂无付费内容', 'aksrt') . '</p>';
}
$max_pages = max(1, (int) $q->max_num_pages);
$total = (int) $q->found_posts;
$has_more = $page < $max_pages;
$base_qs = array_filter([
'count' => $count,
'orderby' => sanitize_key((string) ($instance['orderby'] ?? 'date')),
'cat' => max(0, (int) ($instance['cat'] ?? 0)),
'limit_day' => max(0, (int) ($instance['limit_day'] ?? 0)),
'style' => $style,
'paginate' => $paginate,
], static function ($v) {
return $v !== '' && $v !== null;
});
$rest_base = add_query_arg($base_qs, rest_url('aksrt/v1/widgets/paid-posts'));
$paginate_html = '';
if ($paginate === 'ajax' && $has_more) {
$next = add_query_arg(['page' => $page + 1], $rest_base);
$paginate_html = function_exists('aksrt_get_ajax_next_paginate')
? aksrt_get_ajax_next_paginate($total, $page, $count, $next)
: '<div class="text-center theme-pagination ajax-pag"><div class="next-page ajax-next"><a href="' . esc_url($next) . '">' . esc_html__('加载更多', 'aksrt') . '</a></div></div>';
} elseif ($paginate === 'number' && $max_pages > 1) {
$paginate_html = function_exists('aksrt_get_ajax_number_paginate')
? aksrt_get_ajax_number_paginate($total, $page, $count, $rest_base)
: '<div class="ajax-pag hide"><div class="next-page ajax-next"><a href="#"></a></div></div>';
} else {
$paginate_html = '<div class="ajax-pag hide"><div class="next-page ajax-next"><a href="#"></a></div></div>';
}
return [
'html' => $html,
'paginate_html' => $paginate_html,
'has_more' => $has_more,
'page' => $page,
'max_pages' => $max_pages,
'total' => $total,
];
}
public function widget($args, $instance): void {
$paginate = sanitize_key((string) ($instance['paginate'] ?? 'ajax'));
$instance['paginate'] = $paginate;
$payload = self::query_payload($instance, 1);
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base);
if ($title !== '') {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
$affix = function_exists('aksrt_widget_affix_attrs') ? aksrt_widget_affix_attrs($instance) : ['class' => '', 'attr' => ''];
$rest = rest_url('aksrt/v1/widgets/paid-posts');
$wrap = 'aksrt-paid-posts-wrap ajaxpager' . $affix['class'];
echo '<div class="' . esc_attr($wrap) . '" data-aksrt-ajaxpager data-aksrt-ajax-url="' . esc_url($rest) . '"' . $affix['attr'] . '>';
echo $payload['html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $payload['paginate_html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '<div class="post_ajax_loader" data-aksrt-ajax-loader data-aksrt-ajax-loader-hide hidden></div>';
echo '</div>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
public function form($instance): void {
?>
<p>
<label><?php esc_html_e('标题', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('title')); ?>" value="<?php echo esc_attr((string) ($instance['title'] ?? __('付费精选', 'aksrt'))); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('数量', 'aksrt'); ?>
<input class="tiny-text" type="number" min="1" max="20" name="<?php echo esc_attr($this->get_field_name('count')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['count'] ?? 6))); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('样式', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('style')); ?>">
<option value="mini" <?php selected(($instance['style'] ?? 'mini'), 'mini'); ?>><?php esc_html_e('迷你', 'aksrt'); ?></option>
<option value="list" <?php selected(($instance['style'] ?? ''), 'list'); ?>><?php esc_html_e('列表', 'aksrt'); ?></option>
<option value="card" <?php selected(($instance['style'] ?? ''), 'card'); ?>><?php esc_html_e('卡片', 'aksrt'); ?></option>
</select>
</label>
</p>
<p>
<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="views" <?php selected(($instance['orderby'] ?? ''), 'views'); ?>><?php esc_html_e('浏览', 'aksrt'); ?></option>
<option value="price" <?php selected(($instance['orderby'] ?? ''), 'price'); ?>><?php esc_html_e('价格', 'aksrt'); ?></option>
</select>
</label>
</p>
<p>
<label><?php esc_html_e('分页', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('paginate')); ?>">
<option value="ajax" <?php selected(($instance['paginate'] ?? 'ajax'), 'ajax'); ?>><?php esc_html_e('AJAX', 'aksrt'); ?></option>
<option value="number" <?php selected(($instance['paginate'] ?? ''), 'number'); ?>><?php esc_html_e('数字分页', 'aksrt'); ?></option>
<option value="none" <?php selected(($instance['paginate'] ?? ''), 'none'); ?>><?php esc_html_e('无', 'aksrt'); ?></option>
</select>
</label>
</p>
<p>
<label><?php esc_html_e('分类 ID(0=全部)', 'aksrt'); ?>
<input class="tiny-text" type="number" min="0" name="<?php echo esc_attr($this->get_field_name('cat')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['cat'] ?? 0))); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('最近 N 天(0=不限)', 'aksrt'); ?>
<input class="tiny-text" type="number" min="0" name="<?php echo esc_attr($this->get_field_name('limit_day')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['limit_day'] ?? 0))); ?>">
</label>
</p>
<?php $this->render_common_form_section($instance); ?>
<?php
}
public function update($new_instance, $old_instance): array {
$orderby = sanitize_key((string) ($new_instance['orderby'] ?? 'date'));
if (!in_array($orderby, ['date', 'views', 'price'], true)) {
$orderby = 'date';
}
return $this->update_common_fields($new_instance, $old_instance, [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'count' => max(1, min(20, (int) ($new_instance['count'] ?? 6))),
'orderby' => $orderby,
'style' => in_array(sanitize_key((string) ($new_instance['style'] ?? 'mini')), ['mini', 'list', 'card'], true) ? sanitize_key((string) ($new_instance['style'] ?? 'mini')) : 'mini',
'cat' => max(0, (int) ($new_instance['cat'] ?? 0)),
'limit_day' => max(0, (int) ($new_instance['limit_day'] ?? 0)),
'paginate' => in_array(sanitize_key((string) ($new_instance['paginate'] ?? 'ajax')), ['none', 'ajax', 'number'], true) ? sanitize_key((string) ($new_instance['paginate'] ?? 'ajax')) : 'ajax',
]);
}
}
-360
View File
@@ -1,360 +0,0 @@
<?php
/**
* Horizontal partners / friends scroll (zibll zib_widget_ui_partners_scroll).
*
* @package AKSRT
*/
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetPartnersScroll extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
'aksrt_partners_scroll',
esc_html__('AKSRT 合作伙伴滚动', 'aksrt'),
[
'description' => esc_html__('横向无缝滚动的合作伙伴/友情链接条。', 'aksrt'),
'classname' => 'widget_aksrt_partners_scroll',
]
);
}
public function widget($args, $instance): void {
$partners = $this->resolve_partners($instance);
if (!$partners) {
if (current_user_can('manage_options')) {
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo function_exists('aksrt_get_widget_null')
? aksrt_get_widget_null(__('暂无合作伙伴,请在模块中添加', 'aksrt')) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
: '<p class="widget-empty">' . esc_html__('暂无合作伙伴', 'aksrt') . '</p>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
return;
}
$rows = max(1, min(4, (int) ($instance['rows'] ?? 1)));
$m_rows = max(1, min(4, (int) ($instance['m_rows'] ?? 1)));
$style_type = sanitize_key((string) ($instance['style_type'] ?? 'card'));
if (!in_array($style_type, ['card', 'logo', 'chip'], true)) {
$style_type = 'card';
}
$card_w = max(60, min(400, (int) ($instance['card_width'] ?? 220)));
$card_h = max(40, min(200, (int) ($instance['card_height'] ?? 90)));
$speed = max(1, min(20, (int) ($instance['speed'] ?? 3)));
$direction = sanitize_key((string) ($instance['direction'] ?? 'alternate'));
$pause = !empty($instance['pause_hover']) || !isset($instance['pause_hover']);
$card_bg = !empty($instance['card_bg']) || !isset($instance['card_bg']);
$end_fade = !empty($instance['end_fade']) || !isset($instance['end_fade']);
$go_link = !empty($instance['go_link']);
$show_bg = !empty($instance['show_widget_bg']) || !isset($instance['show_widget_bg']);
$radius = max(0, min(40, (int) ($instance['card_radius'] ?? 10)));
$use_rows = wp_is_mobile() ? $m_rows : $rows;
$rows_data = array_fill(0, $use_rows, []);
foreach (array_values($partners) as $i => $p) {
$rows_data[$i % $use_rows][] = $p;
}
$rows_data = array_values(array_filter($rows_data, static fn($r) => !empty($r)));
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base);
if ($title !== '') {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
$wrap_class = 'aksrt-partners-scroll aksrt-ps-style-' . $style_type;
if ($pause) {
$wrap_class .= ' aksrt-ps-pause-hover';
}
if ($card_bg) {
$wrap_class .= ' aksrt-ps-card-bg';
}
if ($end_fade) {
$wrap_class .= ' aksrt-ps-end-fade';
}
$style = sprintf(
'--aksrt-ps-w:%dpx;--aksrt-ps-h:%dpx;--aksrt-ps-radius:%dpx;--aksrt-ps-speed:%d;',
$card_w,
$card_h,
$radius,
$speed
);
$outer = $show_bg ? 'aksrt-partners-scroll-wrap' : 'aksrt-partners-scroll-wrap is-bare';
echo '<div class="' . esc_attr($outer) . '">';
echo '<div class="' . esc_attr($wrap_class) . '" style="' . esc_attr($style) . '">';
foreach ($rows_data as $row_index => $row_partners) {
switch ($direction) {
case 'left':
$dir = 'left';
break;
case 'right':
$dir = 'right';
break;
default:
$dir = ($row_index % 2 === 0) ? 'left' : 'right';
}
echo '<div class="aksrt-ps-row aksrt-ps-dir-' . esc_attr($dir) . '">';
echo '<div class="aksrt-ps-track">';
// Duplicate group for seamless CSS marquee.
for ($dup = 0; $dup < 2; $dup++) {
echo '<div class="aksrt-ps-group">';
foreach ($row_partners as $partner) {
echo $this->render_card($partner, $style_type, $go_link); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
echo '</div>';
}
echo '</div></div>';
}
echo '</div></div>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* @param array<string,mixed> $partner
*/
private function render_card(array $partner, string $style_type, bool $go_link): string {
$title = trim((string) ($partner['title'] ?? ''));
$desc = trim((string) ($partner['desc'] ?? ''));
$image = (string) ($partner['image'] ?? '');
$url = (string) ($partner['url'] ?? '');
$target = (string) ($partner['target'] ?? '');
if ($image === '' && $title === '') {
return '';
}
$alt = esc_attr($title !== '' ? $title : __('合作伙伴', 'aksrt'));
$img_tag = $image !== ''
? '<img class="aksrt-ps-img aksrt-lazy lazyload" data-src="' . esc_url($image) . '" src="' . esc_url(get_template_directory_uri() . '/assets/img/placeholder.svg') . '" alt="' . $alt . '" loading="lazy" />'
: '';
$inner = '';
if ($style_type === 'logo') {
if ($img_tag === '') {
return '';
}
$inner = $img_tag;
} elseif ($style_type === 'chip') {
if ($img_tag !== '') {
$inner .= '<span class="aksrt-ps-chip-icon">' . $img_tag . '</span>';
}
$inner .= '<span class="aksrt-ps-chip-title">' . esc_html($title) . '</span>';
} else {
if ($img_tag !== '') {
$inner .= '<div class="aksrt-ps-card-icon">' . $img_tag . '</div>';
}
$inner .= '<div class="aksrt-ps-card-text">';
if ($title !== '') {
$inner .= '<div class="aksrt-ps-card-title">' . esc_html($title) . '</div>';
}
if ($desc !== '') {
$inner .= '<div class="aksrt-ps-card-desc">' . esc_html($desc) . '</div>';
}
$inner .= '</div>';
}
if ($url !== '') {
if ($go_link && function_exists('aksrt_go_link')) {
$url = aksrt_go_link($url, true);
}
$tgt = $target !== '' ? ' target="' . esc_attr($target) . '" rel="noopener noreferrer"' : '';
return '<a class="aksrt-ps-item" href="' . esc_url($url) . '"' . $tgt . '>' . $inner . '</a>';
}
return '<div class="aksrt-ps-item">' . $inner . '</div>';
}
/**
* @param array<string,mixed> $instance
* @return array<int, array{title:string,desc:string,image:string,url:string,target:string}>
*/
private function resolve_partners(array $instance): array {
$source = sanitize_key((string) ($instance['link_type'] ?? 'manual'));
if ($source === 'link') {
$args = [
'orderby' => sanitize_key((string) ($instance['link_orderby'] ?? 'name')) ?: 'name',
'order' => strtoupper((string) ($instance['link_order'] ?? 'ASC')) === 'DESC' ? 'DESC' : 'ASC',
'limit' => max(1, min(100, (int) ($instance['link_limit'] ?? 50))),
];
$cats = $instance['link_cats'] ?? '';
if (is_array($cats)) {
$cats = implode(',', array_map('intval', $cats));
}
$cats = trim((string) $cats);
if ($cats !== '') {
$args['category'] = $cats;
}
$bookmarks = get_bookmarks($args);
$out = [];
foreach ($bookmarks as $b) {
$out[] = [
'title' => (string) $b->link_name,
'desc' => (string) $b->link_description,
'image' => (string) $b->link_image,
'url' => (string) $b->link_url,
'target' => (string) $b->link_target,
];
}
return $out;
}
return $this->normalize_manual($instance['partners'] ?? []);
}
/**
* @param mixed $raw
* @return array<int, array{title:string,desc:string,image:string,url:string,target:string}>
*/
private function normalize_manual($raw): array {
if (is_string($raw)) {
$decoded = json_decode($raw, true);
$raw = is_array($decoded) ? $decoded : [];
}
if (!is_array($raw)) {
return [];
}
$out = [];
foreach ($raw as $row) {
if (!is_array($row)) {
continue;
}
$title = sanitize_text_field((string) ($row['title'] ?? ''));
$image = esc_url_raw(trim((string) ($row['image'] ?? '')));
$url = esc_url_raw(trim((string) ($row['url'] ?? $row['link'] ?? '')));
if ($title === '' && $image === '') {
continue;
}
$out[] = [
'title' => $title,
'desc' => sanitize_text_field((string) ($row['desc'] ?? '')),
'image' => $image,
'url' => $url,
'target' => sanitize_text_field((string) ($row['target'] ?? '_blank')),
];
if (count($out) >= 40) {
break;
}
}
return $out;
}
public function form($instance): void {
$title = (string) ($instance['title'] ?? '');
$link_type = (string) ($instance['link_type'] ?? 'manual');
$partners_json = is_string($instance['partners'] ?? null)
? (string) $instance['partners']
: wp_json_encode($this->normalize_manual($instance['partners'] ?? []), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
?>
<p>
<label><?php esc_html_e('标题', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('title')); ?>" value="<?php echo esc_attr($title); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('数据来源', 'aksrt'); ?>
<select class="widefat" name="<?php echo esc_attr($this->get_field_name('link_type')); ?>">
<option value="manual" <?php selected($link_type, 'manual'); ?>><?php esc_html_e('手动 JSON', 'aksrt'); ?></option>
<option value="link" <?php selected($link_type, 'link'); ?>><?php esc_html_e('WordPress 链接(书签)', 'aksrt'); ?></option>
</select>
</label>
</p>
<p>
<label><?php esc_html_e('合作伙伴 JSON', 'aksrt'); ?>
<textarea class="widefat" rows="6" name="<?php echo esc_attr($this->get_field_name('partners')); ?>" placeholder='[{"title":"Name","image":"https://...","url":"https://...","desc":""}]'><?php echo esc_textarea((string) $partners_json); ?></textarea>
</label>
<small><?php esc_html_e('字段: title, image, url, desc, target', 'aksrt'); ?></small>
</p>
<p>
<label><?php esc_html_e('链接分类 ID(逗号分隔,书签模式)', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('link_cats')); ?>" value="<?php echo esc_attr((string) ($instance['link_cats'] ?? '')); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('书签数量上限', 'aksrt'); ?>
<input class="tiny-text" type="number" min="1" max="100" name="<?php echo esc_attr($this->get_field_name('link_limit')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['link_limit'] ?? 50))); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('样式', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('style_type')); ?>">
<option value="card" <?php selected(($instance['style_type'] ?? 'card'), 'card'); ?>><?php esc_html_e('图文卡片', 'aksrt'); ?></option>
<option value="logo" <?php selected(($instance['style_type'] ?? ''), 'logo'); ?>><?php esc_html_e('纯 Logo', 'aksrt'); ?></option>
<option value="chip" <?php selected(($instance['style_type'] ?? ''), 'chip'); ?>><?php esc_html_e('胶囊', 'aksrt'); ?></option>
</select>
</label>
</p>
<p>
<label><?php esc_html_e('PC 行数', 'aksrt'); ?>
<input class="tiny-text" type="number" min="1" max="4" name="<?php echo esc_attr($this->get_field_name('rows')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['rows'] ?? 1))); ?>">
</label>
<label><?php esc_html_e('移动行数', 'aksrt'); ?>
<input class="tiny-text" type="number" min="1" max="4" name="<?php echo esc_attr($this->get_field_name('m_rows')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['m_rows'] ?? 1))); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('卡片宽', 'aksrt'); ?>
<input class="tiny-text" type="number" min="60" max="400" name="<?php echo esc_attr($this->get_field_name('card_width')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['card_width'] ?? 220))); ?>">
</label>
<label><?php esc_html_e('高', 'aksrt'); ?>
<input class="tiny-text" type="number" min="40" max="200" name="<?php echo esc_attr($this->get_field_name('card_height')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['card_height'] ?? 90))); ?>">
</label>
<label><?php esc_html_e('速度', 'aksrt'); ?>
<input class="tiny-text" type="number" min="1" max="20" name="<?php echo esc_attr($this->get_field_name('speed')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['speed'] ?? 3))); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('方向', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('direction')); ?>">
<option value="alternate" <?php selected(($instance['direction'] ?? 'alternate'), 'alternate'); ?>><?php esc_html_e('交替', 'aksrt'); ?></option>
<option value="left" <?php selected(($instance['direction'] ?? ''), 'left'); ?>><?php esc_html_e('向左', 'aksrt'); ?></option>
<option value="right" <?php selected(($instance['direction'] ?? ''), 'right'); ?>><?php esc_html_e('向右', 'aksrt'); ?></option>
</select>
</label>
</p>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('pause_hover')); ?>" value="1" <?php checked(!empty($instance['pause_hover']) || !isset($instance['pause_hover'])); ?>> <?php esc_html_e('悬停暂停', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('card_bg')); ?>" value="1" <?php checked(!empty($instance['card_bg']) || !isset($instance['card_bg'])); ?>> <?php esc_html_e('卡片背景', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('end_fade')); ?>" value="1" <?php checked(!empty($instance['end_fade']) || !isset($instance['end_fade'])); ?>> <?php esc_html_e('两端渐隐', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('go_link')); ?>" value="1" <?php checked(!empty($instance['go_link'])); ?>> <?php esc_html_e('外链跳转页', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_widget_bg')); ?>" value="1" <?php checked(!empty($instance['show_widget_bg']) || !isset($instance['show_widget_bg'])); ?>> <?php esc_html_e('显示背景盒子', 'aksrt'); ?></label>
</p>
<?php
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'link_type' => in_array(($new_instance['link_type'] ?? ''), ['manual', 'link'], true) ? $new_instance['link_type'] : 'manual',
'partners' => $this->normalize_manual($new_instance['partners'] ?? []),
'link_cats' => sanitize_text_field((string) ($new_instance['link_cats'] ?? '')),
'link_limit' => max(1, min(100, (int) ($new_instance['link_limit'] ?? 50))),
'link_orderby' => sanitize_key((string) ($new_instance['link_orderby'] ?? 'name')),
'link_order' => strtoupper((string) ($new_instance['link_order'] ?? 'ASC')) === 'DESC' ? 'DESC' : 'ASC',
'style_type' => in_array(($new_instance['style_type'] ?? ''), ['card', 'logo', 'chip'], true) ? $new_instance['style_type'] : 'card',
'rows' => max(1, min(4, (int) ($new_instance['rows'] ?? 1))),
'm_rows' => max(1, min(4, (int) ($new_instance['m_rows'] ?? 1))),
'card_width' => max(60, min(400, (int) ($new_instance['card_width'] ?? 220))),
'card_height' => max(40, min(200, (int) ($new_instance['card_height'] ?? 90))),
'card_radius' => max(0, min(40, (int) ($new_instance['card_radius'] ?? 10))),
'speed' => max(1, min(20, (int) ($new_instance['speed'] ?? 3))),
'direction' => in_array(($new_instance['direction'] ?? ''), ['alternate', 'left', 'right'], true) ? $new_instance['direction'] : 'alternate',
'pause_hover' => !empty($new_instance['pause_hover']) ? 1 : 0,
'card_bg' => !empty($new_instance['card_bg']) ? 1 : 0,
'end_fade' => !empty($new_instance['end_fade']) ? 1 : 0,
'go_link' => !empty($new_instance['go_link']) ? 1 : 0,
'show_widget_bg' => !empty($new_instance['show_widget_bg']) ? 1 : 0,
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+90 -87
View File
@@ -1,6 +1,6 @@
<?php
/**
* Singular post pay purchase sidebar card (zibll zib_widget_ui_posts_pay).
* Compact paid-content purchase card for the article sidebar.
*
* @package AKSRT
*/
@@ -14,13 +14,12 @@ defined('ABSPATH') || exit;
class WidgetPostPayBox extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
'aksrt_post_pay_box',
esc_html__('AKSRT 付费购买', 'aksrt'),
[
'description' => esc_html__('显示当前文章/帖子的付费购买模块,推荐放在详情侧栏。', 'aksrt'),
'description' => esc_html__('仅在文章页 - 侧边栏展示当前文章的紧凑购买卡。', 'aksrt'),
'classname' => 'widget_aksrt_post_pay_box',
]
);
@@ -30,44 +29,55 @@ class WidgetPostPayBox extends \WP_Widget {
* @param array<string,mixed> $instance
*/
public static function render_html(array $instance = []): string {
if (!is_singular(['post', 'page'])) {
return '';
}
$post = get_queried_object();
if (!$post instanceof \WP_Post) {
return '';
}
if (!function_exists('aksrt_get_pay_meta') || !function_exists('aksrt_is_paid_post')) {
return '';
}
if (!aksrt_is_paid_post($post)) {
if (!is_single() || get_post_type() !== 'post') {
return '';
}
$meta = aksrt_get_pay_meta($post);
$post = get_queried_object();
if (!($post instanceof \WP_Post) || !function_exists('aksrt_get_pay_meta') || !function_exists('aksrt_is_paid_post') || !aksrt_is_paid_post($post)) {
return '';
}
$meta = aksrt_get_pay_meta($post);
$pay_type = (string) ($meta['pay_type'] ?? 'no');
if ($pay_type === '' || $pay_type === 'no') {
return '';
}
$theme = sanitize_html_class((string) ($instance['theme'] ?? 'jb-red'));
if ($theme === '') {
$allowed_themes = ['jb-red', 'jb-yellow', 'jb-blue', 'jb-green', 'jb-purple', 'jb-vip1', 'jb-vip2'];
if (!in_array($theme, $allowed_themes, true)) {
$theme = 'jb-red';
}
$has_access = function_exists('aksrt_has_paid_access') ? aksrt_has_paid_access($post) : false;
$priced = function_exists('aksrt_get_user_pay_price') ? aksrt_get_user_pay_price($post) : null;
$pay_modo = is_array($priced) && (($priced['modo'] ?? '') === 'points') ? 'points' : '0';
$amount = is_array($priced) ? (float) ($priced['amount'] ?? 0) : (float) ($meta['price'] ?? 0);
$currency = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_currency_name() : __('积分', 'aksrt');
$cash_symbol = '¥';
if (class_exists('\AKSRT\Settings\Settings')) {
$paid_s = \AKSRT\Settings\Settings::get_paid_content_settings();
$cash_symbol = (string) ($paid_s['display']['currency_symbol'] ?? '¥');
$price = function_exists('aksrt_get_user_pay_price')
? aksrt_get_user_pay_price($post)
: [
'amount' => (float) ($meta['price'] ?? 0),
'base' => (float) ($meta['price'] ?? 0),
'original' => (float) ($meta['original_price'] ?? 0),
'modo' => '0',
'currency' => '¥',
];
$amount = max(0.0, (float) ($price['amount'] ?? 0));
$base = max(0.0, (float) ($price['base'] ?? $amount));
$original = max(0.0, (float) ($price['original'] ?? 0));
$pay_modo = (($price['modo'] ?? '0') === 'points') ? 'points' : '0';
$currency = (string) ($price['currency'] ?? ($pay_modo === 'points' ? __('积分', 'aksrt') : '¥'));
$format = static function (float $value) use ($pay_modo, $currency): string {
$number = function_exists('aksrt_format_price') ? aksrt_format_price($value) : number_format($value, 2, '.', '');
return $pay_modo === 'points' ? $number . ' ' . $currency : $currency . $number;
};
$has_access = function_exists('aksrt_has_paid_access') && aksrt_has_paid_access($post);
$is_logged_in = is_user_logged_in();
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink($post));
$demo = trim((string) ($meta['demo_url'] ?? ''));
$sales = 0;
if (function_exists('aksrt_get_download_box_meta')) {
$box = aksrt_get_download_box_meta($post);
$sales = max(0, (int) ($box['sales_count'] ?? 0));
}
$price_text = $pay_modo === 'points'
? (function_exists('aksrt_format_price') ? aksrt_format_price($amount) : (string) $amount) . ' ' . $currency
: $cash_symbol . (function_exists('aksrt_format_price') ? aksrt_format_price($amount) : number_format($amount, 2, '.', ''));
$type_labels = [
'reading' => __('付费阅读', 'aksrt'),
@@ -77,65 +87,56 @@ class WidgetPostPayBox extends \WP_Widget {
'vip' => __('会员专属', 'aksrt'),
];
$type_label = $type_labels[$pay_type] ?? __('付费内容', 'aksrt');
$cta_map = [
$action_labels = [
'reading' => __('查看内容', 'aksrt'),
'download' => __('获取下载', 'aksrt'),
'download' => __('前往下载', 'aksrt'),
'gallery' => __('查看图片', 'aksrt'),
'video' => __('查看视频', 'aksrt'),
'vip' => __('查看内容', 'aksrt'),
];
$cta_label = $cta_map[$pay_type] ?? __('查看内容', 'aksrt');
$sales = 0;
if (function_exists('aksrt_get_download_box_meta')) {
$box = aksrt_get_download_box_meta($post);
$sales = (int) ($box['sales_count'] ?? 0);
}
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink($post));
$is_logged_in = is_user_logged_in();
$demo = trim((string) ($meta['demo_url'] ?? ''));
$action_label = $action_labels[$pay_type] ?? __('查看内容', 'aksrt');
$show_original = $original > $amount && $original > 0;
$show_member_discount = !$has_access && $amount > 0 && $base > $amount;
ob_start();
echo '<div class="zib-widget pay-box pay-widget aksrt-post-pay-box order-type-' . esc_attr($pay_type) . ($has_access ? ' is-paid' : ' is-unpaid') . '" style="padding:0;">';
echo '<badge class="pay-tag abs-center">' . esc_html($type_label) . '</badge>';
echo '<div class="relative-h ' . esc_attr($theme) . ' aksrt-post-pay-box__hero">';
echo '<div class="absolute radius ' . esc_attr($theme) . ' aksrt-post-pay-box__blob aksrt-post-pay-box__blob--a"></div>';
echo '<div class="absolute radius ' . esc_attr($theme) . ' aksrt-post-pay-box__blob aksrt-post-pay-box__blob--b"></div>';
echo '<div class="relative box-body text-center">';
if ($has_access) {
echo '<p class="mt10 mb6"><i class="fa fa-shopping-bag fa-3x" aria-hidden="true"></i></p>';
echo '<b class="em14">' . esc_html__('已解锁', 'aksrt') . '</b>';
} else {
echo '<div class="text-center mt10 aksrt-post-pay-box__price">';
if ($amount > 0) {
echo '<div class="em14 font-bold">' . esc_html($price_text) . '</div>';
} else {
echo '<div class="em14 font-bold">' . esc_html__('免费', 'aksrt') . '</div>';
}
echo '</div>';
}
echo '</div></div>';
echo '<div class="box-body">';
if ($has_access) {
echo '<div class="mt10"><a href="#posts-pay" class="but padding-lg btn-block ' . esc_attr($theme) . '"><i class="fa fa-dot-circle-o fa-fw" aria-hidden="true"></i> ' . esc_html($cta_label) . '</a></div>';
} elseif (!$is_logged_in) {
echo '<div class="mt10"><a href="' . esc_url($login_url) . '" class="but padding-lg btn-block jb-blue"><i class="fa fa-sign-in" aria-hidden="true"></i> ' . esc_html__('登录后购买', 'aksrt') . '</a></div>';
if ($amount > 0 && $pay_modo !== 'points' && function_exists('aksrt_allow_guest_content_pay') && aksrt_allow_guest_content_pay($post)) {
echo '<div class="mt10"><button type="button" class="but padding-lg btn-block ' . esc_attr($theme) . '" data-aksrt-pay-content="' . esc_attr((string) $post->ID) . '"><i class="fa fa-shopping-cart fa-fw" aria-hidden="true"></i> ' . esc_html(sprintf(/* translators: price */ __('游客购买(%s)', 'aksrt'), $price_text)) . '</button></div>';
}
} else {
echo '<div class="mt10"><button type="button" class="but padding-lg btn-block ' . esc_attr($theme) . ' cashier-link" data-aksrt-pay-content="' . esc_attr((string) $post->ID) . '"><i class="fa fa-shopping-cart fa-fw" aria-hidden="true"></i> ' . esc_html(sprintf(/* translators: price */ __('立即购买(%s)', 'aksrt'), $price_text)) . '</button></div>';
}
if ($demo !== '') {
echo '<div class="mt10"><a class="but hollow btn-block" href="' . esc_url($demo) . '" target="_blank" rel="noopener noreferrer"><i class="fa fa-external-link" aria-hidden="true"></i> ' . esc_html__('演示地址', 'aksrt') . '</a></div>';
}
echo '</div>';
if ($sales > 0) {
echo '<badge class="img-badge hot jb-blue px12">' . esc_html(sprintf(/* translators: sales count */ __('已售 %s', 'aksrt'), (string) $sales)) . '</badge>';
}
echo '</div>';
?>
<section class="aksrt-post-pay-card <?php echo esc_attr($theme); ?><?php echo $has_access ? ' is-unlocked' : ''; ?>" aria-label="<?php esc_attr_e('付费购买', 'aksrt'); ?>">
<header class="aksrt-post-pay-card__header">
<span class="aksrt-post-pay-card__type"><?php echo esc_html($type_label); ?></span>
<?php if ($sales > 0) : ?>
<span class="aksrt-post-pay-card__sales"><?php echo esc_html(sprintf(__('已售 %s', 'aksrt'), number_format_i18n($sales))); ?></span>
<?php endif; ?>
</header>
<div class="aksrt-post-pay-card__price-row">
<?php if ($has_access) : ?>
<strong class="aksrt-post-pay-card__status"><i class="fa fa-check-circle" aria-hidden="true"></i><?php esc_html_e('已解锁', 'aksrt'); ?></strong>
<?php elseif ($amount > 0) : ?>
<strong class="aksrt-post-pay-card__price"><?php echo esc_html($format($amount)); ?></strong>
<?php if ($show_original) : ?><del><?php echo esc_html($format($original)); ?></del><?php endif; ?>
<?php else : ?>
<strong class="aksrt-post-pay-card__price"><?php esc_html_e('免费', 'aksrt'); ?></strong>
<?php endif; ?>
</div>
<?php if ($show_member_discount) : ?>
<p class="aksrt-post-pay-card__hint"><?php echo esc_html(sprintf(__('已应用会员优惠,原价 %s', 'aksrt'), $format($base))); ?></p>
<?php endif; ?>
<div class="aksrt-post-pay-card__actions">
<?php if ($has_access) : ?>
<a class="but btn-block <?php echo esc_attr($theme); ?>" href="#posts-pay"><i class="fa fa-arrow-down" aria-hidden="true"></i><?php echo esc_html($action_label); ?></a>
<?php elseif (!$is_logged_in) : ?>
<a class="but btn-block jb-blue" href="<?php echo esc_url($login_url); ?>"><i class="fa fa-sign-in" aria-hidden="true"></i><?php esc_html_e('登录后购买', 'aksrt'); ?></a>
<?php if ($amount > 0 && $pay_modo !== 'points' && function_exists('aksrt_allow_guest_content_pay') && aksrt_allow_guest_content_pay($post)) : ?>
<button type="button" class="but btn-block <?php echo esc_attr($theme); ?> cashier-link" data-aksrt-pay-content="<?php echo esc_attr((string) $post->ID); ?>"><i class="fa fa-shopping-cart" aria-hidden="true"></i><?php echo esc_html(sprintf(__('游客购买(%s)', 'aksrt'), $format($amount))); ?></button>
<?php endif; ?>
<?php else : ?>
<button type="button" class="but btn-block <?php echo esc_attr($theme); ?> cashier-link" data-aksrt-pay-content="<?php echo esc_attr((string) $post->ID); ?>"><i class="fa fa-shopping-cart" aria-hidden="true"></i><?php echo esc_html(sprintf(__('立即购买(%s)', 'aksrt'), $format($amount))); ?></button>
<?php endif; ?>
<?php if ($demo !== '') : ?>
<a class="but hollow btn-block" href="<?php echo esc_url($demo); ?>" target="_blank" rel="noopener noreferrer"><i class="fa fa-external-link" aria-hidden="true"></i><?php esc_html_e('演示地址', 'aksrt'); ?></a>
<?php endif; ?>
</div>
</section>
<?php
return (string) ob_get_clean();
}
@@ -144,6 +145,7 @@ class WidgetPostPayBox extends \WP_Widget {
if ($html === '') {
return;
}
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
@@ -151,7 +153,7 @@ class WidgetPostPayBox extends \WP_Widget {
public function form($instance): void {
$theme = sanitize_html_class((string) ($instance['theme'] ?? 'jb-red'));
$opts = [
$options = [
'jb-red' => __('红', 'aksrt'),
'jb-yellow' => __('橙', 'aksrt'),
'jb-blue' => __('蓝', 'aksrt'),
@@ -161,19 +163,19 @@ class WidgetPostPayBox extends \WP_Widget {
'jb-vip2' => __('会员灰', 'aksrt'),
];
?>
<p class="description"><?php esc_html_e('仅在文章/帖子详情侧栏显示当前内容的付费购买卡。', 'aksrt'); ?></p>
<p class="description"><?php esc_html_e('仅在文章页 - 侧边栏显示付费文章的紧凑购买卡。', 'aksrt'); ?></p>
<p>
<label><?php esc_html_e('色彩主题', 'aksrt'); ?>
<select class="widefat" name="<?php echo esc_attr($this->get_field_name('theme')); ?>">
<?php foreach ($opts as $k => $label) : ?>
<option value="<?php echo esc_attr($k); ?>" <?php selected($theme, $k); ?>><?php echo esc_html($label); ?></option>
<?php foreach ($options as $value => $label) : ?>
<option value="<?php echo esc_attr($value); ?>" <?php selected($theme, $value); ?>><?php echo esc_html($label); ?></option>
<?php endforeach; ?>
</select>
</label>
</p>
<?php $this->render_common_form_section($instance); ?>
<?php
$this->render_common_form_section($instance);
}
}
public function update($new_instance, $old_instance): array {
$theme = sanitize_html_class((string) ($new_instance['theme'] ?? 'jb-red'));
@@ -181,6 +183,7 @@ class WidgetPostPayBox extends \WP_Widget {
if (!in_array($theme, $allowed, true)) {
$theme = 'jb-red';
}
$business = ['theme' => $theme]; return $this->update_common_fields($new_instance, $old_instance, $business);
return $this->update_common_fields($new_instance, $old_instance, ['theme' => $theme]);
}
}
-95
View File
@@ -1,95 +0,0 @@
<?php
/**
* In-article / topic TOC shell (zibll widget_ui_posts_navs parity).
*
* Shell matches the built-in single sidebar TOC so page-template.js can fill
* `.posts-nav-box.toc-widget > .toc-list` from `[data-nav]` headings.
*
* @package AKSRT
*/
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetPostsNavs extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
'aksrt_posts_navs',
esc_html__('AKSRT 文章目录树', 'aksrt'),
[
'description' => esc_html__('显示当前文章/帖子标题目录,非详情页不输出。标题超过 2 个才填充。', 'aksrt'),
'classname' => 'widget_aksrt_posts_navs',
]
);
}
public function widget($args, $instance): void {
if (!is_singular(['post', 'page'])) {
if (current_user_can('manage_options')) {
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo function_exists('aksrt_get_widget_null')
? aksrt_get_widget_null(__('目录树仅在文章页面显示', 'aksrt')) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
: '<p class="widget-empty">' . esc_html__('目录树仅在详情页显示', 'aksrt') . '</p>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
return;
}
$title = sanitize_text_field((string) ($instance['title'] ?? __('文章目录', 'aksrt')));
$mini_title = sanitize_text_field((string) ($instance['mini_title'] ?? ''));
$affix = function_exists('aksrt_widget_affix_attrs') ? aksrt_widget_affix_attrs($instance) : ['class' => '', 'attr' => ''];
$data_title = $title;
if ($mini_title !== '') {
$data_title .= ($data_title !== '' ? ' · ' : '') . $mini_title;
}
if ($data_title === '') {
$data_title = __('文章目录', 'aksrt');
}
// Mirror aksrt-single-render built-in TOC shell so page-template.js binds.
$classes = 'zib-widget toc-widget posts-nav-box' . $affix['class'];
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
printf(
'<div class="%s"%s data-title="%s" aria-label="%s">',
esc_attr($classes),
$affix['attr'],
esc_attr($data_title),
esc_attr($data_title)
);
echo '<h3 class="widget-title toc-widget__title">' . esc_html($data_title) . '</h3>';
echo '<ol class="toc-list"></ol>';
echo '</div>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
public function form($instance): void {
$title = (string) ($instance['title'] ?? __('文章目录', 'aksrt'));
$mini_title = (string) ($instance['mini_title'] ?? '');
?>
<p>
<label for="<?php echo esc_attr($this->get_field_id('title')); ?>"><?php esc_html_e('标题:', 'aksrt'); ?></label>
<input class="widefat" type="text" id="<?php echo esc_attr($this->get_field_id('title')); ?>" name="<?php echo esc_attr($this->get_field_name('title')); ?>" value="<?php echo esc_attr($title); ?>">
</p>
<p>
<label for="<?php echo esc_attr($this->get_field_id('mini_title')); ?>"><?php esc_html_e('副标题:', 'aksrt'); ?></label>
<input class="widefat" type="text" id="<?php echo esc_attr($this->get_field_id('mini_title')); ?>" name="<?php echo esc_attr($this->get_field_name('mini_title')); ?>" value="<?php echo esc_attr($mini_title); ?>">
</p>
<p class="description"><?php esc_html_e('请添加到文章/帖子侧栏。正文标题 ≥3 个时由前端填充目录;无标题则自动移除。', 'aksrt'); ?></p>
<?php $this->render_common_form_section($instance); ?>
<?php
}
public function update($new_instance, $old_instance): array {
return $this->update_common_fields($new_instance, $old_instance, [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'mini_title' => sanitize_text_field((string) ($new_instance['mini_title'] ?? '')),
]);
}
}
-251
View File
@@ -1,251 +0,0 @@
<?php
/**
* Pricing plan cards (zibll zib_widget_pricing_plans lite).
*
* @package AKSRT
*/
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetPricingPlans extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
'aksrt_pricing_plans',
esc_html__('AKSRT 价格方案', 'aksrt'),
[
'description' => esc_html__('多列定价卡片 + 信任条(落地页/VIP 介绍)。', 'aksrt'),
'classname' => 'widget_aksrt_pricing_plans',
]
);
}
public function widget($args, $instance): void {
$plans = $this->normalize_plans($instance['lists'] ?? $instance['plans'] ?? []);
if (!$plans) {
if (current_user_can('manage_options')) {
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo function_exists('aksrt_get_widget_null')
? aksrt_get_widget_null(__('请配置至少一个价格方案', 'aksrt')) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
: '<p class="widget-empty">' . esc_html__('请配置至少一个价格方案', 'aksrt') . '</p>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
return;
}
$trust = $this->normalize_trust($instance['trust_items'] ?? []);
$currency = sanitize_text_field((string) ($instance['currency'] ?? '¥'));
if ($currency === '') {
$currency = '¥';
}
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base);
if ($title !== '') {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
echo '<div class="aksrt-pricing">';
echo '<div class="aksrt-pricing__cards">';
$pi = 0;
foreach ($plans as $plan) {
$accent = sanitize_key((string) ($plan['color'] ?? 'blue'));
if (!in_array($accent, ['pink', 'purple', 'blue', 'green', 'yellow', 'cyan'], true)) {
$accent = 'blue';
}
$featured = !empty($plan['is_featured']);
$pack = function_exists('aksrt_obs_animate_attrs') ? aksrt_obs_animate_attrs('slideup', false, $pi * 0.12) : ['class' => '', 'attr' => ''];
$class = 'aksrt-pricing__card accent-' . $accent . ($featured ? ' is-featured' : '') . $pack['class'];
echo '<div class="' . esc_attr(trim($class)) . '"' . $pack['attr'] . '>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$pi++;
if ($featured || !empty($plan['tag'])) {
$badge = (string) ($plan['tag'] !== '' ? $plan['tag'] : __('推荐', 'aksrt'));
echo '<span class="aksrt-pricing__badge">' . esc_html($badge) . '</span>';
}
echo '<h3 class="aksrt-pricing__name">' . esc_html($plan['name']) . '</h3>';
if ($plan['desc'] !== '') {
echo '<p class="aksrt-pricing__desc">' . esc_html($plan['desc']) . '</p>';
}
$price = (float) $plan['price'];
$show = (float) $plan['show_price'];
echo '<div class="aksrt-pricing__price">';
echo '<span class="aksrt-pricing__main"><span class="aksrt-pricing__mark">' . esc_html($currency) . '</span>' . esc_html($this->fmt($price)) . '</span>';
if ($show > $price && $show > 0) {
echo '<span class="aksrt-pricing__original"><span class="aksrt-pricing__mark">' . esc_html($currency) . '</span>' . esc_html($this->fmt($show)) . '</span>';
}
echo '</div>';
if ($plan['features']) {
echo '<ul class="aksrt-pricing__features">';
foreach ($plan['features'] as $ft) {
echo '<li><i class="fa fa-check" aria-hidden="true"></i><span>' . esc_html($ft) . '</span></li>';
}
echo '</ul>';
}
if ($plan['btn_text'] !== '' && $plan['btn_url'] !== '') {
$url = $plan['btn_url'];
if (!empty($instance['go_link']) && function_exists('aksrt_go_link')) {
$url = aksrt_go_link($url, true);
}
echo '<div class="aksrt-pricing__action"><a class="aksrt-pricing__btn" href="' . esc_url($url) . '">' . esc_html($plan['btn_text']) . '</a></div>';
}
echo '</div>';
}
echo '</div>';
if ($trust) {
echo '<div class="aksrt-pricing__trust">';
foreach ($trust as $ti) {
echo '<div class="aksrt-pricing__trust-item">';
if ($ti['icon'] !== '') {
echo '<i class="' . esc_attr($ti['icon']) . '" aria-hidden="true"></i>';
}
echo '<div><strong>' . esc_html($ti['title']) . '</strong>';
if ($ti['desc'] !== '') {
echo '<span>' . esc_html($ti['desc']) . '</span>';
}
echo '</div></div>';
}
echo '</div>';
}
echo '</div>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
private function fmt(float $n): string {
if (abs($n - round($n)) < 0.001) {
return (string) (int) round($n);
}
return number_format($n, 2, '.', '');
}
/**
* @param mixed $raw
* @return array<int, array<string,mixed>>
*/
private function normalize_plans($raw): array {
if (is_string($raw)) {
$decoded = json_decode($raw, true);
$raw = is_array($decoded) ? $decoded : [];
}
if (!is_array($raw)) {
return [];
}
$out = [];
foreach ($raw as $row) {
if (!is_array($row)) {
continue;
}
$name = sanitize_text_field((string) ($row['name'] ?? $row['title'] ?? ''));
if ($name === '') {
continue;
}
$intro = (string) ($row['intro'] ?? $row['features'] ?? '');
if (is_array($row['features'] ?? null)) {
$features = array_values(array_filter(array_map('sanitize_text_field', $row['features'])));
} else {
$features = array_values(array_filter(array_map('sanitize_text_field', preg_split('/\r\n|\r|\n/', $intro) ?: [])));
}
$btn = is_array($row['btn'] ?? null) ? $row['btn'] : [];
$out[] = [
'name' => $name,
'desc' => sanitize_text_field((string) ($row['desc'] ?? '')),
'price' => (float) ($row['price'] ?? 0),
'show_price' => (float) ($row['show_price'] ?? 0),
'color' => sanitize_key((string) ($row['color'] ?? 'blue')),
'is_featured' => !empty($row['is_featured']) ? 1 : 0,
'tag' => sanitize_text_field((string) ($row['tag'] ?? '')),
'features' => $features,
'btn_text' => sanitize_text_field((string) ($row['btn_text'] ?? $btn['text'] ?? '')),
'btn_url' => esc_url_raw(trim((string) ($row['btn_url'] ?? $btn['url'] ?? ''))),
];
if (count($out) >= 6) {
break;
}
}
return $out;
}
/**
* @param mixed $raw
* @return array<int, array{title:string,desc:string,icon:string}>
*/
private function normalize_trust($raw): array {
if (is_string($raw)) {
$decoded = json_decode($raw, true);
$raw = is_array($decoded) ? $decoded : [];
}
if (!is_array($raw)) {
return [];
}
$out = [];
foreach ($raw as $row) {
if (!is_array($row)) {
continue;
}
$title = sanitize_text_field((string) ($row['title'] ?? ''));
if ($title === '') {
continue;
}
$out[] = [
'title' => $title,
'desc' => sanitize_text_field((string) ($row['desc'] ?? '')),
'icon' => sanitize_text_field((string) ($row['icon'] ?? 'fa fa-check-circle')),
];
if (count($out) >= 6) {
break;
}
}
return $out;
}
public function form($instance): void {
$plans = is_string($instance['lists'] ?? null)
? (string) $instance['lists']
: wp_json_encode($this->normalize_plans($instance['lists'] ?? []), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$trust = is_string($instance['trust_items'] ?? null)
? (string) $instance['trust_items']
: wp_json_encode($this->normalize_trust($instance['trust_items'] ?? []), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
?>
<p>
<label><?php esc_html_e('标题', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('title')); ?>" value="<?php echo esc_attr((string) ($instance['title'] ?? '')); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('货币符号', 'aksrt'); ?>
<input class="tiny-text" type="text" name="<?php echo esc_attr($this->get_field_name('currency')); ?>" value="<?php echo esc_attr((string) ($instance['currency'] ?? '¥')); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('方案 JSON', 'aksrt'); ?>
<textarea class="widefat" rows="8" name="<?php echo esc_attr($this->get_field_name('lists')); ?>" placeholder='[{"name":"Pro","price":99,"show_price":199,"desc":"适合创作者","features":["无限下载","优先支持"],"btn_text":"立即开通","btn_url":"#","is_featured":1,"color":"pink"}]'><?php echo esc_textarea((string) $plans); ?></textarea>
</label>
</p>
<p>
<label><?php esc_html_e('信任条 JSON', 'aksrt'); ?>
<textarea class="widefat" rows="3" name="<?php echo esc_attr($this->get_field_name('trust_items')); ?>"><?php echo esc_textarea((string) $trust); ?></textarea>
</label>
</p>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('go_link')); ?>" value="1" <?php checked(!empty($instance['go_link'])); ?>> <?php esc_html_e('外链跳转页', 'aksrt'); ?></label>
</p>
<?php
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'currency' => sanitize_text_field((string) ($new_instance['currency'] ?? '¥')),
'lists' => $this->normalize_plans($new_instance['lists'] ?? []),
'trust_items' => $this->normalize_trust($new_instance['trust_items'] ?? []),
'go_link' => !empty($new_instance['go_link']) ? 1 : 0,
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
-166
View File
@@ -1,166 +0,0 @@
<?php
/**
* Testimonials / virtual reviews carousel (zibll zib_widget_reviews lite).
*
* @package AKSRT
*/
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetReviews extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
'aksrt_reviews',
esc_html__('AKSRT 用户评价', 'aksrt'),
[
'description' => esc_html__('虚拟用户评价卡片横向滚动。', 'aksrt'),
'classname' => 'widget_aksrt_reviews',
]
);
}
public function widget($args, $instance): void {
$reviews = $this->normalize($instance['reviews'] ?? []);
if (!$reviews) {
if (current_user_can('manage_options')) {
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo function_exists('aksrt_get_widget_null')
? aksrt_get_widget_null(__('请添加至少一条评价', 'aksrt')) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
: '<p class="widget-empty">' . esc_html__('请添加至少一条评价', 'aksrt') . '</p>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
return;
}
$pc = max(1, min(6, (float) ($instance['desktop_per_view'] ?? 3)));
$m = max(1, min(3, (float) ($instance['mobile_per_view'] ?? 1.2)));
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base);
if ($title !== '') {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
$style = sprintf('--aksrt-rev-pc:%s;--aksrt-rev-m:%s;', rtrim(rtrim(number_format($pc, 2, '.', ''), '0'), '.'), rtrim(rtrim(number_format($m, 2, '.', ''), '0'), '.'));
echo '<div class="aksrt-reviews" style="' . esc_attr($style) . '">';
echo '<div class="swiper-container swiper-scroll aksrt-reviews-swiper">';
echo '<div class="swiper-wrapper aksrt-reviews__track">';
foreach ($reviews as $r) {
echo '<div class="swiper-slide aksrt-reviews__slide"><div class="aksrt-reviews__card" data-aksrt-tilt data-tilt-max="8">';
echo '<div class="aksrt-reviews__head">';
if ($r['avatar'] !== '') {
$ph = get_template_directory_uri() . '/assets/img/placeholder.svg';
echo '<img class="aksrt-reviews__avatar aksrt-lazy lazyload" data-src="' . esc_url($r['avatar']) . '" src="' . esc_url($ph) . '" alt="" loading="lazy" width="44" height="44" />';
} else {
echo '<span class="aksrt-reviews__avatar is-placeholder"><i class="fa fa-user" aria-hidden="true"></i></span>';
}
echo '<div class="aksrt-reviews__meta">';
if ($r['name'] !== '') {
$name_html = esc_html($r['name']);
if ($r['url'] !== '') {
$name_html = '<a href="' . esc_url($r['url']) . '" target="_blank" rel="noopener noreferrer">' . $name_html . '</a>';
}
echo '<div class="aksrt-reviews__name">' . $name_html . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
if ($r['role'] !== '') {
echo '<div class="aksrt-reviews__role">' . esc_html($r['role']) . '</div>';
}
echo '</div></div>';
echo '<div class="aksrt-reviews__stars" aria-label="' . esc_attr(sprintf(__('%d 星', 'aksrt'), $r['rating'])) . '">';
for ($i = 1; $i <= 5; $i++) {
echo '<i class="fa fa-star' . ($i <= $r['rating'] ? ' is-on' : ' is-off') . '" aria-hidden="true"></i>';
}
echo '</div>';
if ($r['content'] !== '') {
echo '<p class="aksrt-reviews__content">' . nl2br(esc_html($r['content'])) . '</p>';
}
echo '<i class="fa fa-quote-right aksrt-reviews__quote" aria-hidden="true"></i>';
echo '</div></div>';
}
echo '</div>';
echo '<div class="swiper-button-prev" aria-label="' . esc_attr__('上一页', 'aksrt') . '"></div>';
echo '<div class="swiper-button-next" aria-label="' . esc_attr__('下一页', 'aksrt') . '"></div>';
echo '</div></div>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* @param mixed $raw
* @return array<int, array{name:string,role:string,avatar:string,content:string,rating:int,url:string}>
*/
private function normalize($raw): array {
if (is_string($raw)) {
$decoded = json_decode($raw, true);
$raw = is_array($decoded) ? $decoded : [];
}
if (!is_array($raw)) {
return [];
}
$out = [];
foreach ($raw as $row) {
if (!is_array($row)) {
continue;
}
$name = sanitize_text_field((string) ($row['name'] ?? ''));
$content = sanitize_textarea_field((string) ($row['content'] ?? ''));
if ($name === '' && $content === '') {
continue;
}
$out[] = [
'name' => $name,
'role' => sanitize_text_field((string) ($row['role'] ?? '')),
'avatar' => esc_url_raw(trim((string) ($row['avatar'] ?? ''))),
'content' => $content,
'rating' => max(0, min(5, (int) ($row['rating'] ?? 5))),
'url' => esc_url_raw(trim((string) ($row['url'] ?? ''))),
];
if (count($out) >= 20) {
break;
}
}
return $out;
}
public function form($instance): void {
$json = is_string($instance['reviews'] ?? null)
? (string) $instance['reviews']
: wp_json_encode($this->normalize($instance['reviews'] ?? []), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
?>
<p>
<label><?php esc_html_e('标题', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('title')); ?>" value="<?php echo esc_attr((string) ($instance['title'] ?? '')); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('评价 JSON', 'aksrt'); ?>
<textarea class="widefat" rows="8" name="<?php echo esc_attr($this->get_field_name('reviews')); ?>" placeholder='[{"name":"张三","role":"站长","rating":5,"content":"很好用","avatar":""}]'><?php echo esc_textarea((string) $json); ?></textarea>
</label>
</p>
<p>
<label><?php esc_html_e('PC 每屏', 'aksrt'); ?>
<input class="tiny-text" type="number" min="1" max="6" step="0.1" name="<?php echo esc_attr($this->get_field_name('desktop_per_view')); ?>" value="<?php echo esc_attr((string) ($instance['desktop_per_view'] ?? '3')); ?>">
</label>
<label><?php esc_html_e('移动每屏', 'aksrt'); ?>
<input class="tiny-text" type="number" min="1" max="3" step="0.1" name="<?php echo esc_attr($this->get_field_name('mobile_per_view')); ?>" value="<?php echo esc_attr((string) ($instance['mobile_per_view'] ?? '1.2')); ?>">
</label>
</p>
<?php
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'reviews' => $this->normalize($new_instance['reviews'] ?? []),
'desktop_per_view' => max(1, min(6, (float) ($new_instance['desktop_per_view'] ?? 3))),
'mobile_per_view' => max(1, min(3, (float) ($new_instance['mobile_per_view'] ?? 1.2))),
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
-194
View File
@@ -1,194 +0,0 @@
<?php
/**
* Stats number cards (zibll zib_widget_stats lite).
*
* @package AKSRT
*/
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetStats extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
'aksrt_stats',
esc_html__('AKSRT 数据统计', 'aksrt'),
[
'description' => esc_html__('数字统计卡片网格(站点数据展示)。', 'aksrt'),
'classname' => 'widget_aksrt_stats',
]
);
}
public function widget($args, $instance): void {
$stats = $this->normalize_stats($instance['stats'] ?? []);
if (!$stats) {
if (current_user_can('manage_options')) {
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo function_exists('aksrt_get_widget_null')
? aksrt_get_widget_null(__('请配置至少一项统计数据', 'aksrt')) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
: '<p class="widget-empty">' . esc_html__('请配置至少一项统计数据', 'aksrt') . '</p>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
return;
}
$cols = max(1, min(4, (int) ($instance['columns'] ?? 4)));
$m_cols = max(1, min(3, (int) ($instance['mobile_columns'] ?? 2)));
$intro_title = trim((string) ($instance['intro_title'] ?? ''));
$intro_sub = trim((string) ($instance['intro_subtitle'] ?? ''));
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base);
if ($title !== '') {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
$style = sprintf('--aksrt-stats-pc:%d;--aksrt-stats-m:%d;', $cols, $m_cols);
echo '<div class="aksrt-stats" style="' . esc_attr($style) . '">';
if ($intro_title !== '' || $intro_sub !== '') {
echo '<div class="aksrt-stats__intro">';
if ($intro_title !== '') {
echo '<h2 class="aksrt-stats__intro-title">' . esc_html($intro_title) . '</h2>';
}
if ($intro_sub !== '') {
echo '<p class="aksrt-stats__intro-sub">' . esc_html($intro_sub) . '</p>';
}
echo '</div>';
}
echo '<div class="aksrt-stats__grid">';
$palette = ['pink', 'purple', 'orange', 'blue', 'green'];
$i = 0;
foreach ($stats as $s) {
$color = sanitize_key((string) ($s['color'] ?? 'auto'));
if ($color === '' || $color === 'auto') {
$color = $palette[$i % count($palette)];
}
$i++;
echo '<div class="aksrt-stats__item color-' . esc_attr($color) . '" data-aksrt-animate="fade-up">';
if ($s['icon'] !== '') {
echo '<div class="aksrt-stats__icon"><i class="' . esc_attr($s['icon']) . '" aria-hidden="true"></i></div>';
}
echo '<div class="aksrt-stats__value">';
if ($s['prefix'] !== '') {
echo '<span class="aksrt-stats__prefix">' . esc_html($s['prefix']) . '</span>';
}
// Wire effects.js count-up when number is numeric.
$raw_num = preg_replace('/[^\d.]/', '', (string) $s['number']);
$is_numeric = $raw_num !== '' && is_numeric($raw_num);
if ($is_numeric) {
$dec = 0;
if (str_contains((string) $raw_num, '.')) {
$dec = strlen(substr(strrchr((string) $raw_num, '.'), 1));
}
printf(
'<span class="aksrt-stats__number" data-aksrt-countup data-target="%s" data-duration="1600" data-decimals="%d">0</span>',
esc_attr((string) $raw_num),
(int) $dec
);
} else {
echo '<span class="aksrt-stats__number" data-target="' . esc_attr($s['number']) . '">' . esc_html($s['number']) . '</span>';
}
if ($s['unit'] !== '') {
echo '<span class="aksrt-stats__unit">' . esc_html($s['unit']) . '</span>';
}
echo '</div>';
if ($s['label'] !== '') {
echo '<div class="aksrt-stats__label">' . esc_html($s['label']) . '</div>';
}
echo '</div>';
}
echo '</div></div>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* @param mixed $raw
* @return array<int, array{number:string,label:string,unit:string,prefix:string,icon:string,color:string}>
*/
private function normalize_stats($raw): array {
if (is_string($raw)) {
$decoded = json_decode($raw, true);
$raw = is_array($decoded) ? $decoded : [];
}
if (!is_array($raw)) {
return [];
}
$out = [];
foreach ($raw as $row) {
if (!is_array($row)) {
continue;
}
$number = sanitize_text_field((string) ($row['number'] ?? ''));
if ($number === '') {
continue;
}
$out[] = [
'number' => $number,
'label' => sanitize_text_field((string) ($row['label'] ?? '')),
'unit' => sanitize_text_field((string) ($row['unit'] ?? '')),
'prefix' => sanitize_text_field((string) ($row['prefix'] ?? '')),
'icon' => sanitize_text_field((string) ($row['icon'] ?? '')),
'color' => sanitize_key((string) ($row['color'] ?? 'auto')),
];
if (count($out) >= 12) {
break;
}
}
return $out;
}
public function form($instance): void {
$stats = is_string($instance['stats'] ?? null)
? (string) $instance['stats']
: wp_json_encode($this->normalize_stats($instance['stats'] ?? []), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
?>
<p>
<label><?php esc_html_e('标题', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('title')); ?>" value="<?php echo esc_attr((string) ($instance['title'] ?? '')); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('数据 JSON', 'aksrt'); ?>
<textarea class="widefat" rows="6" name="<?php echo esc_attr($this->get_field_name('stats')); ?>" placeholder='[{"number":"1280","label":"用户","unit":"+","icon":"fas fa-users","color":"blue"}]'><?php echo esc_textarea((string) $stats); ?></textarea>
</label>
</p>
<p>
<label><?php esc_html_e('PC 列数', 'aksrt'); ?>
<input class="tiny-text" type="number" min="1" max="4" name="<?php echo esc_attr($this->get_field_name('columns')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['columns'] ?? 4))); ?>">
</label>
<label><?php esc_html_e('移动列数', 'aksrt'); ?>
<input class="tiny-text" type="number" min="1" max="3" name="<?php echo esc_attr($this->get_field_name('mobile_columns')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['mobile_columns'] ?? 2))); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('介绍标题', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('intro_title')); ?>" value="<?php echo esc_attr((string) ($instance['intro_title'] ?? '')); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('介绍副标题', 'aksrt'); ?>
<textarea class="widefat" rows="2" name="<?php echo esc_attr($this->get_field_name('intro_subtitle')); ?>"><?php echo esc_textarea((string) ($instance['intro_subtitle'] ?? '')); ?></textarea>
</label>
</p>
<?php
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'stats' => $this->normalize_stats($new_instance['stats'] ?? []),
'columns' => max(1, min(4, (int) ($new_instance['columns'] ?? 4))),
'mobile_columns' => max(1, min(3, (int) ($new_instance['mobile_columns'] ?? 2))),
'intro_title' => sanitize_text_field((string) ($new_instance['intro_title'] ?? '')),
'intro_subtitle' => sanitize_text_field((string) ($new_instance['intro_subtitle'] ?? '')),
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+1 -1
View File
@@ -17,7 +17,7 @@ class WidgetUserCard extends \WP_Widget {
public function __construct() {
parent::__construct(
'aksrt_user_card',
__('用户卡片', 'aksrt'),
__('AKSRT 用户卡片', 'aksrt'),
[
'description' => __('未登录显示登录入口;登录后显示当前用户卡片、签到、快捷按钮与更多文章。', 'aksrt'),
'classname' => 'widget_aksrt_user_card',
+1 -1
View File
@@ -17,7 +17,7 @@ class WidgetUserRanking extends \WP_Widget {
public function __construct() {
parent::__construct(
'aksrt_user_ranking',
__('用户排行榜', 'aksrt'),
__('AKSRT 用户排行榜', 'aksrt'),
[
'description' => __('按积分、文章数或粉丝数排行的用户列表。', 'aksrt'),
'classname' => 'widget_aksrt_user_ranking',
+1 -1
View File
@@ -18,7 +18,7 @@ class WidgetVideoPlayer extends \WP_Widget {
public function __construct() {
parent::__construct(
'aksrt_video_player',
__('视频播放器', 'aksrt'),
__('AKSRT 视频播放器', 'aksrt'),
[
'description' => __('侧栏/流体区嵌入视频(共享 aksrt-video 播放器)。', 'aksrt'),
'classname' => 'widget_aksrt_video_player',
+1 -1
View File
@@ -43,7 +43,7 @@ class WidgetYiyan extends \WP_Widget {
public function __construct() {
parent::__construct(
'aksrt_yiyan',
__('一言轮播', 'aksrt'),
__('AKSRT 一言轮播', 'aksrt'),
[
'description' => __('显示随机一言/每日语录,支持自动轮播。', 'aksrt'),
'classname' => 'widget_aksrt_yiyan yiyan-box',