From 2396ca98fbcb5f5cd80fb4f6dd991c9d1fec61fe Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 20 Jul 2026 22:57:10 +0800 Subject: [PATCH 1/3] feat(forum): plate info follow/compose + mods grid + board depth Sidebar plate widgets gain follow/compose actions, super-mod roles, board list sorting/styles, and matching CSS polish. --- assets/css/frontend/widget-batch5.css | 96 +++++++ src/AKSRT/Widgets/WidgetForumBoardList.php | 249 ++++++++++++------ src/AKSRT/Widgets/WidgetForumPlateInfo.php | 115 ++++++-- .../Widgets/WidgetForumPlateModerator.php | 113 ++++---- 4 files changed, 424 insertions(+), 149 deletions(-) diff --git a/assets/css/frontend/widget-batch5.css b/assets/css/frontend/widget-batch5.css index 835d1ac8..3fc342b3 100644 --- a/assets/css/frontend/widget-batch5.css +++ b/assets/css/frontend/widget-batch5.css @@ -787,3 +787,99 @@ overflow: hidden; text-overflow: ellipsis; } + +/* ---- batch9: plate info actions / mods grid / board list / term polish ---- */ +.aksrt-plate-info__cover.is-icon { + display: flex; + align-items: center; + justify-content: center; + font-size: 32px; + color: var(--color-primary, #3b82f6); + background: linear-gradient(135deg, #eff6ff, #f5f3ff); +} +.aksrt-plate-info__actions { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 12px; +} +.aksrt-plate-info__btn { + display: inline-flex; + align-items: center; + gap: 6px; + border: 0; + border-radius: 999px; + padding: 6px 12px; + font-size: 12px; + font-weight: 600; + cursor: pointer; + text-decoration: none; + line-height: 1.2; +} +.aksrt-plate-info__follow { + background: #ef4444; + color: #fff !important; +} +.aksrt-plate-info__follow.is-followed { + background: var(--surface-2, #e5e7eb); + color: inherit !important; +} +.aksrt-plate-info__compose { + background: var(--color-primary, #3b82f6); + color: #fff !important; +} + +.aksrt-plate-mods.style-grid .aksrt-plate-mods__list { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 10px; +} +.aksrt-plate-mods.style-grid .aksrt-plate-mods__item { + flex-direction: column; + text-align: center; + gap: 4px; +} +.aksrt-plate-mods.style-grid .aksrt-plate-mods__name { + font-size: 12px; + width: 100%; +} +.aksrt-plate-mods.style-grid .aksrt-plate-mods__role { + font-size: 10px; +} +.aksrt-plate-mods__item.role-author .aksrt-plate-mods__role, +.aksrt-plate-mods__item.role-super .aksrt-plate-mods__role { + color: #b45309; +} + +.aksrt-forum-widget__boards .aksrt-forum-widget__name { + flex: 1; + min-width: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.aksrt-forum-widget__boards .aksrt-forum-widget__count { + margin-left: 8px; + color: var(--muted-2-color, #999); + font-size: 12px; + flex-shrink: 0; +} +.aksrt-forum-widget__boards.style-mini .aksrt-forum-widget__board a { + gap: 8px; +} +.aksrt-forum-widget__thumb { + width: 28px; + height: 28px; + border-radius: 6px; + object-fit: cover; + flex-shrink: 0; + margin-right: 4px; +} + +.aksrt-tag-cloud .c-blue-2 { background: #bfdbfe; color: #1e40af; } +.aksrt-tag-cloud .c-green-2 { background: #bbf7d0; color: #166534; } +.aksrt-tag-cloud .c-yellow-2 { background: #fde68a; color: #92400e; } +.aksrt-tag-cloud .c-purple-2 { background: #ddd6fe; color: #5b21b6; } +.aksrt-tag-cloud .c-red-2 { background: #fecaca; color: #991b1b; } + +.aksrt-home-widget__ajax .ajax-pag { margin-top: 12px; text-align: center; } diff --git a/src/AKSRT/Widgets/WidgetForumBoardList.php b/src/AKSRT/Widgets/WidgetForumBoardList.php index 5cd9d5d0..6127f973 100644 --- a/src/AKSRT/Widgets/WidgetForumBoardList.php +++ b/src/AKSRT/Widgets/WidgetForumBoardList.php @@ -1,5 +1,7 @@ __('显示论坛版块列表及话题数量。', 'aksrt')] - ); - } + public function __construct() { + parent::__construct( + 'aksrt_forum_board_list', + __('论坛 - 版块列表', 'aksrt'), + ['description' => __('显示论坛版块列表及话题数量。', 'aksrt')] + ); + } - public function widget($args, $instance): void { - $title = !empty($instance['title']) ? $instance['title'] : __('版块列表', 'aksrt'); + public static function render_html(array $instance): string { + $limit = max(1, min(50, (int) ($instance['count'] ?? $instance['showposts'] ?? 30))); + $orderby = sanitize_key((string) ($instance['orderby'] ?? 'menu_order')); + $style = sanitize_key((string) ($instance['style'] ?? 'list')); + if (!in_array($style, ['list', 'mini'], true)) { + $style = 'list'; + } - echo $args['before_widget']; - echo $args['before_title'] . esc_html($title) . $args['after_title']; + // Prefer plate CPT (same as plate_lists lite). + if (post_type_exists('aksrt_forum_plate')) { + $query_args = [ + 'post_type' => 'aksrt_forum_plate', + 'post_status' => 'publish', + 'posts_per_page' => $limit, + 'order' => 'ASC', + 'no_found_rows' => true, + ]; + switch ($orderby) { + case 'posts': + $query_args['meta_key'] = '_aksrt_posts_count'; + $query_args['orderby'] = 'meta_value_num'; + $query_args['order'] = 'DESC'; + break; + case 'views': + $query_args['meta_key'] = '_aksrt_views'; + $query_args['orderby'] = 'meta_value_num'; + $query_args['order'] = 'DESC'; + break; + case 'date': + $query_args['orderby'] = 'date'; + $query_args['order'] = 'DESC'; + break; + default: + $query_args['orderby'] = 'menu_order title'; + } + $plates = get_posts($query_args); + if ($plates) { + ob_start(); + echo ''; + return (string) ob_get_clean(); + } + } - $rendered = false; - if (post_type_exists('aksrt_forum_plate')) { - $plates = get_posts([ - 'post_type' => 'aksrt_forum_plate', - 'post_status' => 'publish', - 'posts_per_page' => 30, - 'orderby' => 'menu_order title', - 'order' => 'ASC', - ]); - if ($plates) { - $rendered = true; - echo ''; - } - } - if (!$rendered) { - $boards = get_terms([ - 'taxonomy' => 'aksrt_forum_board', - 'hide_empty' => true, - 'orderby' => 'meta_value_num', - 'meta_key' => '_aksrt_sort_order', - 'order' => 'ASC', - ]); - if (!is_wp_error($boards) && !empty($boards)) { - echo ''; - } else { - echo '

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

'; - } - } + // Taxonomy fallback. + $term_args = [ + 'taxonomy' => 'aksrt_forum_board', + 'hide_empty' => !isset($instance['hide_empty']) || !empty($instance['hide_empty']), + 'number' => $limit, + 'order' => 'ASC', + ]; + if ($orderby === 'count') { + $term_args['orderby'] = 'count'; + $term_args['order'] = 'DESC'; + } elseif ($orderby === 'name') { + $term_args['orderby'] = 'name'; + } else { + $term_args['orderby'] = 'meta_value_num'; + $term_args['meta_key'] = '_aksrt_sort_order'; + } + $boards = get_terms($term_args); + if (is_wp_error($boards) || empty($boards)) { + return function_exists('aksrt_get_ajax_null') + ? aksrt_get_ajax_null(__('暂无版块', 'aksrt')) + : '

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

'; + } + ob_start(); + echo ''; + return (string) ob_get_clean(); + } - echo $args['after_widget']; - } + public function widget($args, $instance): void { + $title = !empty($instance['title']) ? $instance['title'] : __('版块列表', 'aksrt'); + $html = self::render_html($instance); - public function form($instance): void { - $title = !empty($instance['title']) ? $instance['title'] : __('版块列表', 'aksrt'); - ?> -

- - -

- +

+ + +

+

+ +

+

+ +

+

+ +

+

+ +

+ sanitize_text_field($new_instance['title'] ?? ''), + 'count' => max(1, min(50, (int) ($new_instance['count'] ?? 30))), + 'orderby' => $orderby, + 'style' => ($new_instance['style'] ?? '') === 'mini' ? 'mini' : 'list', + 'hide_empty' => !empty($new_instance['hide_empty']) ? 1 : 0, + ]; + } } diff --git a/src/AKSRT/Widgets/WidgetForumPlateInfo.php b/src/AKSRT/Widgets/WidgetForumPlateInfo.php index b0bf8b8b..0a1a2957 100644 --- a/src/AKSRT/Widgets/WidgetForumPlateInfo.php +++ b/src/AKSRT/Widgets/WidgetForumPlateInfo.php @@ -22,41 +22,96 @@ class WidgetForumPlateInfo extends \WP_Widget { ); } - public function widget($args, $instance): void { - $plate = $this->resolve_current_plate(); + /** + * @return array{html:string} + */ + public static function render_payload(array $instance, ?\WP_Post $plate = null): array { + $plate = $plate ?: self::resolve_current_plate_static(); if (!$plate) { - return; + return ['html' => '']; } - echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + $show_follow = !isset($instance['show_follow']) || !empty($instance['show_follow']); + $show_compose = !isset($instance['show_compose']) || !empty($instance['show_compose']); + $show_stats = !isset($instance['show_stats']) || !empty($instance['show_stats']); - $id = (int) $plate->ID; - $url = get_permalink($id); - $thumb = get_the_post_thumbnail_url($id, 'medium'); - $posts = (int) get_post_meta($id, '_aksrt_posts_count', true); - $reply = (int) get_post_meta($id, '_aksrt_reply_count', true); + $id = (int) $plate->ID; + $url = get_permalink($id); + $thumb = get_the_post_thumbnail_url($id, 'medium'); + $posts = (int) get_post_meta($id, '_aksrt_posts_count', true); + $reply = (int) get_post_meta($id, '_aksrt_reply_count', true); $follow = (int) get_post_meta($id, '_aksrt_follow_count', true); - $desc = $plate->post_excerpt !== '' ? $plate->post_excerpt : wp_trim_words(wp_strip_all_tags($plate->post_content), 30); + $views = (int) get_post_meta($id, '_aksrt_views', true); + $desc = $plate->post_excerpt !== '' ? $plate->post_excerpt : wp_trim_words(wp_strip_all_tags($plate->post_content), 30); + $icon = (string) get_post_meta($id, '_aksrt_icon', true); - echo '
'; + $forum = class_exists('\AKSRT\Forum\Forum') ? \AKSRT\Forum\Forum::get_instance() : null; + $uid = get_current_user_id(); + $is_followed = $forum && $uid > 0 ? $forum->is_user_following_plate($uid, $id) : false; + + $compose_url = function_exists('aksrt_get_forum_compose_url') + ? aksrt_get_forum_compose_url(['board' => $id]) + : home_url('/'); + + ob_start(); + echo '
'; if ($thumb) { echo '
'; + } elseif ($icon !== '') { + echo '
'; } echo '
'; echo '' . esc_html(get_the_title($plate)) . ''; if ($desc !== '') { echo '
' . esc_html($desc) . '
'; } - echo '
'; - echo ' ' . esc_html((string) $posts) . ''; - echo ' ' . esc_html((string) $reply) . ''; - echo ' ' . esc_html((string) $follow) . ''; - echo '
'; + if ($show_stats) { + echo '
'; + echo ' ' . esc_html((string) $posts) . ''; + echo ' ' . esc_html((string) $reply) . ''; + if ($views > 0) { + echo ' ' . esc_html((string) $views) . ''; + } + echo ' ' . esc_html((string) $follow) . ''; + echo '
'; + } + $actions = []; + if ($show_follow && $uid > 0) { + $actions[] = sprintf( + '', + $is_followed ? ' is-followed' : '', + $id, + $is_followed ? '1' : '0', + esc_html($is_followed ? __('已关注', 'aksrt') : __('关注', 'aksrt')) + ); + } + if ($show_compose) { + $actions[] = ' ' . esc_html__('发帖', 'aksrt') . ''; + } + if ($actions) { + echo '
' . implode('', $actions) . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + echo '
'; + return ['html' => (string) ob_get_clean()]; + } + + public function widget($args, $instance): void { + $payload = self::render_payload($instance); + if ($payload['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 $payload['html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } - private function resolve_current_plate(): ?\WP_Post { + public static function resolve_current_plate_static(): ?\WP_Post { if (is_singular('aksrt_forum_plate')) { $p = get_queried_object(); return $p instanceof \WP_Post ? $p : null; @@ -65,7 +120,6 @@ class WidgetForumPlateInfo extends \WP_Widget { $topic_id = (int) get_queried_object_id(); $plate_id = (int) get_post_meta($topic_id, '_aksrt_plate_id', true); if ($plate_id <= 0) { - // Fallback: first linked board term → plate meta, or parent. $boards = get_the_terms($topic_id, 'aksrt_forum_board'); if (!is_wp_error($boards) && !empty($boards[0])) { $linked = (int) get_term_meta((int) $boards[0]->term_id, '_aksrt_plate_id', true); @@ -84,11 +138,32 @@ class WidgetForumPlateInfo extends \WP_Widget { return null; } + private function resolve_current_plate(): ?\WP_Post { + return self::resolve_current_plate_static(); + } + public function form($instance): void { - echo '

' . esc_html__('无需配置。仅在版块页或帖子页显示对应版块信息。', 'aksrt') . '

'; + ?> +

+ +

+

+
+
+ +

+

+ sanitize_text_field((string) ($new_instance['title'] ?? '')), + 'show_stats' => !empty($new_instance['show_stats']) ? 1 : 0, + 'show_follow' => !empty($new_instance['show_follow']) ? 1 : 0, + 'show_compose' => !empty($new_instance['show_compose']) ? 1 : 0, + ]; } } diff --git a/src/AKSRT/Widgets/WidgetForumPlateModerator.php b/src/AKSRT/Widgets/WidgetForumPlateModerator.php index d2fe667d..5ad4a27f 100644 --- a/src/AKSRT/Widgets/WidgetForumPlateModerator.php +++ b/src/AKSRT/Widgets/WidgetForumPlateModerator.php @@ -22,45 +22,65 @@ class WidgetForumPlateModerator extends \WP_Widget { ); } - public function widget($args, $instance): void { - $plate = $this->resolve_current_plate(); + /** + * @return array{html:string} + */ + public static function render_payload(array $instance, ?\WP_Post $plate = null): array { + $plate = $plate ?: WidgetForumPlateInfo::resolve_current_plate_static(); if (!$plate) { - return; + return ['html' => '']; } - $id = (int) $plate->ID; + $id = (int) $plate->ID; $author_id = (int) $plate->post_author; - $mods = get_post_meta($id, '_aksrt_moderators', true); + $mods = get_post_meta($id, '_aksrt_moderators', true); if (!is_array($mods)) { $mods = []; } $mods = array_values(array_unique(array_filter(array_map('intval', $mods)))); + // Super-mods meta (zibll-like). + $super = get_post_meta($id, '_aksrt_super_moderators', true); + if (!is_array($super)) { + $super = []; + } + $super = array_values(array_unique(array_filter(array_map('intval', $super)))); + $people = []; if ($author_id > 0) { $people[] = ['id' => $author_id, 'role' => 'author']; } + foreach ($super as $sid) { + if ($sid === $author_id) { + continue; + } + $people[] = ['id' => $sid, 'role' => 'super']; + } foreach ($mods as $mid) { - if ($mid === $author_id) { + if ($mid === $author_id || in_array($mid, $super, true)) { continue; } $people[] = ['id' => $mid, 'role' => 'moderator']; } if (!$people) { - return; + return ['html' => '']; } - $title_text = (string) ($instance['title_text'] ?? __('版主', 'aksrt')); + $title_text = (string) ($instance['title_text'] ?? __('版主', 'aksrt')); + $show_avatar = !isset($instance['show_avatar']) || !empty($instance['show_avatar']); + $style = sanitize_key((string) ($instance['style'] ?? 'list')); + if (!in_array($style, ['list', 'grid'], true)) { + $style = 'list'; + } - echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - - echo '
'; + ob_start(); + echo '
'; if ($title_text !== '') { echo '
' . esc_html($title_text) . '
'; } echo '
'; foreach ($people as $row) { - $uid = (int) $row['id']; + $uid = (int) $row['id']; $user = get_userdata($uid); if (!$user) { continue; @@ -71,61 +91,66 @@ class WidgetForumPlateModerator extends \WP_Widget { $home = function_exists('aksrt_get_user_home_url') ? aksrt_get_user_home_url($uid) : get_author_posts_url($uid); - $role_label = $row['role'] === 'author' - ? __('超级版主', 'aksrt') - : __('版主', 'aksrt'); - echo ''; - echo ''; + if ($row['role'] === 'author' || $row['role'] === 'super') { + $role_label = __('超级版主', 'aksrt'); + } else { + $role_label = __('版主', 'aksrt'); + } + echo ''; + if ($show_avatar) { + echo ''; + } echo '' . esc_html($user->display_name) . ''; echo '' . esc_html($role_label) . ''; echo ''; } echo '
'; - - echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + return ['html' => (string) ob_get_clean()]; } - private function resolve_current_plate(): ?\WP_Post { - if (is_singular('aksrt_forum_plate')) { - $p = get_queried_object(); - return $p instanceof \WP_Post ? $p : null; + public function widget($args, $instance): void { + $payload = self::render_payload($instance); + if ($payload['html'] === '') { + return; } - if (is_singular('aksrt_forum_topic')) { - $topic_id = (int) get_queried_object_id(); - $plate_id = (int) get_post_meta($topic_id, '_aksrt_plate_id', true); - if ($plate_id <= 0) { - $boards = get_the_terms($topic_id, 'aksrt_forum_board'); - if (!is_wp_error($boards) && !empty($boards[0])) { - $linked = (int) get_term_meta((int) $boards[0]->term_id, '_aksrt_plate_id', true); - if ($linked > 0) { - $plate_id = $linked; - } - } - } - if ($plate_id > 0) { - $p = get_post($plate_id); - if ($p instanceof \WP_Post && $p->post_type === 'aksrt_forum_plate') { - return $p; - } - } + + echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + // title_text is rendered inside payload head (zibll-like); optional WP title still allowed. + $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 } - return null; + echo $payload['html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } public function form($instance): void { ?>

-

+

+ +

+

+ +

sanitize_text_field((string) ($new_instance['title_text'] ?? '')), + 'title_text' => sanitize_text_field((string) ($new_instance['title_text'] ?? '')), + 'style' => ($new_instance['style'] ?? '') === 'grid' ? 'grid' : 'list', + 'show_avatar' => !empty($new_instance['show_avatar']) ? 1 : 0, ]; } } From 5a900d7385e7b17e496f52c7eb149cd2232c8f6f Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 20 Jul 2026 22:57:10 +0800 Subject: [PATCH 2/3] feat(widgets): category block AJAX + recommend/term render_html Expose static HTML/query payloads for homepage category widgets, tag cloud, categories, and term card/aggregation remote refresh. --- src/AKSRT/Widgets/CategoriesWidget.php | 57 +++++++----- src/AKSRT/Widgets/CategoryBlockWidget.php | 92 ++++++++++++++++--- src/AKSRT/Widgets/CategoryRecommendWidget.php | 38 ++++---- src/AKSRT/Widgets/TagCloudWidget.php | 30 +++--- src/AKSRT/Widgets/WidgetTermAggregation.php | 58 +++++++----- src/AKSRT/Widgets/WidgetTermCards.php | 57 +++++++----- 6 files changed, 227 insertions(+), 105 deletions(-) diff --git a/src/AKSRT/Widgets/CategoriesWidget.php b/src/AKSRT/Widgets/CategoriesWidget.php index fe2362ce..5e412231 100644 --- a/src/AKSRT/Widgets/CategoriesWidget.php +++ b/src/AKSRT/Widgets/CategoriesWidget.php @@ -22,14 +22,22 @@ class CategoriesWidget extends \WP_Widget { ); } - public function widget($args, $instance): void { - 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 + /** @return array */ + 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 { $count = max(1, min(50, (int) ($instance['count'] ?? 8))); $orderby = sanitize_key((string) ($instance['orderby'] ?? 'count')); if (!in_array($orderby, ['count', 'name', 'id', 'slug'], true)) { @@ -40,8 +48,8 @@ class CategoriesWidget extends \WP_Widget { $hierarchical = !empty($instance['hierarchical']); $parent = max(0, (int) ($instance['parent'] ?? 0)); $show_count = !isset($instance['show_count']) || !empty($instance['show_count']); - $exclude = $this->parse_ids((string) ($instance['exclude'] ?? '')); - $include = $this->parse_ids((string) ($instance['include'] ?? '')); + $exclude = self::parse_ids_static((string) ($instance['exclude'] ?? '')); + $include = self::parse_ids_static((string) ($instance['include'] ?? '')); $term_args = [ 'taxonomy' => 'category', @@ -65,11 +73,13 @@ class CategoriesWidget extends \WP_Widget { $cats = get_categories($term_args); if (!$cats) { - echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - return; + return function_exists('aksrt_get_ajax_null') + ? aksrt_get_ajax_null(__('暂无分类', 'aksrt')) + : '

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

'; } - echo '
    '; + ob_start(); + echo '
      '; foreach ($cats as $cat) { echo '
    • '; echo '' . esc_html($cat->name) . ''; @@ -79,23 +89,26 @@ class CategoriesWidget extends \WP_Widget { echo '
    • '; } 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'] ?? ''), $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/categories'); + echo '
    '; + echo self::render_html($instance); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + 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/CategoryBlockWidget.php b/src/AKSRT/Widgets/CategoryBlockWidget.php index 88dff81b..767328f9 100644 --- a/src/AKSRT/Widgets/CategoryBlockWidget.php +++ b/src/AKSRT/Widgets/CategoryBlockWidget.php @@ -323,33 +323,85 @@ class CategoryBlockWidget extends \WP_Widget { ); } - public function widget($args, $instance): void { - $context = aksrt_home_widget_resolve_context($args); - $category_id = $this->resolve_category_id($instance); - $term = $category_id > 0 ? get_term($category_id, 'category') : null; - $title = apply_filters('widget_title', $instance['title'] ?? '', $instance, $this->id_base); - $title = $title !== '' ? $title : (($term && ! is_wp_error($term)) ? $term->name : __('精选分类', 'aksrt')); + /** + * @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, string $context = 'homepage'): array { + $page = max(1, $page); + $category_id = self::resolve_category_id_static($instance); $count = max(1, min(12, (int) ($instance['count'] ?? 6))); $columns = aksrt_home_widget_effective_columns((int) ($instance['columns'] ?? 3), $context); $layout = aksrt_home_widget_effective_layout( aksrt_home_widget_resolve_layout($instance['layout'] ?? 'card', 'card'), $context ); - $show_more = ! empty($instance['show_more']); + $paginate = sanitize_key((string) ($instance['paginate'] ?? 'none')); + if (!in_array($paginate, ['ajax', 'none'], true)) { + $paginate = 'none'; + } $query_args = [ 'post_type' => aksrt_home_widget_post_types(), 'post_status' => 'publish', 'posts_per_page' => $count, + 'paged' => $page, 'ignore_sticky_posts' => true, - 'no_found_rows' => true, ]; - + if ($paginate === 'none') { + $query_args['no_found_rows'] = true; + } if ($category_id > 0) { $query_args['cat'] = $category_id; } $query = new \WP_Query($query_args); + ob_start(); + aksrt_home_widget_render_post_grid($query, $layout, $columns, $context); + $html = (string) ob_get_clean(); + if ($html === '' && $page === 1) { + $html = function_exists('aksrt_get_widget_null') + ? aksrt_get_widget_null(__('暂无内容', 'aksrt')) + : '

    ' . esc_html__('暂无内容', 'aksrt') . '

    '; + } + + $max_pages = max(1, (int) $query->max_num_pages); + $total = (int) $query->found_posts; + $has_more = $paginate === 'ajax' && $page < $max_pages; + $paginate_html = ''; + if ($paginate === 'ajax' && $has_more) { + $next = add_query_arg([ + 'page' => $page + 1, + 'count' => $count, + 'cat' => $category_id, + 'columns' => $columns, + 'layout' => $layout, + 'context' => $context, + 'paginate' => 'ajax', + ], rest_url('aksrt/v1/widgets/category-block')); + $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'); + } + + public function widget($args, $instance): void { + $context = aksrt_home_widget_resolve_context($args); + $category_id = self::resolve_category_id_static($instance); + $term = $category_id > 0 ? get_term($category_id, 'category') : null; + $title = apply_filters('widget_title', $instance['title'] ?? '', $instance, $this->id_base); + $title = $title !== '' ? $title : (($term && ! is_wp_error($term)) ? $term->name : __('精选分类', 'aksrt')); + $show_more = ! empty($instance['show_more']); + $paginate = sanitize_key((string) ($instance['paginate'] ?? 'none')); + if (!in_array($paginate, ['ajax', 'none'], true)) { + $paginate = 'none'; + } + $instance['paginate'] = $paginate; + + $payload = self::query_payload($instance, 1, $context); echo $args['before_widget']; echo '
    '; @@ -360,7 +412,12 @@ class CategoryBlockWidget extends \WP_Widget { __('查看分类', 'aksrt') ); - aksrt_home_widget_render_post_grid($query, $layout, $columns, $context); + $rest = rest_url('aksrt/v1/widgets/category-block'); + echo '
    '; + echo $payload['html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo $payload['paginate_html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo ''; + echo '
    '; echo '
    '; echo $args['after_widget']; @@ -412,6 +469,14 @@ class CategoryBlockWidget extends \WP_Widget { id="get_field_id('show_more')); ?>" name="get_field_name('show_more')); ?>" value="1">

    +

    + +

    max(1, min(4, absint($new_instance['columns'] ?? 3))), 'layout' => aksrt_home_widget_resolve_layout($new_instance['layout'] ?? 'card', 'card'), 'show_more' => ! empty($new_instance['show_more']) ? 1 : 0, + 'paginate' => sanitize_key((string) ($new_instance['paginate'] ?? 'none')) === 'ajax' ? 'ajax' : 'none', ]; } - private function resolve_category_id(array $instance): int { + public static function resolve_category_id_static(array $instance): int { $category_id = absint($instance['category_id'] ?? 0); if ($category_id > 0) { return $category_id; @@ -436,4 +502,8 @@ class CategoryBlockWidget extends \WP_Widget { $fallback = aksrt_home_widget_fallback_category_ids(1); return $fallback[0] ?? 0; } + + private function resolve_category_id(array $instance): int { + return self::resolve_category_id_static($instance); + } } diff --git a/src/AKSRT/Widgets/CategoryRecommendWidget.php b/src/AKSRT/Widgets/CategoryRecommendWidget.php index 49db1d85..7d002742 100644 --- a/src/AKSRT/Widgets/CategoryRecommendWidget.php +++ b/src/AKSRT/Widgets/CategoryRecommendWidget.php @@ -24,9 +24,7 @@ class CategoryRecommendWidget extends \WP_Widget { ); } - public function widget($args, $instance): void { - $title = apply_filters('widget_title', $instance['title'] ?? '', $instance, $this->id_base); - $title = $title !== '' ? $title : __('分类推荐', 'aksrt'); + public static function render_html(array $instance): string { $category_ids = aksrt_home_widget_normalize_category_ids($instance['category_ids'] ?? [], 4); $posts_per_cat = max(1, min(8, (int) ($instance['posts_per_category'] ?? 5))); $columns = max(1, min(4, (int) ($instance['columns'] ?? 2))); @@ -39,23 +37,16 @@ class CategoryRecommendWidget extends \WP_Widget { $category_ids = aksrt_home_widget_fallback_category_ids(4); } - $cards = $this->build_cards($category_ids, $posts_per_cat, $show_views, $show_description); - - echo $args['before_widget']; - echo '
    '; - - aksrt_home_widget_render_header($title); - + $cards = self::build_cards_static($category_ids, $posts_per_cat, $show_views, $show_description); if (empty($cards)) { - echo function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无推荐分类', 'aksrt')) : '

    ' . esc_html__('暂无推荐分类', 'aksrt') . '

    '; - echo '
    '; - echo $args['after_widget']; - return; + return function_exists('aksrt_get_widget_null') + ? aksrt_get_widget_null(__('暂无推荐分类', 'aksrt')) + : '

    ' . esc_html__('暂无推荐分类', 'aksrt') . '

    '; } $lazy = aksrt_home_widget_get_lazyload_args(); - printf('
    ', (int) $columns); - + ob_start(); + printf('
    ', (int) $columns); foreach ($cards as $card) { ?>
    @@ -108,8 +99,19 @@ class CategoryRecommendWidget extends \WP_Widget {
    '; + return (string) ob_get_clean(); + } + + public function widget($args, $instance): void { + $title = apply_filters('widget_title', $instance['title'] ?? '', $instance, $this->id_base); + $title = $title !== '' ? $title : __('分类推荐', 'aksrt'); + $html = self::render_html($instance); + + echo $args['before_widget']; + echo '
    '; + aksrt_home_widget_render_header($title); + echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo '
    '; echo $args['after_widget']; } @@ -190,7 +192,7 @@ class CategoryRecommendWidget extends \WP_Widget { * @param bool $show_description Whether to fetch description. * @return array> */ - private function build_cards(array $category_ids, int $posts_per_cat, bool $show_views, bool $show_description): array { + public static function build_cards_static(array $category_ids, int $posts_per_cat, bool $show_views, bool $show_description): array { $cards = []; foreach ($category_ids as $category_id) { diff --git a/src/AKSRT/Widgets/TagCloudWidget.php b/src/AKSRT/Widgets/TagCloudWidget.php index 53044419..466439f7 100644 --- a/src/AKSRT/Widgets/TagCloudWidget.php +++ b/src/AKSRT/Widgets/TagCloudWidget.php @@ -22,13 +22,7 @@ class TagCloudWidget extends \WP_Widget { ); } - public function widget($args, $instance): void { - 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 - } - + public static function render_html(array $instance): string { $taxonomy = sanitize_key((string) ($instance['taxonomy'] ?? 'post_tag')); if (!taxonomy_exists($taxonomy)) { $taxonomy = 'post_tag'; @@ -46,18 +40,20 @@ class TagCloudWidget extends \WP_Widget { $tags = get_terms([ 'taxonomy' => $taxonomy, 'orderby' => $orderby, - 'order' => 'DESC', + 'order' => $orderby === 'name' ? 'ASC' : 'DESC', 'number' => $number, 'hide_empty' => !empty($instance['hide_empty']), ]); if (is_wp_error($tags) || !$tags) { - echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - return; + return function_exists('aksrt_get_ajax_null') + ? aksrt_get_ajax_null(__('暂无标签', 'aksrt')) + : '

    ' . esc_html__('暂无标签', 'aksrt') . '

    '; } $palette = ['c-blue', 'c-yellow', 'c-green', 'c-purple', 'c-red', '', 'c-blue-2', 'c-yellow-2', 'c-green-2', 'c-purple-2', 'c-red-2', '']; $i = random_int(0, 10); - $wrap = 'tag-cloud aksrt-tag-cloud author-tag' . ($fixed_width ? ' fixed-width' : ''); + $wrap = 'tag-cloud aksrt-tag-cloud author-tag ajax-item' . ($fixed_width ? ' fixed-width' : ''); + ob_start(); echo '
    '; foreach ($tags as $tag) { $url = get_term_link($tag); @@ -79,7 +75,19 @@ class TagCloudWidget extends \WP_Widget { echo '' . esc_html($tag->name) . $count . ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } 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'] ?? ''), $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/tag-cloud'); + echo '
    '; + echo self::render_html($instance); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo '
    '; echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } diff --git a/src/AKSRT/Widgets/WidgetTermAggregation.php b/src/AKSRT/Widgets/WidgetTermAggregation.php index be75dcff..fed1a4f1 100644 --- a/src/AKSRT/Widgets/WidgetTermAggregation.php +++ b/src/AKSRT/Widgets/WidgetTermAggregation.php @@ -22,18 +22,32 @@ class WidgetTermAggregation extends \WP_Widget { ); } - public function widget($args, $instance): void { + /** @return array */ + 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 { $taxonomy = sanitize_key((string) ($instance['taxonomy'] ?? 'category')); if (!taxonomy_exists($taxonomy)) { $taxonomy = 'category'; } - $term_ids = $this->parse_ids((string) ($instance['term_ids'] ?? '')); + $term_ids = self::parse_ids_static((string) ($instance['term_ids'] ?? '')); $count = max(1, min(12, (int) ($instance['count'] ?? 4))); $orderby = sanitize_key((string) ($instance['orderby'] ?? 'date')); $columns = max(1, min(3, (int) ($instance['columns'] ?? 1))); if (!$term_ids) { - // Fallback: top terms by count. $terms = get_terms([ 'taxonomy' => $taxonomy, 'number' => max(1, min(4, (int) ($instance['auto_terms'] ?? 2))), @@ -42,18 +56,15 @@ class WidgetTermAggregation extends \WP_Widget { 'hide_empty' => true, ]); if (is_wp_error($terms) || !$terms) { - return; + return function_exists('aksrt_get_ajax_null') + ? aksrt_get_ajax_null(__('暂无分类', 'aksrt')) + : '

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

    '; } $term_ids = array_map(static fn($t) => (int) $t->term_id, $terms); } - 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 '
    '; foreach ($term_ids as $term_id) { $term = get_term($term_id, $taxonomy); if (!$term || is_wp_error($term)) { @@ -118,23 +129,26 @@ class WidgetTermAggregation 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 + } + 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 []; - } - $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/WidgetTermCards.php b/src/AKSRT/Widgets/WidgetTermCards.php index 3fd1cf40..dd881738 100644 --- a/src/AKSRT/Widgets/WidgetTermCards.php +++ b/src/AKSRT/Widgets/WidgetTermCards.php @@ -22,14 +22,29 @@ class WidgetTermCards extends \WP_Widget { ); } - public function widget($args, $instance): void { + /** @return array */ + 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 { $taxonomy = sanitize_key((string) ($instance['taxonomy'] ?? 'category')); if (!taxonomy_exists($taxonomy)) { $taxonomy = 'category'; } $number = max(1, min(30, (int) ($instance['number'] ?? 8))); $orderby = sanitize_key((string) ($instance['orderby'] ?? 'count')); - $include = $this->parse_ids((string) ($instance['include'] ?? '')); + $include = self::parse_ids_static((string) ($instance['include'] ?? '')); $hide_empty = !isset($instance['hide_empty']) || !empty($instance['hide_empty']); $term_args = [ @@ -47,17 +62,14 @@ class WidgetTermCards extends \WP_Widget { $terms = get_terms($term_args); if (is_wp_error($terms) || !$terms) { - 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 + return function_exists('aksrt_get_ajax_null') + ? aksrt_get_ajax_null(__('暂无分类', 'aksrt')) + : '

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

    '; } $cols = max(1, min(4, (int) ($instance['columns'] ?? 2))); - echo '
    '; + ob_start(); + echo '
    '; foreach ($terms as $term) { $link = get_term_link($term); if (is_wp_error($link)) { @@ -88,23 +100,26 @@ class WidgetTermCards 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 + } + 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 []; - } - $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 { From 5eb04f4bd2c26d436d73d1cdf5123ffe077ed183 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 20 Jul 2026 22:57:11 +0800 Subject: [PATCH 3/3] feat(widgets): expand WidgetAjax routes and ajaxpager wraps Register category-block/recommend, boards, tag-cloud, categories, and term endpoints; wrap oneline/comments/users with ajaxpager shells. --- src/AKSRT/Core/WidgetAjax.php | 150 +++++++++++++++++++-- src/AKSRT/Widgets/RecentCommentsWidget.php | 3 + src/AKSRT/Widgets/WidgetOnelinePosts.php | 3 + src/AKSRT/Widgets/WidgetUserLists.php | 3 + 4 files changed, 148 insertions(+), 11 deletions(-) diff --git a/src/AKSRT/Core/WidgetAjax.php b/src/AKSRT/Core/WidgetAjax.php index 5cd0166d..82145a25 100644 --- a/src/AKSRT/Core/WidgetAjax.php +++ b/src/AKSRT/Core/WidgetAjax.php @@ -25,17 +25,24 @@ class WidgetAjax { public function register_routes(): void { $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', - ]; + '/widgets/posts' => 'rest_main_posts', + '/widgets/tab-posts' => 'rest_tab_posts', + '/widgets/paid-posts' => 'rest_paid_posts', + '/widgets/category-tabs' => 'rest_category_tabs', + '/widgets/category-block' => 'rest_category_block', + '/widgets/category-recommend' => 'rest_category_recommend', + '/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', + '/widgets/forum-boards' => 'rest_forum_boards', + '/widgets/tag-cloud' => 'rest_tag_cloud', + '/widgets/categories' => 'rest_categories', + '/widgets/term-cards' => 'rest_term_cards', + '/widgets/term-aggregation' => 'rest_term_aggregation', + ]; foreach ($routes as $path => $cb) { register_rest_route('aksrt/v1', $path, [ 'methods' => 'GET', @@ -222,6 +229,127 @@ class WidgetAjax { return new \WP_REST_Response(['success' => true, 'html' => $html], 200); } + + public function rest_category_block(\WP_REST_Request $request) { + if (!class_exists('\AKSRT\Widgets\CategoryBlockWidget')) { + return $this->fail(); + } + $instance = [ + 'category_id' => max(0, (int) ($request->get_param('cat') ?? $request->get_param('category_id') ?? 0)), + 'count' => max(1, min(12, (int) ($request->get_param('count') ?? 6))), + 'columns' => max(1, min(4, (int) ($request->get_param('columns') ?? 3))), + 'layout' => sanitize_key((string) ($request->get_param('layout') ?? 'card')), + 'paginate' => sanitize_key((string) ($request->get_param('paginate') ?? 'ajax')), + ]; + $context = sanitize_key((string) ($request->get_param('context') ?? 'homepage')); + $page = max(1, (int) ($request->get_param('page') ?? 1)); + return $this->payload(\AKSRT\Widgets\CategoryBlockWidget::query_payload($instance, $page, $context), $page); + } + + public function rest_category_recommend(\WP_REST_Request $request) { + if (!class_exists('\AKSRT\Widgets\CategoryRecommendWidget')) { + return $this->fail(); + } + $raw_ids = $request->get_param('category_ids'); + if (is_string($raw_ids)) { + $raw_ids = array_filter(array_map('intval', preg_split('/[\s,]+/', $raw_ids) ?: [])); + } + $instance = [ + 'category_ids' => is_array($raw_ids) ? $raw_ids : [], + 'posts_per_category' => max(1, min(8, (int) ($request->get_param('posts_per_category') ?? 5))), + 'columns' => max(1, min(4, (int) ($request->get_param('columns') ?? 2))), + 'show_views' => (int) ($request->get_param('show_views') ?? 0), + 'show_description' => $request->get_param('show_description') === null ? 1 : (int) $request->get_param('show_description'), + 'show_dates' => $request->get_param('show_dates') === null ? 1 : (int) $request->get_param('show_dates'), + 'show_more' => $request->get_param('show_more') === null ? 1 : (int) $request->get_param('show_more'), + ]; + $html = \AKSRT\Widgets\CategoryRecommendWidget::render_html($instance); + return new \WP_REST_Response(['success' => true, 'html' => $html], 200); + } + + public function rest_forum_boards(\WP_REST_Request $request) { + if (!class_exists('\AKSRT\Widgets\WidgetForumBoardList')) { + return $this->fail(); + } + $instance = [ + 'count' => max(1, min(50, (int) ($request->get_param('count') ?? 30))), + 'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'menu_order')), + 'style' => sanitize_key((string) ($request->get_param('style') ?? 'list')), + 'hide_empty' => $request->get_param('hide_empty') === null ? 1 : (int) $request->get_param('hide_empty'), + ]; + $html = \AKSRT\Widgets\WidgetForumBoardList::render_html($instance); + return new \WP_REST_Response(['success' => true, 'html' => $html], 200); + } + + public function rest_tag_cloud(\WP_REST_Request $request) { + if (!class_exists('\AKSRT\Widgets\TagCloudWidget')) { + return $this->fail(); + } + $instance = [ + 'taxonomy' => sanitize_key((string) ($request->get_param('taxonomy') ?? 'post_tag')), + 'count' => max(1, min(80, (int) ($request->get_param('count') ?? 15))), + 'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'count')), + 'color' => sanitize_html_class((string) ($request->get_param('color') ?? 'rand')), + 'show_count' => (int) ($request->get_param('show_count') ?? 0), + 'blank' => (int) ($request->get_param('blank') ?? 0), + 'fixed_width' => (int) ($request->get_param('fixed_width') ?? 0), + 'hide_empty' => (int) ($request->get_param('hide_empty') ?? 0), + ]; + $html = \AKSRT\Widgets\TagCloudWidget::render_html($instance); + return new \WP_REST_Response(['success' => true, 'html' => $html], 200); + } + + public function rest_categories(\WP_REST_Request $request) { + if (!class_exists('\AKSRT\Widgets\CategoriesWidget')) { + return $this->fail(); + } + $instance = [ + 'count' => max(1, min(50, (int) ($request->get_param('count') ?? 8))), + 'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'count')), + 'order' => sanitize_key((string) ($request->get_param('order') ?? 'DESC')), + 'parent' => max(0, (int) ($request->get_param('parent') ?? 0)), + 'include' => sanitize_text_field((string) ($request->get_param('include') ?? '')), + 'exclude' => sanitize_text_field((string) ($request->get_param('exclude') ?? '')), + 'show_count' => $request->get_param('show_count') === null ? 1 : (int) $request->get_param('show_count'), + 'hide_empty' => $request->get_param('hide_empty') === null ? 1 : (int) $request->get_param('hide_empty'), + 'hierarchical' => (int) ($request->get_param('hierarchical') ?? 0), + ]; + $html = \AKSRT\Widgets\CategoriesWidget::render_html($instance); + return new \WP_REST_Response(['success' => true, 'html' => $html], 200); + } + + public function rest_term_cards(\WP_REST_Request $request) { + if (!class_exists('\AKSRT\Widgets\WidgetTermCards')) { + return $this->fail(); + } + $instance = [ + 'taxonomy' => sanitize_key((string) ($request->get_param('taxonomy') ?? 'category')), + 'number' => max(1, min(30, (int) ($request->get_param('number') ?? 8))), + 'columns' => max(1, min(4, (int) ($request->get_param('columns') ?? 2))), + 'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'count')), + 'include' => sanitize_text_field((string) ($request->get_param('include') ?? '')), + 'hide_empty' => $request->get_param('hide_empty') === null ? 1 : (int) $request->get_param('hide_empty'), + ]; + $html = \AKSRT\Widgets\WidgetTermCards::render_html($instance); + return new \WP_REST_Response(['success' => true, 'html' => $html], 200); + } + + public function rest_term_aggregation(\WP_REST_Request $request) { + if (!class_exists('\AKSRT\Widgets\WidgetTermAggregation')) { + return $this->fail(); + } + $instance = [ + 'taxonomy' => sanitize_key((string) ($request->get_param('taxonomy') ?? 'category')), + 'term_ids' => sanitize_text_field((string) ($request->get_param('term_ids') ?? '')), + 'count' => max(1, min(12, (int) ($request->get_param('count') ?? 4))), + 'columns' => max(1, min(3, (int) ($request->get_param('columns') ?? 1))), + 'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'date')), + 'auto_terms' => max(1, min(4, (int) ($request->get_param('auto_terms') ?? 2))), + ]; + $html = \AKSRT\Widgets\WidgetTermAggregation::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); } diff --git a/src/AKSRT/Widgets/RecentCommentsWidget.php b/src/AKSRT/Widgets/RecentCommentsWidget.php index bfaa45d7..2a382d95 100644 --- a/src/AKSRT/Widgets/RecentCommentsWidget.php +++ b/src/AKSRT/Widgets/RecentCommentsWidget.php @@ -78,7 +78,10 @@ class RecentCommentsWidget extends \WP_Widget { if ($title !== '') { echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } + $rest = rest_url('aksrt/v1/widgets/recent-comments'); + echo '
    '; echo self::render_html($instance); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo '
    '; echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } diff --git a/src/AKSRT/Widgets/WidgetOnelinePosts.php b/src/AKSRT/Widgets/WidgetOnelinePosts.php index 5051c58e..f5299348 100644 --- a/src/AKSRT/Widgets/WidgetOnelinePosts.php +++ b/src/AKSRT/Widgets/WidgetOnelinePosts.php @@ -105,7 +105,10 @@ class WidgetOnelinePosts extends \WP_Widget { if ($title !== '') { echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } + $rest = rest_url('aksrt/v1/widgets/oneline-posts'); + echo '
    '; echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo '
    '; echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } diff --git a/src/AKSRT/Widgets/WidgetUserLists.php b/src/AKSRT/Widgets/WidgetUserLists.php index 8da26633..86015a3c 100644 --- a/src/AKSRT/Widgets/WidgetUserLists.php +++ b/src/AKSRT/Widgets/WidgetUserLists.php @@ -129,7 +129,10 @@ class WidgetUserLists extends \WP_Widget { if ($title !== '') { echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } + $rest = rest_url('aksrt/v1/widgets/user-lists'); + echo '
    '; echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo '
    '; echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped }