Files
wordpress-theme-aksrt/src/Admin/UserControllerRestrictionOperations.php
T
kate522 a4af024f32 feat(ban): align ban prompts with the reference theme modals and block page
对照参考主题(Zibll)修正四项偏差,申诉链路从页面整体迁移至弹窗:

- feat(ban): 新增三个 1:1 复刻弹窗(REST /user-ban/{info,appeal,log}-modal):
  查看禁封状态(多彩头 c-gray + 信息行 + 申诉按钮)、禁封申诉(edit-header
  gavel/c-blue + 信息框 + 资料项 data[key] 输入 + 审核中态 + 禁止申诉态)、
  禁封记录(多彩头 c-yellow + 计数 + 逐条卡片);user-ban.js 增加
  data-aksrt-ban-info/appeal/log 委托与 admin-ajax 申诉提交
- feat(ban): 禁封/解封弹窗的「当前用户已有N次禁封记录」静态文案改为可点击
  黄链(N>1),打开禁封记录弹窗;解封视图信息行改复用统一数据源
- refactor(ban): 治理展示态收敛至 Restriction::state_badges()(封号=c-red
  违规已封号 / 小黑屋=c-yellow 小黑屋禁封中 / 禁言=c-gray 禁言中,颜色与
  字段统一管理)+ get_ban_info()(状态/起止时间/原因/说明/申诉权限,
  三弹窗与禁封页共用)
- feat(ban): 登录行为对齐参考主题——移除登录期拦截(enforce_login_ban)
  与 complete_login 禁封闸门,封号用户可正常登录;新增 template_redirect
  前台禁封页(1:1 zib_user_ban_restrict_login/zib_die_page:红盒提示 +
  信息行 + 申诉权限 + 退出登录/申诉按钮,模板 templates/parts/user/ban-block.php)
- feat(profile): 公开主页禁封状态从资料卡下方提示框改为占据身份行元信息位
  的 badg 徽章(参考主题 author_header_identity 语义);用户中心元信息改为
  but 按钮(徽章+箭头)打开查看禁封状态弹窗;评论拒绝态链接同源
- refactor: 移除申诉页面模板(page-ban-appeal.php)及其资产/注册/入队、
  get_profile_notice/profile_notice 链、state_labels/state_notices/
  login_ban_message、UserRuntime 的 public_notice/ban_notice_text 死配置、
  report_settings 死键、无消费方的 aksrt_ban_appeal_status 端点
- feat(ban): BanAppeal::ajax_submit 兼容弹窗 data[key] 表单结构
  (逐项清洗入库,申诉说明取 data['申诉说明'],旧平铺字段保持兼容)
- chore: 重新构建 public.css/public.min.css(三个新弹窗几何与头部样式)
- test: 契约测试重写(徽章元信息位、三弹窗与记录链接、禁封页、允许登录 +
  前台拦截、死代码移除断言),全量 362 项通过
2026-09-22 19:14:02 +08:00

