Files
wordpress-theme-aksrt/inc/helpers/user-ban.php
T
kate522 7f520803ff feat(ban): mirror reference theme ban/restriction prompt surface
修复公开主页禁封提示框为空的问题,并对齐参考主题(Zibll)全部前台禁封提示入口:
- fix(profile): 资料卡下方治理提示框渲染为空——模板读取的 tone/message 键与
  数据层实际输出的 state/label/text 契约不匹配;现按禁封类型展示
  「违规已封号/小黑屋禁封中/禁言中」+ 可配置说明文案
- feat(ban): 用户中心页头展示治理状态徽章 + 查看详情入口,直达禁封申诉页
  (参考主题 user_page_header_desc / zib_get_user_ban_info_link)
- feat(ban): 评论拒绝态区分禁封/小黑屋/禁言,附状态徽章与申诉链接
  (参考主题 zib_get_user_ban_nocan_info,原为笼统的无权限文案)
- feat(ban): 禁封申诉页补全状态信息卡(状态/开始时间/结束时间/原因/说明/
  申诉权限),对齐 zib_get_ban_info_modal;此前仅有「禁封原因」一行且无入口
- feat(ban): 昵称旁禁封徽章 + 头像禁封角标(覆盖 VIP 位,参考主题
  user_name_badge / user_avatar_badge 钩子语义),main.css 与入队的
  main.min.css 同步新增角标样式
- fix(ban): 举报入口对已封禁/小黑屋目标隐藏(评论、文章下拉、封面菜单三处,
  对齐 zib_get_report_link 排除语义;禁言为轻量限制不隐藏)
- refactor(ban): 治理展示态收敛为单一数据源 Restriction::get_display_state()
  + state_labels(),资料卡/用户中心/评论拒绝态/昵称徽章共用
- feat(ban): 小黑屋用户开放申诉(对齐参考主题 zib_user_is_ban 双类型语义),
  禁封站内信附加申诉页链接
- test: report-ban 契约测试 +3(展示态单一数据源、提示面入口矩阵、举报排除),
  45 个测试文件全部通过
2026-09-22 13:28:52 +08:00

