feat: full parity pass - suspend enforcement, report modal polish, wechat notify

- Restriction: add is_suspended/get_active_suspend; comment block now covers
  suspend (小黑屋) in addition to mute, with reason in the deny message
- FrontSubmitSave::persist: block post/draft/edit submission for suspended
  users (reference-theme semantics: 小黑屋 loses all publishing rights)
- report.js: close icon switched from fa-times to the SVG sprite ic-close
  (#icon-close), same as reference theme; textareas gain autoHeight growth
  (hidden 'other reason' box skips the first measurement)
- WeChatNotify: NOTIFY_TYPES now includes report/system so report results
  and account restriction notices fan out to WeChat template messages;
  generic title/description fallback for template values
- contract tests: +3 tests (suspend enforcement, modal polish, wechat types)
This commit is contained in:
2026-09-20 16:09:12 +08:00
parent c38616b290
commit 0df6e760e3
5 changed files with 121 additions and 8 deletions
+10
View File
@@ -43,6 +43,16 @@ trait FrontSubmitSave {
}
}
// 小黑屋(suspend):剥夺发布权限,发帖/草稿/编辑均拦截(对齐参考主题)。
if (!$is_guest && \AKSRT\User\Restriction::get_instance()->is_suspended($user_id)) {
$suspend = \AKSRT\User\Restriction::get_instance()->get_active_suspend($user_id);
$message = __('您已被关入小黑屋,暂时无法发布内容', 'aksrt');
if (!empty($suspend['reason'])) {
$message .= ',原因:' . $suspend['reason'];
}
return ['success' => false, 'message' => $message];
}
if ($is_edit) {
if (!$this->can_edit_post($posts_id, $user_id)) {
return ['success' => false, 'message' => __('没有编辑权限', 'aksrt')];
+20 -5
View File
@@ -11,6 +11,12 @@ class WeChatNotify {
private static $instance = null;
/**
* 可联动微信模板消息的站内通知类型。
* report:举报处理结果;system:禁封/限制/解除等账号限制通知。
*/
const NOTIFY_TYPES = ['comment_reply', 'system_notice', 'order_paid', 'credits_change', 'report', 'system'];
public static function get_instance(): self {
if (null === self::$instance) self::$instance = new self();
return self::$instance;
@@ -134,6 +140,14 @@ class WeChatNotify {
$url = $data['url'] ?? home_url('/');
$values = $data['values'] ?? [];
// 通用兜底:站内通知未带模板字段时,用标题/内容合成。
if (empty($values)) {
$values = array_filter([
'first' => (string) ($data['title'] ?? ''),
'keyword1' => (string) ($data['description'] ?? ''),
], static function ($v) { return $v !== ''; });
}
$template_data = [];
foreach ($values as $key => $value) {
$template_data[$key] = [
@@ -172,9 +186,8 @@ class WeChatNotify {
$config = $this->get_config();
if (empty($config['enabled'])) return;
// 仅特定类型通知发送微信
$allowed_types = ['comment_reply', 'system_notice', 'order_paid', 'credits_change'];
if (!in_array($type, $allowed_types, true)) return;
// 仅特定类型通知发送微信(含举报处理结果与账号限制通知)
if (!in_array($type, self::NOTIFY_TYPES, true)) return;
$this->send($user_id, $type, $data);
}
@@ -219,7 +232,7 @@ class WeChatNotify {
'has_template' => !empty($config['template_id']),
'bound_users' => $bound_count,
'app_secret' => !empty($config['app_secret']) ? '***' : '',
'allowed_types' => ['comment_reply', 'system_notice', 'order_paid', 'credits_change'],
'allowed_types' => self::NOTIFY_TYPES,
];
}
@@ -234,6 +247,8 @@ class WeChatNotify {
'system_notice' => true,
'order_paid' => true,
'credits_change' => false,
'report' => true,
'system' => true,
];
}
return $subs;
@@ -243,7 +258,7 @@ class WeChatNotify {
* Update notification subscriptions for a user.
*/
public function update_user_subscriptions(int $user_id, array $subscriptions): bool {
$allowed = ['comment_reply', 'system_notice', 'order_paid', 'credits_change'];
$allowed = self::NOTIFY_TYPES;
$clean = [];
foreach ($allowed as $key) {
$clean[$key] = !empty($subscriptions[$key]);
+38 -2
View File
@@ -182,6 +182,28 @@ class Restriction {
return $this->is_restricted($user_id, self::TYPE_BAN, self::SCOPE_GLOBAL);
}
/**
* Check if a user is in 小黑屋 (active global suspend).
*
* 对齐参考主题语义:小黑屋用户失去所有发布权限(发帖、评论等)及大部分操作权限。
*/
public function is_suspended(int $user_id): bool {
return $this->is_restricted($user_id, self::TYPE_SUSPEND, self::SCOPE_GLOBAL);
}
/**
* Get the active suspend restriction for a user (if any).
*/
public function get_active_suspend(int $user_id): ?array {
$restrictions = $this->get_user_restrictions($user_id, true);
foreach ($restrictions as $r) {
if ($r['type'] === self::TYPE_SUSPEND && $r['scope'] === self::SCOPE_GLOBAL) {
return $r;
}
}
return null;
}
/**
* Get the active ban restriction for a user (if any).
*/
@@ -214,13 +236,27 @@ class Restriction {
}
/**
* Enforce comment mute.
* Enforce comment restriction on submission (mute / 小黑屋).
*
* 禁言(mute,评论域)与小黑屋(suspend,全域)都会拦截评论——
* 对齐参考主题:小黑屋用户失去评论等发布权限。
*/
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)) {
if ($user_id <= 0) {
return $post_id;
}
if ($this->is_restricted($user_id, self::TYPE_MUTE, self::SCOPE_COMMENT)) {
wp_die('您已被限制评论。', '评论限制', ['response' => 403]);
}
if ($this->is_suspended($user_id)) {
$suspend = $this->get_active_suspend($user_id);
$message = '您已被关入小黑屋,暂时无法评论。';
if (!empty($suspend['reason'])) {
$message .= '原因:' . $suspend['reason'];
}
wp_die($message, '发布限制', ['response' => 403]);
}
return $post_id;
}