411 lines
14 KiB
PHP
411 lines
14 KiB
PHP
<?php
|
|
defined('ABSPATH') || exit;
|
|
|
|
trait AKSRT_Forum_View_Builder_Part_1 {
|
|
|
|
public function get_user_managed_plates(int $user_id): array {
|
|
return $this->forum->get_user_managed_plates($user_id);
|
|
}
|
|
|
|
/**
|
|
* Forum stats for a user (counts, cached per request).
|
|
*
|
|
* @return array{topic_count:int,reply_count:int,favorite_count:int,followed_plate_count:int,managed_plate_count:int}
|
|
*/
|
|
public function get_user_forum_stats(int $user_id): array {
|
|
$topic_count = (int) count_user_posts($user_id, 'aksrt_forum_topic', true);
|
|
|
|
global $wpdb;
|
|
$reply_count = (int) $wpdb->get_var($wpdb->prepare(
|
|
"SELECT COUNT(*) FROM {$wpdb->comments} c
|
|
INNER JOIN {$wpdb->posts} p ON p.ID = c.comment_post_ID
|
|
WHERE c.user_id = %d AND p.post_type = 'aksrt_forum_topic' AND c.comment_approved = '1'",
|
|
$user_id
|
|
));
|
|
|
|
$fav_ids = get_user_meta($user_id, 'aksrt_favorite_forum_posts', true);
|
|
$favorite_count = is_array($fav_ids) ? count(array_filter($fav_ids)) : 0;
|
|
|
|
$follow_ids = get_user_meta($user_id, 'aksrt_follow_plate', true);
|
|
$followed_plate_count = is_array($follow_ids) ? count(array_filter($follow_ids)) : 0;
|
|
|
|
$managed = $this->forum->get_user_managed_plates($user_id);
|
|
$managed_count = count($managed['owned']) + count($managed['moderating']);
|
|
|
|
return [
|
|
'topic_count' => $topic_count,
|
|
'reply_count' => $reply_count,
|
|
'favorite_count' => $favorite_count,
|
|
'followed_plate_count' => $followed_plate_count,
|
|
'managed_plate_count' => $managed_count,
|
|
];
|
|
}
|
|
|
|
public function get_topic_view(int $topic_id, int $user_id = 0): array {
|
|
$post = get_post($topic_id);
|
|
if (!$post || $post->post_type !== 'aksrt_forum_topic') {
|
|
return [
|
|
'topic' => null,
|
|
'board' => null,
|
|
'permissions' => [],
|
|
'board_options' => [],
|
|
'visibility_options' => $this->forum->get_visibility_options(),
|
|
'review_options' => $this->forum->get_review_options(),
|
|
];
|
|
}
|
|
|
|
$topic = $this->forum->build_topic_payload($post, $user_id);
|
|
$boards = !empty($topic['boards']) ? $topic['boards'] : [];
|
|
$board = !empty($boards[0]) ? $boards[0] : null;
|
|
|
|
return [
|
|
'topic' => $topic,
|
|
'board' => $board,
|
|
'permissions' => [
|
|
'can_edit' => $this->forum->can_edit_topic($user_id, $topic_id),
|
|
'can_moderate' => $this->forum->can_moderate_topic($user_id, $topic_id),
|
|
// 加锁语义统一到原生 comment_status(§7.3):comment_status=closed 或 _aksrt_locked=1 均不可回复。
|
|
'can_reply' => comments_open($topic_id) && empty($topic['locked']),
|
|
],
|
|
'board_options' => $this->forum->get_boards(),
|
|
'visibility_options' => $this->forum->get_visibility_options(),
|
|
'review_options' => $this->forum->get_review_options(),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get compose page view.
|
|
*/
|
|
public function get_compose_view(int $user_id, array $args = []): array {
|
|
$board_id = (int) ($args['board_id'] ?? 0);
|
|
|
|
return [
|
|
'boards' => $this->forum->get_boards(),
|
|
'selected_board_id' => $board_id,
|
|
'visibility_options' => $this->forum->get_visibility_options(),
|
|
'publish_mode' => $this->forum->can_user_publish_directly($user_id, $board_id) ? 'publish' : 'pending',
|
|
'draft_count' => $this->forum->get_user_draft_count($user_id),
|
|
'drafts' => $this->forum->get_user_drafts($user_id, 10),
|
|
'capabilities' => $this->forum->get_compose_capabilities($user_id),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get forum stats.
|
|
*/
|
|
public function get_stats(): array {
|
|
$boards = $this->forum->get_boards();
|
|
$topic_count = post_type_exists('aksrt_forum_topic')
|
|
? (int) (wp_count_posts('aksrt_forum_topic')->publish ?? 0)
|
|
: 0;
|
|
$pending_topic_count = post_type_exists('aksrt_forum_topic')
|
|
? (int) (wp_count_posts('aksrt_forum_topic')->pending ?? 0)
|
|
: 0;
|
|
|
|
global $wpdb;
|
|
$post_count = (int) $wpdb->get_var(
|
|
"SELECT COUNT(*) FROM {$wpdb->comments} c
|
|
INNER JOIN {$wpdb->posts} p ON c.comment_post_ID = p.ID
|
|
WHERE p.post_type = 'aksrt_forum_topic' AND c.comment_approved = '1'"
|
|
);
|
|
|
|
$moderator_count = 0;
|
|
$pending_application_count = 0;
|
|
foreach ($boards as $board) {
|
|
$moderator_count += (int) ($board['moderator_count'] ?? 0);
|
|
$pending_application_count += (int) ($board['pending_application_count'] ?? 0);
|
|
}
|
|
|
|
return [
|
|
'board_count' => count($boards),
|
|
'topic_count' => $topic_count,
|
|
'post_count' => $post_count,
|
|
'pending_topic_count' => $pending_topic_count,
|
|
'moderator_count' => $moderator_count,
|
|
'pending_application_count' => $pending_application_count,
|
|
];
|
|
}
|
|
|
|
public function get_sticky_topics_for_board(int $board_id): array {
|
|
$viewer_user_id = get_current_user_id();
|
|
|
|
// 先取本版块内的置顶帖(plate/super),再取全局置顶帖(跨版块)。
|
|
$query = new WP_Query([
|
|
'post_type' => 'aksrt_forum_topic',
|
|
'post_status' => 'publish',
|
|
'posts_per_page' => 20,
|
|
'ignore_sticky_posts' => true,
|
|
'no_found_rows' => true,
|
|
'orderby' => 'meta_value_num date',
|
|
'order' => 'DESC DESC',
|
|
'meta_query' => [
|
|
'relation' => 'OR',
|
|
[
|
|
'key' => '_aksrt_sticky',
|
|
'value' => [AKSRT_Forum::STICKY_PLATE, AKSRT_Forum::STICKY_SUPER],
|
|
'compare' => 'IN',
|
|
'type' => 'NUMERIC',
|
|
],
|
|
[
|
|
'key' => '_aksrt_sticky',
|
|
'value' => AKSRT_Forum::STICKY_GLOBAL,
|
|
'compare' => '=',
|
|
'type' => 'NUMERIC',
|
|
],
|
|
],
|
|
'tax_query' => [[
|
|
'taxonomy' => 'aksrt_forum_board',
|
|
'field' => 'term_id',
|
|
'terms' => $board_id,
|
|
]],
|
|
]);
|
|
|
|
$items = [];
|
|
while ($query->have_posts()) {
|
|
$query->the_post();
|
|
$post = get_post(get_the_ID());
|
|
if (!$post) {
|
|
continue;
|
|
}
|
|
|
|
$topic = $this->forum->build_topic_payload($post, $viewer_user_id);
|
|
if (!$topic['can_view']) {
|
|
continue;
|
|
}
|
|
$items[] = $topic;
|
|
}
|
|
wp_reset_postdata();
|
|
|
|
// 全局置顶帖可能不归属本版块,单独查询后合并去重。
|
|
$global = new WP_Query([
|
|
'post_type' => 'aksrt_forum_topic',
|
|
'post_status' => 'publish',
|
|
'posts_per_page' => 5,
|
|
'ignore_sticky_posts' => true,
|
|
'no_found_rows' => true,
|
|
'meta_query' => [[
|
|
'key' => '_aksrt_sticky',
|
|
'value' => AKSRT_Forum::STICKY_GLOBAL,
|
|
'compare' => '=',
|
|
'type' => 'NUMERIC',
|
|
]],
|
|
]);
|
|
|
|
$existing_ids = array_map(static fn($t) => (int) $t['id'], $items);
|
|
while ($global->have_posts()) {
|
|
$global->the_post();
|
|
$post = get_post(get_the_ID());
|
|
if (!$post || in_array((int) $post->ID, $existing_ids, true)) {
|
|
continue;
|
|
}
|
|
|
|
$topic = $this->forum->build_topic_payload($post, $viewer_user_id);
|
|
if (!$topic['can_view']) {
|
|
continue;
|
|
}
|
|
$items[] = $topic;
|
|
}
|
|
wp_reset_postdata();
|
|
|
|
// 按 sticky_level DESC, date DESC 排序(全局 > 超级 > 版块)。
|
|
usort($items, function (array $a, array $b): int {
|
|
$la = (int) ($a['sticky_level'] ?? 0);
|
|
$lb = (int) ($b['sticky_level'] ?? 0);
|
|
if ($la !== $lb) {
|
|
return $lb <=> $la;
|
|
}
|
|
return strcmp((string) $b['date'], (string) $a['date']);
|
|
});
|
|
|
|
return array_slice($items, 0, 5);
|
|
}
|
|
|
|
/**
|
|
* Get topic detail payload for admin.
|
|
*/
|
|
public function get_topic_detail(int $topic_id, int $viewer_user_id = 0): array {
|
|
$post = get_post($topic_id);
|
|
if (!$post || $post->post_type !== 'aksrt_forum_topic') {
|
|
return [
|
|
'topic' => null,
|
|
'options' => [
|
|
'boards' => $this->forum->get_boards(),
|
|
'visibility' => $this->forum->get_visibility_options(),
|
|
'review' => $this->forum->get_review_options(),
|
|
],
|
|
];
|
|
}
|
|
|
|
return [
|
|
'topic' => $this->forum->build_topic_payload($post, $viewer_user_id),
|
|
'options' => [
|
|
'boards' => $this->forum->get_boards(),
|
|
'visibility' => $this->forum->get_visibility_options(),
|
|
'review' => $this->forum->get_review_options(),
|
|
],
|
|
];
|
|
}
|
|
|
|
// ── Tags + Search (extracted from facade) ──
|
|
|
|
public function get_tags(): array {
|
|
$terms = get_terms([
|
|
'taxonomy' => 'aksrt_forum_tag',
|
|
'hide_empty' => true,
|
|
'orderby' => 'count',
|
|
'order' => 'DESC',
|
|
]);
|
|
|
|
if (is_wp_error($terms)) {
|
|
return [];
|
|
}
|
|
|
|
return array_map(static function ($term): array {
|
|
$url = get_term_link($term);
|
|
|
|
return [
|
|
'id' => (int) $term->term_id,
|
|
'name' => (string) $term->name,
|
|
'count' => (int) $term->count,
|
|
'url' => !is_wp_error($url) ? (string) $url : '',
|
|
];
|
|
}, $terms);
|
|
}
|
|
|
|
/**
|
|
* Get tag archive page payload.
|
|
*/
|
|
public function get_tag_archive_view(array $args = []): array {
|
|
$term_id = (int) ($args['term_id'] ?? 0);
|
|
$paged = max(1, (int) ($args['paged'] ?? 1));
|
|
$per_page = 20;
|
|
|
|
$term = get_term($term_id, 'aksrt_forum_tag');
|
|
if (is_wp_error($term) || !$term) {
|
|
return ['topics' => [], 'term' => null, 'total' => 0, 'max_pages' => 0];
|
|
}
|
|
|
|
$query = new WP_Query([
|
|
'post_type' => 'aksrt_forum_topic',
|
|
'posts_per_page' => $per_page,
|
|
'paged' => $paged,
|
|
'post_status' => 'publish',
|
|
'tax_query' => [[
|
|
'taxonomy' => 'aksrt_forum_tag',
|
|
'field' => 'term_id',
|
|
'terms' => $term_id,
|
|
]],
|
|
]);
|
|
|
|
$topics = [];
|
|
while ($query->have_posts()) {
|
|
$query->the_post();
|
|
$post = get_post(get_the_ID());
|
|
if ($post) {
|
|
$topics[] = $this->forum->build_topic_payload($post, get_current_user_id());
|
|
}
|
|
}
|
|
wp_reset_postdata();
|
|
|
|
return [
|
|
'topics' => $topics,
|
|
'term' => [
|
|
'id' => (int) $term->term_id,
|
|
'name' => (string) $term->name,
|
|
'description' => (string) $term->description,
|
|
'count' => (int) $term->count,
|
|
],
|
|
'total' => (int) $query->found_posts,
|
|
'max_pages' => (int) $query->max_num_pages,
|
|
];
|
|
}
|
|
|
|
public function increment_view_count(int $topic_id): void {
|
|
if ($topic_id <= 0) {
|
|
return;
|
|
}
|
|
|
|
$views = (int) get_post_meta($topic_id, '_aksrt_views', true);
|
|
update_post_meta($topic_id, '_aksrt_views', $views + 1);
|
|
}
|
|
|
|
public function ajax_search_boards(): void {
|
|
$search = sanitize_text_field((string) ($_GET['q'] ?? $_POST['q'] ?? ''));
|
|
$limit = min(50, max(5, (int) ($_GET['limit'] ?? 20)));
|
|
|
|
if ($this->forum->is_plate_cpt_active()) {
|
|
$args = [
|
|
'post_type' => 'aksrt_forum_plate',
|
|
'post_status' => 'publish',
|
|
'posts_per_page' => $limit,
|
|
'no_found_rows' => true,
|
|
'orderby' => 'title',
|
|
'order' => 'ASC',
|
|
];
|
|
if ($search !== '') {
|
|
$args['s'] = $search;
|
|
}
|
|
$query = new WP_Query($args);
|
|
$results = [];
|
|
while ($query->have_posts()) {
|
|
$query->the_post();
|
|
$p = get_post();
|
|
if ($p) {
|
|
$results[] = ['id' => $p->ID, 'name' => $p->post_title];
|
|
}
|
|
}
|
|
wp_reset_postdata();
|
|
} else {
|
|
$args = [
|
|
'taxonomy' => 'aksrt_forum_board',
|
|
'hide_empty' => false,
|
|
'number' => $limit,
|
|
'orderby' => 'name',
|
|
'order' => 'ASC',
|
|
];
|
|
if ($search !== '') {
|
|
$args['search'] = $search;
|
|
}
|
|
$terms = get_terms($args);
|
|
$results = [];
|
|
if (!is_wp_error($terms)) {
|
|
foreach ($terms as $term) {
|
|
$results[] = ['id' => (int) $term->term_id, 'name' => (string) $term->name];
|
|
}
|
|
}
|
|
}
|
|
|
|
wp_send_json_success($results);
|
|
}
|
|
|
|
public function ajax_search_tags(): void {
|
|
$search = sanitize_text_field((string) ($_GET['q'] ?? $_POST['q'] ?? ''));
|
|
$limit = min(50, max(5, (int) ($_GET['limit'] ?? 20)));
|
|
|
|
$args = [
|
|
'taxonomy' => 'aksrt_forum_tag',
|
|
'hide_empty' => false,
|
|
'number' => $limit,
|
|
'orderby' => 'count',
|
|
'order' => 'DESC',
|
|
];
|
|
if ($search !== '') {
|
|
$args['search'] = $search;
|
|
}
|
|
|
|
$terms = get_terms($args);
|
|
$results = [];
|
|
if (!is_wp_error($terms)) {
|
|
foreach ($terms as $term) {
|
|
$results[] = [
|
|
'id' => (int) $term->term_id,
|
|
'name' => (string) $term->name,
|
|
'count' => (int) $term->count,
|
|
];
|
|
}
|
|
}
|
|
|
|
wp_send_json_success($results);
|
|
}
|
|
}
|