diff --git a/assets/css/frontend/widget-batch5.css b/assets/css/frontend/widget-batch5.css
index a8c0a29e..835d1ac8 100644
--- a/assets/css/frontend/widget-batch5.css
+++ b/assets/css/frontend/widget-batch5.css
@@ -776,3 +776,14 @@
flex-direction: column;
align-items: flex-start;
}
+
+/* User ranking extras (batch8) */
+.aksrt-user-ranking__item { align-items: center; }
+.aksrt-user-ranking__meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
+.aksrt-user-ranking__desc {
+ font-size: 11px;
+ color: var(--muted-2-color, #999);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
diff --git a/src/AKSRT/Core/WidgetAjax.php b/src/AKSRT/Core/WidgetAjax.php
index c16a409e..5cd0166d 100644
--- a/src/AKSRT/Core/WidgetAjax.php
+++ b/src/AKSRT/Core/WidgetAjax.php
@@ -1,6 +1,6 @@
'GET',
- 'callback' => [$this, 'rest_main_posts'],
- 'permission_callback' => '__return_true',
- ]);
- register_rest_route('aksrt/v1', '/widgets/tab-posts', [
- 'methods' => 'GET',
- 'callback' => [$this, 'rest_tab_posts'],
- 'permission_callback' => '__return_true',
- ]);
- register_rest_route('aksrt/v1', '/widgets/paid-posts', [
- 'methods' => 'GET',
- 'callback' => [$this, 'rest_paid_posts'],
- 'permission_callback' => '__return_true',
- ]);
- register_rest_route('aksrt/v1', '/widgets/category-tabs', [
- 'methods' => 'GET',
- 'callback' => [$this, 'rest_category_tabs'],
- 'permission_callback' => '__return_true',
- ]);
+ $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/hot-posts' => 'rest_hot_posts',
+ '/widgets/oneline-posts' => 'rest_oneline_posts',
+ '/widgets/recent-comments'=> 'rest_recent_comments',
+ '/widgets/user-lists' => 'rest_user_lists',
+ '/widgets/forum-topics' => 'rest_forum_topics',
+ '/widgets/forum-terms' => 'rest_forum_terms',
+ ];
+ foreach ($routes as $path => $cb) {
+ register_rest_route('aksrt/v1', $path, [
+ 'methods' => 'GET',
+ 'callback' => [$this, $cb],
+ 'permission_callback' => '__return_true',
+ ]);
+ }
}
public function rest_main_posts(\WP_REST_Request $request) {
if (!class_exists('\AKSRT\Widgets\WidgetMainPosts')) {
- return new \WP_REST_Response(['success' => false, 'message' => 'unavailable'], 500);
+ return $this->fail();
}
$instance = [
'count' => max(1, min(30, (int) ($request->get_param('count') ?? 6))),
@@ -62,21 +61,12 @@ class WidgetAjax {
'paginate' => sanitize_key((string) ($request->get_param('paginate') ?? 'ajax')),
];
$page = max(1, (int) ($request->get_param('page') ?? $request->get_param('paged') ?? 1));
- $payload = \AKSRT\Widgets\WidgetMainPosts::query_payload($instance, $page);
- return new \WP_REST_Response([
- 'success' => true,
- 'html' => (string) ($payload['html'] ?? ''),
- 'paginate_html' => (string) ($payload['paginate_html'] ?? ''),
- 'has_more' => !empty($payload['has_more']),
- 'page' => (int) ($payload['page'] ?? $page),
- 'max_pages' => (int) ($payload['max_pages'] ?? 1),
- 'total' => (int) ($payload['total'] ?? 0),
- ], 200);
+ return $this->payload(\AKSRT\Widgets\WidgetMainPosts::query_payload($instance, $page), $page);
}
public function rest_tab_posts(\WP_REST_Request $request) {
if (!class_exists('\AKSRT\Widgets\TabPostsWidget')) {
- return new \WP_REST_Response(['success' => false, 'message' => 'unavailable'], 500);
+ return $this->fail();
}
$default_count = max(1, min(20, (int) ($request->get_param('count') ?? 5)));
$tab = [
@@ -94,15 +84,12 @@ class WidgetAjax {
} else {
$html = '
' . $html . '
';
}
- return new \WP_REST_Response([
- 'success' => true,
- 'html' => $html,
- ], 200);
+ return new \WP_REST_Response(['success' => true, 'html' => $html], 200);
}
public function rest_paid_posts(\WP_REST_Request $request) {
if (!class_exists('\AKSRT\Widgets\WidgetPaidPosts')) {
- return new \WP_REST_Response(['success' => false, 'message' => 'unavailable'], 500);
+ return $this->fail();
}
$instance = [
'count' => max(1, min(20, (int) ($request->get_param('count') ?? 6))),
@@ -110,16 +97,7 @@ class WidgetAjax {
'paginate' => sanitize_key((string) ($request->get_param('paginate') ?? 'ajax')),
];
$page = max(1, (int) ($request->get_param('page') ?? $request->get_param('paged') ?? 1));
- $payload = \AKSRT\Widgets\WidgetPaidPosts::query_payload($instance, $page);
- return new \WP_REST_Response([
- 'success' => true,
- 'html' => (string) ($payload['html'] ?? ''),
- 'paginate_html' => (string) ($payload['paginate_html'] ?? ''),
- 'has_more' => !empty($payload['has_more']),
- 'page' => (int) ($payload['page'] ?? $page),
- 'max_pages' => (int) ($payload['max_pages'] ?? 1),
- 'total' => (int) ($payload['total'] ?? 0),
- ], 200);
+ return $this->payload(\AKSRT\Widgets\WidgetPaidPosts::query_payload($instance, $page), $page);
}
public function rest_category_tabs(\WP_REST_Request $request) {
@@ -152,7 +130,111 @@ class WidgetAjax {
echo '';
}
wp_reset_postdata();
- $html = (string) ob_get_clean();
+ return new \WP_REST_Response(['success' => true, 'html' => (string) ob_get_clean()], 200);
+ }
+
+ public function rest_hot_posts(\WP_REST_Request $request) {
+ if (!class_exists('\AKSRT\Widgets\HotPostsWidget')) {
+ return $this->fail();
+ }
+ $instance = [
+ 'count' => max(1, min(20, (int) ($request->get_param('count') ?? 5))),
+ 'order_by' => sanitize_key((string) ($request->get_param('order_by') ?? 'views')),
+ 'days' => max(0, (int) ($request->get_param('days') ?? 30)),
+ 'show_thumb' => $request->get_param('show_thumb') === null ? 1 : (int) $request->get_param('show_thumb'),
+ 'paginate' => sanitize_key((string) ($request->get_param('paginate') ?? 'ajax')),
+ ];
+ $page = max(1, (int) ($request->get_param('page') ?? 1));
+ return $this->payload(\AKSRT\Widgets\HotPostsWidget::query_payload($instance, $page), $page);
+ }
+
+ public function rest_oneline_posts(\WP_REST_Request $request) {
+ if (!class_exists('\AKSRT\Widgets\WidgetOnelinePosts')) {
+ return $this->fail();
+ }
+ $instance = [
+ 'count' => max(1, min(20, (int) ($request->get_param('count') ?? 8))),
+ 'style' => sanitize_key((string) ($request->get_param('style') ?? 'card')),
+ '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)),
+ ];
+ $html = \AKSRT\Widgets\WidgetOnelinePosts::render_html($instance);
return new \WP_REST_Response(['success' => true, 'html' => $html], 200);
}
+
+ public function rest_recent_comments(\WP_REST_Request $request) {
+ if (!class_exists('\AKSRT\Widgets\RecentCommentsWidget')) {
+ return $this->fail();
+ }
+ $instance = [
+ 'count' => max(1, min(20, (int) ($request->get_param('count') ?? 5))),
+ 'show_avatar' => $request->get_param('show_avatar') === null ? 1 : (int) $request->get_param('show_avatar'),
+ 'outpost' => max(0, (int) ($request->get_param('outpost') ?? 0)),
+ 'outer' => sanitize_text_field((string) ($request->get_param('outer') ?? '')),
+ ];
+ $html = \AKSRT\Widgets\RecentCommentsWidget::render_html($instance);
+ return new \WP_REST_Response(['success' => true, 'html' => $html], 200);
+ }
+
+ public function rest_user_lists(\WP_REST_Request $request) {
+ if (!class_exists('\AKSRT\Widgets\WidgetUserLists')) {
+ return $this->fail();
+ }
+ $instance = [
+ 'number' => max(1, min(50, (int) ($request->get_param('number') ?? 8))),
+ 'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'user_registered')),
+ 'order' => sanitize_key((string) ($request->get_param('order') ?? 'DESC')),
+ 'include' => sanitize_text_field((string) ($request->get_param('include') ?? '')),
+ 'exclude' => sanitize_text_field((string) ($request->get_param('exclude') ?? '')),
+ 'hide_box' => (int) ($request->get_param('hide_box') ?? 0),
+ ];
+ $html = \AKSRT\Widgets\WidgetUserLists::render_html($instance);
+ return new \WP_REST_Response(['success' => true, 'html' => $html], 200);
+ }
+
+ public function rest_forum_topics(\WP_REST_Request $request) {
+ if (!class_exists('\AKSRT\Widgets\WidgetForumHotTopics')) {
+ return $this->fail();
+ }
+ $instance = [
+ 'count' => max(1, min(20, (int) ($request->get_param('count') ?? 5))),
+ 'period' => sanitize_key((string) ($request->get_param('period') ?? 'week')),
+ 'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'views')),
+ 'paginate' => sanitize_key((string) ($request->get_param('paginate') ?? 'ajax')),
+ ];
+ $page = max(1, (int) ($request->get_param('page') ?? 1));
+ return $this->payload(\AKSRT\Widgets\WidgetForumHotTopics::query_payload($instance, $page), $page);
+ }
+
+ public function rest_forum_terms(\WP_REST_Request $request) {
+ if (!class_exists('\AKSRT\Widgets\WidgetForumTopicTerms')) {
+ return $this->fail();
+ }
+ $instance = [
+ 'number' => max(1, min(40, (int) ($request->get_param('number') ?? 12))),
+ 'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'count')),
+ 'hide_empty' => $request->get_param('hide_empty') === null ? 1 : (int) $request->get_param('hide_empty'),
+ 'include' => sanitize_text_field((string) ($request->get_param('include') ?? '')),
+ 'style' => sanitize_key((string) ($request->get_param('style') ?? 'mini')),
+ ];
+ $html = \AKSRT\Widgets\WidgetForumTopicTerms::render_html($instance);
+ return new \WP_REST_Response(['success' => true, 'html' => $html], 200);
+ }
+
+ private function fail(): \WP_REST_Response {
+ return new \WP_REST_Response(['success' => false, 'message' => 'unavailable'], 500);
+ }
+
+ private function payload(array $payload, int $page): \WP_REST_Response {
+ return new \WP_REST_Response([
+ 'success' => true,
+ 'html' => (string) ($payload['html'] ?? ''),
+ 'paginate_html' => (string) ($payload['paginate_html'] ?? ''),
+ 'has_more' => !empty($payload['has_more']),
+ 'page' => (int) ($payload['page'] ?? $page),
+ 'max_pages' => (int) ($payload['max_pages'] ?? 1),
+ 'total' => (int) ($payload['total'] ?? 0),
+ ], 200);
+ }
}
diff --git a/src/AKSRT/Widgets/HotPostsWidget.php b/src/AKSRT/Widgets/HotPostsWidget.php
index f24d9b81..fb011cc5 100644
--- a/src/AKSRT/Widgets/HotPostsWidget.php
+++ b/src/AKSRT/Widgets/HotPostsWidget.php
@@ -1,5 +1,7 @@
esc_html__('按浏览量/评论数/点赞数排序的热门文章列表', 'aksrt'),
- 'customize_selective_refresh' => true,
- ]
- );
- }
+ public function __construct() {
+ parent::__construct(
+ 'aksrt_hot_posts',
+ esc_html__('AKSRT 热榜文章', 'aksrt'),
+ [
+ 'description' => esc_html__('按浏览量/评论数/点赞数排序的热门文章列表', 'aksrt'),
+ 'customize_selective_refresh' => true,
+ ]
+ );
+ }
- public function widget($args, $instance): void {
- echo $args['before_widget'];
+ public static function build_query_args(array $instance, int $paged = 1): array {
+ $count = max(1, min(20, (int) ($instance['count'] ?? 5)));
+ $order_by = sanitize_key((string) ($instance['order_by'] ?? 'views'));
+ $days = max(0, (int) ($instance['days'] ?? 30));
+ $query_args = [
+ 'post_type' => 'post',
+ 'post_status' => 'publish',
+ 'posts_per_page' => $count,
+ 'paged' => max(1, $paged),
+ 'ignore_sticky_posts' => true,
+ ];
+ if ($days > 0 && $days < 365) {
+ $query_args['date_query'] = [['after' => $days . ' days ago']];
+ }
+ switch ($order_by) {
+ case 'comments':
+ $query_args['orderby'] = 'comment_count';
+ $query_args['order'] = 'DESC';
+ break;
+ case 'likes':
+ $query_args['meta_key'] = '_aksrt_like_count';
+ $query_args['orderby'] = 'meta_value_num';
+ $query_args['order'] = 'DESC';
+ break;
+ case 'favorites':
+ $query_args['meta_key'] = '_aksrt_favorite_count';
+ $query_args['orderby'] = 'meta_value_num';
+ $query_args['order'] = 'DESC';
+ break;
+ case 'downloads':
+ $query_args['meta_key'] = '_aksrt_res_download_count';
+ $query_args['orderby'] = 'meta_value_num';
+ $query_args['order'] = 'DESC';
+ break;
+ case 'views':
+ default:
+ $query_args['meta_key'] = 'aksrt_post_views';
+ $query_args['orderby'] = 'meta_value_num';
+ $query_args['order'] = 'DESC';
+ break;
+ }
+ return $query_args;
+ }
- $title = apply_filters('widget_title', $instance['title'] ?? __('热门文章', 'aksrt'), $instance, $this->id_base);
- $count = absint($instance['count'] ?? 5);
- $order_by = $instance['order_by'] ?? 'views';
- $days = absint($instance['days'] ?? 30);
- $show_thumb = !empty($instance['show_thumb']);
+ public static function render_items_html(\WP_Query $query, array $instance, int $start = 0): string {
+ if (!$query->have_posts()) {
+ return '';
+ }
+ $show_thumb = !empty($instance['show_thumb']);
+ ob_start();
+ $index = $start;
+ while ($query->have_posts()) {
+ $query->the_post();
+ $index++;
+ $rank_class = $index <= 3 ? ' rank-top' : '';
+ $views = (int) get_post_meta(get_the_ID(), 'aksrt_post_views', true);
+ ?>
+ ' . esc_html__('暂无热门文章', 'aksrt') . '
';
+ }
+ $max_pages = max(1, (int) $q->max_num_pages);
+ $total = (int) $q->found_posts;
+ $has_more = $page < $max_pages;
+ $paginate_html = '';
+ if ($paginate === 'ajax' && $has_more) {
+ $next = add_query_arg([
+ 'page' => $page + 1,
+ 'count' => $count,
+ 'order_by' => sanitize_key((string) ($instance['order_by'] ?? 'views')),
+ 'days' => max(0, (int) ($instance['days'] ?? 30)),
+ 'show_thumb' => !empty($instance['show_thumb']) ? 1 : 0,
+ 'paginate' => 'ajax',
+ ], rest_url('aksrt/v1/widgets/hot-posts'));
+ $paginate_html = function_exists('aksrt_get_ajax_next_paginate')
+ ? aksrt_get_ajax_next_paginate($total, $page, $count, $next)
+ : '';
+ } else {
+ $paginate_html = '';
- $index = 0;
- while ($query->have_posts()) {
- $query->the_post();
- $index++;
- $rank_class = $index <= 3 ? ' rank-top' : '';
- $views = (int) get_post_meta(get_the_ID(), 'aksrt_post_views', true);
- ?>
-
-
-
-
-
-
-
-
-
- ';
- wp_reset_postdata();
- } else {
- echo function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无热门文章', 'aksrt')) : '
' . esc_html__('暂无热门文章', 'aksrt') . '
';
- }
-
- echo $args['after_widget'];
- }
-
- public function form($instance): void {
- $title = $instance['title'] ?? __('热门文章', 'aksrt');
- $count = (int) ($instance['count'] ?? 5);
- $order_by = $instance['order_by'] ?? 'views';
- $days = (int) ($instance['days'] ?? 30);
- ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- sanitize_text_field($new_instance['title'] ?? ''),
- 'count' => absint($new_instance['count'] ?? 5),
- 'order_by' => in_array($new_instance['order_by'] ?? '', ['views', 'comments', 'likes', 'favorites', 'downloads'], true) ? $new_instance['order_by'] : 'views',
- 'days' => absint($new_instance['days'] ?? 30),
- 'show_thumb' => !empty($new_instance['show_thumb']) ? 1 : 0,
- ];
- }
+ public function update($new_instance, $old_instance): array {
+ return [
+ 'title' => sanitize_text_field($new_instance['title'] ?? ''),
+ 'count' => absint($new_instance['count'] ?? 5),
+ 'order_by' => in_array($new_instance['order_by'] ?? '', ['views', 'comments', 'likes', 'favorites', 'downloads'], true) ? $new_instance['order_by'] : 'views',
+ 'days' => absint($new_instance['days'] ?? 30),
+ 'show_thumb' => !empty($new_instance['show_thumb']) ? 1 : 0,
+ 'paginate' => sanitize_key((string) ($new_instance['paginate'] ?? 'ajax')) === 'none' ? 'none' : 'ajax',
+ ];
+ }
}
diff --git a/src/AKSRT/Widgets/RecentCommentsWidget.php b/src/AKSRT/Widgets/RecentCommentsWidget.php
index 5820e1aa..bfaa45d7 100644
--- a/src/AKSRT/Widgets/RecentCommentsWidget.php
+++ b/src/AKSRT/Widgets/RecentCommentsWidget.php
@@ -1,6 +1,6 @@
id_base);
+ public static function render_html(array $instance): string {
$count = max(1, min(20, (int) ($instance['count'] ?? $instance['limit'] ?? 5)));
$show_avatar = !isset($instance['show_avatar']) || !empty($instance['show_avatar']);
$outpost = max(0, (int) ($instance['outpost'] ?? 0));
$outer = sanitize_text_field((string) ($instance['outer'] ?? ''));
-
- if ($title !== '') {
- echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
- }
-
$query = [
'number' => $count,
'status' => 'approve',
@@ -37,10 +30,6 @@ class RecentCommentsWidget extends \WP_Widget {
if ($outpost > 0) {
$query['post__not_in'] = [$outpost];
}
- if ($outer !== '') {
- // Exclude comments containing outer keywords (comma separated).
- // Implemented post-filter below.
- }
$comments = get_comments($query);
if ($outer !== '' && $comments) {
$keys = array_filter(array_map('trim', preg_split('/[,,]/', $outer) ?: []));
@@ -56,32 +45,40 @@ class RecentCommentsWidget extends \WP_Widget {
}));
}
}
-
- if ($comments) {
- echo '';
- } else {
- echo '
' . esc_html__('暂无评论', 'aksrt') . '
';
+ if (!$comments) {
+ return '
' . esc_html__('暂无评论', 'aksrt') . '
';
}
+ ob_start();
+ echo '';
+ return (string) ob_get_clean();
+ }
+ public function widget($args, $instance): void {
+ echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ $title = apply_filters('widget_title', (string) ($instance['title'] ?? __('最新评论', 'aksrt')), $instance, $this->id_base);
+ if ($title !== '') {
+ echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ }
+ echo self::render_html($instance); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
@@ -97,6 +94,9 @@ class RecentCommentsWidget extends \WP_Widget {
+
+
+
-
-
-
sanitize_text_field((string) ($new_instance['title'] ?? '')),
'count' => max(1, min(20, (int) ($new_instance['count'] ?? 5))),
+ 'show_avatar' => !empty($new_instance['show_avatar']) ? 1 : 0,
'outpost' => max(0, (int) ($new_instance['outpost'] ?? 0)),
'outer' => sanitize_text_field((string) ($new_instance['outer'] ?? '')),
- 'show_avatar' => !empty($new_instance['show_avatar']) ? 1 : 0,
];
}
}
diff --git a/src/AKSRT/Widgets/WidgetForumHotTopics.php b/src/AKSRT/Widgets/WidgetForumHotTopics.php
index a6cb4776..aa14b29e 100644
--- a/src/AKSRT/Widgets/WidgetForumHotTopics.php
+++ b/src/AKSRT/Widgets/WidgetForumHotTopics.php
@@ -1,5 +1,7 @@
__('显示论坛热门话题列表。', 'aksrt')]
- );
- }
+ public function __construct() {
+ parent::__construct(
+ 'aksrt_forum_hot_topics',
+ __('论坛 - 热门话题', 'aksrt'),
+ ['description' => __('显示论坛热门话题列表。', 'aksrt')]
+ );
+ }
- public function widget($args, $instance): void {
- $title = !empty($instance['title']) ? $instance['title'] : __('热门话题', 'aksrt');
- $count = !empty($instance['count']) ? max(1, min(20, (int) $instance['count'])) : 5;
- $period = !empty($instance['period']) ? sanitize_key($instance['period']) : 'week';
+ public static function build_query_args(array $instance, int $paged = 1): array {
+ $count = max(1, min(20, (int) ($instance['count'] ?? 5)));
+ $period = sanitize_key((string) ($instance['period'] ?? 'week'));
+ $orderby = sanitize_key((string) ($instance['orderby'] ?? 'views'));
+ $date_query = [];
+ if ($period === 'week') {
+ $date_query = [['after' => '7 days ago']];
+ } elseif ($period === 'month') {
+ $date_query = [['after' => '30 days ago']];
+ }
+ $query = [
+ 'post_type' => 'aksrt_forum_topic',
+ 'posts_per_page' => $count,
+ 'paged' => max(1, $paged),
+ 'post_status' => 'publish',
+ 'date_query' => $date_query,
+ ];
+ if ($orderby === 'comments') {
+ $query['orderby'] = 'comment_count';
+ $query['order'] = 'DESC';
+ } elseif ($orderby === 'date') {
+ $query['orderby'] = 'date';
+ $query['order'] = 'DESC';
+ } else {
+ $query['meta_key'] = '_aksrt_views';
+ $query['orderby'] = 'meta_value_num';
+ $query['order'] = 'DESC';
+ }
+ return $query;
+ }
- echo $args['before_widget'];
- echo $args['before_title'] . esc_html($title) . $args['after_title'];
+ public static function render_items_html(\WP_Query $query, int $start = 0): string {
+ if (!$query->have_posts()) {
+ return '';
+ }
+ ob_start();
+ $i = $start;
+ while ($query->have_posts()) {
+ $query->the_post();
+ $i++;
+ $views = (int) get_post_meta(get_the_ID(), '_aksrt_views', true);
+ echo '
';
+ echo '' . esc_html((string) $i) . '';
+ echo '';
+ echo esc_html(get_the_title());
+ echo '';
+ echo ' ' . esc_html((string) $views) . '';
+ echo '';
+ }
+ wp_reset_postdata();
+ return (string) ob_get_clean();
+ }
- $date_query = [];
- if ($period === 'week') {
- $date_query = [['after' => '7 days ago']];
- } elseif ($period === 'month') {
- $date_query = [['after' => '30 days ago']];
- }
+ /**
+ * @return array{html:string,paginate_html:string,has_more:bool,page:int,max_pages:int,total:int}
+ */
+ public static function query_payload(array $instance, int $page = 1): array {
+ $page = max(1, $page);
+ $count = max(1, min(20, (int) ($instance['count'] ?? 5)));
+ $paginate = sanitize_key((string) ($instance['paginate'] ?? 'ajax'));
+ $q = new \WP_Query(self::build_query_args($instance, $page));
+ $items = self::render_items_html($q, ($page - 1) * $count);
+ $html = $items !== '' ? '
' : '';
+ if ($html === '' && $page === 1) {
+ $html = '
' . esc_html__('暂无话题', 'aksrt') . '
';
+ }
+ $max_pages = max(1, (int) $q->max_num_pages);
+ $total = (int) $q->found_posts;
+ $has_more = $page < $max_pages;
+ $paginate_html = '';
+ if ($paginate === 'ajax' && $has_more) {
+ $next = add_query_arg([
+ 'page' => $page + 1,
+ 'count' => $count,
+ 'period' => sanitize_key((string) ($instance['period'] ?? 'week')),
+ 'orderby' => sanitize_key((string) ($instance['orderby'] ?? 'views')),
+ 'paginate' => 'ajax',
+ ], rest_url('aksrt/v1/widgets/forum-topics'));
+ $paginate_html = function_exists('aksrt_get_ajax_next_paginate')
+ ? aksrt_get_ajax_next_paginate($total, $page, $count, $next)
+ : '';
+ } else {
+ $paginate_html = '
';
+ }
+ return compact('html', 'paginate_html', 'has_more', 'page', 'max_pages', 'total');
+ }
- $query = new \WP_Query([
- 'post_type' => 'aksrt_forum_topic',
- 'posts_per_page' => $count,
- 'post_status' => 'publish',
- 'meta_key' => '_aksrt_views',
- 'orderby' => 'meta_value_num',
- 'order' => 'DESC',
- 'date_query' => $date_query,
- ]);
+ public function widget($args, $instance): void {
+ $title = !empty($instance['title']) ? $instance['title'] : __('热门话题', 'aksrt');
+ $instance['paginate'] = sanitize_key((string) ($instance['paginate'] ?? 'ajax'));
+ $payload = self::query_payload($instance, 1);
- if ($query->have_posts()) {
- echo '
';
- wp_reset_postdata();
- } else {
- echo '
' . esc_html__('暂无话题', 'aksrt') . '
';
- }
+ echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ $rest = rest_url('aksrt/v1/widgets/forum-topics');
+ echo '';
+ echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ }
- echo $args['after_widget'];
- }
+ public function form($instance): void {
+ $title = !empty($instance['title']) ? $instance['title'] : __('热门话题', 'aksrt');
+ $count = !empty($instance['count']) ? (int) $instance['count'] : 5;
+ $period = !empty($instance['period']) ? $instance['period'] : 'week';
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
- sanitize_text_field($new_instance['title'] ?? ''),
+ 'count' => max(1, min(20, (int) ($new_instance['count'] ?? 5))),
+ 'period' => in_array($new_instance['period'] ?? '', ['all', 'week', 'month'], true) ? $new_instance['period'] : 'week',
+ 'paginate' => sanitize_key((string) ($new_instance['paginate'] ?? 'ajax')) === 'none' ? 'none' : 'ajax',
+ ];
+ }
}
diff --git a/src/AKSRT/Widgets/WidgetForumRecentTopics.php b/src/AKSRT/Widgets/WidgetForumRecentTopics.php
index 97565ea4..2202bda8 100644
--- a/src/AKSRT/Widgets/WidgetForumRecentTopics.php
+++ b/src/AKSRT/Widgets/WidgetForumRecentTopics.php
@@ -1,5 +1,7 @@
__('显示论坛最新话题列表。', 'aksrt')]
- );
- }
+ public function __construct() {
+ parent::__construct(
+ 'aksrt_forum_recent_topics',
+ __('论坛 - 最新话题', 'aksrt'),
+ ['description' => __('显示论坛最新话题列表。', 'aksrt')]
+ );
+ }
- public function widget($args, $instance): void {
- $title = !empty($instance['title']) ? $instance['title'] : __('最新话题', 'aksrt');
- $count = !empty($instance['count']) ? max(1, min(20, (int) $instance['count'])) : 5;
+ public function widget($args, $instance): void {
+ $title = !empty($instance['title']) ? $instance['title'] : __('最新话题', 'aksrt');
+ $instance['orderby'] = 'date';
+ $instance['period'] = 'all';
+ $instance['paginate'] = sanitize_key((string) ($instance['paginate'] ?? 'ajax'));
+ $payload = WidgetForumHotTopics::query_payload($instance, 1);
- echo $args['before_widget'];
- echo $args['before_title'] . esc_html($title) . $args['after_title'];
+ echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ $rest = rest_url('aksrt/v1/widgets/forum-topics');
+ echo '';
+ echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ }
- $query = new \WP_Query([
- 'post_type' => 'aksrt_forum_topic',
- 'posts_per_page' => $count,
- 'post_status' => 'publish',
- 'orderby' => 'date',
- 'order' => 'DESC',
- ]);
+ public function form($instance): void {
+ $title = !empty($instance['title']) ? $instance['title'] : __('最新话题', 'aksrt');
+ $count = !empty($instance['count']) ? (int) $instance['count'] : 5;
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+ have_posts()) {
- echo '
';
- wp_reset_postdata();
- } else {
- echo '
' . esc_html__('暂无话题', 'aksrt') . '
';
- }
-
- echo $args['after_widget'];
- }
-
- public function form($instance): void {
- $title = !empty($instance['title']) ? $instance['title'] : __('最新话题', 'aksrt');
- $count = !empty($instance['count']) ? (int) $instance['count'] : 5;
- ?>
-
-
-
-
-
-
-
-
- sanitize_text_field($new_instance['title'] ?? ''),
+ 'count' => max(1, min(20, (int) ($new_instance['count'] ?? 5))),
+ 'paginate' => sanitize_key((string) ($new_instance['paginate'] ?? 'ajax')) === 'none' ? 'none' : 'ajax',
+ ];
+ }
}
diff --git a/src/AKSRT/Widgets/WidgetForumTopicTerms.php b/src/AKSRT/Widgets/WidgetForumTopicTerms.php
index 26f426af..14da9c03 100644
--- a/src/AKSRT/Widgets/WidgetForumTopicTerms.php
+++ b/src/AKSRT/Widgets/WidgetForumTopicTerms.php
@@ -1,6 +1,6 @@
*/
+ public static function parse_ids_static(string $raw): array {
+ if ($raw === '') {
+ return [];
+ }
+ $ids = [];
+ foreach (preg_split('/[\s,]+/', $raw) ?: [] as $p) {
+ $id = (int) $p;
+ if ($id > 0) {
+ $ids[] = $id;
+ }
+ }
+ return array_values(array_unique($ids));
+ }
+
+ public static function render_html(array $instance): string {
$tax = taxonomy_exists('aksrt_forum_tag') ? 'aksrt_forum_tag' : (taxonomy_exists('aksrt_forum_topic_cat') ? 'aksrt_forum_topic_cat' : '');
if ($tax === '') {
- return;
+ return '';
}
-
$number = max(1, min(40, (int) ($instance['number'] ?? 12)));
$orderby = sanitize_key((string) ($instance['orderby'] ?? 'count'));
$hide_empty = !isset($instance['hide_empty']) || !empty($instance['hide_empty']);
- $include = $this->parse_ids((string) ($instance['include'] ?? ''));
+ $include = self::parse_ids_static((string) ($instance['include'] ?? ''));
$style = sanitize_key((string) ($instance['style'] ?? 'mini'));
$term_args = [
'taxonomy' => $tax,
'number' => $number,
- 'hide_empty' => $hide_empty,
+ 'hide_empty' => (bool) $hide_empty,
];
if ($include) {
$term_args['include'] = $include;
$term_args['orderby'] = 'include';
+ } elseif ($orderby === 'name') {
+ $term_args['orderby'] = 'name';
+ $term_args['order'] = 'ASC';
+ } elseif ($orderby === 'views') {
+ $term_args['meta_key'] = '_aksrt_views';
+ $term_args['orderby'] = 'meta_value_num';
+ $term_args['order'] = 'DESC';
} else {
- if ($orderby === 'name') {
- $term_args['orderby'] = 'name';
- $term_args['order'] = 'ASC';
- } elseif ($orderby === 'views') {
- $term_args['meta_key'] = '_aksrt_views';
- $term_args['orderby'] = 'meta_value_num';
- $term_args['order'] = 'DESC';
- } else {
- $term_args['orderby'] = 'count';
- $term_args['order'] = 'DESC';
- }
+ $term_args['orderby'] = 'count';
+ $term_args['order'] = 'DESC';
}
$terms = get_terms($term_args);
if (is_wp_error($terms) || !$terms) {
- return;
+ return function_exists('aksrt_get_ajax_null')
+ ? aksrt_get_ajax_null(__('暂无话题', 'aksrt'))
+ : '
' . esc_html__('暂无话题', 'aksrt') . '
';
}
-
- 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
- }
-
- $list_cls = $style === 'card' ? 'aksrt-ftag-list is-card' : 'aksrt-ftag-list is-mini';
+ ob_start();
+ $list_cls = $style === 'card' ? 'aksrt-ftag-list is-card ajax-item' : 'aksrt-ftag-list is-mini ajax-item';
echo '
';
foreach ($terms as $term) {
$link = get_term_link($term);
@@ -84,23 +92,28 @@ class WidgetForumTopicTerms extends \WP_Widget {
echo '';
}
echo '
';
+ return (string) ob_get_clean();
+ }
+ public function widget($args, $instance): void {
+ $html = self::render_html($instance);
+ if ($html === '') {
+ return;
+ }
+ 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
+ }
+ // IAS-style wrap for remote refresh.
+ echo '';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
- /** @return array
*/
private function parse_ids(string $raw): array {
- if ($raw === '') {
- return [];
- }
- $ids = [];
- foreach (preg_split('/[\s,]+/', $raw) ?: [] as $p) {
- $id = (int) $p;
- if ($id > 0) {
- $ids[] = $id;
- }
- }
- return array_values(array_unique($ids));
+ return self::parse_ids_static($raw);
}
public function form($instance): void {
diff --git a/src/AKSRT/Widgets/WidgetOnelinePosts.php b/src/AKSRT/Widgets/WidgetOnelinePosts.php
index 98a445df..5051c58e 100644
--- a/src/AKSRT/Widgets/WidgetOnelinePosts.php
+++ b/src/AKSRT/Widgets/WidgetOnelinePosts.php
@@ -1,6 +1,6 @@
'post',
'post_status' => 'publish',
@@ -62,20 +57,23 @@ class WidgetOnelinePosts extends \WP_Widget {
$query['orderby'] = 'date';
$query['order'] = 'DESC';
}
+ return $query;
+ }
- $q = new \WP_Query($query);
+ public static function render_html(array $instance): string {
+ $style = sanitize_key((string) ($instance['style'] ?? 'card'));
+ if (!in_array($style, ['card', 'mini', 'list'], true)) {
+ $style = 'card';
+ }
+ $q = new \WP_Query(self::build_query_args($instance));
if (!$q->have_posts()) {
wp_reset_postdata();
- return;
+ return function_exists('aksrt_get_ajax_null')
+ ? aksrt_get_ajax_null(__('暂无文章', 'aksrt'))
+ : '' . esc_html__('暂无文章', 'aksrt') . '
';
}
-
- 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 '';
+ ob_start();
+ echo '
';
while ($q->have_posts()) {
$q->the_post();
$thumb = get_the_post_thumbnail_url(get_the_ID(), $style === 'card' ? 'medium' : 'thumbnail');
@@ -94,7 +92,20 @@ class WidgetOnelinePosts extends \WP_Widget {
}
echo '
';
wp_reset_postdata();
+ return (string) ob_get_clean();
+ }
+ public function widget($args, $instance): void {
+ $html = self::render_html($instance);
+ if ($html === '') {
+ return;
+ }
+ 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 $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
diff --git a/src/AKSRT/Widgets/WidgetProductOneline.php b/src/AKSRT/Widgets/WidgetProductOneline.php
index 22cdcd5f..81848bcd 100644
--- a/src/AKSRT/Widgets/WidgetProductOneline.php
+++ b/src/AKSRT/Widgets/WidgetProductOneline.php
@@ -1,5 +1,7 @@
__('AKSRT 单行横滑商品 Swiper 组件。', 'aksrt')]
- );
- }
+ public function __construct() {
+ \WP_Widget::__construct(
+ 'aksrt_product_oneline',
+ __('横滑商品', 'aksrt'),
+ ['description' => __('AKSRT 单行横滑商品列表(AJAX 同源)。', 'aksrt')]
+ );
+ }
- public function widget($args, $instance): void {
- $instance['style'] = 'oneline';
- parent::widget($args, $instance);
- }
+ public function widget($args, $instance): void {
+ $instance['style'] = 'oneline';
+ // Prefer no paginate for horizontal scroller (zibll loads fixed count).
+ if (!isset($instance['paginate'])) {
+ $instance['paginate'] = 'none';
+ }
+ parent::widget($args, $instance);
+ }
- public function form($instance): void {
- $instance = wp_parse_args($instance, ['style' => 'oneline']);
- parent::form($instance);
- }
+ public function form($instance): void {
+ $instance = wp_parse_args($instance, ['style' => 'oneline', 'paginate' => 'none']);
+ parent::form($instance);
+ }
+
+ public function update($new_instance, $old_instance): array {
+ $out = parent::update($new_instance, $old_instance);
+ $out['style'] = 'oneline';
+ return $out;
+ }
}
diff --git a/src/AKSRT/Widgets/WidgetUserLists.php b/src/AKSRT/Widgets/WidgetUserLists.php
index 52012158..8da26633 100644
--- a/src/AKSRT/Widgets/WidgetUserLists.php
+++ b/src/AKSRT/Widgets/WidgetUserLists.php
@@ -1,6 +1,6 @@
*/
+ public static function parse_ids_static(string $raw): array {
+ if ($raw === '') {
+ return [];
+ }
+ $ids = [];
+ foreach (preg_split('/[\s,]+/', $raw) ?: [] as $p) {
+ $id = (int) $p;
+ if ($id > 0) {
+ $ids[] = $id;
+ }
+ }
+ return array_values(array_unique($ids));
+ }
+
+ public static function render_html(array $instance): string {
$number = max(1, min(50, (int) ($instance['number'] ?? 8)));
$orderby = sanitize_key((string) ($instance['orderby'] ?? 'user_registered'));
$order = strtoupper((string) ($instance['order'] ?? 'DESC')) === 'ASC' ? 'ASC' : 'DESC';
- $include = $this->parse_ids((string) ($instance['include'] ?? ''));
- $exclude = $this->parse_ids((string) ($instance['exclude'] ?? ''));
+ $include = self::parse_ids_static((string) ($instance['include'] ?? ''));
+ $exclude = self::parse_ids_static((string) ($instance['exclude'] ?? ''));
$hide_box = !empty($instance['hide_box']);
$query = [
- 'number' => $number,
- 'order' => $order,
- 'fields' => ['ID', 'display_name', 'user_nicename'],
+ 'number' => $number,
+ 'order' => $order,
+ 'fields' => ['ID', 'display_name', 'user_nicename'],
];
-
if ($include) {
$query['include'] = $include;
$query['orderby'] = 'include';
@@ -52,7 +66,6 @@ class WidgetUserLists extends \WP_Widget {
$query['orderby'] = 'meta_value';
break;
case 'followed-user-count':
- // Fallback: registered date; fan ranking needs denormalized meta when available.
$query['meta_key'] = 'aksrt_follower_count';
$query['orderby'] = 'meta_value_num';
break;
@@ -66,11 +79,12 @@ class WidgetUserLists extends \WP_Widget {
$users = get_users($query);
if (!$users) {
- return;
+ return function_exists('aksrt_get_ajax_null')
+ ? aksrt_get_ajax_null(__('暂无用户', 'aksrt'))
+ : '
' . esc_html__('暂无用户', 'aksrt') . '
';
}
- // If follower meta empty for all, re-query by registered when fan order requested.
- if ($orderby === 'followed-user-count' && !$include) {
+ if ($orderby === 'followed-user-count' && !$include && class_exists('\AKSRT\User\Follow')) {
$has_meta = false;
foreach ($users as $u) {
if ((int) get_user_meta($u->ID, 'aksrt_follower_count', true) > 0) {
@@ -78,8 +92,7 @@ class WidgetUserLists extends \WP_Widget {
break;
}
}
- if (!$has_meta && class_exists('\AKSRT\User\Follow')) {
- // Rank by live follower counts (small N only).
+ if (!$has_meta) {
usort($users, static function ($a, $b) {
$fa = \AKSRT\User\Follow::get_instance()->get_follower_count((int) $a->ID);
$fb = \AKSRT\User\Follow::get_instance()->get_follower_count((int) $b->ID);
@@ -88,13 +101,8 @@ class WidgetUserLists extends \WP_Widget {
}
}
- 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 = $hide_box ? 'aksrt-user-lists is-bare' : 'aksrt-user-lists';
+ ob_start();
+ $wrap = $hide_box ? 'aksrt-user-lists is-bare ajax-item' : 'aksrt-user-lists ajax-item';
echo '
';
foreach ($users as $user) {
$uid = (int) $user->ID;
@@ -111,26 +119,22 @@ class WidgetUserLists extends \WP_Widget {
echo '';
}
echo '
';
+ return (string) ob_get_clean();
+ }
+ public function widget($args, $instance): void {
+ $html = self::render_html($instance);
+ 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 $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
- /**
- * @return array
- */
private function parse_ids(string $raw): array {
- if ($raw === '') {
- return [];
- }
- $parts = preg_split('/[\s,]+/', $raw) ?: [];
- $ids = [];
- foreach ($parts as $p) {
- $id = (int) $p;
- if ($id > 0) {
- $ids[] = $id;
- }
- }
- return array_values(array_unique($ids));
+ return self::parse_ids_static($raw);
}
public function form($instance): void {
@@ -187,13 +191,13 @@ class WidgetUserLists extends \WP_Widget {
$orderby = 'user_registered';
}
return [
- 'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
- 'number' => max(1, min(50, (int) ($new_instance['number'] ?? 8))),
- 'include' => sanitize_text_field((string) ($new_instance['include'] ?? '')),
- 'exclude' => sanitize_text_field((string) ($new_instance['exclude'] ?? '')),
- 'orderby' => $orderby,
- 'order' => strtoupper((string) ($new_instance['order'] ?? 'DESC')) === 'ASC' ? 'ASC' : 'DESC',
- 'hide_box'=> !empty($new_instance['hide_box']) ? 1 : 0,
+ 'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
+ 'number' => max(1, min(50, (int) ($new_instance['number'] ?? 8))),
+ 'include' => sanitize_text_field((string) ($new_instance['include'] ?? '')),
+ 'exclude' => sanitize_text_field((string) ($new_instance['exclude'] ?? '')),
+ 'orderby' => $orderby,
+ 'order' => strtoupper((string) ($new_instance['order'] ?? 'DESC')) === 'ASC' ? 'ASC' : 'DESC',
+ 'hide_box' => !empty($new_instance['hide_box']) ? 1 : 0,
];
}
}
' . esc_html(wp_trim_words($comment->comment_content, 15, '...')) . '
'; - echo '