Route forum essence/adopt/hot/score rewards through free-points day cap, notify users on withdraw complete/reject, surface 海报分享 on single share grid, and extend admin free-task keys for the remaining matrix.
605 lines
29 KiB
PHP
605 lines
29 KiB
PHP
<?php
|
|
/**
|
|
* Forum comment/reply service.
|
|
*
|
|
* Extracted from AKSRT_Forum to isolate the comment engine:
|
|
* querying, rendering, pinning, hot-comment detection, and adopt-answer flow.
|
|
*
|
|
* @package AKSRT
|
|
*/
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
class AKSRT_Forum_Comment_Service {
|
|
|
|
const COMMENT_TOP_NONE = 0;
|
|
const COMMENT_TOP_NORMAL = 1;
|
|
const COMMENT_TOP_HOT = 2;
|
|
const COMMENT_TOP_BEST = 3;
|
|
|
|
/** @var AKSRT_Forum */
|
|
private $forum;
|
|
|
|
public function __construct($forum) {
|
|
$this->forum = $forum;
|
|
}
|
|
|
|
// ── Hook: forum comment posted ───────────────────────────────────
|
|
|
|
public function on_forum_comment_posted(int $comment_id, $approved, array $commentdata = []): void {
|
|
$comment = get_comment($comment_id);
|
|
if (!$comment) return;
|
|
$post = get_post((int) $comment->comment_post_ID);
|
|
if (!$post || $post->post_type !== 'aksrt_forum_topic') return;
|
|
if ((int) $comment->comment_approved !== 1) return;
|
|
$commenter_id = (int) $comment->user_id;
|
|
if ($commenter_id <= 0) return;
|
|
|
|
// Topic-level last_reply for board sort tabs.
|
|
update_post_meta((int) $post->ID, '_aksrt_last_reply', (string) $comment->comment_date);
|
|
|
|
$this->forum->notify_topic_reply((int) $post->ID, $commenter_id);
|
|
}
|
|
|
|
// ── AJAX: get forum comments ─────────────────────────────────────
|
|
|
|
/**
|
|
* AJAX: Get forum comments with sort/filter/pagination.
|
|
*/
|
|
public function ajax_forum_comments(): void {
|
|
check_ajax_referer('aksrt_public_nonce', 'nonce');
|
|
|
|
$topic_id = (int) ($_POST['topic_id'] ?? 0);
|
|
$sort = sanitize_key((string) ($_POST['sort'] ?? 'newest'));
|
|
$sort = in_array($sort, ['newest', 'oldest', 'hottest'], true) ? $sort : 'newest';
|
|
$author_only = !empty($_POST['author_only']);
|
|
$page = max(1, (int) ($_POST['page'] ?? 1));
|
|
$per_page = max(1, min(100, (int) ($_POST['per_page'] ?? 30)));
|
|
|
|
if ($topic_id <= 0) {
|
|
wp_send_json_error(['message' => __('参数无效', 'aksrt')]);
|
|
}
|
|
|
|
$result = $this->get_forum_comments($topic_id, $sort, $author_only, $page, $per_page);
|
|
|
|
ob_start();
|
|
$this->render_forum_comments($result['comments'], $topic_id, $result['adopted_id']);
|
|
$html = ob_get_clean();
|
|
|
|
wp_send_json_success([
|
|
'html' => $html,
|
|
'total' => $result['total'],
|
|
'max_pages' => $result['max_pages'],
|
|
'page' => $result['page'],
|
|
'has_more' => $result['page'] < $result['max_pages'],
|
|
]);
|
|
}
|
|
|
|
// ── AJAX: comment topping ────────────────────────────────────────
|
|
|
|
/**
|
|
* AJAX: Set comment topping level (moderator action).
|
|
*/
|
|
public function ajax_comment_topping(): void {
|
|
check_ajax_referer('aksrt_public_nonce', 'nonce');
|
|
|
|
$comment_id = (int) ($_POST['comment_id'] ?? 0);
|
|
$level = max(0, min(3, (int) ($_POST['level'] ?? 0)));
|
|
$user_id = get_current_user_id();
|
|
|
|
$comment = get_comment($comment_id);
|
|
if (!$comment) {
|
|
wp_send_json_error(['message' => __('评论不存在', 'aksrt')]);
|
|
}
|
|
|
|
$topic_id = (int) $comment->comment_post_ID;
|
|
if (!$this->forum->forum_user_can($user_id, 'reply_top', $topic_id)) {
|
|
wp_send_json_error(['message' => __('权限不足', 'aksrt')]);
|
|
}
|
|
|
|
if ($this->set_comment_topping($comment_id, $level)) {
|
|
wp_send_json_success([
|
|
'message' => $level > 0 ? __('已置顶', 'aksrt') : __('已取消置顶', 'aksrt'),
|
|
'level' => $level,
|
|
]);
|
|
}
|
|
|
|
wp_send_json_error(['message' => __('操作失败', 'aksrt')]);
|
|
}
|
|
|
|
// ── Render forum comments ────────────────────────────────────────
|
|
|
|
/**
|
|
* Render forum comments HTML for AJAX and initial load.
|
|
*
|
|
* @param WP_Comment[] $comments Comments (parents + children mixed).
|
|
* @param int $topic_id Topic post ID.
|
|
* @param int $adopted_id Adopted comment ID (0 if none).
|
|
*/
|
|
public function render_forum_comments(array $comments, int $topic_id, int $adopted_id = 0): void {
|
|
$post = get_post($topic_id);
|
|
$post_author_id = $post ? (int) $post->post_author : 0;
|
|
$current_user_id = get_current_user_id();
|
|
$is_question = 'question' === (string) get_post_meta($topic_id, '_aksrt_bbs_type', true);
|
|
$can_adopt = $is_question && $current_user_id > 0
|
|
&& ($current_user_id === $post_author_id || $this->forum->forum_user_can($current_user_id, 'reply_adopt_other', $topic_id));
|
|
$can_moderate = $this->forum->forum_user_can($current_user_id, 'topic_moderate', $topic_id);
|
|
|
|
if (empty($comments)) {
|
|
echo '<div class="aksrt-forum-replies__empty"><i class="far fa-comment-dots"></i> ' . esc_html__('暂无回复,来抢沙发吧!', 'aksrt') . '</div>';
|
|
return;
|
|
}
|
|
|
|
foreach ($comments as $comment) {
|
|
if (!($comment instanceof \WP_Comment)) continue;
|
|
if ((int) $comment->comment_parent !== 0) continue;
|
|
|
|
$comment_id = (int) $comment->comment_ID;
|
|
$commenter_uid = (int) $comment->user_id;
|
|
$is_adopted = $comment_id === $adopted_id;
|
|
$is_hot = $this->is_hot_comment($comment_id);
|
|
$is_post_author = $commenter_uid > 0 && $commenter_uid === $post_author_id;
|
|
$topping = (int) get_comment_meta($comment_id, '_aksrt_comment_topping', true);
|
|
$badge = $this->get_commenter_badge($commenter_uid, $topic_id);
|
|
|
|
$like_count = function_exists('aksrt_get_comment_likes') ? aksrt_get_comment_likes($comment_id) : 0;
|
|
$is_liked = function_exists('aksrt_is_comment_liked') ? aksrt_is_comment_liked($comment_id) : false;
|
|
|
|
$classes = ['aksrt-forum-reply'];
|
|
if ($is_adopted) $classes[] = 'is-adopted';
|
|
if ($is_hot) $classes[] = 'is-hot';
|
|
if ($topping > 0) $classes[] = 'is-pinned';
|
|
?>
|
|
<div class="<?php echo esc_attr(implode(' ', $classes)); ?>" id="comment-<?php echo esc_attr((string) $comment_id); ?>" data-comment-id="<?php echo esc_attr((string) $comment_id); ?>">
|
|
<div class="aksrt-forum-reply__avatar"><?php echo get_avatar($comment, 40); ?></div>
|
|
<div class="aksrt-forum-reply__body">
|
|
<div class="aksrt-forum-reply__meta">
|
|
<strong><?php comment_author($comment_id); ?></strong>
|
|
<?php if ($is_post_author) : ?>
|
|
<span class="aksrt-forum-reply__role-badge aksrt-forum-reply__role-badge--author"><?php esc_html_e('楼主', 'aksrt'); ?></span>
|
|
<?php endif; ?>
|
|
<?php if ($badge) : ?>
|
|
<span class="aksrt-forum-reply__role-badge <?php echo esc_attr($badge['class']); ?>"><?php echo esc_html($badge['label']); ?></span>
|
|
<?php endif; ?>
|
|
<?php if ($topping > 0) : ?>
|
|
<span class="aksrt-forum-reply__badge aksrt-forum-reply__badge--pinned"><i class="fas fa-thumbtack"></i> <?php esc_html_e('置顶', 'aksrt'); ?></span>
|
|
<?php endif; ?>
|
|
<?php if ($is_hot) : ?>
|
|
<span class="aksrt-forum-reply__badge aksrt-forum-reply__badge--hot"><i class="fas fa-fire"></i> <?php esc_html_e('神评', 'aksrt'); ?></span>
|
|
<?php endif; ?>
|
|
<?php if ($is_adopted) : ?>
|
|
<span class="aksrt-forum-reply__adopted-badge"><i class="fas fa-check-circle"></i> <?php esc_html_e('最佳答案', 'aksrt'); ?></span>
|
|
<?php endif; ?>
|
|
<time datetime="<?php echo esc_attr(get_comment_date('c', $comment)); ?>"><?php echo esc_html(get_comment_date('Y-m-d H:i', $comment)); ?></time>
|
|
</div>
|
|
|
|
<div class="aksrt-forum-reply__content"><?php echo function_exists('aksrt_render_comment_content') ? aksrt_render_comment_content((string) get_comment_text($comment_id)) : wp_kses_post(get_comment_text($comment_id)); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
|
|
<div class="aksrt-forum-reply__actions">
|
|
<button type="button" class="aksrt-forum-reply__like-btn js-aksrt-comment-like<?php echo $is_liked ? ' is-active' : ''; ?>"
|
|
data-comment-id="<?php echo esc_attr((string) $comment_id); ?>"
|
|
data-active="<?php echo $is_liked ? '1' : '0'; ?>">
|
|
<i class="<?php echo $is_liked ? 'fas' : 'far'; ?> fa-thumbs-up"></i>
|
|
<span class="aksrt-forum-reply__like-count"><?php echo $like_count > 0 ? esc_html((string) $like_count) : ''; ?></span>
|
|
</button>
|
|
<?php if (is_user_logged_in()) : ?>
|
|
<button type="button" class="aksrt-forum-reply__report-btn js-aksrt-report-trigger"
|
|
data-target-type="comment"
|
|
data-target-id="<?php echo esc_attr((string) $comment_id); ?>"
|
|
data-target-title="<?php echo esc_attr(wp_strip_all_tags(get_comment_excerpt($comment))); ?>">
|
|
<i class="far fa-flag"></i> <?php esc_html_e('举报', 'aksrt'); ?>
|
|
</button>
|
|
<?php endif; ?>
|
|
<?php if (comments_open($topic_id)) : ?>
|
|
<button type="button" class="aksrt-forum-reply__reply-btn js-aksrt-comment-reply"
|
|
data-comment-id="<?php echo esc_attr((string) $comment_id); ?>"
|
|
data-author="<?php echo esc_attr(get_comment_author($comment_id)); ?>">
|
|
<i class="fas fa-reply"></i> <?php esc_html_e('回复', 'aksrt'); ?>
|
|
</button>
|
|
<?php endif; ?>
|
|
<?php if ($can_adopt && !$is_adopted) : ?>
|
|
<button type="button" class="aksrt-forum-reply__adopt-btn js-aksrt-adopt-answer"
|
|
data-topic-id="<?php echo esc_attr((string) $topic_id); ?>"
|
|
data-comment-id="<?php echo esc_attr((string) $comment_id); ?>">
|
|
<i class="fas fa-check"></i> <?php esc_html_e('采纳', 'aksrt'); ?>
|
|
</button>
|
|
<?php endif; ?>
|
|
<?php if ($can_moderate) : ?>
|
|
<button type="button" class="aksrt-forum-reply__top-btn js-aksrt-comment-top"
|
|
data-comment-id="<?php echo esc_attr((string) $comment_id); ?>"
|
|
data-level="<?php echo esc_attr((string) $topping); ?>">
|
|
<i class="fas fa-thumbtack"></i> <?php echo $topping > 0 ? esc_html__('取消置顶', 'aksrt') : esc_html__('置顶', 'aksrt'); ?>
|
|
</button>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
|
|
<?php
|
|
// Render nested children (楼中楼)
|
|
$child_comments = array_filter($comments, function ($c) use ($comment_id) {
|
|
return ($c instanceof \WP_Comment) && (int) $c->comment_parent === $comment_id;
|
|
});
|
|
if (!empty($child_comments)) : ?>
|
|
<div class="aksrt-forum-reply__children">
|
|
<?php foreach ($child_comments as $child) :
|
|
$child_id = (int) $child->comment_ID;
|
|
$child_uid = (int) $child->user_id;
|
|
$child_badge = $this->get_commenter_badge($child_uid, $topic_id);
|
|
$child_is_author = $child_uid > 0 && $child_uid === $post_author_id;
|
|
$child_like_count = function_exists('aksrt_get_comment_likes') ? aksrt_get_comment_likes($child_id) : 0;
|
|
$child_is_liked = function_exists('aksrt_is_comment_liked') ? aksrt_is_comment_liked($child_id) : false;
|
|
$parent_comment = get_comment((int) $child->comment_parent);
|
|
$parent_name = $parent_comment ? get_comment_author((int) $parent_comment->comment_ID) : '';
|
|
?>
|
|
<div class="aksrt-forum-reply aksrt-forum-reply--nested" id="comment-<?php echo esc_attr((string) $child_id); ?>" data-comment-id="<?php echo esc_attr((string) $child_id); ?>">
|
|
<div class="aksrt-forum-reply__avatar aksrt-forum-reply__avatar--sm"><?php echo get_avatar($child, 28); ?></div>
|
|
<div class="aksrt-forum-reply__body">
|
|
<div class="aksrt-forum-reply__meta">
|
|
<strong><?php comment_author($child_id); ?></strong>
|
|
<?php if ($child_is_author) : ?>
|
|
<span class="aksrt-forum-reply__role-badge aksrt-forum-reply__role-badge--author"><?php esc_html_e('楼主', 'aksrt'); ?></span>
|
|
<?php endif; ?>
|
|
<?php if ($child_badge) : ?>
|
|
<span class="aksrt-forum-reply__role-badge <?php echo esc_attr($child_badge['class']); ?>"><?php echo esc_html($child_badge['label']); ?></span>
|
|
<?php endif; ?>
|
|
<?php if ($parent_name && (int) $child->comment_parent !== $comment_id) : ?>
|
|
<span class="aksrt-forum-reply__reply-to"><?php printf(esc_html__('回复 %s', 'aksrt'), esc_html($parent_name)); ?></span>
|
|
<?php endif; ?>
|
|
<time datetime="<?php echo esc_attr(get_comment_date('c', $child)); ?>"><?php echo esc_html(get_comment_date('Y-m-d H:i', $child)); ?></time>
|
|
</div>
|
|
<div class="aksrt-forum-reply__content"><?php echo function_exists('aksrt_render_comment_content') ? aksrt_render_comment_content((string) get_comment_text($child_id)) : wp_kses_post(get_comment_text($child_id)); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
|
|
<div class="aksrt-forum-reply__actions">
|
|
<button type="button" class="aksrt-forum-reply__like-btn js-aksrt-comment-like<?php echo $child_is_liked ? ' is-active' : ''; ?>"
|
|
data-comment-id="<?php echo esc_attr((string) $child_id); ?>"
|
|
data-active="<?php echo $child_is_liked ? '1' : '0'; ?>">
|
|
<i class="<?php echo $child_is_liked ? 'fas' : 'far'; ?> fa-thumbs-up"></i>
|
|
<span class="aksrt-forum-reply__like-count"><?php echo $child_like_count > 0 ? esc_html((string) $child_like_count) : ''; ?></span>
|
|
</button>
|
|
<?php if (comments_open($topic_id)) : ?>
|
|
<button type="button" class="aksrt-forum-reply__reply-btn js-aksrt-comment-reply"
|
|
data-comment-id="<?php echo esc_attr((string) $child_id); ?>"
|
|
data-author="<?php echo esc_attr(get_comment_author($child_id)); ?>">
|
|
<i class="fas fa-reply"></i> <?php esc_html_e('回复', 'aksrt'); ?>
|
|
</button>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
<?php
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ── Adopt answer (best answer) ──────────────────────────────────
|
|
|
|
public function adopt_answer(int $topic_id, int $comment_id, int $operator_id) {
|
|
$post = get_post($topic_id);
|
|
if (!$post || $post->post_type !== 'aksrt_forum_topic') {
|
|
return new \WP_Error('invalid_topic', __('帖子不存在', 'aksrt'));
|
|
}
|
|
|
|
$bbs_type = sanitize_key((string) get_post_meta($topic_id, '_aksrt_bbs_type', true));
|
|
if ($bbs_type !== 'question') {
|
|
return new \WP_Error('not_question', __('该帖不是提问类型', 'aksrt'));
|
|
}
|
|
|
|
$author_id = (int) $post->post_author;
|
|
if ($operator_id !== $author_id && !$this->forum->can_moderate_topic($operator_id, $topic_id)) {
|
|
return new \WP_Error('forbidden', __('仅提问者或版主可采纳答案', 'aksrt'));
|
|
}
|
|
|
|
$comment = get_comment($comment_id);
|
|
if (!$comment || (int) $comment->comment_post_ID !== $topic_id) {
|
|
return new \WP_Error('invalid_comment', __('回复不存在', 'aksrt'));
|
|
}
|
|
|
|
$existing = get_post_meta($topic_id, '_aksrt_adopted_comment', true);
|
|
if ($existing && (int) $existing === $comment_id) {
|
|
return new \WP_Error('already_adopted', __('该回复已被采纳', 'aksrt'));
|
|
}
|
|
|
|
if ($existing) {
|
|
delete_comment_meta((int) $existing, '_aksrt_adopted');
|
|
}
|
|
|
|
update_comment_meta($comment_id, '_aksrt_adopted', '1');
|
|
update_post_meta($topic_id, '_aksrt_adopted_comment', $comment_id);
|
|
update_post_meta($topic_id, '_aksrt_question_status', 1); // QUESTION_SOLVED
|
|
|
|
$adoptee_id = (int) $comment->user_id;
|
|
if ($adoptee_id > 0) {
|
|
$config = $this->forum->get_config();
|
|
$amount = (int) ($config['credits']['adopted'] ?? 20);
|
|
if ($amount > 0) {
|
|
$already = get_comment_meta($comment_id, '_aksrt_adopt_credited', true);
|
|
if (!$already) {
|
|
if (class_exists('AKSRT_Points_Free')) {
|
|
$granted = \AKSRT_Points_Free::grant($adoptee_id, 'bbs_adopt', $amount, [
|
|
'type' => 'comment',
|
|
'id' => $comment_id,
|
|
]);
|
|
if (!is_wp_error($granted) && (int) $granted > 0) {
|
|
update_comment_meta($comment_id, '_aksrt_adopt_credited', '1');
|
|
}
|
|
} elseif (class_exists('AKSRT_Credits')) {
|
|
\AKSRT_Credits::add($adoptee_id, $amount, 'forum_adopted', [
|
|
'type' => 'comment',
|
|
'id' => $comment_id,
|
|
]);
|
|
update_comment_meta($comment_id, '_aksrt_adopt_credited', '1');
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->forum->notify($adoptee_id, 'system', sprintf(
|
|
__('你的回复被采纳为最佳答案(帖子「%s」)', 'aksrt'),
|
|
get_the_title($topic_id)
|
|
), [
|
|
'title' => __('回复被采纳', 'aksrt'),
|
|
'target_url' => get_permalink($topic_id) . '#comment-' . $comment_id,
|
|
]);
|
|
}
|
|
|
|
return [
|
|
'message' => __('已采纳该回复为最佳答案', 'aksrt'),
|
|
'comment_id' => $comment_id,
|
|
'solved' => true,
|
|
];
|
|
}
|
|
|
|
|
|
|
|
public function get_adopted_comment_id(int $topic_id): int {
|
|
return (int) get_post_meta($topic_id, '_aksrt_adopted_comment', true);
|
|
}
|
|
|
|
// ── Get forum comments (query) ──────────────────────────────────
|
|
|
|
/**
|
|
* Get forum comments with sort/filter support.
|
|
*
|
|
* @param int $topic_id Topic post ID.
|
|
* @param string $sort 'newest'|'oldest'|'hottest'
|
|
* @param bool $author_only Only show post-author replies.
|
|
* @param int $page Page number.
|
|
* @param int $per_page Items per page.
|
|
* @return array{comments:WP_Comment[],total:int,max_pages:int,page:int,adopted_id:int}
|
|
*/
|
|
public function get_forum_comments(int $topic_id, string $sort = 'newest', bool $author_only = false, int $page = 1, int $per_page = 30): array {
|
|
$post = get_post($topic_id);
|
|
if (!$post || $post->post_type !== 'aksrt_forum_topic') {
|
|
return ['comments' => [], 'total' => 0, 'max_pages' => 1, 'page' => 1, 'adopted_id' => 0];
|
|
}
|
|
|
|
$args = [
|
|
'post_id' => $topic_id,
|
|
'status' => 'approve',
|
|
'parent' => 0,
|
|
'number' => $per_page,
|
|
'offset' => ($page - 1) * $per_page,
|
|
];
|
|
|
|
if ($author_only) {
|
|
$args['user_id'] = (int) $post->post_author;
|
|
}
|
|
|
|
if ($sort === 'hottest') {
|
|
$args['meta_key'] = 'aksrt_comment_likes';
|
|
$args['orderby'] = 'meta_value_num';
|
|
$args['order'] = 'DESC';
|
|
} elseif ($sort === 'oldest') {
|
|
$args['orderby'] = 'comment_date_gmt';
|
|
$args['order'] = 'ASC';
|
|
} else {
|
|
$args['orderby'] = 'comment_date_gmt';
|
|
$args['order'] = 'DESC';
|
|
}
|
|
|
|
// Total count for pagination
|
|
$count_args = $args;
|
|
unset($count_args['number'], $count_args['offset']);
|
|
$count_args['count'] = true;
|
|
$total = (int) get_comments($count_args);
|
|
|
|
$top_level = get_comments($args);
|
|
|
|
|
|
// Fetch child comments for these parents
|
|
if (!empty($top_level)) {
|
|
$parent_ids = wp_list_pluck($top_level, 'comment_ID');
|
|
$children = get_comments([
|
|
'post_id' => $topic_id,
|
|
'status' => 'approve',
|
|
'parent__in' => $parent_ids,
|
|
'orderby' => 'comment_date_gmt',
|
|
'order' => 'ASC',
|
|
'number' => 200,
|
|
]);
|
|
} else {
|
|
$children = [];
|
|
}
|
|
|
|
// Float pinned top-level comments
|
|
$pinned = [];
|
|
$normal = [];
|
|
foreach ($top_level as $c) {
|
|
$topping = (int) get_comment_meta((int) $c->comment_ID, '_aksrt_comment_topping', true);
|
|
if ($topping > 0) {
|
|
$c->aksrt_topping = $topping;
|
|
$pinned[] = $c;
|
|
} else {
|
|
$normal[] = $c;
|
|
}
|
|
}
|
|
usort($pinned, function ($a, $b) {
|
|
return ($b->aksrt_topping ?? 0) <=> ($a->aksrt_topping ?? 0);
|
|
});
|
|
$comments = array_merge($pinned, $normal, $children);
|
|
|
|
return [
|
|
'comments' => $comments,
|
|
'total' => $total,
|
|
'max_pages' => max(1, (int) ceil($total / $per_page)),
|
|
'page' => $page,
|
|
'adopted_id' => $this->get_adopted_comment_id($topic_id),
|
|
];
|
|
}
|
|
|
|
// ── Set comment topping ─────────────────────────────────────────
|
|
|
|
/**
|
|
* Set comment topping level.
|
|
*
|
|
* @param int $comment_id Comment ID.
|
|
* @param int $level 0=unpin, 1=normal pin, 2=hot pin, 3=best answer pin.
|
|
*/
|
|
public function set_comment_topping(int $comment_id, int $level): bool {
|
|
$comment = get_comment($comment_id);
|
|
if (!$comment) {
|
|
return false;
|
|
}
|
|
if ($level <= 0) {
|
|
delete_comment_meta($comment_id, '_aksrt_comment_topping');
|
|
delete_comment_meta($comment_id, '_aksrt_comment_pinned');
|
|
} else {
|
|
update_comment_meta($comment_id, '_aksrt_comment_topping', $level);
|
|
update_comment_meta($comment_id, '_aksrt_comment_pinned', '1');
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
// ── Maybe mark hot comment ──────────────────────────────────────
|
|
|
|
/**
|
|
* Check and mark a comment as hot ("神评") if it crosses the like threshold.
|
|
*
|
|
* @return bool True if newly marked as hot.
|
|
*/
|
|
public function maybe_mark_hot_comment(int $comment_id): bool {
|
|
$comment = get_comment($comment_id);
|
|
if (!$comment || '1' !== (string) $comment->comment_approved) {
|
|
return false;
|
|
}
|
|
|
|
$post = get_post((int) $comment->comment_post_ID);
|
|
if (!$post || $post->post_type !== 'aksrt_forum_topic') {
|
|
return false;
|
|
}
|
|
|
|
$already_hot = '1' === (string) get_comment_meta($comment_id, '_aksrt_is_hot', true);
|
|
if ($already_hot) {
|
|
return false;
|
|
}
|
|
|
|
$config = $this->forum->get_config();
|
|
$threshold = max(1, (int) ($config['reply']['hot_threshold'] ?? 10));
|
|
$likes = max(0, (int) get_comment_meta($comment_id, 'aksrt_comment_likes', true));
|
|
|
|
if ($likes < $threshold) {
|
|
return false;
|
|
}
|
|
|
|
update_comment_meta($comment_id, '_aksrt_is_hot', '1');
|
|
|
|
// Credit the comment author (free-task day cap).
|
|
$author_id = (int) $comment->user_id;
|
|
if ($author_id > 0) {
|
|
$amount = (int) ($config['credits']['comment_hot'] ?? $config['credits']['comment_like'] ?? 5);
|
|
if ($amount > 0) {
|
|
if (class_exists('AKSRT_Points_Free')) {
|
|
\AKSRT_Points_Free::grant($author_id, 'bbs_comment_hot', $amount, [
|
|
'type' => 'comment',
|
|
'id' => $comment_id,
|
|
]);
|
|
} elseif (class_exists('AKSRT_Credits')) {
|
|
\AKSRT_Credits::add($author_id, $amount, 'forum_hot_comment', [
|
|
'type' => 'comment',
|
|
'id' => $comment_id,
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Notify the author
|
|
if ($author_id > 0) {
|
|
$this->forum->notify($author_id, 'system', sprintf(
|
|
__('你的回复成为了神评(帖子「%s」)', 'aksrt'),
|
|
get_the_title((int) $comment->comment_post_ID)
|
|
), [
|
|
'title' => __('恭喜获得神评', 'aksrt'),
|
|
'target_url' => get_permalink((int) $comment->comment_post_ID) . '#comment-' . $comment_id,
|
|
]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ── Is hot comment ──────────────────────────────────────────────
|
|
|
|
/**
|
|
* Check if a comment is a hot comment ("神评").
|
|
*/
|
|
public function is_hot_comment(int $comment_id): bool {
|
|
return '1' === (string) get_comment_meta($comment_id, '_aksrt_is_hot', true);
|
|
}
|
|
|
|
// ── Get commenter badge ─────────────────────────────────────────
|
|
|
|
/**
|
|
* Get the user's moderator tier label for badge rendering in comments.
|
|
*
|
|
* @return array{tier:int,label:string,class:string}|null
|
|
*/
|
|
public function get_commenter_badge(int $user_id, int $topic_id): ?array {
|
|
if ($user_id <= 0) {
|
|
return null;
|
|
}
|
|
|
|
if ($this->forum->is_forum_admin($user_id)) {
|
|
return [
|
|
'tier' => AKSRT_Forum::TIER_FORUM_ADMIN,
|
|
'label' => __('管理员', 'aksrt'),
|
|
'class' => 'aksrt-forum-reply__role-badge--admin',
|
|
];
|
|
}
|
|
|
|
$plate_id = $this->forum->get_topic_plate_id($topic_id);
|
|
if ($plate_id > 0) {
|
|
$tier = $this->forum->get_user_plate_tier($user_id, $plate_id);
|
|
if ($tier >= AKSRT_Forum::TIER_CAT_MOD) {
|
|
$names = $this->forum->get_config()['names'] ?? [];
|
|
return [
|
|
'tier' => $tier,
|
|
'label' => $names['cat_moderator'] ?? __('分区版主', 'aksrt'),
|
|
'class' => 'aksrt-forum-reply__role-badge--catmod',
|
|
];
|
|
}
|
|
if ($tier >= AKSRT_Forum::TIER_PLATE_AUTHOR) {
|
|
$names = $this->forum->get_config()['names'] ?? [];
|
|
return [
|
|
'tier' => $tier,
|
|
'label' => $names['plate_author'] ?? __('超级版主', 'aksrt'),
|
|
'class' => 'aksrt-forum-reply__role-badge--super',
|
|
];
|
|
}
|
|
if ($tier >= AKSRT_Forum::TIER_MODERATOR) {
|
|
$names = $this->forum->get_config()['names'] ?? [];
|
|
return [
|
|
'tier' => $tier,
|
|
'label' => $names['plate_moderator'] ?? __('版主', 'aksrt'),
|
|
'class' => 'aksrt-forum-reply__role-badge--mod',
|
|
];
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|