'POST', 'callback' => [$this, 'rest_test_mail'], 'permission_callback' => [$this, 'check_permission'], ]); // Footer Tabbar register_rest_route(self::API_NAMESPACE, '/settings/footer-tabbar', [ 'methods' => ['GET', 'POST'], 'callback' => [$this, 'rest_handle_footer_tabbar'], 'permission_callback' => [$this, 'check_permission'], ]); register_rest_route(self::API_NAMESPACE, '/settings/footer-tabbar/reorder', [ 'methods' => 'POST', 'callback' => [$this, 'rest_reorder_footer_tabbar'], 'permission_callback' => [$this, 'check_permission'], ]); // Floating Buttons register_rest_route(self::API_NAMESPACE, '/settings/floating-buttons', [ 'methods' => ['GET', 'POST'], 'callback' => [$this, 'rest_handle_floating_buttons'], 'permission_callback' => [$this, 'check_permission'], ]); // Friend Links register_rest_route(self::API_NAMESPACE, '/friend-links', [ 'methods' => ['GET', 'POST'], 'callback' => [$this, 'rest_handle_friend_links'], 'permission_callback' => [$this, 'check_permission'], ]); register_rest_route(self::API_NAMESPACE, '/friend-links/(?P\d+)', [ 'methods' => 'DELETE', 'callback' => [$this, 'rest_delete_friend_link'], 'permission_callback' => [$this, 'check_permission'], ]); register_rest_route(self::API_NAMESPACE, '/friend-links/reorder', [ 'methods' => 'POST', 'callback' => [$this, 'rest_reorder_friend_links'], 'permission_callback' => [$this, 'check_permission'], ]); // CMS Features register_rest_route(self::API_NAMESPACE, '/settings/cms-features', [ 'methods' => ['GET', 'POST'], 'callback' => [$this, 'rest_handle_cms_features'], 'permission_callback' => [$this, 'check_permission'], ]); // Category recommend register_rest_route(self::API_NAMESPACE, '/settings/category-recommend', [ 'methods' => ['GET', 'POST'], 'callback' => [$this, 'rest_handle_category_recommend'], 'permission_callback' => [$this, 'check_permission'], ]); // Categories list (for admin selectors) register_rest_route(self::API_NAMESPACE, '/categories', [ 'methods' => 'GET', 'callback' => [$this, 'rest_get_categories'], 'permission_callback' => [$this, 'check_permission'], ]); // Category term meta register_rest_route(self::API_NAMESPACE, '/categories/(?P\d+)/meta', [ 'methods' => 'POST', 'callback' => [$this, 'rest_update_category_meta'], 'permission_callback' => [$this, 'check_permission'], ]); // Homepage categories (for admin tab editor) register_rest_route(self::API_NAMESPACE, '/homepage/categories', [ 'methods' => 'GET', 'callback' => [$this, 'rest_homepage_categories'], 'permission_callback' => [$this, 'check_permission'], ]); // User search (for admin user selectors) register_rest_route(self::API_NAMESPACE, '/users/search', [ 'methods' => 'GET', 'callback' => [$this, 'rest_search_users'], 'permission_callback' => [$this, 'check_permission'], 'args' => [ 'search' => ['type' => 'string', 'default' => '', 'sanitize_callback' => 'sanitize_text_field'], 'number' => ['type' => 'integer', 'default' => 20, 'sanitize_callback' => 'absint'], ], ]); } public function rest_handle_cms_features(\WP_REST_Request $request): \WP_REST_Response { if ($request->get_method() === 'GET') { return $this->success(['cms_features' => \Jio\Settings\Settings::get_cms_features()]); } $params = $this->get_json_params($request); $allowed = [ 'resource_cpt_enabled' => 'bool', 'search_include_resource' => 'bool', ]; $sanitized = $this->sanitize_fields($params, $allowed); $settings = \Jio\Settings\Settings::get_all(); $settings['cms_features'] = array_merge( is_array($settings['cms_features'] ?? null) ? $settings['cms_features'] : [], $sanitized ); update_option('jio_settings', $settings); \Jio\Settings\Settings::flush_settings_cache(); return $this->success(['message' => 'CMS 功能已保存', 'cms_features' => \Jio\Settings\Settings::get_cms_features()]); } public function rest_test_mail(\WP_REST_Request $request): \WP_REST_Response { $params = $this->get_json_params($request); $to = sanitize_email($params['to'] ?? ''); if (!is_email($to)) { return $this->error(__('请输入有效的邮箱地址', 'jio')); } $result = \Jio\Settings\Mail::send_test_email($to); if (is_wp_error($result)) { return $this->error(__('邮件发送失败,详细原因已写入服务器错误日志', 'jio')); } return $this->success(['message' => '测试邮件已发送']); } public function rest_homepage_categories(): \WP_REST_Response { $data = []; $cats = get_categories(['hide_empty' => false, 'orderby' => 'name', 'order' => 'ASC']); foreach ($cats as $cat) { $data[] = [ 'id' => (int) $cat->term_id, 'term_id' => (int) $cat->term_id, 'name' => $cat->name, 'slug' => $cat->slug, 'count' => (int) $cat->count, 'parent' => (int) $cat->parent, 'taxonomy' => 'category', ]; } return $this->success(['data' => $data]); } public function rest_handle_friend_links(\WP_REST_Request $request): \WP_REST_Response { if ($request->get_method() === 'GET') { $links = jio_get_friend_links(); return rest_ensure_response(['code' => 'success', 'data' => $links]); } $params = $this->get_json_params($request); $links = get_option('jio_friend_links', []); $new_link = [ 'id' => uniqid('fl_'), 'name' => sanitize_text_field($params['name'] ?? ''), 'url' => esc_url_raw($params['url'] ?? ''), 'logo' => esc_url_raw($params['logo'] ?? ''), 'description' => sanitize_textarea_field($params['description'] ?? ''), 'sort_order' => (int) ($params['sort_order'] ?? 0), 'active' => 1, ]; $links[] = $new_link; usort($links, function ($a, $b) { return ($a['sort_order'] ?? 0) <=> ($b['sort_order'] ?? 0); }); update_option('jio_friend_links', $links); do_action('jio_friend_link_submitted', $new_link); return rest_ensure_response(['code' => 'success', 'data' => $new_link, 'message' => __('Friend link added.', 'jio')]); } public function rest_delete_friend_link(\WP_REST_Request $request): \WP_REST_Response { $id = $request->get_param('id'); $links = get_option('jio_friend_links', []); $links = array_filter($links, function ($link) use ($id) { return ($link['id'] ?? '') !== $id; }); $links = array_values($links); update_option('jio_friend_links', $links); return rest_ensure_response(['code' => 'success', 'message' => __('Friend link deleted.', 'jio')]); } public function rest_reorder_friend_links(\WP_REST_Request $request): \WP_REST_Response { $ordered_ids = $request->get_param('ids'); if (!is_array($ordered_ids)) { return new \WP_REST_Response(['code' => 'error', 'message' => __('参数错误', 'jio')], 400); } $links = get_option('jio_friend_links', []); $order_map = array_flip($ordered_ids); usort($links, function ($a, $b) use ($order_map) { $idx_a = $order_map[($a['id'] ?? '')] ?? 999; $idx_b = $order_map[($b['id'] ?? '')] ?? 999; return $idx_a <=> $idx_b; }); foreach ($links as $index => &$link) { $link['sort_order'] = $index; } update_option('jio_friend_links', $links); return rest_ensure_response(['code' => 'success', 'message' => __('Order saved.', 'jio')]); } public function rest_handle_footer_tabbar(\WP_REST_Request $request): \WP_REST_Response { if ($request->get_method() === 'GET') { $settings = class_exists('\Jio\Settings\Settings') ? \Jio\Settings\Settings::get_footer_tabbar_settings() : ['enabled' => true, 'scroll_hide' => false, 'scroll_down_hide' => true, 'items' => []]; return rest_ensure_response(['code' => 'success', 'data' => $settings]); } $params = $this->get_json_params($request); $items = isset($params['items']) ? array_map([$this, 'sanitize_tabbar_item'], (array) $params['items']) : []; $max_items = max(3, min(7, (int) ($params['max_items'] ?? 5))); if (count($items) > $max_items) { return new \WP_REST_Response([ 'code' => 'error', 'message' => sprintf(__('Footer tabbar supports at most %d items.', 'jio'), $max_items), ], 400); } $tabbar = [ 'enabled' => !empty($params['enabled']), 'scroll_hide' => !empty($params['scroll_hide']), 'scroll_down_hide' => !empty($params['scroll_down_hide']), 'max_items' => $max_items, 'icon_size' => max(16, min(32, (int) ($params['icon_size'] ?? 22))), 'text_size' => max(8, min(14, (int) ($params['text_size'] ?? 10))), 'show_text' => !empty($params['show_text']), 'active_color' => \Jio\Core\Color::sanitize($params['active_color'] ?? '') ?: '#f04494', 'bg_color' => \Jio\Core\Color::sanitize($params['bg_color'] ?? '') ?: '', 'border' => !empty($params['border']), 'shadow' => !empty($params['shadow']), 'animation' => !empty($params['animation']), 'double_click_home' => !empty($params['double_click_home']), 'badge_dot' => !empty($params['badge_dot']), 'items' => $items, ]; update_option('jio_footer_tabbar', $tabbar); if (class_exists('\Jio\Settings\Settings')) { \Jio\Settings\Settings::flush_settings_cache(); } return rest_ensure_response(['code' => 'success', 'message' => __('Footer tabbar settings saved.', 'jio')]); } }