forum = $forum; } /** * Get board list (plate CPT). * * 读取 aksrt_forum_plate CPT,支持层级查询。 * Fallback:若 plate CPT 不存在或迁移未完成,降级读取 taxonomy。 */ public function get_boards(array $args = []): array { // 优先使用 plate CPT if ($this->forum->is_plate_cpt_active()) { return $this->get_boards_from_cpt($args); } // Fallback: 读取旧 taxonomy(向后兼容) return $this->get_boards_from_taxonomy(); } /** * 从 plate CPT 读取板块列表(新实现)。 */ private function get_boards_from_cpt(array $args = []): array { $defaults = [ 'post_type' => 'aksrt_forum_plate', 'post_status' => 'publish', 'posts_per_page' => -1, 'orderby' => 'menu_order', 'order' => 'ASC', 'hierarchical' => true, ]; $query_args = wp_parse_args($args, $defaults); $plates = get_posts($query_args); if (empty($plates)) { return []; } $boards = []; foreach ($plates as $plate) { $plate_id = (int) $plate->ID; $moderators = $this->forum->get_plate_moderators($plate_id); $applications = $this->forum->get_plate_moderator_applications($plate_id); $boards[] = [ 'id' => $plate_id, 'name' => (string) $plate->post_title, 'description' => (string) $plate->post_excerpt, 'content' => (string) $plate->post_content, 'icon' => (string) get_post_meta($plate_id, '_aksrt_icon', true), 'sort_order' => (int) $plate->menu_order, 'post_count' => (int) get_post_meta($plate_id, '_aksrt_posts_count', true), 'reply_count' => (int) get_post_meta($plate_id, '_aksrt_reply_count', true), 'today_reply_count' => (int) get_post_meta($plate_id, '_aksrt_today_reply_count', true), 'views' => (int) get_post_meta($plate_id, '_aksrt_views', true), 'follow_count' => (int) get_post_meta($plate_id, '_aksrt_follow_count', true), 'last_reply' => (string) get_post_meta($plate_id, '_aksrt_last_reply', true), 'last_post' => (string) get_post_meta($plate_id, '_aksrt_last_post', true), 'moderator_count' => count($moderators), 'pending_application_count' => count(array_filter($applications, static function ($item): bool { return (string) ($item['status'] ?? '') === AKSRT_Forum::APPLICATION_PENDING; })), 'url' => (string) get_permalink($plate), 'author_id' => (int) $plate->post_author, 'parent_id' => (int) $plate->post_parent, 'created_at' => (string) $plate->post_date, 'thumbnail_url' => (string) get_the_post_thumbnail_url($plate, 'thumbnail'), ]; } return $this->build_hierarchical_boards($boards); } /** * 从 taxonomy 读取板块(向后兼容)。 */ private function get_boards_from_taxonomy(): array { $terms = get_terms([ 'taxonomy' => 'aksrt_forum_board', 'hide_empty' => false, 'orderby' => 'meta_value_num', 'meta_key' => '_aksrt_sort_order', 'order' => 'ASC', ]); if (is_wp_error($terms) || empty($terms)) { return []; } $boards = []; foreach ($terms as $term) { $board_id = (int) $term->term_id; $moderators = $this->forum->get_board_moderators($board_id); $applications = $this->forum->get_board_moderator_applications($board_id); $board_link = get_term_link($term); $boards[] = [ 'id' => $board_id, 'name' => (string) $term->name, 'description' => (string) $term->description, 'content' => '', 'icon' => (string) get_term_meta($board_id, '_aksrt_icon', true), 'sort_order' => (int) get_term_meta($board_id, '_aksrt_sort_order', true), 'post_count' => (int) $term->count, 'reply_count' => 0, 'today_reply_count' => 0, 'views' => 0, 'follow_count' => 0, 'last_reply' => '', 'last_post' => '', 'moderator_count' => count($moderators), 'pending_application_count' => count(array_filter($applications, static function ($item): bool { return (string) ($item['status'] ?? '') === AKSRT_Forum::APPLICATION_PENDING; })), 'url' => !is_wp_error($board_link) ? (string) $board_link : '', 'author_id' => 0, 'parent_id' => (int) $term->parent, 'created_at' => '', 'thumbnail_url' => '', ]; } return $boards; } /** * 构建层级板块树(子板块嵌套)。 */ private function build_hierarchical_boards(array $boards): array { $by_id = []; foreach ($boards as $board) { $by_id[$board['id']] = $board; $by_id[$board['id']]['children'] = []; } $roots = []; foreach ($boards as $board) { if ($board['parent_id'] > 0 && isset($by_id[$board['parent_id']])) { $by_id[$board['parent_id']]['children'][] = &$by_id[$board['id']]; } else { $roots[] = &$by_id[$board['id']]; } } return $roots; } /** * Create board (统一入口,自动选择 CPT/taxonomy). */ public function create_board(array $data): int { if ($this->forum->is_plate_cpt_active()) { return $this->create_plate($data); } // Fallback: taxonomy $name = sanitize_text_field((string) ($data['name'] ?? '')); if ($name === '') { return 0; } $result = wp_insert_term($name, 'aksrt_forum_board', [ 'description' => sanitize_textarea_field((string) ($data['description'] ?? '')), ]); if (is_wp_error($result)) { return 0; } $term_id = (int) $result['term_id']; $icon = sanitize_text_field((string) ($data['icon'] ?? '')); $sort_order = (int) ($data['sort_order'] ?? $term_id); if ($icon !== '') { update_term_meta($term_id, '_aksrt_icon', $icon); } update_term_meta($term_id, '_aksrt_sort_order', $sort_order); return $term_id; } /** * Update board (统一入口,自动选择 CPT/taxonomy). */ public function update_board(int $board_id, array $data): bool { if ($this->forum->is_plate_cpt_active()) { return $this->update_plate($board_id, $data); } // Fallback: taxonomy $term = get_term($board_id, 'aksrt_forum_board'); if (!$term || is_wp_error($term)) { return false; } $update = ['taxonomy' => 'aksrt_forum_board']; if (isset($data['name'])) { $name = sanitize_text_field((string) $data['name']); if ($name !== '') { $update['name'] = $name; } } if (isset($data['description'])) { $update['description'] = sanitize_textarea_field((string) $data['description']); } $result = wp_update_term($board_id, 'aksrt_forum_board', $update); if (is_wp_error($result)) { return false; } if (isset($data['icon'])) { update_term_meta($board_id, '_aksrt_icon', sanitize_text_field((string) $data['icon'])); } if (isset($data['sort_order'])) { update_term_meta($board_id, '_aksrt_sort_order', (int) $data['sort_order']); } return true; } /** * Delete board (统一入口,自动选择 CPT/taxonomy). */ public function delete_board(int $board_id): bool { if ($this->forum->is_plate_cpt_active()) { return $this->delete_plate($board_id); } // Fallback: taxonomy $term = get_term($board_id, 'aksrt_forum_board'); if (!$term || is_wp_error($term)) { return false; } $result = wp_delete_term($board_id, 'aksrt_forum_board'); return !is_wp_error($result) && !empty($result); } /** * Reorder boards (统一入口). */ public function reorder_boards(array $order_map): bool { if ($this->forum->is_plate_cpt_active()) { return $this->reorder_plates($order_map); } foreach ($order_map as $term_id => $sort_order) { $term_id = (int) $term_id; if ($term_id <= 0) { continue; } update_term_meta($term_id, '_aksrt_sort_order', (int) $sort_order); } return true; } // ================================================================ // §7.0 Plate CPT methods —板块 CPT 化 CRUD // ================================================================ /** * Get all plates (板块 CPT posts). * * @return array> */ public function get_plates(array $args = []): array { $query_args = [ 'post_type' => 'aksrt_forum_plate', 'post_status' => 'publish', 'posts_per_page' => (int) ($args['limit'] ?? 100), 'orderby' => 'menu_order', 'order' => 'ASC', ]; if (!empty($args['parent'])) { $query_args['post_parent'] = (int) $args['parent']; } if (!empty($args['plate_cat'])) { $query_args['tax_query'] = [[ 'taxonomy' => 'aksrt_plate_cat', 'field' => 'term_id', 'terms' => (int) $args['plate_cat'], ]]; } $posts = get_posts($query_args); $plates = []; foreach ($posts as $post) { $plates[] = $this->build_plate_payload($post); } return $plates; } /** * Get single plate by ID. */ public function get_plate(int $plate_id): ?array { $post = get_post($plate_id); if (!$post || $post->post_type !== 'aksrt_forum_plate') { return null; } return $this->build_plate_payload($post); } /** * Build plate data payload from WP_Post. */ public function build_plate_payload(\WP_Post $post): array { $plate_id = (int) $post->ID; $moderators = get_post_meta($plate_id, '_aksrt_moderators', true); if (!is_array($moderators)) { $moderators = []; } $cover_url = (string) get_the_post_thumbnail_url($plate_id, 'medium'); if ($cover_url === '') { $config = $this->forum->get_config(); $default_thumbs = $config['other']['bbs_term_thumb'] ?? []; if (!empty($default_thumbs) && is_array($default_thumbs)) { $cover_url = $default_thumbs[array_rand($default_thumbs)]; } } $plate_cats = wp_get_object_terms($plate_id, 'aksrt_plate_cat', ['fields' => 'all']); $plate_cat = (!is_wp_error($plate_cats) && !empty($plate_cats)) ? [ 'id' => (int) $plate_cats[0]->term_id, 'name' => (string) $plate_cats[0]->name, 'slug' => (string) $plate_cats[0]->slug, ] : null; return [ 'id' => $plate_id, 'name' => (string) $post->post_title, 'description' => (string) $post->post_excerpt, 'content' => (string) $post->post_content, 'icon' => (string) get_post_meta($plate_id, '_aksrt_icon', true), 'cover_url' => $cover_url, 'sort_order' => (int) $post->menu_order, 'parent_id' => (int) $post->post_parent, 'author_id' => (int) $post->post_author, 'post_count' => (int) get_post_meta($plate_id, '_aksrt_posts_count', true), 'reply_count' => (int) get_post_meta($plate_id, '_aksrt_reply_count', true), 'today_reply_count' => (int) get_post_meta($plate_id, '_aksrt_today_reply_count', true), 'views' => (int) get_post_meta($plate_id, '_aksrt_views', true), 'follow_count' => (int) get_post_meta($plate_id, '_aksrt_follow_count', true), 'last_reply' => (string) get_post_meta($plate_id, '_aksrt_last_reply', true), 'last_post' => (string) get_post_meta($plate_id, '_aksrt_last_post', true), 'moderator_count' => count($moderators), 'moderators' => $moderators, 'url' => (string) get_permalink($plate_id), 'created_at' => (string) $post->post_date, 'allow_view' => (string) (get_post_meta($plate_id, '_aksrt_allow_view', true) ?: 'public'), 'add_limit' => (string) (get_post_meta($plate_id, '_aksrt_add_limit', true) ?: 'all'), 'is_hot' => $this->forum->is_hot_plate($plate_id), 'plate_cat' => $plate_cat, // Alias fields for backward compat with get_board_view consumer 'topic_count' => (int) get_post_meta($plate_id, '_aksrt_posts_count', true), 'thumbnail_url' => $cover_url, ]; } /** * Create plate (CPT post). */ }