From d508a9aef8cba7446969ebeb42f02f4adbde8840 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 20 Jul 2026 20:55:02 +0800 Subject: [PATCH] feat(widgets): paid posts AJAX + hot posts thumb persist Wire /widgets/paid-posts load-more; fix hot posts show_thumb not saved in update(). --- src/AKSRT/Core/WidgetAjax.php | 27 +++++++ src/AKSRT/Widgets/HotPostsWidget.php | 9 ++- src/AKSRT/Widgets/WidgetPaidPosts.php | 109 +++++++++++++++++++++----- 3 files changed, 120 insertions(+), 25 deletions(-) diff --git a/src/AKSRT/Core/WidgetAjax.php b/src/AKSRT/Core/WidgetAjax.php index 7fd0c9b9..f4878580 100644 --- a/src/AKSRT/Core/WidgetAjax.php +++ b/src/AKSRT/Core/WidgetAjax.php @@ -34,6 +34,11 @@ class WidgetAjax { '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', + ]); } public function rest_main_posts(\WP_REST_Request $request) { @@ -89,4 +94,26 @@ class WidgetAjax { '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); + } + $instance = [ + 'count' => max(1, min(20, (int) ($request->get_param('count') ?? 6))), + 'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'date')), + '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); + } } diff --git a/src/AKSRT/Widgets/HotPostsWidget.php b/src/AKSRT/Widgets/HotPostsWidget.php index 0c52ba49..f24d9b81 100644 --- a/src/AKSRT/Widgets/HotPostsWidget.php +++ b/src/AKSRT/Widgets/HotPostsWidget.php @@ -149,10 +149,11 @@ class HotPostsWidget extends \WP_Widget { 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), + '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, ]; } } diff --git a/src/AKSRT/Widgets/WidgetPaidPosts.php b/src/AKSRT/Widgets/WidgetPaidPosts.php index 21472e78..8eef0742 100644 --- a/src/AKSRT/Widgets/WidgetPaidPosts.php +++ b/src/AKSRT/Widgets/WidgetPaidPosts.php @@ -1,6 +1,6 @@ esc_html__('展示开启付费/积分可见的文章。', 'aksrt'), + 'description' => esc_html__('展示开启付费/积分可见的文章(支持 AJAX)。', 'aksrt'), 'classname' => 'widget_aksrt_paid_posts', ] ); } - public function widget($args, $instance): void { + 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', @@ -53,7 +53,7 @@ class WidgetPaidPosts extends \WP_Widget { ], ]; if ($orderby === 'views') { - $query['meta_key'] = '_aksrt_views'; + $query['meta_key'] = 'aksrt_post_views'; $query['orderby'] = 'meta_value_num'; } elseif ($orderby === 'price') { $query['meta_key'] = '_aksrt_pay_price'; @@ -61,26 +61,20 @@ class WidgetPaidPosts extends \WP_Widget { } else { $query['orderby'] = 'date'; } + return $query; + } - $q = new \WP_Query($query); + public static function render_items_html(\WP_Query $q): string { if (!$q->have_posts()) { - wp_reset_postdata(); - return; + 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 ''; wp_reset_postdata(); + return (string) ob_get_clean(); + } + + /** + * @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); + $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)); + $items = self::render_items_html($q); + $html = $items !== '' ? '' : ''; + if ($html === '' && $page === 1) { + $html = function_exists('aksrt_get_ajax_null') + ? aksrt_get_ajax_null(__('暂无付费内容', 'aksrt')) + : '

' . 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, + 'orderby' => sanitize_key((string) ($instance['orderby'] ?? 'date')), + 'paginate' => 'ajax', + ], rest_url('aksrt/v1/widgets/paid-posts')); + $paginate_html = function_exists('aksrt_get_ajax_next_paginate') + ? aksrt_get_ajax_next_paginate($total, $page, $count, $next) + : '
'; + } else { + $paginate_html = '
'; + } + 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); + if ($payload['html'] === '' && empty($payload['total'])) { + 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 + } + + $rest = rest_url('aksrt/v1/widgets/paid-posts'); + echo '
'; + echo $payload['html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo $payload['paginate_html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo ''; + echo '
'; echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } @@ -123,6 +181,14 @@ class WidgetPaidPosts extends \WP_Widget {

+

+ +

sanitize_text_field((string) ($new_instance['title'] ?? '')), - 'count' => max(1, min(20, (int) ($new_instance['count'] ?? 6))), - 'orderby' => $orderby, + 'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')), + 'count' => max(1, min(20, (int) ($new_instance['count'] ?? 6))), + 'orderby' => $orderby, + 'paginate' => sanitize_key((string) ($new_instance['paginate'] ?? 'ajax')) === 'none' ? 'none' : 'ajax', ]; } }