diff --git a/assets/js/widget-set.js b/assets/js/widget-set.js index 0bba475a..8726220f 100644 --- a/assets/js/widget-set.js +++ b/assets/js/widget-set.js @@ -17,6 +17,39 @@ var tabs = widget.querySelectorAll('.widget-tab-posts__tab'); var panels = widget.querySelectorAll('.widget-tab-posts__panel'); if (!tabs.length || !panels.length) return; + var rest = widget.getAttribute('data-rest') || ''; + var defaultCount = widget.getAttribute('data-count') || '5'; + + function loadPanel(panel, tab) { + if (!panel || panel.getAttribute('data-loaded') === '1' || !rest) return; + var orderby = tab.getAttribute('data-orderby') || 'date'; + var cat = tab.getAttribute('data-cat') || '0'; + var tabCount = tab.getAttribute('data-tab-count') || '0'; + var qs = new URLSearchParams({ + count: defaultCount, + orderby: orderby, + cat: cat, + tab_count: tabCount, + }); + var url = rest + (rest.indexOf('?') > -1 ? '&' : '?') + qs.toString(); + panel.classList.add('is-ajax-loading'); + var headers = { Accept: 'application/json' }; + if (window.aksrtData && aksrtData.restNonce) { + headers['X-WP-Nonce'] = aksrtData.restNonce; + } + fetch(url, { credentials: 'same-origin', headers: headers }) + .then(function (r) { return r.json(); }) + .then(function (j) { + var d = (j && j.data) || j || {}; + var html = d.html || ''; + panel.innerHTML = html || '

暂无文章

'; + panel.setAttribute('data-loaded', '1'); + panel.classList.remove('is-ajax-loading'); + }) + .catch(function () { + panel.classList.remove('is-ajax-loading'); + }); + } tabs.forEach(function (tab) { tab.addEventListener('click', function () { @@ -33,6 +66,7 @@ panel.classList.toggle('active', active); if (active) { panel.removeAttribute('hidden'); + loadPanel(panel, tab); } else { panel.setAttribute('hidden', ''); } diff --git a/functions.php b/functions.php index 684ca546..a8463623 100644 --- a/functions.php +++ b/functions.php @@ -234,6 +234,10 @@ if (class_exists('\AKSRT\Core\DocsPresenter')) { if (class_exists('\AKSRT\Core\HomeModules')) { \AKSRT\Core\HomeModules::get_instance()->init(); } +// Public widget list REST (posts / tab-posts ajax) +if (class_exists('\AKSRT\Core\WidgetAjax')) { + \AKSRT\Core\WidgetAjax::get_instance()->init(); +} if (class_exists('\AKSRT\Content\Visibility')) { \AKSRT\Content\Visibility::get_instance()->init(); } diff --git a/src/AKSRT/Core/WidgetAjax.php b/src/AKSRT/Core/WidgetAjax.php new file mode 100644 index 00000000..c16a409e --- /dev/null +++ b/src/AKSRT/Core/WidgetAjax.php @@ -0,0 +1,158 @@ + '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', + ]); + } + + 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); + } + $instance = [ + 'count' => max(1, min(30, (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') ?? 'list')), + 'show_thumb' => $request->get_param('show_thumb') === null ? 1 : (int) $request->get_param('show_thumb'), + 'show_meta' => $request->get_param('show_meta') === null ? 1 : (int) $request->get_param('show_meta'), + 'show_number' => (int) ($request->get_param('show_number') ?? 0), + '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); + } + + 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); + } + $default_count = max(1, min(20, (int) ($request->get_param('count') ?? 5))); + $tab = [ + 'title' => '', + 'category_id' => max(0, (int) ($request->get_param('cat') ?? $request->get_param('category_id') ?? 0)), + 'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'date')), + 'count' => max(0, min(20, (int) ($request->get_param('tab_count') ?? 0))), + 'enabled' => true, + ]; + $html = \AKSRT\Widgets\TabPostsWidget::render_tab_html($tab, $default_count); + if ($html === '') { + $html = function_exists('aksrt_get_ajax_null') + ? aksrt_get_ajax_null(__('暂无文章', 'aksrt')) + : '

' . esc_html__('暂无文章', 'aksrt') . '

'; + } else { + $html = '
' . $html . '
'; + } + 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); + } + $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); + } + + 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))); + $layout = sanitize_key((string) ($request->get_param('layout') ?? 'card')); + $columns = max(1, min(4, (int) ($request->get_param('columns') ?? 3))); + $context = sanitize_key((string) ($request->get_param('context') ?? 'homepage')); + if ($cat <= 0) { + return new \WP_REST_Response(['success' => true, 'html' => ''], 200); + } + $post_types = function_exists('aksrt_home_widget_post_types') ? aksrt_home_widget_post_types() : ['post']; + $query = new \WP_Query([ + 'post_type' => $post_types, + 'post_status' => 'publish', + 'posts_per_page' => $count, + 'ignore_sticky_posts' => true, + 'no_found_rows' => true, + 'cat' => $cat, + ]); + ob_start(); + if (function_exists('aksrt_home_widget_render_post_grid')) { + aksrt_home_widget_render_post_grid($query, $layout, $columns, $context); + } elseif ($query->have_posts()) { + echo ''; + } + wp_reset_postdata(); + $html = (string) ob_get_clean(); + return new \WP_REST_Response(['success' => true, 'html' => $html], 200); + } +} diff --git a/src/AKSRT/Forum/Forum.php b/src/AKSRT/Forum/Forum.php index f04561e8..1d9b8fae 100644 --- a/src/AKSRT/Forum/Forum.php +++ b/src/AKSRT/Forum/Forum.php @@ -252,6 +252,11 @@ class Forum { 'callback' => [$this, 'rest_widget_topics'], 'permission_callback' => '__return_true', ]); + register_rest_route('aksrt/v1', '/forum/frontend/widgets/plates', [ + 'methods' => 'GET', + 'callback' => [$this, 'rest_widget_plates'], + 'permission_callback' => '__return_true', + ]); } public function rest_widget_topics(\WP_REST_Request $request) { @@ -278,6 +283,24 @@ class Forum { ], 200); } + public function rest_widget_plates(\WP_REST_Request $request) { + if (!class_exists('\AKSRT\Widgets\WidgetForumPlateLists')) { + return new \WP_REST_Response(['success' => false, 'message' => 'widget unavailable'], 500); + } + $instance = [ + 'showposts' => max(1, min(30, (int) ($request->get_param('showposts') ?? $request->get_param('count') ?? 8))), + 'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'menu_order')), + 'style' => sanitize_key((string) ($request->get_param('style') ?? 'default')), + 'filter' => sanitize_key((string) ($request->get_param('filter') ?? '')), + 'cat' => sanitize_text_field((string) ($request->get_param('cat') ?? '')), + ]; + $payload = \AKSRT\Widgets\WidgetForumPlateLists::query_payload($instance); + return new \WP_REST_Response([ + 'success' => true, + 'html' => (string) ($payload['html'] ?? ''), + ], 200); + } + public function render_board_cards(array $boards): void { diff --git a/src/AKSRT/Widgets/CategoryTabsWidget.php b/src/AKSRT/Widgets/CategoryTabsWidget.php index a996a157..aa738b19 100644 --- a/src/AKSRT/Widgets/CategoryTabsWidget.php +++ b/src/AKSRT/Widgets/CategoryTabsWidget.php @@ -38,7 +38,7 @@ class CategoryTabsWidget extends \WP_Widget { $tabs = $this->resolve_tabs($instance); echo $args['before_widget']; - echo '
'; + echo '
'; if (empty($tabs)) { echo function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无可用分类', 'aksrt')) : '

