Files
wordpress-theme-aksrt/src/User/AuthApply.php
T

195 lines
8.6 KiB
PHP

<?php
/** General user identity certification matching the reference theme flow. @package AKSRT */
namespace AKSRT\User;
use AKSRT\Identity\ApplicationRepository;
use AKSRT\Identity\EventRepository;
use AKSRT\Identity\TransitionService;
defined('ABSPATH') || exit;
final class AuthApply {
private static ?self $instance = null;
public static function get_instance(): self {
return self::$instance ??= new self();
}
public function init(): void {
if (!is_admin()) return;
add_action('show_user_profile', [$this, 'render_admin_profile']);
add_action('edit_user_profile', [$this, 'render_admin_profile']);
add_action('personal_options_update', [$this, 'save_admin_profile']);
add_action('edit_user_profile_update', [$this, 'save_admin_profile']);
}
/** @return array<string,mixed> */
public static function settings(): array {
$all = class_exists('AKSRT\\Settings\\Settings') ? \AKSRT\Settings\Settings::get_user_identity_settings() : [];
return is_array($all['verify'] ?? null) ? $all['verify'] : [];
}
/** @return array<string,mixed> */
public static function snapshot(int $user_id): array {
$repo = new ApplicationRepository();
$active = $user_id > 0 ? $repo->active_for_user($user_id) : null;
$verified = self::is_verified($user_id);
$settings = self::settings();
$minimum_level = max(0, (int) ($settings['minimum_level'] ?? 0));
$level_settings = class_exists('AKSRT\\Settings\\Settings')
? \AKSRT\Settings\Settings::get_user_identity_settings()
: [];
$level_enabled = !isset($level_settings['level']['enabled']) || !empty($level_settings['level']['enabled']);
$level_ok = self::meets_minimum_level($user_id, $minimum_level, $level_enabled);
return [
'user_id' => $user_id,
'verified' => $verified,
'status' => $verified ? 'verified' : ($active ? TransitionService::public_status((string) ($active['status'] ?? '')) : ''),
'can_apply' => !empty($settings['enabled']) && !empty($settings['allow_apply']) && !$verified && !$active && $level_ok,
'level_ok' => $level_ok,
'minimum_level' => $minimum_level,
'application' => $active,
'info' => self::get_auth_info($user_id),
];
}
public static function is_verified(int $user_id): bool {
return $user_id > 0 && (bool) get_user_meta($user_id, 'auth', true);
}
/** @return array<string,mixed> */
public static function get_auth_info(int $user_id): array {
$stored = get_user_meta($user_id, 'auth_info', true);
if (!is_array($stored)) {
return [];
}
return [
'name' => sanitize_text_field((string) ($stored['name'] ?? '')),
'desc' => sanitize_textarea_field((string) ($stored['desc'] ?? '')),
'time' => sanitize_text_field((string) ($stored['time'] ?? '')),
];
}
public static function meets_minimum_level(int $user_id, int $minimum_level, bool $level_enabled = true): bool {
return $minimum_level <= 0
|| !$level_enabled
|| !class_exists('AKSRT\\User\\Level')
|| Level::get_level($user_id) >= $minimum_level;
}
/** @param array<string,string> $profile_fields */
public static function review(int $user_id, string $application_uuid, string $status, string $note, int $reviewer_id, array $profile_fields) {
$status = sanitize_key($status);
$note = sanitize_textarea_field($note);
if (!in_array($status, ['verified', 'rejected'], true)) {
return new \WP_Error('aksrt_auth_status', __('审核结果无效', 'aksrt'));
}
if ($status === 'rejected' && $note === '') {
return new \WP_Error('aksrt_auth_note', __('请填写驳回原因', 'aksrt'));
}
$repo = new ApplicationRepository();
$application = $repo->find_by_uuid(sanitize_text_field($application_uuid));
if (!$application || (int) $application['user_id'] !== $user_id) {
return new \WP_Error('aksrt_auth_missing', __('认证申请不存在', 'aksrt'));
}
if ((string) $application['status'] !== 'manual_pending') {
return new \WP_Error('aksrt_auth_conflict', __('该申请已处理,请刷新后重试', 'aksrt'));
}
$name = sanitize_text_field((string) ($profile_fields['name'] ?? $application['public_name'] ?? ''));
$description = sanitize_textarea_field((string) ($profile_fields['description'] ?? $application['description'] ?? ''));
if ($status === 'verified' && $name === '') {
return new \WP_Error('aksrt_auth_name', __('请填写认证名称', 'aksrt'));
}
$updated = $repo->transition((int) $application['id'], 'manual_pending', $status, [
'review_note' => $note,
'reviewer_id' => $reviewer_id,
'completed_at' => current_time('mysql', true),
...($status === 'verified' ? ['public_name' => $name, 'description' => $description] : []),
]);
if (!$updated) {
return new \WP_Error('aksrt_auth_conflict', __('认证状态已变化,请刷新后重试', 'aksrt'));
}
if ($status === 'verified') {
update_user_meta($user_id, 'auth', 1);
update_user_meta($user_id, 'auth_info', ['name' => $name, 'desc' => $description, 'time' => current_time('Y-m-d H:i')]);
} else {
delete_user_meta($user_id, 'auth');
delete_user_meta($user_id, 'auth_info');
}
(new EventRepository())->record([
'application_id' => (int) $application['id'],
'source' => 'admin',
'event_type' => 'manual_review',
'dedupe_key' => hash('sha256', $application_uuid . '|review|' . $status),
'from_status' => 'manual_pending',
'to_status' => $status,
'result_code' => $note,
'actor_user_id' => $reviewer_id,
]);
if (class_exists('AKSRT\\Messages\\Message')) {
$title = $status === 'verified' ? __('身份认证通过', 'aksrt') : __('身份认证未通过', 'aksrt');
$content = $status === 'verified' ? __('您的身份认证申请已通过审核', 'aksrt') : sprintf(__('您的身份认证申请未通过:%s', 'aksrt'), $note);
\AKSRT\Messages\Message::get_instance()->send($user_id, 'auth_apply_reply', $content, [
'title' => $title,
'target_url' => function_exists('aksrt_get_user_center_tab_url') ? aksrt_get_user_center_tab_url('auth') : home_url('/'),
]);
}
if (class_exists('AKSRT\\Settings\\MailNotifications')) {
\AKSRT\Settings\MailNotifications::auth_reviewed($user_id, $status, $note, $reviewer_id);
}
return ['status' => $status];
}
public function render_admin_profile(\WP_User $user): void {
if (
empty(self::settings()['enabled'])
|| !current_user_can('aksrt_review_identity')
|| !current_user_can('edit_user', (int) $user->ID)
) {
return;
}
$info = self::get_auth_info((int) $user->ID);
$name = (string) ($info['name'] ?? '');
$description = (string) ($info['desc'] ?? '');
$time = (string) ($info['time'] ?? '');
?>
<h2><?php esc_html_e('身份认证', 'aksrt'); ?></h2>
<table class="form-table" role="presentation">
<tr><th><label for="aksrt_auth_enabled"><?php esc_html_e('已认证用户', 'aksrt'); ?></label></th><td><input id="aksrt_auth_enabled" type="checkbox" name="aksrt_auth_enabled" value="1" <?php checked(self::is_verified((int) $user->ID)); ?>></td></tr>
<tr><th><label for="aksrt_auth_name"><?php esc_html_e('认证名称', 'aksrt'); ?></label></th><td><input id="aksrt_auth_name" class="regular-text" type="text" maxlength="64" name="aksrt_auth_name" value="<?php echo esc_attr($name); ?>"></td></tr>
<tr><th><label for="aksrt_auth_desc"><?php esc_html_e('认证简介', 'aksrt'); ?></label></th><td><textarea id="aksrt_auth_desc" class="large-text" rows="2" name="aksrt_auth_desc"><?php echo esc_textarea($description); ?></textarea></td></tr>
<tr><th><label for="aksrt_auth_time"><?php esc_html_e('认证时间', 'aksrt'); ?></label></th><td><input id="aksrt_auth_time" class="regular-text" type="text" name="aksrt_auth_time" value="<?php echo esc_attr($time); ?>"></td></tr>
</table>
<?php wp_nonce_field('aksrt_auth_profile_' . (int) $user->ID, 'aksrt_auth_profile_nonce'); ?>
<?php
}
public function save_admin_profile(int $user_id): void {
if (
empty(self::settings()['enabled'])
|| !current_user_can('aksrt_review_identity')
|| !current_user_can('edit_user', $user_id)
|| !isset($_POST['aksrt_auth_profile_nonce'])
) {
return;
}
$nonce = sanitize_text_field((string) wp_unslash($_POST['aksrt_auth_profile_nonce']));
if (!wp_verify_nonce($nonce, 'aksrt_auth_profile_' . $user_id)) return;
if (empty($_POST['aksrt_auth_enabled'])) {
delete_user_meta($user_id, 'auth');
delete_user_meta($user_id, 'auth_info');
return;
}
$name = sanitize_text_field((string) wp_unslash($_POST['aksrt_auth_name'] ?? ''));
if ($name === '') return;
update_user_meta($user_id, 'auth', 1);
update_user_meta($user_id, 'auth_info', [
'name' => $name,
'desc' => sanitize_textarea_field((string) wp_unslash($_POST['aksrt_auth_desc'] ?? '')),
'time' => sanitize_text_field((string) wp_unslash($_POST['aksrt_auth_time'] ?? '')) ?: current_time('Y-m-d H:i'),
]);
}
}