Files
wordpress-theme-aksrt/inc/classes/forum/view-builder-traits/trait-aksrt-forum-view-builder-part-1.php
T

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);
}
}