' . esc_html__('暂无可用分类', 'aksrt') . '

'; @@ -69,33 +69,96 @@ class CategoryTabsWidget extends \WP_Widget { $tab) { - $query = new \WP_Query([ - 'post_type' => aksrt_home_widget_post_types(), - 'post_status' => 'publish', - 'posts_per_page' => $count, - 'ignore_sticky_posts' => true, - 'no_found_rows' => true, - 'cat' => (int) $tab['id'], - ]); ?> -
> - - - +
> + aksrt_home_widget_post_types(), + 'post_status' => 'publish', + 'posts_per_page' => $count, + 'ignore_sticky_posts' => true, + 'no_found_rows' => true, + 'cat' => (int) $tab['id'], + ]); + aksrt_home_widget_render_post_grid($query, $layout, $columns, $context); + wp_reset_postdata(); + if ($show_more) : ?> + + +
+ + '; echo $args['after_widget']; } - public function form($instance): void { +public function form($instance): void { $title = (string) ($instance['title'] ?? ''); $category_ids = aksrt_home_widget_normalize_category_ids($instance['category_ids'] ?? [], 6); $count = (int) ($instance['count'] ?? 6); 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/TabPostsWidget.php b/src/AKSRT/Widgets/TabPostsWidget.php index 3805acfe..22cdbabb 100644 --- a/src/AKSRT/Widgets/TabPostsWidget.php +++ b/src/AKSRT/Widgets/TabPostsWidget.php @@ -1,5 +1,7 @@ esc_html__('Tab 切换显示不同分类的文章列表', 'aksrt'), - 'customize_selective_refresh' => true, - ] - ); - } + public function __construct() { + parent::__construct( + 'aksrt_tab_posts', + esc_html__('AKSRT 多栏目文章', 'aksrt'), + [ + 'description' => esc_html__('Tab 切换显示不同分类/排序的文章(懒加载)。', 'aksrt'), + 'customize_selective_refresh' => true, + ] + ); + } - /** - * Normalize tabs from legacy comma-separated string or structured array. - * - * @param mixed $tabs Raw tabs data. - * @return array Normalized structured tabs. - */ - private function normalize_tabs($tabs): array { - // Legacy comma-separated format - if (is_string($tabs) && $tabs !== '') { - $legacy_keys = array_map('trim', explode(',', $tabs)); - $legacy_defs = [ - 'latest' => ['title' => '最新', 'orderby' => 'date'], - 'comments' => ['title' => '热评', 'orderby' => 'comments'], - 'random' => ['title' => '随机', 'orderby' => 'rand'], - 'views' => ['title' => '热门', 'orderby' => 'views'], - ]; - $result = []; - foreach ($legacy_keys as $key) { - if (isset($legacy_defs[$key])) { - $result[] = [ - 'title' => __($legacy_defs[$key]['title'], 'aksrt'), - 'category_id' => 0, - 'orderby' => $legacy_defs[$key]['orderby'], - 'count' => 0, - 'enabled' => true, - ]; - } elseif (is_numeric($key) && absint($key) > 0) { - $term = get_term(absint($key), 'category'); - $result[] = [ - 'title' => ($term && !is_wp_error($term)) ? $term->name : '', - 'category_id' => absint($key), - 'orderby' => 'date', - 'count' => 0, - 'enabled' => true, - ]; - } - } - return $result; - } + /** + * @param mixed $tabs + * @return list + */ + public static function normalize_tabs_static($tabs): array { + if (is_string($tabs) && $tabs !== '') { + $legacy_keys = array_map('trim', explode(',', $tabs)); + $legacy_defs = [ + 'latest' => ['title' => '最新', 'orderby' => 'date'], + 'comments' => ['title' => '热评', 'orderby' => 'comments'], + 'random' => ['title' => '随机', 'orderby' => 'rand'], + 'views' => ['title' => '热门', 'orderby' => 'views'], + ]; + $result = []; + foreach ($legacy_keys as $key) { + if (isset($legacy_defs[$key])) { + $result[] = [ + 'title' => __($legacy_defs[$key]['title'], 'aksrt'), + 'category_id' => 0, + 'orderby' => $legacy_defs[$key]['orderby'], + 'count' => 0, + 'enabled' => true, + ]; + } elseif (is_numeric($key) && absint($key) > 0) { + $term = get_term(absint($key), 'category'); + $result[] = [ + 'title' => ($term && !is_wp_error($term)) ? $term->name : '', + 'category_id' => absint($key), + 'orderby' => 'date', + 'count' => 0, + 'enabled' => true, + ]; + } + } + return $result; + } + if (is_array($tabs)) { + $allowed = ['date', 'modified', 'views', 'likes', 'comments', 'favorites', 'downloads', 'rand']; + $result = []; + foreach ($tabs as $tab) { + if (!is_array($tab)) { + continue; + } + $orderby = sanitize_key($tab['orderby'] ?? 'date'); + if (!in_array($orderby, $allowed, true)) { + $orderby = 'date'; + } + $result[] = [ + 'title' => sanitize_text_field((string) ($tab['title'] ?? '')), + 'category_id' => absint($tab['category_id'] ?? 0), + 'orderby' => $orderby, + 'count' => max(0, min(20, (int) ($tab['count'] ?? 0))), + 'enabled' => !empty($tab['enabled']), + ]; + } + return $result; + } + return []; + } - // Structured array format - if (is_array($tabs)) { - $allowed_orderby = ['date', 'modified', 'views', 'likes', 'comments', 'favorites', 'downloads', 'rand']; - $result = []; - foreach ($tabs as $tab) { - if (!is_array($tab)) continue; - $orderby = sanitize_key($tab['orderby'] ?? 'date'); - if (!in_array($orderby, $allowed_orderby, true)) $orderby = 'date'; - $result[] = [ - 'title' => sanitize_text_field($tab['title'] ?? ''), - 'category_id' => absint($tab['category_id'] ?? 0), - 'orderby' => $orderby, - 'count' => max(0, min(20, (int) ($tab['count'] ?? 0))), - 'enabled' => !empty($tab['enabled']), - ]; - } - return $result; - } + private function normalize_tabs($tabs): array { + return self::normalize_tabs_static($tabs); + } - return []; - } + /** + * Build query for a tab → reuse main posts payload renderer. + * + * @param array $tab + * @param int $default_count + * @return array + */ + public static function tab_to_main_instance(array $tab, int $default_count): array { + $count = !empty($tab['count']) ? (int) $tab['count'] : $default_count; + return [ + 'count' => max(1, min(30, $count)), + 'orderby' => sanitize_key((string) ($tab['orderby'] ?? 'date')), + 'cat' => absint($tab['category_id'] ?? 0), + 'style' => 'mini', + 'show_thumb' => 1, + 'show_meta' => 1, + 'show_number'=> 0, + 'paginate' => 'none', + ]; + } - /** - * Build \WP_Query args for a tab's orderby. - */ - private function build_tab_query_args(array $tab, int $default_count): array { - $count = $tab['count'] > 0 ? $tab['count'] : $default_count; - $args = [ - 'post_type' => 'post', - 'post_status' => 'publish', - 'posts_per_page' => $count, - 'ignore_sticky_posts' => true, - 'no_found_rows' => true, - ]; + /** + * REST helper: render one tab list HTML. + */ + public static function render_tab_html(array $tab, int $default_count): string { + $inst = self::tab_to_main_instance($tab, $default_count); + if (class_exists('\AKSRT\Widgets\WidgetMainPosts')) { + $payload = WidgetMainPosts::query_payload($inst, 1); + return (string) ($payload['html'] ?? ''); + } + return ''; + } - if ($tab['category_id'] > 0) { - $args['cat'] = $tab['category_id']; - } + public function widget($args, $instance): void { + echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - switch ($tab['orderby']) { - case 'views': - $args['meta_key'] = 'aksrt_post_views'; - $args['orderby'] = 'meta_value_num'; - $args['order'] = 'DESC'; - break; - case 'likes': - $args['meta_key'] = '_aksrt_like_count'; - $args['orderby'] = 'meta_value_num'; - $args['order'] = 'DESC'; - break; - case 'favorites': - $args['meta_key'] = '_aksrt_favorite_count'; - $args['orderby'] = 'meta_value_num'; - $args['order'] = 'DESC'; - break; - case 'downloads': - $args['meta_key'] = '_aksrt_res_download_count'; - $args['orderby'] = 'meta_value_num'; - $args['order'] = 'DESC'; - break; - case 'comments': - $args['orderby'] = 'comment_count'; - $args['order'] = 'DESC'; - break; - case 'rand': - $args['orderby'] = 'rand'; - break; - case 'modified': - $args['orderby'] = 'modified'; - $args['order'] = 'DESC'; - break; - case 'date': - default: - $args['orderby'] = 'date'; - $args['order'] = 'DESC'; - break; - } + $title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base); + $count = absint($instance['count'] ?? 5); + $tabs = $this->normalize_tabs($instance['tabs'] ?? 'latest,comments,random'); + $tabs = array_values(array_filter($tabs, static fn($t) => !empty($t['enabled']))); + if (!$tabs) { + echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + return; + } - return $args; - } + if ($title !== '') { + echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } - public function widget($args, $instance): void { - echo $args['before_widget']; + $widget_id = $this->id; + $rest = rest_url('aksrt/v1/widgets/tab-posts'); + $use_shared = function_exists('aksrt_render_mini_list'); + ?> +
+
    + $tab) : + $tab_key = 'tab-' . $i; + $tab_title = $tab['title'] !== '' ? $tab['title'] : sprintf(__('Tab %d', 'aksrt'), $i + 1); + ?> +
  • + +
  • + +