442 lines
23 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
/**
* 前台用户禁封(小黑屋 / 封号)— 1:1 复刻参考主题 Zibll 前台禁封能力:
* zib_get_edit_user_ban_link → aksrt_get_edit_user_ban_link(三点菜单入口)
* zib_get_set_user_ban_modal → aksrt_get_user_ban_modal(禁封 / 解封弹窗)
*
* 数据源唯一:\AKSRT\User\Restriction(wp_aksrt_user_restriction 表)。
* 弹窗内容经 REST /user-ban/modal 输出(AksrtModalRuntime.openRemote 拉取,
* 与参考主题 refresh_modal 每次刷新同语义),提交走 REST /user-ban/set。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
/**
* 用户治理展示态(禁封 / 小黑屋 / 禁言),含请求级缓存。
*
* 单一数据源为 \AKSRT\User\Restriction::get_display_state() / state_labels();
* 本 helper 供用户中心页头、评论拒绝态、昵称徽章等位置复用,避免重复查库。
*
* 对齐参考主题:
* zib_user_is_ban → aksrt_get_user_ban_state(状态判定)
* zib_ban_author_header_identity_filter → 资料卡/用户中心身份行徽章
*
* @return array{state:string,label:string,icon:string}|array{} state 为空串表示无治理状态。
*/
function aksrt_get_user_ban_state(int $user_id): array
{
static $cache = [];
if ($user_id <= 0) {
return [];
}
if (isset($cache[$user_id])) {
return $cache[$user_id];
}
$state = '';
if (class_exists('\AKSRT\User\Restriction')) {
$state = (string) \AKSRT\User\Restriction::get_instance()->get_display_state($user_id);
}
if ($state === '') {
return $cache[$user_id] = [];
}
$labels = class_exists('\AKSRT\User\Restriction') ? \AKSRT\User\Restriction::state_labels() : [];
$icons = [
'banned' => 'ban',
'restricted' => 'ban',
'muted' => 'comment-slash',
];
return $cache[$user_id] = [
'state' => $state,
'label' => (string) ($labels[$state] ?? ''),
'icon' => (string) ($icons[$state] ?? 'ban'),
];
}
/**
* 目标是否处于封禁/小黑屋状态(举报入口隐藏判定,参考主题
* zib_get_report_link 中 zib_user_is_ban($user_id) 的排除语义;
* 禁言为轻量评论域限制,不隐藏举报入口)。
*/
function aksrt_is_user_ban_restricted(int $user_id): bool
{
$state = aksrt_get_user_ban_state($user_id);
return in_array((string) ($state['state'] ?? ''), ['banned', 'restricted'], true);
}
/**
* 禁封申诉页 URL(自定义页面模板 templates/pages/page-ban-appeal.php,
* 即参考主题 zib_get_ban_info_modal / zib_get_user_ban_info_link 的落地视图)。
* 站点未创建该页面时返回空串,调用方应降级为纯文本徽章。
*/
function aksrt_get_ban_appeal_page_url(): string
{
static $url = null;
if (null !== $url) {
return $url;
}
$pages = get_posts([
'post_type' => 'page',
'post_status' => 'publish',
'posts_per_page' => 1,
'fields' => 'ids',
'no_found_rows' => true,
'meta_query' => [
[
'key' => '_wp_page_template',
'value' => 'templates/pages/page-ban-appeal.php',
],
],
]);
return $url = !empty($pages) ? (string) get_permalink((int) $pages[0]) : '';
}
/**
* 治理状态徽章 +「查看详情」入口(用户中心页头,参考主题
* user_page_header_desc 钩子 / zib_get_user_ban_info_link 语义)。
*/
function aksrt_get_user_ban_notice_html(int $user_id): string
{
$state = aksrt_get_user_ban_state($user_id);
if (empty($state['state'])) {
return '';
}
$tone = 'banned' === $state['state'] ? 'c-red' : 'c-yellow';
$chip = '<span class="badg badg-sm ' . $tone . '" title="' . esc_attr((string) $state['label']) . '">'
. '<i class="fa fas fa-' . esc_attr((string) $state['icon']) . ' mr6" aria-hidden="true"></i>'
. esc_html((string) $state['label']) . '</span>';
$link = aksrt_get_ban_appeal_page_url();
if ($link === '') {
return $chip;
}
return '<a class="aksrt-user-ban-notice" href="' . esc_url($link) . '">' . $chip
. '<i class="fa fas fa-angle-right em09 ml6" aria-hidden="true"></i></a>';
}
/**
* 昵称旁禁封徽章(参考主题 user_name_badge 钩子语义:占据 VIP 图标位,
* 头像角标 user_avatar_badge 为可选项,见 aksrt_get_user_ban_avatar_badge)。
*/
function aksrt_get_user_ban_name_badge(int $user_id): string
{
$state = aksrt_get_user_ban_state($user_id);
if (empty($state['state'])) {
return '';
}
$tone = 'banned' === $state['state'] ? 'c-red' : 'c-yellow';
return '<i class="fa fas fa-' . esc_attr((string) $state['icon']) . ' ' . $tone . ' ml3 aksrt-user-ban-badge"'
. ' title="' . esc_attr((string) $state['label']) . '" aria-hidden="true"></i>';
}
/**
* 头像禁封角标(参考主题 user_avatar_badge 钩子语义:覆盖 VIP 角标位)。
*/
function aksrt_get_user_ban_avatar_badge(int $user_id): string
{
$state = aksrt_get_user_ban_state($user_id);
if (empty($state['state'])) {
return '';
}
$tone = 'banned' === $state['state'] ? 'is-banned' : 'is-restricted';
return '<span class="avatar-badge aksrt-avatar-ban-badge ' . $tone . '" title="' . esc_attr((string) $state['label']) . '">'
. '<i class="fa fas fa-' . esc_attr((string) $state['icon']) . '" aria-hidden="true"></i></span>';
}
/**
* 受限位置治理提示(评论拒绝态,参考主题 zib_get_user_ban_nocan_info 语义:
* 状态徽章 +「暂时无法操作」+ 查看禁封状态入口)。
*
* @param string $msg 具体受阻的操作描述,如「发表评论」。
*/
function aksrt_get_user_ban_nocan_html(int $user_id, string $msg = ''): string
{
$state = aksrt_get_user_ban_state($user_id);
if (empty($state['state'])) {
return '';
}
$text = (string) $state['label'] . ($msg !== '' ? ',' . $msg : '');
$tone = 'banned' === $state['state'] ? 'c-red' : 'c-yellow';
$link = aksrt_get_ban_appeal_page_url();
$inner = '<i class="fa fas fa-' . esc_attr((string) $state['icon']) . ' mr6" aria-hidden="true"></i>'
. '<span class="' . $tone . '">' . esc_html($text) . '</span>';
if ($link !== '') {
$inner .= ' <a class="c-blue" href="' . esc_url($link) . '">' . esc_html__('查看禁封状态', 'aksrt')
. '<i class="fa fas fa-angle-right em09 ml3" aria-hidden="true"></i></a>';
}
return '<p class="comments-permission-denied aksrt-user-ban-nocan">' . $inner . '</p>';
}
/**
* 禁封功能开关(后台“用户&互动 → 禁封/举报 → 禁封功能”,
* 对齐参考主题 _pz('user_ban_s', true):缺键默认开启)。
*/
function aksrt_user_ban_enabled(): bool
{
$settings = class_exists('\AKSRT\Admin\SchemaProvider')
? \AKSRT\Settings\Settings::read_domain('users', 'report')
: (class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : []);
$settings = is_array($settings) ? $settings : [];
return !isset($settings['user_ban_s']) || !empty($settings['user_ban_s']);
}
/**
* 当前用户能否禁封目标用户(对齐参考主题 zib_current_user_can('set_user_ban') 语义)。
*/
function aksrt_current_user_can_ban(int $user_id): bool
{
if ($user_id <= 0 || !aksrt_user_ban_enabled()) {
return false;
}
$current_id = get_current_user_id();
if ($current_id <= 0 || $current_id === $user_id) {
return false;
}
if (!current_user_can('manage_options')) {
return false;
}
if (function_exists('is_super_admin') && is_super_admin($user_id)) {
return false;
}
return true;
}
/**
* 取用户当前生效中的禁封记录(ban 优先于 suspend,对齐后台单一状态语义)。
*/
function aksrt_get_user_current_ban(int $user_id): ?array
{
if ($user_id <= 0 || !class_exists('\AKSRT\User\Restriction')) {
return null;
}
$svc = \AKSRT\User\Restriction::get_instance();
return $svc->get_active_ban($user_id) ?: $svc->get_active_suspend($user_id);
}
/**
* 禁封 / 解封触发器(参考主题 zib_get_edit_user_ban_link)。
* 输出 data-aksrt-ban 委托入口,由 assets/js/user-ban.js 打开禁封弹窗。
*/
function aksrt_get_edit_user_ban_link(int $user_id = 0, string $class = '', string $con = ''): string
{
if (!aksrt_current_user_can_ban($user_id)) {
return '';
}
if ($con === '') {
$con = aksrt_get_user_current_ban($user_id)
? '<i class="fa fas fa-ban mr6" aria-hidden="true"></i>' . __('解封此用户', 'aksrt')
: '<i class="fa fas fa-ban mr6" aria-hidden="true"></i>' . __('禁封此用户', 'aksrt');
}
return '<a href="javascript:;" class="' . esc_attr($class) . '" data-aksrt-ban="' . esc_attr((string) $user_id) . '">' . $con . '</a>';
}
/**
* 用户主页封面三点菜单项(参考主题 author_header_drop_lists 钩子语义):
* 有禁封权限 → 禁封入口;否则(开启举报且非本人)→ 举报入口。
*/
function aksrt_get_user_cover_menu_action_html(int $user_id): string
{
if (aksrt_current_user_can_ban($user_id)) {
$ban = aksrt_get_user_current_ban($user_id);
$label = $ban ? __('解封此用户', 'aksrt') : __('禁封此用户', 'aksrt');
return '<button type="button" role="menuitem" data-aksrt-ban="' . esc_attr((string) $user_id) . '"><i class="fa fas fa-ban" aria-hidden="true"></i>' . esc_html($label) . '</button>';
}
return '';
}
/**
* 评论三点菜单项(参考主题 comments_action_lists 钩子语义):
* 管理员 → 禁封用户(固定文案,与参考主题 zib_ban_comments_action_lists_filter
* 一致——解封态由弹窗内容呈现);否则 → 举报( Aksrt 既有评论举报契约)。
*/
function aksrt_get_comment_ban_or_report_item(\WP_Comment $comment): string
{
$user_id = (int) $comment->user_id;
if ($user_id > 0 && aksrt_current_user_can_ban($user_id)) {
return '<li><a class="comment-ban-link" href="javascript:;" data-aksrt-ban="' . esc_attr((string) $user_id) . '"><i class="fa fas fa-ban mr6 fa-fw c-red" aria-hidden="true"></i>' . esc_html__('禁封用户', 'aksrt') . '</a></li>';
}
if (function_exists('aksrt_get_comment_report_html')) {
return aksrt_get_comment_report_html($comment);
}
return '';
}
/**
* 文章作者资料卡三点菜单项(参考主题 zib_get_post_more_dropdown 语义,菜单首项):
* 管理员 → 禁封用户(固定文案,解封态由弹窗内容呈现);否则 → 空(由既有
* 文章举报项兜底,与参考主题「禁封优先、否则举报」互斥语义一致)。
*/
function aksrt_get_post_author_ban_item(int $author_id, string $post_url = ''): string
{
if ($author_id <= 0 || get_current_user_id() === $author_id) {
return '';
}
if (aksrt_current_user_can_ban($author_id)) {
return '<a href="javascript:;" class="post-drop-meta__item" data-aksrt-ban="' . esc_attr((string) $author_id) . '"><i class="fa fas fa-ban" aria-hidden="true"></i><span>' . esc_html__('禁封用户', 'aksrt') . '</span></a>';
}
return '';
}
/**
* 弹窗关闭图标(与 report.js 同款 SVG 雪碧图契约)。
*/
function aksrt_user_ban_close_svg(): string
{
return '<svg class="ic-close" aria-hidden="true"><use xlink:href="#icon-close"></use></svg>';
}
/**
* 禁封 / 解封弹窗内容 — 1:1 复刻参考主题 zib_get_set_user_ban_modal:
* 未禁封 → 禁封表单(禁封方式 / 禁封时间 / 预置原因徽章 / 其它原因 /
* 禁封说明 / 删除全部内容 / 禁止用户申诉(含说明));
* 已禁封 → 解封视图(状态信息 + 解封留言表单)。
*
* @return string 弹窗 HTML(空串 = 无权操作或用户不存在)
*/
function aksrt_get_user_ban_modal(int $user_id): string
{
$user = $user_id > 0 ? get_userdata($user_id) : null;
if (!$user || !aksrt_current_user_can_ban($user_id)) {
return '';
}
$ban = aksrt_get_user_current_ban($user_id);
// 用户行(头像 + 昵称),与参考主题弹窗头部同构。
$avatar_url = get_avatar_url($user_id, ['size' => 40]);
$user_name = function_exists('aksrt_get_user_name')
? aksrt_get_user_name(['id' => $user_id, 'vip' => false, 'medal' => false])
: esc_html($user->display_name);
$user_row = '<div class="flex ac mb6">'
. '<img class="avatar-mini mr6" src="' . esc_url((string) $avatar_url) . '" alt="">'
. '<b>' . $user_name . '</b></div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- aksrt_get_user_name escapes internally.
$html = '<div class="aksrt-user-ban-modal">';
if ($ban) {
// ── 已禁封视图:解封(参考主题解封弹窗同构) ──
$is_ban = (string) ($ban['type'] ?? '') === 'ban';
$unban_title = $is_ban ? __('解封用户', 'aksrt') : __('移出小黑屋', 'aksrt');
$status_text = $is_ban ? __('禁封中(禁止登录)', 'aksrt') : __('小黑屋', 'aksrt');
$end_time = !empty($ban['expires_at']) ? (string) $ban['expires_at'] : __('永久', 'aksrt');
// AKSRT 单一 reason 列:禁封说明以“禁封说明:”后缀并入(set_user_ban 同语义),
// 解封视图拆回参考主题的「原因 / 说明」两行。
$raw_reason = (string) ($ban['reason'] ?? '');
$reason = $raw_reason;
$ban_desc = '';
$desc_marker = __('禁封说明:', 'aksrt');
$marker_pos = strpos($raw_reason, "\n" . $desc_marker);
if (false !== $marker_pos) {
$reason = substr($raw_reason, 0, $marker_pos);
$ban_desc = substr($raw_reason, $marker_pos + strlen("\n" . $desc_marker));
}
// 用户名徽章(参考主题 zib_get_user_name(...&class=inflex ac badg p2-10 em09))。
$user_badge = function_exists('aksrt_get_user_name')
? aksrt_get_user_name(['id' => $user_id, 'class' => 'inflex ac badg p2-10 em09', 'vip' => false, 'medal' => false, 'auth' => false, 'level' => false])
: '<b>' . esc_html($user->display_name) . '</b>';
$set_row = static function (string $label, string $value): string {
return '<div class="mb10 flex"><div class="author-set-left" style="min-width: 80px;">' . esc_html($label) . '</div><div class="author-set-right mt6">' . $value . '</div></div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- callers pass escaped HTML.
};
// 弹窗头(参考主题 zib_get_modal_colorful_header('c-yellow', ban 图标, 标题):
// 120px 多彩横幅 + 大图标 + 居中标题,与禁封视图的 edit-header 头对应)。
$html .= '<div class="modal-colorful-header colorful-bg c-yellow"><button type="button" class="close" data-aksrt-modal-close aria-label="' . esc_attr__('关闭', 'aksrt') . '">' . aksrt_user_ban_close_svg() . '</button><div class="colorful-make"></div><div class="text-center"><div class="em2x"><i class="fa fas fa-ban" aria-hidden="true"></i></div><div class="mt10 em12 padding-w10">' . esc_html($unban_title) . '</div></div></div>';
$html .= '<form class="aksrt-user-ban-form" data-aksrt-ban-form data-aksrt-ban-mode="unban" data-aksrt-ban-user="' . esc_attr((string) $user_id) . '">';
$html .= '<div class="mb20">';
$html .= $set_row(__('用户', 'aksrt'), $user_badge); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$html .= $set_row(__('状态', 'aksrt'), esc_html($status_text));
$html .= $set_row(__('开始时间', 'aksrt'), esc_html((string) ($ban['created_at'] ?? '')));
$html .= $set_row(__('结束时间', 'aksrt'), esc_html($end_time));
$html .= $set_row(__('原因', 'aksrt'), nl2br(esc_html($reason)));
$html .= $set_row(__('说明', 'aksrt'), nl2br(esc_html($ban_desc)));
$html .= '</div>';
$html .= '<div class="mb20"><textarea class="form-control" name="desc" tabindex="2" placeholder="' . esc_attr__('给用户的留言', 'aksrt') . '" rows="2" autoHeight="true" maxHeight="110"></textarea></div>';
$html .= '<div class="mt20 but-average modal-buts">';
$html .= '<button type="button" class="but" data-aksrt-modal-close>' . esc_html__('取消', 'aksrt') . '</button>';
$html .= '<button type="button" class="but c-yellow" data-aksrt-unban-submit>' . esc_html(sprintf(__('确认%s', 'aksrt'), $unban_title)) . '</button>';
$html .= '</div></form></div>';
return $html;
}
// ── 未禁封视图:禁封表单(参考主题禁封弹窗同构) ──
$close_svg = aksrt_user_ban_close_svg();
$html .= '<div class="mb10 edit-header touch"><button type="button" class="close" data-aksrt-modal-close aria-label="' . esc_attr__('关闭', 'aksrt') . '">' . $close_svg . '</button><b class="flex ac c-red"><span class="toggle-radius mr10 c-red"><i class="fa fas fa-ban" aria-hidden="true"></i></span>' . esc_html__('禁封用户', 'aksrt') . '</b></div>';
// 历史禁封次数提示(参考主题:>1 次时展示禁封记录链接,此处展示计数提示)。
$log_count = 0;
if (class_exists('\AKSRT\User\Restriction')) {
$all = \AKSRT\User\Restriction::get_instance()->get_user_restrictions($user_id, false);
foreach ((array) $all as $r) {
if (in_array((string) ($r['type'] ?? ''), ['ban', 'suspend'], true)) {
$log_count++;
}
}
}
$log_view = $log_count > 1
? '<div class="mb10 flex"><div class="author-set-left" style="min-width: 80px;">' . esc_html__('禁封记录', 'aksrt') . '</div><div class="author-set-right mt6"><span class="em09 c-yellow">' . esc_html(sprintf(__('当前用户已有%d次禁封记录,请谨慎操作', 'aksrt'), $log_count)) . '</span></div></div>'
: '';
$html .= '<form class="aksrt-user-ban-form" data-aksrt-ban-form data-aksrt-ban-mode="ban" data-aksrt-ban-user="' . esc_attr((string) $user_id) . '">';
$html .= '<div class="scroll-y mini-scrollbar max-vh7">';
$html .= '<div class="mt10 mb20">' . $user_row . $log_view . '</div>';
// 禁封方式(1=拉入小黑屋 suspend / 2=禁封用户 ban)。
$html .= '<div class="mb6 muted-2-color">' . esc_html__('禁封方式', 'aksrt') . '</div>';
$html .= '<div class="form-radio form-but-radio">';
$html .= '<label><input type="radio" name="ban" value="1" checked="checked"><span class="p2-10 mr6 but but-radio">' . esc_html__('拉入小黑屋', 'aksrt') . '</span></label>';
$html .= '<label><input type="radio" name="ban" value="2"><span class="p2-10 mr6 but but-radio">' . esc_html__('禁封用户', 'aksrt') . '</span></label>';
$html .= '</div>';
$html .= '<div class="em09 c-red opacity8 mb10">';
$html .= '<div data-aksrt-ban-desc="1">' . esc_html__('将用户拉入小黑屋后,用户将失去所有的发布权限及大部分的操作权限,例如发帖、评论等', 'aksrt') . '</div>';
$html .= '<div data-aksrt-ban-desc="2" style="display:none;">' . esc_html__('禁封的用户将不能登录', 'aksrt') . '</div>';
$html .= '</div>';
// 禁封时间(1/7/14/30/100/永久,默认 7 天,对齐参考主题 checked(7, ...))。
$html .= '<div class="muted-2-color mb6">' . esc_html__('禁封时间', 'aksrt') . '</div>';
$html .= '<div class="form-radio mb10 form-but-radio">';
foreach ([1 => __('1天', 'aksrt'), 7 => __('7天', 'aksrt'), 14 => __('14天', 'aksrt'), 30 => __('30天', 'aksrt'), 100 => __('100天', 'aksrt'), 0 => __('永久', 'aksrt')] as $days => $label) {
$checked = 7 === $days ? ' checked="checked"' : '';
$html .= '<label><input type="radio" name="time_day" value="' . esc_attr((string) $days) . '"' . $checked . '><span class="p2-10 mr6 but but-radio">' . esc_html($label) . '</span></label>';
}
$html .= '</div>';
// 选择禁封原因(预置徽章 + 其它原因)。
$html .= '<div class="muted-2-color mb6">' . esc_html__('选择禁封原因', 'aksrt') . '</div>';
$html .= '<div class="form-radio mb10 form-but-radio">';
$reasons = class_exists('\AKSRT\Settings\UserRuntime') ? (\AKSRT\Settings\UserRuntime::ban_settings()['reasons'] ?? []) : [];
foreach ($reasons as $reason) {
$html .= '<label><input type="radio" name="reason" value="' . esc_attr((string) $reason['name']) . '"><span class="p2-10 mr6 but but-radio">' . esc_html((string) $reason['name']) . '</span></label>';
}
$html .= '<label><input type="radio" name="reason" value="other"><span class="p2-10 mr6 but but-radio">' . esc_html__('其它原因', 'aksrt') . '</span></label>';
$html .= '</div>';
$html .= '<div class="mb20" data-aksrt-ban-reason-other="1" style="display:none;"><input type="text" class="form-control" name="reason_other" tabindex="1" placeholder="' . esc_attr__('请填写禁封原因', 'aksrt') . '"></div>';
// 禁封说明。
$html .= '<div class="muted-2-color mb6">' . esc_html__('禁封说明', 'aksrt') . '</div>';
$html .= '<textarea class="form-control mb20" name="desc" tabindex="2" placeholder="' . esc_attr__('请输入禁封说明或给用户的留言', 'aksrt') . '" rows="2" autoHeight="true" maxHeight="110"></textarea>';
// 删除全部内容。
$html .= '<div class="mb20"><label class="flex jsb ac" style="font-weight:normal;"><input class="hide" name="delete_content" type="checkbox"><div class="flex1 mr20"><div class="muted-2-color mb3">' . esc_html__('删除全部内容', 'aksrt') . '</div><div class="muted-3-color px12">' . esc_html__('将该用户发布的全部文章、帖子、评论移至回收站', 'aksrt') . '</div></div><div class="form-switch flex0"></div></label></div>';
// 禁止用户申诉(含说明联动)。
$html .= '<div class="mb20">';
$html .= '<div class="mb6"><label class="flex jsb ac" style="font-weight:normal;"><input class="hide" name="no_appeal" type="checkbox"><div class="flex1 mr20"><div class="muted-2-color mb3">' . esc_html__('禁止用户申诉', 'aksrt') . '</div><div class="muted-3-color px12">' . esc_html__('开启后用户将无法进行申诉', 'aksrt') . '</div></div><div class="form-switch flex0"></div></label></div>';
$html .= '<div data-aksrt-ban-noappeal-desc style="display:none;"><textarea class="form-control" name="no_appeal_desc" tabindex="1" placeholder="' . esc_attr__('请填写禁止申诉说明', 'aksrt') . '" rows="2"></textarea></div>';
$html .= '</div>';
$html .= '</div>';
$html .= '<div class="mt20 but-average modal-buts">';
$html .= '<button type="button" class="but" data-aksrt-modal-close>' . esc_html__('取消', 'aksrt') . '</button>';
$html .= '<button type="button" class="but c-red" data-aksrt-ban-submit><i class="fa fas fa-ban" aria-hidden="true"></i>' . esc_html__('确认禁封', 'aksrt') . '</button>';
$html .= '</div>';
$html .= '</form></div>';
return $html;
}