From 25ce1d8f43bf6ed314efa47a47e48622833ce76c Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 5 Aug 2026 00:16:39 +0800 Subject: [PATCH] refactor(badges): use modal REST routes Co-Authored-By: Claude Fable 5 --- assets/js/user-badges.js | 4 +- src/User/Badges.php | 88 ++++++++++++++++++++++------------------ 2 files changed, 51 insertions(+), 41 deletions(-) diff --git a/assets/js/user-badges.js b/assets/js/user-badges.js index 24df0b37..04ed5e8d 100644 --- a/assets/js/user-badges.js +++ b/assets/js/user-badges.js @@ -347,8 +347,8 @@ var data = r.json && r.json.data; var isSelf = data && data.is_self; if (isSelf && data.wear) { - refreshed = (window.aksrtData && window.aksrtData.ajaxUrl) - ? window.aksrtData.ajaxUrl + '?action=aksrt_single_badge_info_modal&name=' + encodeURIComponent(data.wear) + '&user_id=' + (uid || 0) + refreshed = uid > 0 && data.wear + ? window.AksrtRequest.url('/users/' + uid + '/badges/' + encodeURIComponent(data.wear) + '/modal') : null; } } catch (_) {} diff --git a/src/User/Badges.php b/src/User/Badges.php index 308dd91a..ff69551e 100644 --- a/src/User/Badges.php +++ b/src/User/Badges.php @@ -30,11 +30,6 @@ class Badges { public function init(): void { add_action('rest_api_init', [$this, 'register_routes']); add_action('aksrt_badge_granted', [$this, 'on_badge_granted_notify'], 10, 3); - // HTML modal for modal runtime (zibll user_medal_info_modal). - add_action('wp_ajax_aksrt_user_badge_info_modal', [$this, 'ajax_info_modal']); - add_action('wp_ajax_nopriv_aksrt_user_badge_info_modal', [$this, 'ajax_info_modal']); - add_action('wp_ajax_aksrt_single_badge_info_modal', [$this, 'ajax_single_info_modal']); - add_action('wp_ajax_nopriv_aksrt_single_badge_info_modal', [$this, 'ajax_single_info_modal']); } /** @@ -71,39 +66,32 @@ class Badges { } /** - * AJAX: full badge list modal HTML. + * REST response for the badge list modal. */ - public function ajax_info_modal(): void { - $user_id = max(0, (int) ($_REQUEST['id'] ?? $_REQUEST['user_id'] ?? 0)); // phpcs:ignore WordPress.Security.NonceVerification.Recommended - if ($user_id <= 0) { - $user_id = get_current_user_id(); - } + public function rest_modal_list(\WP_REST_Request $request) { + $user_id = absint($request->get_param('id')); if ($user_id <= 0 || !get_userdata($user_id)) { - wp_send_json_error(['message' => __('用户不存在', 'aksrt')], 404); + return new \WP_Error('aksrt_badge_user_missing', __('用户不存在', 'aksrt'), ['status' => 404]); } - $html = self::render_info_modal_html($user_id); - wp_send_json_success(['html' => $html]); + return new \WP_REST_Response(['html' => self::render_info_modal_html($user_id)], 200); } /** - * AJAX: single badge detail modal. + * REST response for a single badge modal. */ - public function ajax_single_info_modal(): void { - $slug = sanitize_key((string) ($_REQUEST['name'] ?? $_REQUEST['slug'] ?? '')); // phpcs:ignore WordPress.Security.NonceVerification.Recommended - $user_id = max(0, (int) ($_REQUEST['user_id'] ?? $_REQUEST['id'] ?? 0)); // phpcs:ignore WordPress.Security.NonceVerification.Recommended + public function rest_modal_single(\WP_REST_Request $request) { + $slug = sanitize_key((string) $request->get_param('slug')); + $user_id = absint($request->get_param('id')); if ($slug === '') { - wp_send_json_error(['message' => __('徽章不存在', 'aksrt')], 404); + return new \WP_Error('aksrt_badge_missing', __('徽章不存在', 'aksrt'), ['status' => 404]); } - $html = self::render_single_modal_html($slug, $user_id); - wp_send_json_success(['html' => $html]); + return new \WP_REST_Response(['html' => self::render_single_modal_html($slug, $user_id)], 200); } /** - * Badge list modal HTML (zibll zib_get_user_medal_info_modal parity). + * Badge list modal HTML. * - * Head (avatar + owner + worn medal) + category-grouped medal card grid. - * Each card deep-links into the single badge modal via the modal runtime - * contract (data-aksrt-modal="remote" + data-aksrt-modal-url). + * Head (avatar + owner + worn medal) and category-grouped medal card grid. */ public static function render_info_modal_html(int $user_id): string { $user = get_userdata($user_id); @@ -162,7 +150,7 @@ class Badges { // Footer: quick jump to my own badges while browsing someone else (zibll). if (!$is_self && $current > 0) { - $my_url = add_query_arg(['action' => 'aksrt_user_badge_info_modal', 'id' => $current], admin_url('admin-ajax.php')); + $my_url = rest_url('aksrt/v1/users/' . $current . '/badges/modal'); $html .= ''; @@ -199,10 +187,7 @@ class Badges { } $card .= ''; - $url = add_query_arg( - ['action' => 'aksrt_single_badge_info_modal', 'name' => $slug, 'user_id' => $user_id], - admin_url('admin-ajax.php') - ); + $url = rest_url('aksrt/v1/users/' . $user_id . '/badges/' . rawurlencode($slug) . '/modal'); return '' . $card . ''; } @@ -262,7 +247,7 @@ class Badges { // Back to the full badge list modal. if ($user_id > 0) { - $back_url = add_query_arg(['action' => 'aksrt_user_badge_info_modal', 'id' => $user_id], admin_url('admin-ajax.php')); + $back_url = rest_url('aksrt/v1/users/' . $user_id . '/badges/modal'); $html .= ' ' . esc_html__('返回徽章列表', 'aksrt') . ''; } @@ -271,7 +256,7 @@ class Badges { } /** - * Remote-modal trigger for a user's badge list (zibll zib_get_user_medal_info_link). + * Declarative modal runtime trigger for a user's badge list. */ public static function get_info_link(int $user_id, string $class = '', ?string $text = null): string { if ($user_id <= 0 || !self::enabled()) { @@ -283,13 +268,7 @@ class Badges { if (!function_exists('aksrt_get_modal_link')) { return ''; } - $url = add_query_arg( - [ - 'action' => 'aksrt_user_badge_info_modal', - 'id' => $user_id, - ], - admin_url('admin-ajax.php') - ); + $url = rest_url('aksrt/v1/users/' . $user_id . '/badges/modal'); return aksrt_get_modal_link([ 'tag' => 'a', 'class' => trim('user-medal-info ' . $class), @@ -315,6 +294,37 @@ class Badges { ], ]); + register_rest_route('aksrt/v1', '/users/(?P\d+)/badges/modal', [ + 'methods' => 'GET', + 'callback' => [$this, 'rest_modal_list'], + 'permission_callback' => '__return_true', + 'args' => [ + 'id' => [ + 'type' => 'integer', + 'required' => true, + 'sanitize_callback' => 'absint', + ], + ], + ]); + + register_rest_route('aksrt/v1', '/users/(?P\d+)/badges/(?P[a-z0-9_-]+)/modal', [ + 'methods' => 'GET', + 'callback' => [$this, 'rest_modal_single'], + 'permission_callback' => '__return_true', + 'args' => [ + 'id' => [ + 'type' => 'integer', + 'required' => true, + 'sanitize_callback' => 'absint', + ], + 'slug' => [ + 'type' => 'string', + 'required' => true, + 'sanitize_callback' => 'sanitize_key', + ], + ], + ]); + register_rest_route('aksrt/v1', '/users/me/badges/wear', [ 'methods' => 'POST', 'callback' => [$this, 'rest_wear'],