- $title = apply_filters('widget_title', $instance['title'] ?? '', $instance, $this->id_base); - $count = absint($instance['count'] ?? 5); - $tabs = $this->normalize_tabs($instance['tabs'] ?? 'latest,comments,random'); + $tab) : + $tab_key = 'tab-' . $i; + ?> +
> + ' . $html . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } else { + // Fallback to old mini list path. + $q = new \WP_Query(WidgetMainPosts::build_query_args(self::tab_to_main_instance($tab, $count), 1)); + if ($q->have_posts() && $use_shared) { + echo '
'; + aksrt_render_mini_list($q, ['context' => 'sidebar', 'show_thumb' => true, 'show_meta' => true], true); + echo '
'; + } else { + echo function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无文章', 'aksrt')) : '

' . esc_html__('暂无文章', 'aksrt') . '

'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + wp_reset_postdata(); + } + else : ?> +
+ +
+ +
+ !empty($t['enabled'])); - if (empty($tabs)) { - echo $args['after_widget']; - return; - } + public function form($instance): void { + $title = $instance['title'] ?? ''; + $count = (int) ($instance['count'] ?? 5); + $tabs = $this->normalize_tabs($instance['tabs'] ?? 'latest,comments,random'); + $orderby_options = [ + 'date' => __('最新', 'aksrt'), + 'modified' => __('更新', 'aksrt'), + 'views' => __('浏览', 'aksrt'), + 'likes' => __('点赞', 'aksrt'), + 'comments' => __('评论', 'aksrt'), + 'favorites' => __('收藏', 'aksrt'), + 'downloads' => __('下载', 'aksrt'), + 'rand' => __('随机', 'aksrt'), + ]; + ?> +

+ + +

+

+ + +

+
+

+ '', 'category_id' => 0, 'orderby' => 'date', 'count' => 0, 'enabled' => false]; + } + foreach ($tabs as $i => $tab) : + ?> +
+

