Files
wordpress-theme-aksrt/src/User/LevelAdmin.php
T
2026-08-25 06:20:16 +08:00

169 lines
6.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Native WordPress user-level operations.
*
* @package AKSRT
*/
namespace AKSRT\User;
defined('ABSPATH') || exit;
class LevelAdmin {
/** @var self|null */
private static $instance = null;
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('admin_enqueue_scripts', [$this, 'enqueue_assets']);
add_filter('manage_users_columns', [$this, 'add_column']);
add_filter('manage_users_custom_column', [$this, 'render_column'], 10, 3);
add_filter('manage_users_sortable_columns', [$this, 'sortable_columns']);
add_action('pre_get_users', [$this, 'sort_users']);
add_filter('user_row_actions', [$this, 'add_row_action'], 25, 2);
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 enabled(): bool {
return class_exists(Level::class) && !empty(Level::settings()['enabled']);
}
public function enqueue_assets(string $hook): void {
if (!in_array($hook, ['users.php', 'user-edit.php', 'profile.php'], true)) {
return;
}
$style_path = get_theme_file_path('/assets/css/admin/user-level.css');
wp_enqueue_style(
'aksrt-user-level-admin',
get_theme_file_uri('/assets/css/admin/user-level.css'),
[],
file_exists($style_path) ? (string) filemtime($style_path) : (defined('AKSRT_VERSION') ? AKSRT_VERSION : null)
);
}
private function can_manage(int $user_id): bool {
return current_user_can('manage_options') && current_user_can('edit_user', $user_id);
}
public function add_column(array $columns): array {
if ($this->enabled()) {
$columns['aksrt_user_level'] = __('用户等级', 'aksrt');
}
return $columns;
}
public function render_column(string $output, string $column_name, int $user_id): string {
if ($column_name !== 'aksrt_user_level' || !$this->enabled()) {
return $output;
}
$level = Level::get_level($user_id);
$icon = $this->render_badge($level);
return '<span class="aksrt-user-level-admin-cell">'
. '<span class="aksrt-user-level-admin-cell__level">' . $icon
. '<strong>' . esc_html(Level::get_level_name($level)) . '</strong></span>'
. '<small class="aksrt-user-level-admin-cell__exp">' . esc_html((string) Level::get_integral($user_id)) . ' EXP</small>'
. '</span>';
}
public function sortable_columns(array $columns): array {
if ($this->enabled()) {
$columns['aksrt_user_level'] = 'aksrt_user_level';
}
return $columns;
}
public function sort_users($query): void {
if (!is_admin() || $query->get('orderby') !== 'aksrt_user_level') {
return;
}
$query->set('meta_key', Level::META_INTEGRAL);
$query->set('orderby', 'meta_value_num');
}
public function add_row_action(array $actions, \WP_User $user): array {
if ($this->enabled() && $this->can_manage((int) $user->ID)) {
$url = get_edit_user_link((int) $user->ID);
if ($url) {
$actions['aksrt_edit_level'] = '<a href="' . esc_url($url . '#aksrt-user-level') . '">' . esc_html__('调整等级经验', 'aksrt') . '</a>';
}
}
return $actions;
}
public function render_section(\WP_User $user): void {
if (!$this->enabled()) {
return;
}
$progress = Level::get_progress((int) $user->ID);
$can_manage = $this->can_manage((int) $user->ID);
?>
<a id="aksrt-user-level"></a>
<h2><?php esc_html_e('用户等级(AKSRT)', 'aksrt'); ?></h2>
<table class="form-table" role="presentation">
<tr>
<th><?php esc_html_e('当前等级', 'aksrt'); ?></th>
<td>
<span style="display:inline-flex;align-items:center;gap:8px;font-size:16px;">
<?php echo $this->render_badge((int) $progress['level']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<strong><?php echo esc_html((string) $progress['name']); ?></strong>
</span>
<p class="description"><?php printf(esc_html__('累计经验:%1$d;距离下一等级:%2$d', 'aksrt'), (int) $progress['integral'], (int) $progress['remaining']); ?></p>
</td>
</tr>
<?php if ($can_manage) : ?>
<tr>
<th><label for="aksrt_user_level"><?php esc_html_e('用户等级', 'aksrt'); ?></label></th>
<td>
<?php wp_nonce_field('aksrt_user_level_admin_' . (int) $user->ID, 'aksrt_user_level_nonce'); ?>
<input type="number" class="small-text" id="aksrt_user_level" name="aksrt_user_level" min="1" max="<?php echo esc_attr((string) Level::settings()['max']); ?>" step="1" value="<?php echo esc_attr((string) $progress['level']); ?>">
<p class="description"><?php esc_html_e('手动设置等级时请同步调整累计经验,使两项符合等级阈值。', 'aksrt'); ?></p>
</td>
</tr>
<tr>
<th><label for="aksrt_level_integral"><?php esc_html_e('累计经验值', 'aksrt'); ?></label></th>
<td>
<input type="number" class="regular-text" id="aksrt_level_integral" name="aksrt_level_integral" min="0" max="9999999999999999" step="1" value="<?php echo esc_attr((string) $progress['integral']); ?>">
</td>
</tr>
<?php endif; ?>
</table>
<?php
}
public function handle_update(int $user_id): void {
if (!$this->enabled() || !$this->can_manage($user_id) || !isset($_POST['aksrt_user_level_nonce'])) {
return;
}
$nonce = sanitize_text_field(wp_unslash((string) $_POST['aksrt_user_level_nonce']));
if (!wp_verify_nonce($nonce, 'aksrt_user_level_admin_' . $user_id)) {
return;
}
$integral = max(0, (int) ($_POST['aksrt_level_integral'] ?? 0));
$level = max(1, min(Level::settings()['max'], (int) ($_POST['aksrt_user_level'] ?? 1)));
update_user_meta($user_id, Level::META_INTEGRAL, $integral);
update_user_meta($user_id, Level::META_LEVEL, $level);
}
private function render_badge(int $level): string {
$name = Level::get_level_name($level);
$icon = Level::get_level_icon_url($level);
if ($icon !== '') {
return '<img class="aksrt-user-level-icon aksrt-user-level-icon--admin" src="' . esc_url($icon) . '" alt="' . esc_attr($name) . '" title="' . esc_attr($name) . '" decoding="async">';
}
return '<span class="aksrt-user-level-badge aksrt-user-level-icon--admin" title="' . esc_attr($name) . '">LV' . esc_html((string) $level) . '</span>';
}
}