195 lines
8.6 KiB
PHP
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'),
|
|
]);
|
|
}
|
|
}
|