+ +

+

+ + " value="" placeholder=""> +

+

+ + " value="" placeholder="0"> +

+

+ + +

+

+ + " value=""> +

+
+ id; - $use_shared = function_exists('aksrt_render_mini_list'); - ?> -
- -
    - $tab) : - $tab_key = 'tab-' . $i; - $tab_title = $tab['title'] !== '' ? $tab['title'] : __('Tab ' . ($i + 1), 'aksrt'); - ?> -
  • - -
  • - -
- - - $tab) : - $tab_key = 'tab-' . $i; - $q = new \WP_Query($this->build_tab_query_args($tab, $count)); - ?> -
> - have_posts()) : ?> - -
- 'sidebar', 'show_thumb' => true, 'show_meta' => true], true); ?> -
- - - - - ' . esc_html__('暂无文章', 'aksrt') . '

'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> - -
- -
- normalize_tabs($instance['tabs'] ?? 'latest,comments,random'); - $orderby_options = [ - 'date' => __('最新', 'aksrt'), - 'modified' => __('更新', 'aksrt'), - 'views' => __('浏览', 'aksrt'), - 'likes' => __('点赞', 'aksrt'), - 'comments' => __('评论', 'aksrt'), - 'favorites' => __('收藏', 'aksrt'), - 'downloads' => __('下载', 'aksrt'), - 'rand' => __('随机', 'aksrt'), - ]; - ?> -

