'全部', 'sort' => 'date'], ['label' => '推荐', 'sort' => 'featured'], ['label' => '最新回复', 'sort' => 'last_reply'], ['label' => '热门', 'sort' => 'hot'], ['label' => '精华', 'sort' => 'essence'], ]; $allowed_sorts = []; foreach ($tabs as $tab) { $sort = sanitize_key((string) ($tab['sort'] ?? '')); if ($sort !== '') { $allowed_sorts[] = $sort; } } if ($orderby === '' || !in_array($orderby, $allowed_sorts, true)) { $active_index = max(0, (int) ($board_page['tab_active_index'] ?? 0)); $orderby = sanitize_key((string) ($tabs[$active_index]['sort'] ?? 'date')); if ($orderby === '') { $orderby = 'date'; } } $compose_url = function_exists('aksrt_get_forum_compose_url') ? aksrt_get_forum_compose_url(['board' => $board_id]) : home_url('/'); $top_info = !isset($board_page['top_info']) || !empty($board_page['top_info']); if ($this->forum->is_plate_cpt_active()) { $view = $this->get_plate_view_compat($board_id, $user_id, $page, $orderby); $view['tabs'] = $tabs; $view['active_sort'] = $orderby; $view['top_info'] = $top_info; $view['compose_url'] = $compose_url; return $view; } $board = get_term($board_id, 'aksrt_forum_board'); if (!$board || is_wp_error($board)) { return [ 'board' => null, 'topics' => [], 'sticky_topics' => [], 'moderators' => [], 'pagination' => ['page' => 1, 'max_pages' => 1, 'total' => 0], 'permissions' => [], 'application' => null, 'board_options' => [], 'tabs' => $tabs, 'active_sort' => $orderby, 'top_info' => $top_info, 'compose_url' => function_exists('aksrt_get_forum_compose_url') ? aksrt_get_forum_compose_url() : home_url('/'), ]; } $topics = $this->forum->get_topic_list([ 'board_id' => $board_id, 'page' => $page, 'limit' => 20, 'status' => 'publish', 'viewer_user_id' => $user_id, 'orderby' => $orderby, ]); $sticky_topics = $this->forum->get_sticky_topics_for_board($board_id); $board_link = get_term_link($board); return [ 'board' => [ 'id' => $board_id, 'name' => (string) $board->name, 'description' => (string) $board->description, 'content' => '', 'icon' => (string) get_term_meta($board_id, '_aksrt_icon', true), 'topic_count' => (int) $board->count, 'reply_count' => 0, 'today_reply_count' => 0, 'views' => 0, 'follow_count' => 0, 'last_reply' => '', 'last_post' => '', 'url' => !is_wp_error($board_link) ? (string) $board_link : '', 'thumbnail_url' => '', 'author_id' => 0, 'allow_view' => 'public', ], 'topics' => $topics['items'], 'sticky_topics' => array_slice($sticky_topics, 0, 5), 'moderators' => $this->forum->get_board_moderator_profiles($board_id), 'pagination' => [ 'page' => $topics['page'], 'max_pages' => $topics['max_pages'], 'total' => $topics['total'], ], 'permissions' => [ 'can_compose' => $user_id > 0, 'can_apply_moderator' => $user_id > 0 && !$this->forum->is_board_moderator($user_id, $board_id), 'can_moderate' => $this->forum->is_board_moderator($user_id, $board_id), ], 'application' => $user_id > 0 ? $this->forum->get_board_application_for_user($user_id, $board_id) : null, 'board_options' => $this->get_boards(), 'tabs' => $tabs, 'active_sort' => $orderby, 'top_info' => $top_info, 'compose_url' => $compose_url, ]; } /** * Plate CPT board view (backwards-compatible with get_board_view return shape). */ private function get_plate_view_compat(int $plate_id, int $user_id, int $page, string $orderby = 'date'): array { $plate = $this->get_plate($plate_id); if (!$plate) { return [ 'board' => null, 'topics' => [], 'sticky_topics' => [], 'moderators' => [], 'pagination' => ['page' => 1, 'max_pages' => 1, 'total' => 0], 'permissions' => [], 'application' => null, 'board_options' => [], ]; } // Check access permission $allow_view = $plate['allow_view'] ?? 'public'; if (!$this->can_view_plate($user_id, $plate_id, $allow_view)) { return [ 'board' => $plate, 'topics' => [], 'sticky_topics' => [], 'moderators' => $this->get_plate_moderator_profiles($plate_id), 'pagination' => ['page' => 1, 'max_pages' => 1, 'total' => 0], 'permissions' => [ 'can_compose' => false, 'can_apply_moderator' => false, 'can_moderate' => false, 'access_denied' => true, 'access_reason' => $allow_view, ], 'application' => null, 'board_options' => $this->get_boards(), ]; } $topics = $this->forum->get_topic_list([ 'plate_id' => $plate_id, 'page' => $page, 'limit' => 20, 'status' => 'publish', 'viewer_user_id' => $user_id, 'orderby' => $orderby, ]); $sticky_topics = $this->get_sticky_topics_for_plate($plate_id); return [ 'board' => $plate, 'topics' => $topics['items'], 'sticky_topics' => array_slice($sticky_topics, 0, 5), 'moderators' => $this->get_plate_moderator_profiles($plate_id), 'pagination' => [ 'page' => $topics['page'], 'max_pages' => $topics['max_pages'], 'total' => $topics['total'], ], 'permissions' => [ 'can_compose' => $user_id > 0 && $this->can_compose_in_plate($user_id, $plate_id), 'can_apply_moderator' => $user_id > 0 && $this->forum->forum_user_can($user_id, 'moderator_apply'), 'can_moderate' => $this->forum->forum_user_can($user_id, 'topic_moderate', $plate_id), 'can_manage_plate' => $this->forum->forum_user_can($user_id, 'plate_manage', $plate_id), 'can_manage_mods' => $this->forum->forum_user_can($user_id, 'moderator_manage', $plate_id), 'user_tier' => $this->forum->get_user_plate_tier($user_id, $plate_id), ], 'application' => $user_id > 0 ? $this->forum->get_board_application_for_user($user_id, $plate_id) : null, 'board_options' => $this->get_boards(), ]; } /** * Check if a user can view a plate (§7.2 板块阅读权限). */ public function can_view_plate(int $user_id, int $plate_id, string $allow_view = ''): bool { if ($allow_view === '') { $allow_view = (string) (get_post_meta($plate_id, '_aksrt_allow_view', true) ?: 'public'); } if ($allow_view === 'public') { return true; } if ($user_id <= 0) { return false; } if ($this->is_plate_moderator($user_id, $plate_id)) { return true; } if ($allow_view === 'login') { return true; } if ($allow_view === 'follow') { return $this->is_user_following_plate($user_id, $plate_id); } if ($allow_view === 'role') { $allowed_roles = get_post_meta($plate_id, '_aksrt_allow_view_roles', true); if (!is_array($allowed_roles) || empty($allowed_roles)) { return true; } $user = get_userdata($user_id); if (!$user) { return false; } return !empty(array_intersect($user->roles, $allowed_roles)); } return true; } /** * Check if a user can compose in a plate (§7.2 板块发帖限制). */ public function can_compose_in_plate(int $user_id, int $plate_id): bool { if ($user_id <= 0) { return false; } if ($this->is_plate_moderator($user_id, $plate_id)) { return true; } $add_limit = (string) (get_post_meta($plate_id, '_aksrt_add_limit', true) ?: 'all'); if ($add_limit === 'all') { return true; } $user = get_userdata($user_id); if (!$user) { return false; } if ($add_limit === 'role') { $allowed_roles = get_post_meta($plate_id, '_aksrt_add_limit_roles', true); if (!is_array($allowed_roles) || empty($allowed_roles)) { return true; } return !empty(array_intersect($user->roles, $allowed_roles)); } return true; } /** * Check if a user is following a plate (§7.2 关注). */ public function is_user_following_plate(int $user_id, int $plate_id): bool { if ($user_id <= 0) { return false; } $follow_list = get_user_meta($user_id, 'aksrt_follow_plate', true); if (!is_array($follow_list)) { return false; } return in_array($plate_id, $follow_list, true); } /** * Toggle user follow on a plate (§7.2 关注/取消关注). * * @return array{followed:bool,follow_count:int} */ public function toggle_follow_plate(int $user_id, int $plate_id): array { if ($user_id <= 0) { return ['followed' => false, 'follow_count' => 0]; } $follow_list = get_user_meta($user_id, 'aksrt_follow_plate', true); if (!is_array($follow_list)) { $follow_list = []; } $followed = in_array($plate_id, $follow_list, true); if ($followed) { $follow_list = array_values(array_diff($follow_list, [$plate_id])); } else { $follow_list[] = $plate_id; } update_user_meta($user_id, 'aksrt_follow_plate', array_values(array_unique(array_map('intval', $follow_list)))); // Update plate follow_count $count = $this->count_plate_followers($plate_id); update_post_meta($plate_id, '_aksrt_follow_count', $count); return [ 'followed' => !$followed, 'follow_count' => $count, ]; } /** * Count followers for a plate. */ private function count_plate_followers(int $plate_id): int { global $wpdb; return (int) $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->usermeta} WHERE meta_key = 'aksrt_follow_plate' AND meta_value LIKE %s", '%i:' . $plate_id . ';%' )); } /** * Get plates a user follows. */ public function get_user_followed_plates(int $user_id): array { $follow_list = get_user_meta($user_id, 'aksrt_follow_plate', true); if (!is_array($follow_list) || empty($follow_list)) { return []; } $plates = get_posts([ 'post_type' => 'aksrt_forum_plate', 'post__in' => array_map('intval', $follow_list), 'post_status' => 'publish', 'posts_per_page' => 100, 'orderby' => 'post__in', ]); $result = []; foreach ($plates as $plate) { $result[] = $this->build_plate_payload($plate); } return $result; } /** * Get a user's managed plates (plates where user is moderator/author). * * @return array{owned:array,moderating:array} */ public function get_user_managed_plates(int $user_id): array { $owned = []; $moderating = []; if (!$this->forum->is_plate_cpt_active()) { return ['owned' => $owned, 'moderating' => $moderating]; } // Plates where user is author (超级版主) $own_plates = get_posts([ 'post_type' => 'aksrt_forum_plate', 'author' => $user_id, 'post_status' => 'publish', 'posts_per_page' => 50, 'orderby' => 'title', 'order' => 'ASC', ]); foreach ($own_plates as $plate) { $owned[] = $this->build_plate_payload($plate); } // Plates where user is a moderator (in _aksrt_moderators list) global $wpdb; $mod_plate_ids = $wpdb->get_col($wpdb->prepare( "SELECT pm.post_id FROM {$wpdb->postmeta} pm INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id AND p.post_type = 'aksrt_forum_plate' AND p.post_status = 'publish' WHERE pm.meta_key = '_aksrt_moderators'", )); foreach ($mod_plate_ids as $plate_id) { $plate_id = (int) $plate_id; // Skip plates already in owned if (in_array($plate_id, wp_list_pluck($own_plates, 'ID'), true)) { continue; } $mods = get_post_meta($plate_id, '_aksrt_moderators', true); if (is_array($mods) && in_array($user_id, $mods, true)) { $plate = get_post($plate_id); if ($plate) { $moderating[] = $this->build_plate_payload($plate); } } } return ['owned' => $owned, 'moderating' => $moderating]; } // ── Plate utilities (extracted from facade) ── public function increment_plate_views(int $plate_id): void { if ($plate_id <= 0 || !$this->forum->is_plate_cpt_active()) { return; } $views = (int) get_post_meta($plate_id, '_aksrt_views', true); update_post_meta($plate_id, '_aksrt_views', $views + 1); } /** * Check if a plate qualifies as "hot" (§7.2 热门版块). */ public function is_hot_plate(int $plate_id): bool { $config = $this->forum->get_config(); $threshold = (int) ($config['other']['hot_plate_threshold'] ?? 100); $posts_count = (int) get_post_meta($plate_id, '_aksrt_posts_count', true); $follow_count = (int) get_post_meta($plate_id, '_aksrt_follow_count', true); return ($posts_count + $follow_count * 2) >= $threshold; } /** * Get plate categories (分区) with moderators. */ public function get_plate_categories(): array { $terms = get_terms([ 'taxonomy' => 'aksrt_plate_cat', 'hide_empty' => false, 'orderby' => 'name', 'order' => 'ASC', ]); if (is_wp_error($terms) || empty($terms)) { return []; } $categories = []; foreach ($terms as $term) { $term_id = (int) $term->term_id; $moderator_ids = get_term_meta($term_id, '_aksrt_cat_moderator', true); if (!is_array($moderator_ids)) { $moderator_ids = []; } $categories[] = [ 'id' => $term_id, 'name' => (string) $term->name, 'description' => (string) $term->description, 'slug' => (string) $term->slug, 'count' => (int) $term->count, 'parent_id' => (int) $term->parent, 'moderators' => array_values(array_unique(array_filter(array_map('intval', $moderator_ids)))), 'add_limit' => (string) (get_term_meta($term_id, '_aksrt_add_limit', true) ?: 'all'), 'url' => (string) get_term_link($term), ]; } return $categories; } }