271 lines
11 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
/**
* @package AKSRT
*/
namespace AKSRT\Admin;
defined('ABSPATH') || exit;
trait UserControllerRestrictionOperations {
public function get_user_restrictions(\WP_REST_Request $request): \WP_REST_Response {
$user_id = (int) $request->get_param('user_id');
$user = get_userdata($user_id);
if (!$user) {
return $this->error('用户不存在', 404);
}
if (!class_exists('\AKSRT\User\Restriction')) {
return $this->success(['data' => ['restrictions' => []]]);
}
$restrictions = \AKSRT\User\Restriction::get_instance()->get_user_restrictions($user_id, false);
return $this->success(['data' => ['restrictions' => $restrictions]]);
}
public function add_user_restriction(\WP_REST_Request $request): \WP_REST_Response {
$user_id = (int) $request->get_param('user_id');
$user = get_userdata($user_id);
if (!$user) {
return $this->error('用户不存在', 404);
}
$params = $request->get_json_params() ?? [];
$type = sanitize_key((string) ($params['type'] ?? ''));
$reason = sanitize_textarea_field((string) ($params['reason'] ?? ''));
$scope = sanitize_key((string) ($params['scope'] ?? 'global'));
$expires_at = sanitize_text_field((string) ($params['expires_at'] ?? ''));
if (!$type) {
return $this->error('请选择限制类型');
}
if (!class_exists('\AKSRT\User\Restriction')) {
return $this->error('限制系统未启用', 501);
}
$result = \AKSRT\User\Restriction::get_instance()->restrict(
$user_id, $type, $reason, $scope, $expires_at, get_current_user_id()
);
if (!$result['success']) {
return $this->error($result['message']);
}
return $this->success(['message' => '限制已添加', 'data' => $result]);
}
public function remove_user_restriction(\WP_REST_Request $request): \WP_REST_Response {
$restriction_id = (int) $request->get_param('id');
if (!class_exists('\AKSRT\User\Restriction')) {
return $this->error('限制系统未启用', 501);
}
$result = \AKSRT\User\Restriction::get_instance()->remove_restriction($restriction_id);
if (!$result) {
return $this->error('移除失败', 500);
}
return $this->success(['message' => '限制已移除']);
}
public function get_restriction_stats(\WP_REST_Request $request): \WP_REST_Response {
if (!class_exists('\AKSRT\User\Restriction')) {
return $this->success(['data' => ['total' => 0, 'active' => 0, 'banned' => 0]]);
}
$stats = \AKSRT\User\Restriction::get_instance()->get_stats();
return $this->success(['data' => $stats]);
}
/**
* 前台禁封弹窗内容(1:1 复刻参考主题 zib_get_set_user_ban_modal,
* 经 aksrt_get_user_ban_modal 渲染;参考主题 refresh_modal 每次刷新同语义)。
*/
public function get_user_ban_modal(\WP_REST_Request $request): \WP_REST_Response {
$user_id = (int) $request->get_param('user_id');
if ($user_id <= 0 || !get_userdata($user_id)) {
return $this->error('用户不存在', 404);
}
if (!function_exists('aksrt_get_user_ban_modal')) {
return $this->error('禁封组件未加载', 501);
}
$html = aksrt_get_user_ban_modal($user_id);
if ($html === '') {
return $this->error('抱歉,您暂时没有此权限', 403);
}
return $this->success(['data' => ['html' => $html]]);
}
/**
* 前台查看禁封状态弹窗内容(1:1 复刻参考主题 zib_get_ban_info_modal,
* 经 aksrt_get_user_ban_info_modal 渲染;本人或管理员可见)。
*/
public function get_user_ban_info_modal(\WP_REST_Request $request): \WP_REST_Response {
$user_id = (int) $request->get_param('user_id');
if ($user_id <= 0 || !get_userdata($user_id)) {
return $this->error('用户不存在', 404);
}
if (!function_exists('aksrt_get_user_ban_info_modal')) {
return $this->error('禁封组件未加载', 501);
}
$html = aksrt_get_user_ban_info_modal($user_id);
if ($html === '') {
return $this->error('抱歉,您暂时没有此权限', 403);
}
return $this->success(['data' => ['html' => $html]]);
}
/**
* 前台禁封申诉弹窗内容(1:1 复刻参考主题 zib_get_user_ban_appeal_modal,
* 经 aksrt_get_user_ban_appeal_modal 渲染;仅本人可见)。
*/
public function get_user_ban_appeal_modal(\WP_REST_Request $request): \WP_REST_Response {
$user_id = (int) $request->get_param('user_id');
if ($user_id <= 0 || !get_userdata($user_id)) {
return $this->error('用户不存在', 404);
}
if (!function_exists('aksrt_get_user_ban_appeal_modal')) {
return $this->error('申诉组件未加载', 501);
}
$html = aksrt_get_user_ban_appeal_modal($user_id);
if ($html === '') {
return $this->error('抱歉,您暂时没有此权限', 403);
}
return $this->success(['data' => ['html' => $html]]);
}
/**
* 前台禁封记录弹窗内容(1:1 复刻参考主题 zib_get_ban_log_modal,
* 经 aksrt_get_user_ban_log_modal 渲染;仅具备禁封权限的管理员可见)。
*/
public function get_user_ban_log_modal(\WP_REST_Request $request): \WP_REST_Response {
$user_id = (int) $request->get_param('user_id');
if ($user_id <= 0 || !get_userdata($user_id)) {
return $this->error('用户不存在', 404);
}
if (!function_exists('aksrt_get_user_ban_log_modal')) {
return $this->error('禁封组件未加载', 501);
}
$html = aksrt_get_user_ban_log_modal($user_id);
if ($html === '') {
return $this->error('抱歉,您暂时没有此权限', 403);
}
return $this->success(['data' => ['html' => $html]]);
}
/**
* 前台禁封 / 解封提交(1:1 复刻参考主题 zib_ajax_set_user_ban 语义):
* ban=0 解封(留言站内信);ban=1 拉入小黑屋 / ban=2 禁封用户,
* 支持禁封时长、预置/其它原因、禁封说明、删除全部内容、禁止申诉(含说明)。
*/
public function set_user_ban(\WP_REST_Request $request): \WP_REST_Response {
$params = $request->get_json_params() ?? [];
$user_id = (int) ($params['user_id'] ?? 0);
if ($user_id <= 0 || !get_userdata($user_id)) {
return $this->error('用户不存在或参数传入错误', 404);
}
if (!function_exists('aksrt_current_user_can_ban') || !aksrt_current_user_can_ban($user_id)) {
return $this->error('抱歉,您暂时没有此权限', 403);
}
if (!class_exists('\AKSRT\User\Restriction')) {
return $this->error('限制系统未启用', 501);
}
$svc = \AKSRT\User\Restriction::get_instance();
$ban = (int) ($params['ban'] ?? 0);
$desc = isset($params['desc']) ? sanitize_textarea_field((string) wp_unslash($params['desc'])) : '';
// ── 解封 ──
if (0 === $ban) {
$svc->lift_user_restriction($user_id);
delete_user_meta($user_id, '_aksrt_no_appeal');
$this->send_ban_notice($user_id, __('您的账号限制已解除。', 'aksrt'), __('限制解除', 'aksrt'), $desc);
return $this->success(['message' => __('已解封此用户', 'aksrt'), 'data' => ['banned' => false]]);
}
// ── 禁封 ──
$type = 2 === $ban ? \AKSRT\User\Restriction::TYPE_BAN : \AKSRT\User\Restriction::TYPE_SUSPEND;
$reason = isset($params['reason']) ? sanitize_text_field((string) wp_unslash($params['reason'])) : '';
if ($reason === '' || $reason === 'other') {
$reason = isset($params['reason_other']) ? sanitize_text_field((string) wp_unslash($params['reason_other'])) : '';
}
if ($reason === '') {
return $this->error(__('请输入禁封原因', 'aksrt'));
}
$time_day = isset($params['time_day']) ? (int) $params['time_day'] : 0;
$expires = $time_day > 0 ? date('Y-m-d H:i:s', strtotime('+' . $time_day . ' day', current_time('timestamp'))) : '';
$no_appeal = !empty($params['no_appeal']);
$no_appeal_desc = isset($params['no_appeal_desc']) ? sanitize_textarea_field((string) wp_unslash($params['no_appeal_desc'])) : '';
if ($no_appeal && $no_appeal_desc === '') {
return $this->error(__('请输入禁止用户申诉的说明或原因', 'aksrt'));
}
// 禁封说明并入原因列(与后台工单禁封表单同存储语义)。
if ($desc !== '') {
$reason .= "\n" . sprintf(__('禁封说明:%s', 'aksrt'), $desc);
}
$result = $svc->restrict($user_id, $type, $reason, \AKSRT\User\Restriction::SCOPE_GLOBAL, $expires, get_current_user_id());
if (empty($result['success'])) {
return $this->error((string) ($result['message'] ?? __('操作失败', 'aksrt')));
}
if ($no_appeal) {
update_user_meta($user_id, '_aksrt_no_appeal', [
'desc' => $no_appeal_desc,
'at' => current_time('mysql'),
]);
}
// 站内信(后台禁封表单 / 参考主题 zib_updata_user_ban_msg 同语义)。
$notice = $type === \AKSRT\User\Restriction::TYPE_BAN
? __('您的账号已被禁封,将无法登录。', 'aksrt')
: __('您已被拉入小黑屋,发布等相关权限已被限制。', 'aksrt');
if ($time_day > 0) {
$notice .= ' ' . sprintf(__('期限:%1$d 天,%2$s 起自动解封。', 'aksrt'), $time_day, $expires);
}
$this->send_ban_notice($user_id, $notice, __('账号限制', 'aksrt'), $desc);
// 删除全部内容:全部公开内容与已审核评论移至回收站(参考主题同行为)。
if (!empty($params['delete_content'])) {
$post_ids = get_posts([
'author' => $user_id,
'post_type' => get_post_types(['public' => true]),
'post_status' => 'publish',
'numberposts' => -1,
'fields' => 'ids',
]);
foreach ($post_ids as $pid) {
wp_trash_post((int) $pid);
}
foreach (get_comments(['user_id' => $user_id, 'status' => 'approve', 'number' => 500]) as $comment) {
wp_trash_comment((int) $comment->comment_ID);
}
}
$send_msg = 2 === $ban ? __('已禁封此用户', 'aksrt') : __('已将此用户拉入小黑屋', 'aksrt');
return $this->success(['message' => $send_msg, 'data' => ['banned' => true]]);
}
/**
* 禁封 / 解封站内信(留言拼附在正文之后)。
*/
private function send_ban_notice(int $user_id, string $notice, string $title, string $desc = ''): void
{
if (!class_exists('\AKSRT\Messages\Message')) {
return;
}
$msg = $notice;
if ($desc !== '') {
$msg .= ' ' . sprintf(__('留言:%s', 'aksrt'), $desc);
}
\AKSRT\Messages\Message::get_instance()->send($user_id, 'system', $msg, ['title' => $title]);
}
}