- - -

-

- - -

-
-

- '', 'category_id' => 0, 'orderby' => 'date', 'count' => 0, 'enabled' => false]; - } - foreach ($tabs as $i => $tab) : - ?> -
-

- -

-

- - " value="" placeholder=""> -

-

- - " value="" placeholder="0"> -

-

- - -

-

- - " value=""> -

-
- - " value=""> - sanitize_text_field($new_instance['title'] ?? ''), - 'count' => max(1, min(20, (int) ($new_instance['count'] ?? 5))), - ]; - - $tabs = []; - if (isset($new_instance['tabs']) && is_array($new_instance['tabs'])) { - $allowed_orderby = ['date', 'modified', 'views', 'likes', 'comments', 'favorites', 'downloads', 'rand']; - foreach ($new_instance['tabs'] as $tab) { - if (!is_array($tab)) continue; - $orderby = sanitize_key($tab['orderby'] ?? 'date'); - if (!in_array($orderby, $allowed_orderby, true)) $orderby = 'date'; - $tabs[] = [ - 'title' => sanitize_text_field($tab['title'] ?? ''), - 'category_id' => absint($tab['category_id'] ?? 0), - 'orderby' => $orderby, - 'count' => max(0, min(20, (int) ($tab['count'] ?? 0))), - 'enabled' => !empty($tab['enabled']), - ]; - } - } - - $result['tabs'] = !empty($tabs) ? $tabs : sanitize_text_field($new_instance['tabs'] ?? 'latest,comments,random'); - return $result; - } + public function update($new_instance, $old_instance): array { + $result = [ + 'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')), + 'count' => max(1, min(20, (int) ($new_instance['count'] ?? 5))), + ]; + $tabs = []; + if (isset($new_instance['tabs']) && is_array($new_instance['tabs'])) { + $allowed = ['date', 'modified', 'views', 'likes', 'comments', 'favorites', 'downloads', 'rand']; + foreach ($new_instance['tabs'] as $tab) { + if (!is_array($tab)) { + continue; + } + $orderby = sanitize_key($tab['orderby'] ?? 'date'); + if (!in_array($orderby, $allowed, true)) { + $orderby = 'date'; + } + $tabs[] = [ + 'title' => sanitize_text_field((string) ($tab['title'] ?? '')), + 'category_id' => absint($tab['category_id'] ?? 0), + 'orderby' => $orderby, + 'count' => max(0, min(20, (int) ($tab['count'] ?? 0))), + 'enabled' => !empty($tab['enabled']), + ]; + } + } + $result['tabs'] = !empty($tabs) ? $tabs : 'latest,comments,random'; + return $result; + } } diff --git a/src/AKSRT/Widgets/WidgetForumPlateLists.php b/src/AKSRT/Widgets/WidgetForumPlateLists.php index 0dbe1afd..89d0e256 100644 --- a/src/AKSRT/Widgets/WidgetForumPlateLists.php +++ b/src/AKSRT/Widgets/WidgetForumPlateLists.php @@ -1,6 +1,6 @@ esc_html__('展示论坛 plate 版块列表,支持排序与卡片样式。', 'aksrt'), + 'description' => esc_html__('展示论坛 plate 版块列表,支持排序/卡片/AJAX。', 'aksrt'), 'classname' => 'widget_aksrt_forum_plate_lists', ] ); } - public function widget($args, $instance): void { - if (!post_type_exists('aksrt_forum_plate')) { - return; - } - + /** + * @return array + */ + public static function build_query_args(array $instance): array { $limit = max(1, min(30, (int) ($instance['showposts'] ?? 8))); $orderby = sanitize_key((string) ($instance['orderby'] ?? 'menu_order')); - $style = sanitize_key((string) ($instance['style'] ?? 'default')); $filter = sanitize_key((string) ($instance['filter'] ?? '')); $cat_raw = (string) ($instance['cat'] ?? ''); $cat_ids = array_values(array_filter(array_map('intval', preg_split('/[\s,]+/', $cat_raw) ?: []))); @@ -39,6 +37,7 @@ class WidgetForumPlateLists extends \WP_Widget { 'post_status' => 'publish', 'posts_per_page' => $limit, 'order' => 'ASC', + 'no_found_rows' => true, ]; switch ($orderby) { @@ -81,8 +80,61 @@ class WidgetForumPlateLists extends \WP_Widget { ]]; } - $plates = get_posts($query_args); + return $query_args; + } + + public static function render_items_html(array $plates, string $style = 'default'): string { if (!$plates) { + return ''; + } + ob_start(); + $self = new self(); + if ($style === 'card') { + echo '
'; + foreach ($plates as $post) { + if ($post instanceof \WP_Post) { + $self->render_card($post); + } + } + echo '
'; + } else { + echo '
'; + foreach ($plates as $post) { + if ($post instanceof \WP_Post) { + $self->render_row($post); + } + } + echo '
'; + } + return (string) ob_get_clean(); + } + + /** + * @return array{html:string} + */ + public static function query_payload(array $instance): array { + if (!post_type_exists('aksrt_forum_plate')) { + return ['html' => '']; + } + $style = sanitize_key((string) ($instance['style'] ?? 'default')); + $plates = get_posts(self::build_query_args($instance)); + $html = self::render_items_html($plates ?: [], $style === 'card' ? 'card' : 'default'); + if ($html === '') { + $html = function_exists('aksrt_get_ajax_null') + ? aksrt_get_ajax_null(__('暂无版块', 'aksrt')) + : '

