Files
wordpress-theme-aksrt/src/User/Restriction.php
T
kate522 8c5b1555c9 refactor: flatten src/AKSRT → src (drop theme-identifier dir)
Move all domain folders from src/AKSRT/{Domain} to src/{Domain}; remap
Composer PSR-4 "AKSRT\\": "src/"; update audit script + README + ADR
paths; remove dead tools/build-psr4-map.php (scanned deleted inc/classes).
2026-08-02 22:06:06 +08:00

225 lines
7.1 KiB
PHP

<?php
/**
* @package AKSRT
*/
namespace AKSRT\User;
defined('ABSPATH') || exit;
class Restriction {
private static $instance = null;
const TYPE_WARN = 'warn';
const TYPE_MUTE = 'mute';
const TYPE_SUSPEND = 'suspend';
const TYPE_BAN = 'ban';
const SCOPE_GLOBAL = 'global';
const SCOPE_COMMENT = 'comment';
const TYPES = [self::TYPE_WARN, self::TYPE_MUTE, self::TYPE_SUSPEND, self::TYPE_BAN];
const SCOPES = [self::SCOPE_GLOBAL, self::SCOPE_COMMENT];
public static function get_instance(): self {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public function init(): void {
// Enforce bans on login
add_filter('authenticate', [$this, 'enforce_login_ban'], 999, 3);
// Enforce comment mute
add_filter('pre_comment_on_post', [$this, 'enforce_comment_mute']);
}
/**
* Get the restriction table name.
*/
private function table(): string {
global $wpdb;
return $wpdb->prefix . 'aksrt_user_restriction';
}
/**
* Add a restriction to a user.
*/
public function restrict(int $user_id, string $type, string $reason, string $scope = self::SCOPE_GLOBAL, string $expires_at = '', int $created_by = 0): array {
global $wpdb;
if (!in_array($type, self::TYPES, true)) {
return ['success' => false, 'message' => 'Invalid restriction type.'];
}
if (!in_array($scope, self::SCOPES, true)) {
return ['success' => false, 'message' => 'Invalid scope.'];
}
if ($created_by <= 0) {
$created_by = get_current_user_id();
}
$data = [
'user_id' => $user_id,
'type' => $type,
'reason' => sanitize_textarea_field($reason),
'scope' => $scope,
'expires_at' => $expires_at ? gmdate('Y-m-d H:i:s', strtotime($expires_at)) : null,
'created_by' => $created_by,
'created_at' => current_time('mysql', true),
];
$result = $wpdb->insert($this->table(), $data);
if ($result === false) {
return ['success' => false, 'message' => 'Database error.'];
}
// Update user meta for quick lookup
$this->sync_user_restriction_meta($user_id);
return ['success' => true, 'id' => $wpdb->insert_id];
}
/**
* Remove a restriction by ID.
*/
public function remove_restriction(int $restriction_id): bool {
global $wpdb;
$restriction = $this->get_restriction($restriction_id);
if (!$restriction) return false;
$result = $wpdb->delete($this->table(), ['id' => $restriction_id]);
if ($result) {
$this->sync_user_restriction_meta((int) $restriction['user_id']);
}
return (bool) $result;
}
/**
* Get a single restriction by ID.
*/
public function get_restriction(int $restriction_id): ?array {
global $wpdb;
$row = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$this->table()} WHERE id = %d",
$restriction_id
), ARRAY_A);
return $row ?: null;
}
/**
* Get all active restrictions for a user.
*/
public function get_user_restrictions(int $user_id, bool $active_only = true): array {
global $wpdb;
$now = current_time('mysql', true);
if ($active_only) {
$rows = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM {$this->table()} WHERE user_id = %d AND (expires_at IS NULL OR expires_at > %s) ORDER BY created_at DESC",
$user_id, $now
), ARRAY_A);
} else {
$rows = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM {$this->table()} WHERE user_id = %d ORDER BY created_at DESC",
$user_id
), ARRAY_A);
}
return $rows ?: [];
}
/**
* Check if a user has an active restriction of a given type and scope.
*/
public function is_restricted(int $user_id, string $type = '', string $scope = ''): bool {
$restrictions = $this->get_user_restrictions($user_id, true);
foreach ($restrictions as $r) {
if ($type && $r['type'] !== $type) continue;
if ($scope && $r['scope'] !== $scope && $r['scope'] !== self::SCOPE_GLOBAL) continue;
return true;
}
return false;
}
/**
* Check if a user is banned (global).
*/
public function is_banned(int $user_id): bool {
return $this->is_restricted($user_id, self::TYPE_BAN, self::SCOPE_GLOBAL);
}
/**
* Get the active ban restriction for a user (if any).
*/
public function get_active_ban(int $user_id): ?array {
$restrictions = $this->get_user_restrictions($user_id, true);
foreach ($restrictions as $r) {
if ($r['type'] === self::TYPE_BAN && $r['scope'] === self::SCOPE_GLOBAL) {
return $r;
}
}
return null;
}
/**
* Enforce ban on login.
*/
public function enforce_login_ban($user, string $username, string $password) {
if (is_wp_error($user) || !$user instanceof \WP_User) {
return $user;
}
if ($this->is_banned($user->ID)) {
$ban = $this->get_active_ban($user->ID);
$message = '您的账号已被封禁。';
if (!empty($ban['reason'])) {
$message .= '原因:' . $ban['reason'];
}
return new \WP_Error('user_banned', $message);
}
return $user;
}
/**
* Enforce comment mute.
*/
public function enforce_comment_mute($post_id) {
$user_id = get_current_user_id();
if ($user_id > 0 && $this->is_restricted($user_id, self::TYPE_MUTE, self::SCOPE_COMMENT)) {
wp_die('您已被限制评论。', '评论限制', ['response' => 403]);
}
return $post_id;
}
/**
* Sync restriction meta for quick lookup.
*/
private function sync_user_restriction_meta(int $user_id): void {
$active = $this->get_user_restrictions($user_id, true);
$types = array_unique(array_column($active, 'type'));
update_user_meta($user_id, '_aksrt_active_restrictions', $types);
update_user_meta($user_id, '_aksrt_is_banned', in_array(self::TYPE_BAN, $types, true) ? 1 : 0);
}
/**
* Get restriction stats for admin overview.
*/
public function get_stats(): array {
global $wpdb;
$now = current_time('mysql', true);
$table = $this->table();
$total = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table}");
$active = (int) $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*) FROM {$table} WHERE expires_at IS NULL OR expires_at > %s", $now
));
$banned = (int) $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*) FROM {$table} WHERE type = %s AND (expires_at IS NULL OR expires_at > %s)",
self::TYPE_BAN, $now
));
return ['total' => $total, 'active' => $active, 'banned' => $banned];
}
}