feat(user): badge wear and grant service
Add AKSRT_User_Badges own/wear/grant REST and WP profile admin grant UI, and route identity signals through the service.
This commit is contained in:
1 parent
c45a0ecef5
commit
219cd0236e
5 files changed
+592
-40
No files matched your search
@@ -364,6 +364,13 @@ if (class_exists('AKSRT_User_Balance_Admin')) {
|
|||||||
AKSRT_User_Balance_Admin::get_instance()->init();
|
AKSRT_User_Balance_Admin::get_instance()->init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (class_exists('AKSRT_User_Badges')) {
|
||||||
|
AKSRT_User_Badges::get_instance()->init();
|
||||||
|
}
|
||||||
|
if (class_exists('AKSRT_User_Badges_Admin')) {
|
||||||
|
AKSRT_User_Badges_Admin::get_instance()->init();
|
||||||
|
}
|
||||||
|
|
||||||
// WordPress 原生侧栏:支付/订单运营中心(zibll zibpay 对齐)
|
// WordPress 原生侧栏:支付/订单运营中心(zibll zibpay 对齐)
|
||||||
if (is_admin() && class_exists('AKSRT_Admin_Pay_Menus')) {
|
if (is_admin() && class_exists('AKSRT_Admin_Pay_Menus')) {
|
||||||
AKSRT_Admin_Pay_Menus::get_instance()->init();
|
AKSRT_Admin_Pay_Menus::get_instance()->init();
|
||||||
|
|||||||
@@ -219,6 +219,8 @@ require_once AKSRT_DIR . '/inc/classes/class-aksrt-email-rollback.php';
|
|||||||
// 原生用户菜单:积分管理
|
// 原生用户菜单:积分管理
|
||||||
require_once AKSRT_DIR . '/inc/classes/class-aksrt-user-credits-admin.php';
|
require_once AKSRT_DIR . '/inc/classes/class-aksrt-user-credits-admin.php';
|
||||||
require_once AKSRT_DIR . '/inc/classes/class-aksrt-user-balance-admin.php';
|
require_once AKSRT_DIR . '/inc/classes/class-aksrt-user-balance-admin.php';
|
||||||
|
require_once AKSRT_DIR . '/inc/classes/class-aksrt-user-badges.php';
|
||||||
|
require_once AKSRT_DIR . '/inc/classes/class-aksrt-user-badges-admin.php';
|
||||||
|
|
||||||
// 加载数据表迁移器(CMS / 积分 / 下载 token)
|
// 加载数据表迁移器(CMS / 积分 / 下载 token)
|
||||||
require_once AKSRT_DIR . '/inc/classes/db-traits/trait-aksrt-db-migrator-part-2.php';
|
require_once AKSRT_DIR . '/inc/classes/db-traits/trait-aksrt-db-migrator-part-2.php';
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Admin grant/revoke badges on WP user profile screens.
|
||||||
|
*
|
||||||
|
* @package AKSRT
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('ABSPATH') || exit;
|
||||||
|
|
||||||
|
class AKSRT_User_Badges_Admin {
|
||||||
|
|
||||||
|
private static $instance = null;
|
||||||
|
|
||||||
|
private function __construct() {}
|
||||||
|
|
||||||
|
public static function get_instance(): self {
|
||||||
|
if (null === self::$instance) {
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function init(): void {
|
||||||
|
if (!is_admin()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
add_action('show_user_profile', [$this, 'render_section']);
|
||||||
|
add_action('edit_user_profile', [$this, 'render_section']);
|
||||||
|
add_action('personal_options_update', [$this, 'handle_update']);
|
||||||
|
add_action('edit_user_profile_update', [$this, 'handle_update']);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function can_manage(): bool {
|
||||||
|
return class_exists('AKSRT_User_Badges') && AKSRT_User_Badges::can_manually_set(get_current_user_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_section(WP_User $user): void {
|
||||||
|
if (!class_exists('AKSRT_User_Badges') || !AKSRT_User_Badges::enabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$dict = AKSRT_User_Badges::get_dictionary();
|
||||||
|
if (!$dict) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$owned = AKSRT_User_Badges::get_owned((int) $user->ID);
|
||||||
|
$wear = AKSRT_User_Badges::get_wear((int) $user->ID);
|
||||||
|
$can = $this->can_manage();
|
||||||
|
?>
|
||||||
|
<a id="aksrt-user-badges"></a>
|
||||||
|
<h2><?php esc_html_e('AKSRT 徽章', 'aksrt'); ?></h2>
|
||||||
|
<table class="form-table" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<th><label><?php esc_html_e('已授予徽章', 'aksrt'); ?></label></th>
|
||||||
|
<td>
|
||||||
|
<?php if (!$can) : ?>
|
||||||
|
<p class="description"><?php esc_html_e('当前账号无权授予/收回徽章。', 'aksrt'); ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<fieldset <?php disabled(!$can); ?>>
|
||||||
|
<?php foreach ($dict as $item) : ?>
|
||||||
|
<?php
|
||||||
|
$slug = $item['slug'];
|
||||||
|
$checked = in_array($slug, $owned, true);
|
||||||
|
?>
|
||||||
|
<label style="display:inline-flex;align-items:center;gap:6px;margin:0 16px 10px 0;">
|
||||||
|
<input type="checkbox" name="aksrt_user_badges[]" value="<?php echo esc_attr($slug); ?>" <?php checked($checked); ?> <?php disabled(!$can); ?>>
|
||||||
|
<?php if (!empty($item['icon_url'])) : ?>
|
||||||
|
<img src="<?php echo esc_url($item['icon_url']); ?>" alt="" width="18" height="18" style="object-fit:contain;vertical-align:middle;">
|
||||||
|
<?php endif; ?>
|
||||||
|
<span><?php echo esc_html($item['label']); ?> <code><?php echo esc_html($slug); ?></code></span>
|
||||||
|
</label>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</fieldset>
|
||||||
|
<p class="description"><?php esc_html_e('勾选即授予;取消勾选并保存即收回。', 'aksrt'); ?></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><label for="aksrt_wear_badge"><?php esc_html_e('当前佩戴', 'aksrt'); ?></label></th>
|
||||||
|
<td>
|
||||||
|
<select name="aksrt_wear_badge" id="aksrt_wear_badge" <?php disabled(!$can); ?>>
|
||||||
|
<option value=""><?php esc_html_e('(未佩戴)', 'aksrt'); ?></option>
|
||||||
|
<?php foreach ($dict as $item) : ?>
|
||||||
|
<?php if (!in_array($item['slug'], $owned, true) && $item['slug'] !== $wear) : ?>
|
||||||
|
<?php continue; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
<option value="<?php echo esc_attr($item['slug']); ?>" <?php selected($wear, $item['slug']); ?>>
|
||||||
|
<?php echo esc_html($item['label']); ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<p class="description"><?php esc_html_e('仅可佩戴已授予徽章;用户也可在前台自行佩戴。', 'aksrt'); ?></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<?php
|
||||||
|
wp_nonce_field('aksrt_user_badges_admin', 'aksrt_user_badges_nonce');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle_update(int $user_id): void {
|
||||||
|
if (!$this->can_manage()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isset($_POST['aksrt_user_badges_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash((string) $_POST['aksrt_user_badges_nonce'])), 'aksrt_user_badges_admin')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!class_exists('AKSRT_User_Badges') || !AKSRT_User_Badges::enabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$raw = isset($_POST['aksrt_user_badges']) ? (array) wp_unslash($_POST['aksrt_user_badges']) : [];
|
||||||
|
$slugs = array_map('sanitize_key', $raw);
|
||||||
|
AKSRT_User_Badges::set_owned($user_id, $slugs);
|
||||||
|
|
||||||
|
$wear = isset($_POST['aksrt_wear_badge']) ? sanitize_key((string) wp_unslash($_POST['aksrt_wear_badge'])) : '';
|
||||||
|
if ($wear === '') {
|
||||||
|
delete_user_meta($user_id, AKSRT_User_Badges::META_WEAR);
|
||||||
|
} else {
|
||||||
|
$res = AKSRT_User_Badges::wear($user_id, $wear);
|
||||||
|
if (is_wp_error($res)) {
|
||||||
|
// leave owned as-is; wear ignored if invalid
|
||||||
|
delete_user_meta($user_id, AKSRT_User_Badges::META_WEAR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,407 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* User badges — own / wear / grant (Zibll medal stack parity).
|
||||||
|
*
|
||||||
|
* Meta:
|
||||||
|
* aksrt_user_badges string[]|CSV owned slug list
|
||||||
|
* aksrt_wear_badge string currently worn slug
|
||||||
|
*
|
||||||
|
* @package AKSRT
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('ABSPATH') || exit;
|
||||||
|
|
||||||
|
class AKSRT_User_Badges {
|
||||||
|
|
||||||
|
public const META_OWNED = 'aksrt_user_badges';
|
||||||
|
public const META_WEAR = 'aksrt_wear_badge';
|
||||||
|
|
||||||
|
private static $instance = null;
|
||||||
|
|
||||||
|
private function __construct() {}
|
||||||
|
|
||||||
|
public static function get_instance(): self {
|
||||||
|
if (null === self::$instance) {
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function init(): void {
|
||||||
|
add_action('rest_api_init', [$this, 'register_routes']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function register_routes(): void {
|
||||||
|
register_rest_route('aksrt/v1', '/users/(?P<id>\d+)/badges', [
|
||||||
|
'methods' => 'GET',
|
||||||
|
'callback' => [$this, 'rest_list'],
|
||||||
|
'permission_callback' => '__return_true',
|
||||||
|
'args' => [
|
||||||
|
'id' => [
|
||||||
|
'type' => 'integer',
|
||||||
|
'required' => true,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
register_rest_route('aksrt/v1', '/users/me/badges/wear', [
|
||||||
|
'methods' => 'POST',
|
||||||
|
'callback' => [$this, 'rest_wear'],
|
||||||
|
'permission_callback' => static function () {
|
||||||
|
return is_user_logged_in();
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
register_rest_route('aksrt/v1', '/users/(?P<id>\d+)/badges/grant', [
|
||||||
|
'methods' => 'POST',
|
||||||
|
'callback' => [$this, 'rest_grant'],
|
||||||
|
'permission_callback' => [$this, 'rest_can_manually_set'],
|
||||||
|
'args' => [
|
||||||
|
'id' => [
|
||||||
|
'type' => 'integer',
|
||||||
|
'required' => true,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
register_rest_route('aksrt/v1', '/users/(?P<id>\d+)/badges/revoke', [
|
||||||
|
'methods' => 'POST',
|
||||||
|
'callback' => [$this, 'rest_revoke'],
|
||||||
|
'permission_callback' => [$this, 'rest_can_manually_set'],
|
||||||
|
'args' => [
|
||||||
|
'id' => [
|
||||||
|
'type' => 'integer',
|
||||||
|
'required' => true,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rest_can_manually_set(): bool {
|
||||||
|
return self::can_manually_set(get_current_user_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function enabled(): bool {
|
||||||
|
if (!class_exists('AKSRT_Settings')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$settings = AKSRT_Settings::get_user_identity_settings();
|
||||||
|
return !empty($settings['badge']['enabled']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function public_display(): bool {
|
||||||
|
if (!class_exists('AKSRT_Settings')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$settings = AKSRT_Settings::get_user_identity_settings();
|
||||||
|
return !empty($settings['badge']['enabled']) && !empty($settings['badge']['public_display']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<array{slug:string,label:string,tone:string,icon_url:string}>
|
||||||
|
*/
|
||||||
|
public static function get_dictionary(): array {
|
||||||
|
if (!class_exists('AKSRT_Settings')) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$settings = AKSRT_Settings::get_user_identity_settings();
|
||||||
|
$dict = is_array($settings['badge']['dictionary'] ?? null) ? $settings['badge']['dictionary'] : [];
|
||||||
|
$out = [];
|
||||||
|
foreach ($dict as $item) {
|
||||||
|
$slug = sanitize_key((string) ($item['slug'] ?? ''));
|
||||||
|
if ($slug === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$out[] = [
|
||||||
|
'slug' => $slug,
|
||||||
|
'label' => (string) ($item['label'] ?? $slug),
|
||||||
|
'tone' => (string) ($item['tone'] ?? 'neutral'),
|
||||||
|
'icon_url' => (string) ($item['icon_url'] ?? ''),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string,array{slug:string,label:string,tone:string,icon_url:string}>
|
||||||
|
*/
|
||||||
|
public static function get_dictionary_map(): array {
|
||||||
|
$map = [];
|
||||||
|
foreach (self::get_dictionary() as $item) {
|
||||||
|
$map[$item['slug']] = $item;
|
||||||
|
}
|
||||||
|
return $map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<string>
|
||||||
|
*/
|
||||||
|
public static function get_owned(int $user_id): array {
|
||||||
|
if ($user_id <= 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$raw = get_user_meta($user_id, self::META_OWNED, true);
|
||||||
|
if (!is_array($raw)) {
|
||||||
|
$raw = preg_split('/[\s,]+/', (string) $raw, -1, PREG_SPLIT_NO_EMPTY);
|
||||||
|
}
|
||||||
|
$map = self::get_dictionary_map();
|
||||||
|
$out = [];
|
||||||
|
foreach ((array) $raw as $slug) {
|
||||||
|
$slug = sanitize_key((string) $slug);
|
||||||
|
if ($slug === '' || !isset($map[$slug])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$out[] = $slug;
|
||||||
|
}
|
||||||
|
return array_values(array_unique($out));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param list<string> $slugs
|
||||||
|
*/
|
||||||
|
public static function set_owned(int $user_id, array $slugs): void {
|
||||||
|
if ($user_id <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$map = self::get_dictionary_map();
|
||||||
|
$clean = [];
|
||||||
|
foreach ($slugs as $slug) {
|
||||||
|
$slug = sanitize_key((string) $slug);
|
||||||
|
if ($slug !== '' && isset($map[$slug])) {
|
||||||
|
$clean[] = $slug;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$clean = array_values(array_unique($clean));
|
||||||
|
update_user_meta($user_id, self::META_OWNED, $clean);
|
||||||
|
|
||||||
|
$wear = self::get_wear($user_id);
|
||||||
|
if ($wear !== '' && !in_array($wear, $clean, true)) {
|
||||||
|
delete_user_meta($user_id, self::META_WEAR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_wear(int $user_id): string {
|
||||||
|
if ($user_id <= 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
$wear = sanitize_key((string) get_user_meta($user_id, self::META_WEAR, true));
|
||||||
|
if ($wear === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
$owned = self::get_owned($user_id);
|
||||||
|
return in_array($wear, $owned, true) ? $wear : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true|WP_Error
|
||||||
|
*/
|
||||||
|
public static function wear(int $user_id, string $slug) {
|
||||||
|
if ($user_id <= 0) {
|
||||||
|
return new WP_Error('aksrt_badge_user', __('无效用户', 'aksrt'), ['status' => 400]);
|
||||||
|
}
|
||||||
|
if (!self::enabled()) {
|
||||||
|
return new WP_Error('aksrt_badge_disabled', __('徽章系统未启用', 'aksrt'), ['status' => 403]);
|
||||||
|
}
|
||||||
|
$slug = sanitize_key($slug);
|
||||||
|
$map = self::get_dictionary_map();
|
||||||
|
if ($slug === '' || !isset($map[$slug])) {
|
||||||
|
return new WP_Error('aksrt_badge_unknown', __('徽章不存在', 'aksrt'), ['status' => 404]);
|
||||||
|
}
|
||||||
|
$owned = self::get_owned($user_id);
|
||||||
|
if (!in_array($slug, $owned, true)) {
|
||||||
|
return new WP_Error('aksrt_badge_not_owned', __('尚未获得该徽章', 'aksrt'), ['status' => 403]);
|
||||||
|
}
|
||||||
|
update_user_meta($user_id, self::META_WEAR, $slug);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true|WP_Error
|
||||||
|
*/
|
||||||
|
public static function grant(int $user_id, string $slug, int $actor = 0) {
|
||||||
|
if ($user_id <= 0) {
|
||||||
|
return new WP_Error('aksrt_badge_user', __('无效用户', 'aksrt'), ['status' => 400]);
|
||||||
|
}
|
||||||
|
if (!self::enabled()) {
|
||||||
|
return new WP_Error('aksrt_badge_disabled', __('徽章系统未启用', 'aksrt'), ['status' => 403]);
|
||||||
|
}
|
||||||
|
$slug = sanitize_key($slug);
|
||||||
|
$map = self::get_dictionary_map();
|
||||||
|
if ($slug === '' || !isset($map[$slug])) {
|
||||||
|
return new WP_Error('aksrt_badge_unknown', __('徽章不存在', 'aksrt'), ['status' => 404]);
|
||||||
|
}
|
||||||
|
$owned = self::get_owned($user_id);
|
||||||
|
if (!in_array($slug, $owned, true)) {
|
||||||
|
$owned[] = $slug;
|
||||||
|
self::set_owned($user_id, $owned);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Fires after a badge is granted.
|
||||||
|
*
|
||||||
|
* @param int $user_id
|
||||||
|
* @param string $slug
|
||||||
|
* @param int $actor
|
||||||
|
*/
|
||||||
|
do_action('aksrt_badge_granted', $user_id, $slug, $actor);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true|WP_Error
|
||||||
|
*/
|
||||||
|
public static function revoke(int $user_id, string $slug, int $actor = 0) {
|
||||||
|
if ($user_id <= 0) {
|
||||||
|
return new WP_Error('aksrt_badge_user', __('无效用户', 'aksrt'), ['status' => 400]);
|
||||||
|
}
|
||||||
|
$slug = sanitize_key($slug);
|
||||||
|
$owned = self::get_owned($user_id);
|
||||||
|
$owned = array_values(array_filter($owned, static function ($s) use ($slug) {
|
||||||
|
return $s !== $slug;
|
||||||
|
}));
|
||||||
|
self::set_owned($user_id, $owned);
|
||||||
|
/**
|
||||||
|
* @param int $user_id
|
||||||
|
* @param string $slug
|
||||||
|
* @param int $actor
|
||||||
|
*/
|
||||||
|
do_action('aksrt_badge_revoked', $user_id, $slug, $actor);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function can_manually_set(int $actor = 0): bool {
|
||||||
|
$actor = $actor > 0 ? $actor : get_current_user_id();
|
||||||
|
if ($actor <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (user_can($actor, 'manage_options')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (class_exists('AKSRT_Settings_Capabilities')) {
|
||||||
|
return AKSRT_Settings_Capabilities::user_can('medal_manually_set', $actor);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public DTO for REST / modals.
|
||||||
|
*
|
||||||
|
* @return array{enabled:bool,public_display:bool,wear:string,items:list<array>}
|
||||||
|
*/
|
||||||
|
public static function list_for_user(int $user_id, bool $include_private = false): array {
|
||||||
|
$enabled = self::enabled();
|
||||||
|
$public = self::public_display();
|
||||||
|
$viewer = get_current_user_id();
|
||||||
|
$can_see = $include_private
|
||||||
|
|| $public
|
||||||
|
|| ($viewer > 0 && $viewer === $user_id)
|
||||||
|
|| self::can_manually_set($viewer);
|
||||||
|
|
||||||
|
if (!$enabled || !$can_see || $user_id <= 0) {
|
||||||
|
return [
|
||||||
|
'enabled' => $enabled,
|
||||||
|
'public_display' => $public,
|
||||||
|
'wear' => '',
|
||||||
|
'items' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$map = self::get_dictionary_map();
|
||||||
|
$owned = self::get_owned($user_id);
|
||||||
|
$wear = self::get_wear($user_id);
|
||||||
|
// Prefer worn first (Zibll).
|
||||||
|
if ($wear !== '') {
|
||||||
|
$owned = array_values(array_unique(array_merge([$wear], $owned)));
|
||||||
|
}
|
||||||
|
$items = [];
|
||||||
|
foreach ($owned as $slug) {
|
||||||
|
if (!isset($map[$slug])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$items[] = [
|
||||||
|
'slug' => $slug,
|
||||||
|
'label' => $map[$slug]['label'],
|
||||||
|
'tone' => $map[$slug]['tone'],
|
||||||
|
'icon_url' => $map[$slug]['icon_url'],
|
||||||
|
'wear' => ($slug === $wear),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'enabled' => true,
|
||||||
|
'public_display' => $public,
|
||||||
|
'wear' => $wear,
|
||||||
|
'items' => $items,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rest_list(WP_REST_Request $request) {
|
||||||
|
$user_id = (int) $request['id'];
|
||||||
|
if ($user_id <= 0 || !get_userdata($user_id)) {
|
||||||
|
return new WP_Error('aksrt_badge_user', __('用户不存在', 'aksrt'), ['status' => 404]);
|
||||||
|
}
|
||||||
|
$data = self::list_for_user($user_id);
|
||||||
|
return rest_ensure_response(['success' => true, 'data' => $data]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rest_wear(WP_REST_Request $request) {
|
||||||
|
$params = $request->get_json_params();
|
||||||
|
if (!is_array($params)) {
|
||||||
|
$params = $request->get_params();
|
||||||
|
}
|
||||||
|
$slug = sanitize_key((string) ($params['slug'] ?? $params['name'] ?? ''));
|
||||||
|
$result = self::wear(get_current_user_id(), $slug);
|
||||||
|
if (is_wp_error($result)) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
return rest_ensure_response([
|
||||||
|
'success' => true,
|
||||||
|
'data' => self::list_for_user(get_current_user_id(), true),
|
||||||
|
'message' => __('已佩戴徽章', 'aksrt'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rest_grant(WP_REST_Request $request) {
|
||||||
|
$user_id = (int) $request['id'];
|
||||||
|
if ($user_id <= 0 || !get_userdata($user_id)) {
|
||||||
|
return new WP_Error('aksrt_badge_user', __('用户不存在', 'aksrt'), ['status' => 404]);
|
||||||
|
}
|
||||||
|
$params = $request->get_json_params();
|
||||||
|
if (!is_array($params)) {
|
||||||
|
$params = $request->get_params();
|
||||||
|
}
|
||||||
|
$slug = sanitize_key((string) ($params['slug'] ?? $params['name'] ?? ''));
|
||||||
|
$result = self::grant($user_id, $slug, get_current_user_id());
|
||||||
|
if (is_wp_error($result)) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
return rest_ensure_response([
|
||||||
|
'success' => true,
|
||||||
|
'data' => self::list_for_user($user_id, true),
|
||||||
|
'message' => __('已授予徽章', 'aksrt'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rest_revoke(WP_REST_Request $request) {
|
||||||
|
$user_id = (int) $request['id'];
|
||||||
|
if ($user_id <= 0 || !get_userdata($user_id)) {
|
||||||
|
return new WP_Error('aksrt_badge_user', __('用户不存在', 'aksrt'), ['status' => 404]);
|
||||||
|
}
|
||||||
|
$params = $request->get_json_params();
|
||||||
|
if (!is_array($params)) {
|
||||||
|
$params = $request->get_params();
|
||||||
|
}
|
||||||
|
$slug = sanitize_key((string) ($params['slug'] ?? $params['name'] ?? ''));
|
||||||
|
$result = self::revoke($user_id, $slug, get_current_user_id());
|
||||||
|
if (is_wp_error($result)) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
return rest_ensure_response([
|
||||||
|
'success' => true,
|
||||||
|
'data' => self::list_for_user($user_id, true),
|
||||||
|
'message' => __('已收回徽章', 'aksrt'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -162,48 +162,61 @@ class AKSRT_User_Home {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($settings['badge']['enabled']) && !empty($settings['badge']['public_display'])) {
|
if (!empty($settings['badge']['enabled']) && !empty($settings['badge']['public_display'])) {
|
||||||
$badge_map = [];
|
|
||||||
foreach ((array) ($settings['badge']['dictionary'] ?? []) as $item) {
|
|
||||||
if (!empty($item['slug'])) {
|
|
||||||
$badge_map[(string) $item['slug']] = $item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$assigned = get_user_meta($user_id, 'aksrt_user_badges', true);
|
|
||||||
if (!is_array($assigned)) {
|
|
||||||
$assigned = preg_split('/[\s,]+/', (string) $assigned, -1, PREG_SPLIT_NO_EMPTY);
|
|
||||||
}
|
|
||||||
$assigned = array_values(array_filter(array_map(static function ($slug) {
|
|
||||||
return sanitize_key((string) $slug);
|
|
||||||
}, (array) $assigned)));
|
|
||||||
|
|
||||||
// Prefer worn badge first (Zibll wear-medal).
|
|
||||||
$wear = sanitize_key((string) get_user_meta($user_id, 'aksrt_wear_badge', true));
|
|
||||||
if ($wear !== '' && in_array($wear, $assigned, true) && isset($badge_map[$wear])) {
|
|
||||||
$assigned = array_values(array_unique(array_merge([$wear], $assigned)));
|
|
||||||
}
|
|
||||||
|
|
||||||
$limit = max(1, (int) ($settings['badge']['limit'] ?? 3));
|
$limit = max(1, (int) ($settings['badge']['limit'] ?? 3));
|
||||||
$badge_count = 0;
|
if (class_exists('AKSRT_User_Badges')) {
|
||||||
foreach ($assigned as $slug) {
|
$list = AKSRT_User_Badges::list_for_user($user_id, true);
|
||||||
if ($slug === '' || !isset($badge_map[$slug])) {
|
$badge_count = 0;
|
||||||
continue;
|
foreach ((array) ($list['items'] ?? []) as $item) {
|
||||||
|
if ($badge_count >= $limit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$signals[] = [
|
||||||
|
'type' => 'badge',
|
||||||
|
'slug' => (string) ($item['slug'] ?? ''),
|
||||||
|
'label' => (string) ($item['label'] ?? ''),
|
||||||
|
'icon' => 'award',
|
||||||
|
'tone' => (string) ($item['tone'] ?? 'neutral'),
|
||||||
|
'icon_url' => (string) ($item['icon_url'] ?? ''),
|
||||||
|
'wear' => !empty($item['wear']),
|
||||||
|
];
|
||||||
|
$badge_count++;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
$icon_url = (string) ($badge_map[$slug]['icon_url'] ?? '');
|
$badge_map = [];
|
||||||
$signals[] = [
|
foreach ((array) ($settings['badge']['dictionary'] ?? []) as $item) {
|
||||||
'type' => 'badge',
|
if (!empty($item['slug'])) {
|
||||||
'slug' => $slug,
|
$badge_map[(string) $item['slug']] = $item;
|
||||||
'label' => (string) $badge_map[$slug]['label'],
|
}
|
||||||
'icon' => 'award',
|
}
|
||||||
'tone' => (string) ($badge_map[$slug]['tone'] ?? 'neutral'),
|
$assigned = get_user_meta($user_id, 'aksrt_user_badges', true);
|
||||||
'icon_url' => $icon_url,
|
if (!is_array($assigned)) {
|
||||||
'wear' => ($wear !== '' && $slug === $wear),
|
$assigned = preg_split('/[\s,]+/', (string) $assigned, -1, PREG_SPLIT_NO_EMPTY);
|
||||||
];
|
}
|
||||||
$badge_count++;
|
$wear = sanitize_key((string) get_user_meta($user_id, 'aksrt_wear_badge', true));
|
||||||
|
$assigned = array_values(array_filter(array_map(static function ($slug) {
|
||||||
if ($badge_count >= $limit) {
|
return sanitize_key((string) $slug);
|
||||||
break;
|
}, (array) $assigned)));
|
||||||
|
if ($wear !== '' && in_array($wear, $assigned, true) && isset($badge_map[$wear])) {
|
||||||
|
$assigned = array_values(array_unique(array_merge([$wear], $assigned)));
|
||||||
|
}
|
||||||
|
$badge_count = 0;
|
||||||
|
foreach ($assigned as $slug) {
|
||||||
|
if ($slug === '' || !isset($badge_map[$slug])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$signals[] = [
|
||||||
|
'type' => 'badge',
|
||||||
|
'slug' => $slug,
|
||||||
|
'label' => (string) $badge_map[$slug]['label'],
|
||||||
|
'icon' => 'award',
|
||||||
|
'tone' => (string) ($badge_map[$slug]['tone'] ?? 'neutral'),
|
||||||
|
'icon_url' => (string) ($badge_map[$slug]['icon_url'] ?? ''),
|
||||||
|
'wear' => ($wear !== '' && $slug === $wear),
|
||||||
|
];
|
||||||
|
$badge_count++;
|
||||||
|
if ($badge_count >= $limit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user