' . esc_html__('暂无版块', 'aksrt') . '

'; + } + return ['html' => $html]; + } + + public function widget($args, $instance): void { + if (!post_type_exists('aksrt_forum_plate')) { + return; + } + + $style = sanitize_key((string) ($instance['style'] ?? 'default')); + $payload = self::query_payload($instance); + if ($payload['html'] === '') { return; } @@ -92,24 +144,16 @@ class WidgetForumPlateLists extends \WP_Widget { echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } - if ($style === 'card') { - echo '
'; - foreach ($plates as $post) { - $this->render_card($post); - } - echo '
'; - } else { - echo '
'; - foreach ($plates as $post) { - $this->render_row($post); - } - echo '
'; - } + // IAS-like: first paint is SSR; optional remote refresh via rest for customize. + $rest = rest_url('aksrt/v1/forum/frontend/widgets/plates'); + echo '
'; + echo $payload['html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo '
'; echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } - private function render_row(\WP_Post $post): void { + public function render_row(\WP_Post $post): void { $id = (int) $post->ID; $url = get_permalink($id); $thumb = get_the_post_thumbnail_url($id, 'thumbnail'); @@ -136,7 +180,7 @@ class WidgetForumPlateLists extends \WP_Widget { echo ''; } - private function render_card(\WP_Post $post): void { + public function render_card(\WP_Post $post): void { $id = (int) $post->ID; $url = get_permalink($id); $thumb = get_the_post_thumbnail_url($id, 'medium'); diff --git a/src/AKSRT/Widgets/WidgetMainPosts.php b/src/AKSRT/Widgets/WidgetMainPosts.php index f2ce4ff8..8469ed6b 100644 --- a/src/AKSRT/Widgets/WidgetMainPosts.php +++ b/src/AKSRT/Widgets/WidgetMainPosts.php @@ -1,6 +1,6 @@ esc_html__('卡片/迷你/列表样式的文章模块。', 'aksrt'), + 'description' => esc_html__('卡片/迷你/列表样式的文章模块(支持 AJAX)。', 'aksrt'), 'classname' => 'widget_aksrt_main_posts', ] ); } - public function widget($args, $instance): void { + /** + * @param array $instance + * @param int $paged + * @return array + */ + public static function build_query_args(array $instance, int $paged = 1): array { $count = max(1, min(30, (int) ($instance['count'] ?? 6))); - $style = sanitize_key((string) ($instance['style'] ?? 'list')); - if (!in_array($style, ['list', 'card', 'mini'], true)) { - $style = 'list'; - } $orderby = sanitize_key((string) ($instance['orderby'] ?? 'date')); $cat = (int) ($instance['cat'] ?? 0); $limit_day = max(0, (int) ($instance['limit_day'] ?? 0)); - $show_thumb = !isset($instance['show_thumb']) || !empty($instance['show_thumb']); - $show_meta = !isset($instance['show_meta']) || !empty($instance['show_meta']); - $show_number = !empty($instance['show_number']); $query = [ 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => $count, + 'paged' => max(1, $paged), 'ignore_sticky_posts' => true, - 'no_found_rows' => true, ]; if ($cat > 0) { $query['cat'] = $cat; @@ -51,39 +49,48 @@ class WidgetMainPosts extends \WP_Widget { switch ($orderby) { case 'views': $query['meta_key'] = 'aksrt_post_views'; - $query['orderby'] = 'meta_value_num'; - $query['order'] = 'DESC'; + $query['orderby'] = 'meta_value_num'; + $query['order'] = 'DESC'; break; case 'comments': $query['orderby'] = 'comment_count'; - $query['order'] = 'DESC'; + $query['order'] = 'DESC'; + break; + case 'likes': + $query['meta_key'] = '_aksrt_like_count'; + $query['orderby'] = 'meta_value_num'; + $query['order'] = 'DESC'; break; case 'rand': $query['orderby'] = 'rand'; break; case 'modified': $query['orderby'] = 'modified'; - $query['order'] = 'DESC'; + $query['order'] = 'DESC'; break; default: $query['orderby'] = 'date'; - $query['order'] = 'DESC'; + $query['order'] = 'DESC'; } + return $query; + } - $q = new \WP_Query($query); + public static function render_items_html(\WP_Query $q, array $instance): 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 + $style = sanitize_key((string) ($instance['style'] ?? 'list')); + if (!in_array($style, ['list', 'card', 'mini'], true)) { + $style = 'list'; } + $show_thumb = !isset($instance['show_thumb']) || !empty($instance['show_thumb']); + $show_meta = !isset($instance['show_meta']) || !empty($instance['show_meta']); + $show_number = !empty($instance['show_number']); + $start = max(0, (int) ($instance['_start_index'] ?? 0)); - echo '
'; - $i = 0; + ob_start(); + // Outer wrapper only on page 1 full render handled by caller for ajax append items. + $i = $start; while ($q->have_posts()) { $q->the_post(); $i++; @@ -91,7 +98,7 @@ class WidgetMainPosts extends \WP_Widget { $views = (int) get_post_meta(get_the_ID(), 'aksrt_post_views', true); if ($style === 'card') { - echo ''; + echo ''; echo '
'; if ($thumb) { echo ''; @@ -108,7 +115,7 @@ class WidgetMainPosts extends \WP_Widget { } echo '
'; } else { - echo ''; + echo ''; if ($show_number) { echo '' . esc_html((string) $i) . ''; } @@ -127,8 +134,84 @@ class WidgetMainPosts extends \WP_Widget { echo ''; } } - 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'] ?? 'none')); + $count = max(1, min(30, (int) ($instance['count'] ?? 6))); + $instance['_start_index'] = ($page - 1) * $count; + $q = new \WP_Query(self::build_query_args($instance, $page)); + $html = self::render_items_html($q, $instance); + 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(array_filter([ + 'page' => $page + 1, + 'count' => $count, + 'orderby' => sanitize_key((string) ($instance['orderby'] ?? 'date')), + 'cat' => (int) ($instance['cat'] ?? 0), + 'limit_day' => (int) ($instance['limit_day'] ?? 0), + 'style' => sanitize_key((string) ($instance['style'] ?? 'list')), + 'show_thumb' => !isset($instance['show_thumb']) || !empty($instance['show_thumb']) ? 1 : 0, + 'show_meta' => !isset($instance['show_meta']) || !empty($instance['show_meta']) ? 1 : 0, + 'show_number' => !empty($instance['show_number']) ? 1 : 0, + 'paginate' => 'ajax', + ]), rest_url('aksrt/v1/widgets/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 { + $style = sanitize_key((string) ($instance['style'] ?? 'list')); + if (!in_array($style, ['list', 'card', 'mini'], true)) { + $style = 'list'; + } + $paginate = sanitize_key((string) ($instance['paginate'] ?? 'none')); + $instance['style'] = $style; + $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/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 } @@ -161,6 +244,7 @@ class WidgetMainPosts extends \WP_Widget { + @@ -175,6 +259,14 @@ class WidgetMainPosts extends \WP_Widget {

+

+ +



@@ -189,7 +281,7 @@ class WidgetMainPosts extends \WP_Widget { $style = 'list'; } $orderby = sanitize_key((string) ($new_instance['orderby'] ?? 'date')); - if (!in_array($orderby, ['date', 'modified', 'views', 'comments', 'rand'], true)) { + if (!in_array($orderby, ['date', 'modified', 'views', 'comments', 'likes', 'rand'], true)) { $orderby = 'date'; } return [ @@ -199,6 +291,7 @@ class WidgetMainPosts extends \WP_Widget { 'orderby' => $orderby, 'cat' => max(0, (int) ($new_instance['cat'] ?? 0)), 'limit_day' => max(0, (int) ($new_instance['limit_day'] ?? 0)), + 'paginate' => sanitize_key((string) ($new_instance['paginate'] ?? 'none')) === 'ajax' ? 'ajax' : 'none', 'show_thumb' => !empty($new_instance['show_thumb']) ? 1 : 0, 'show_meta' => !empty($new_instance['show_meta']) ? 1 : 0, 'show_number' => !empty($new_instance['show_number']) ? 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', ]; } } diff --git a/src/AKSRT/Widgets/WidgetUserRanking.php b/src/AKSRT/Widgets/WidgetUserRanking.php index c64dd3c2..b8fb394d 100644 --- a/src/AKSRT/Widgets/WidgetUserRanking.php +++ b/src/AKSRT/Widgets/WidgetUserRanking.php @@ -23,7 +23,8 @@ class WidgetUserRanking extends \WP_Widget { $count = max(1, min(20, (int) ($instance['count'] ?? 5))); $show_avatar = !empty($instance['show_avatar']); - $users = $this->get_ranking($rank_by, $count); + $desc_mode = sanitize_key((string) ($instance['desc'] ?? 'desc')); + $users = $this->get_ranking($rank_by, $count, $desc_mode); if (empty($users)) return; echo $args['before_widget']; @@ -60,8 +61,13 @@ class WidgetUserRanking extends \WP_Widget { echo ''; } - // 昵称 + // 昵称 + 可选签名/附加信息 + echo '
'; echo '' . esc_html($user['name']) . ''; + if (!empty($user['desc'])) { + echo '
' . esc_html((string) $user['desc']) . '
'; + } + echo '
'; // 数值 echo '' . esc_html($user['value']) . ''; @@ -73,7 +79,7 @@ class WidgetUserRanking extends \WP_Widget { echo $args['after_widget']; } - private function get_ranking(string $rank_by, int $count): array { + private function get_ranking(string $rank_by, int $count, string $desc_mode = 'desc'): array { $users = []; switch ($rank_by) { @@ -93,6 +99,7 @@ class WidgetUserRanking extends \WP_Widget { 'id' => (int) $row['user_id'], 'name' => $user->display_name, 'value' => number_format_i18n((int) $row['credits']) . ' 积分', + 'desc' => $this->user_extra_desc((int) $row['user_id'], $user, $desc_mode), ]; } } @@ -112,6 +119,7 @@ class WidgetUserRanking extends \WP_Widget { 'id' => $user->ID, 'name' => $user->display_name, 'value' => number_format_i18n($post_count) . ' 篇', + 'desc' => $this->user_extra_desc($user->ID, $user, $desc_mode), ]; } } @@ -137,6 +145,7 @@ class WidgetUserRanking extends \WP_Widget { 'id' => $uid, 'name' => $user->display_name, 'value' => number_format_i18n((int) $row['val']) . ' 天', + 'desc' => $this->user_extra_desc($uid, $user, $desc_mode), ]; } if (count($users) >= $count) break; @@ -163,6 +172,7 @@ class WidgetUserRanking extends \WP_Widget { 'id' => $uid, 'name' => $user->display_name, 'value' => number_format_i18n((int) $row['val']) . ' 天', + 'desc' => $this->user_extra_desc($uid, $user, $desc_mode), ]; } if (count($users) >= $count) break; @@ -189,6 +199,7 @@ class WidgetUserRanking extends \WP_Widget { 'id' => (int) $row['following_id'], 'name' => $user->display_name, 'value' => number_format_i18n((int) $row['cnt']) . ' 粉丝', + 'desc' => $this->user_extra_desc((int) $row['following_id'], $user, $desc_mode), ]; } } @@ -200,6 +211,37 @@ class WidgetUserRanking extends \WP_Widget { return $users; } + + private function user_extra_desc(int $user_id, $user, string $mode): string { + switch ($mode) { + case 'user_registered': + if ($user && !empty($user->user_registered)) { + $days = max(0, (int) floor((time() - strtotime($user->user_registered)) / DAY_IN_SECONDS)); + return sprintf(__('加入 %d 天', 'aksrt'), $days); + } + return ''; + case 'last_login': + $t = (string) get_user_meta($user_id, 'aksrt_last_login', true); + if ($t === '') { + $t = (string) get_user_meta($user_id, 'last_login', true); + } + return $t !== '' ? sprintf(__('最近登录 %s', 'aksrt'), $t) : ''; + case 'last_checkin': + $t = (string) get_user_meta($user_id, 'aksrt_last_checkin', true); + if ($t === '') { + $t = (string) get_user_meta($user_id, 'last_checkin', true); + } + return $t !== '' ? sprintf(__('最近签到 %s', 'aksrt'), $t) : ''; + case 'desc': + default: + $bio = get_user_meta($user_id, 'description', true); + if (!is_string($bio) || $bio === '') { + $bio = $user ? (string) $user->description : ''; + } + return $bio !== '' ? wp_trim_words($bio, 12) : ''; + } + } + public function form($instance): void { $title = !empty($instance['title']) ? $instance['title'] : __('排行榜', 'aksrt'); $rank_by = !empty($instance['rank_by']) ? $instance['rank_by'] : 'credits'; @@ -230,6 +272,15 @@ class WidgetUserRanking extends \WP_Widget {

+

+ + +