'aksrt_forum_plate', 'post_title' => $title, 'post_content' => wp_kses_post((string) ($data['content'] ?? '')), 'post_excerpt' => sanitize_textarea_field((string) ($data['description'] ?? '')), 'post_status' => 'publish', 'post_parent' => (int) ($data['parent_id'] ?? 0), 'post_author' => (int) ($data['author_id'] ?? get_current_user_id()), 'menu_order' => (int) ($data['sort_order'] ?? 0), ], true); if (is_wp_error($plate_id)) { return 0; } $icon = sanitize_text_field((string) ($data['icon'] ?? '')); if ($icon !== '') { update_post_meta($plate_id, '_aksrt_icon', $icon); } update_post_meta($plate_id, '_aksrt_sort_order', (int) ($data['sort_order'] ?? 0)); update_post_meta($plate_id, '_aksrt_posts_count', 0); update_post_meta($plate_id, '_aksrt_reply_count', 0); update_post_meta($plate_id, '_aksrt_follow_count', 0); update_post_meta($plate_id, '_aksrt_plate_views', 0); if (!empty($data['plate_cat'])) { wp_set_object_terms($plate_id, [(int) $data['plate_cat']], 'aksrt_plate_cat'); } // Also create a corresponding taxonomy term for backward compat $term_result = wp_insert_term($title, 'aksrt_forum_board', [ 'description' => sanitize_textarea_field((string) ($data['description'] ?? '')), ]); if (!is_wp_error($term_result)) { $term_id = (int) $term_result['term_id']; update_term_meta($term_id, '_aksrt_sort_order', (int) ($data['sort_order'] ?? 0)); if ($icon !== '') { update_term_meta($term_id, '_aksrt_icon', $icon); } update_post_meta($plate_id, '_aksrt_migrated_from_term', $term_id); } return $plate_id; } /** * Update plate. */ public function update_plate(int $plate_id, array $data): bool { $post = get_post($plate_id); if (!$post || $post->post_type !== 'aksrt_forum_plate') { return false; } $update = ['ID' => $plate_id]; if (isset($data['name'])) { $name = sanitize_text_field((string) $data['name']); if ($name !== '') { $update['post_title'] = $name; } } if (isset($data['description'])) { $update['post_excerpt'] = sanitize_textarea_field((string) $data['description']); } if (isset($data['content'])) { $update['post_content'] = wp_kses_post((string) $data['content']); } if (isset($data['parent_id'])) { $update['post_parent'] = (int) $data['parent_id']; } if (isset($data['sort_order'])) { $update['menu_order'] = (int) $data['sort_order']; update_post_meta($plate_id, '_aksrt_sort_order', (int) $data['sort_order']); } if (isset($data['author_id'])) { $update['post_author'] = (int) $data['author_id']; } $result = wp_update_post($update, true); if (is_wp_error($result)) { return false; } if (isset($data['icon'])) { update_post_meta($plate_id, '_aksrt_icon', sanitize_text_field((string) $data['icon'])); } if (isset($data['allow_view'])) { $allowed_views = ['public', 'login', 'role', 'follow']; $view = in_array($data['allow_view'], $allowed_views, true) ? $data['allow_view'] : 'public'; update_post_meta($plate_id, '_aksrt_allow_view', $view); } // Sync to legacy taxonomy term if exists $term_id = (int) get_post_meta($plate_id, '_aksrt_migrated_from_term', true); if ($term_id > 0) { $term_update = []; if (isset($update['post_title'])) { $term_update['name'] = $update['post_title']; } if (isset($update['post_excerpt'])) { $term_update['description'] = $update['post_excerpt']; } if (!empty($term_update)) { wp_update_term($term_id, 'aksrt_forum_board', $term_update); } if (isset($data['icon'])) { update_term_meta($term_id, '_aksrt_icon', sanitize_text_field((string) $data['icon'])); } if (isset($data['sort_order'])) { update_term_meta($term_id, '_aksrt_sort_order', (int) $data['sort_order']); } } return true; } /** * Delete plate (with cascade option). */ public function delete_plate(int $plate_id, string $cascade = 'trash', int $move_to = 0): bool { $post = get_post($plate_id); if (!$post || $post->post_type !== 'aksrt_forum_plate') { return false; } $topic_ids = get_posts([ 'post_type' => 'aksrt_forum_topic', 'meta_key' => '_aksrt_plate_id', 'meta_value' => $plate_id, 'posts_per_page' => -1, 'fields' => 'ids', 'post_status' => 'any', ]); if (!empty($topic_ids)) { if ($cascade === 'move' && $move_to > 0 && $move_to !== $plate_id) { $target = get_post($move_to); if ($target && $target->post_type === 'aksrt_forum_plate') { foreach ($topic_ids as $tid) { update_post_meta((int) $tid, '_aksrt_plate_id', $move_to); } $this->forum->sync_plate_counts($move_to); } } else { foreach ($topic_ids as $tid) { wp_trash_post((int) $tid); } } } // Handle child plates (move to top-level) $children = get_posts([ 'post_type' => 'aksrt_forum_plate', 'post_parent' => $plate_id, 'posts_per_page' => -1, 'fields' => 'ids', 'post_status' => 'any', ]); foreach ($children as $child_id) { wp_update_post(['ID' => (int) $child_id, 'post_parent' => 0]); } // Remove legacy taxonomy term if linked $term_id = (int) get_post_meta($plate_id, '_aksrt_migrated_from_term', true); if ($term_id > 0) { wp_delete_term($term_id, 'aksrt_forum_board'); } wp_delete_post($plate_id, true); return true; } /** * Reorder plates. */ public function reorder_plates(array $order_map): bool { foreach ($order_map as $plate_id => $sort_order) { $plate_id = (int) $plate_id; if ($plate_id <= 0) continue; wp_update_post(['ID' => $plate_id, 'menu_order' => (int) $sort_order]); update_post_meta($plate_id, '_aksrt_sort_order', (int) $sort_order); } return true; } /** * Resolve plate_id for a topic (reads _aksrt_plate_id meta, falls back to taxonomy). */ public function get_topic_plate_id(int $topic_id): int { $plate_id = (int) get_post_meta($topic_id, '_aksrt_plate_id', true); if ($plate_id > 0) { return $plate_id; } // Fallback: find plate via legacy taxonomy term $terms = get_the_terms($topic_id, 'aksrt_forum_board'); if (!$terms || is_wp_error($terms)) { return 0; } $term = $terms[0]; // Look up plate post by migrated_from_term meta $plates = get_posts([ 'post_type' => 'aksrt_forum_plate', 'meta_key' => '_aksrt_migrated_from_term', 'meta_value' => (string) $term->term_id, 'numberposts' => 1, 'fields' => 'ids', 'post_status' => 'publish', ]); if (!empty($plates)) { $resolved = (int) $plates[0]; update_post_meta($topic_id, '_aksrt_plate_id', $resolved); return $resolved; } return 0; } /** * Check if a user is a plate moderator (CPT-based). */ public function is_plate_moderator(int $user_id, int $plate_id): bool { if ($user_id <= 0 || $plate_id <= 0) { return false; } if ($this->forum->is_forum_admin($user_id)) { return true; } $post = get_post($plate_id); if ($post && (int) $post->post_author === $user_id) { return true; } $moderators = get_post_meta($plate_id, '_aksrt_moderators', true); if (is_array($moderators) && in_array($user_id, $moderators, true)) { return true; } return false; } /** * Get plate view (topics + moderators + pagination) for frontend. */ public function get_plate_view(int $plate_id, array $args = []): array { $plate = $this->get_plate($plate_id); if (!$plate) { return [ 'plate' => null, 'topics' => [], 'sticky_topics' => [], 'moderators' => [], 'pagination' => ['page' => 1, 'max_pages' => 1, 'total' => 0], 'permissions' => [], 'children' => [], ]; } $user_id = (int) ($args['user_id'] ?? get_current_user_id()); $paged = max(1, (int) ($args['paged'] ?? 1)); $topics = $this->forum->get_topic_list([ 'plate_id' => $plate_id, 'page' => $paged, 'limit' => 20, ]); // Global + super + plate sticky $sticky_topics = $this->get_sticky_topics_for_plate($plate_id); $children = get_posts([ 'post_type' => 'aksrt_forum_plate', 'post_parent' => $plate_id, 'post_status' => 'publish', 'posts_per_page' => 50, 'orderby' => 'menu_order', 'order' => 'ASC', ]); $child_plates = []; foreach ($children as $child) { $child_plates[] = $this->build_plate_payload($child); } return [ 'plate' => $plate, 'topics' => $topics['items'], 'sticky_topics' => $sticky_topics, '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), ], 'children' => $child_plates, ]; } /** * Get sticky topics for a plate (global + super + plate-level). */ private function get_sticky_topics_for_plate(int $plate_id): array { $global_sticky = get_posts([ 'post_type' => 'aksrt_forum_topic', 'post_status' => 'publish', 'meta_key' => '_aksrt_sticky', 'meta_value' => AKSRT_Forum::STICKY_GLOBAL, 'posts_per_page' => 5, 'orderby' => 'date', 'order' => 'DESC', ]); $plate_sticky = get_posts([ 'post_type' => 'aksrt_forum_topic', 'post_status' => 'publish', 'posts_per_page' => 10, 'orderby' => 'date', 'order' => 'DESC', 'meta_query' => [ 'relation' => 'AND', ['key' => '_aksrt_plate_id', 'value' => $plate_id, 'type' => 'NUMERIC'], ['key' => '_aksrt_sticky', 'value' => 0, 'compare' => '>', 'type' => 'NUMERIC'], ['key' => '_aksrt_sticky', 'value' => AKSRT_Forum::STICKY_GLOBAL, 'compare' => '<', 'type' => 'NUMERIC'], ], ]); $items = []; $seen = []; foreach (array_merge($global_sticky, $plate_sticky) as $post) { if (isset($seen[$post->ID])) continue; $seen[$post->ID] = true; $items[] = $this->forum->build_topic_payload($post); } return array_slice($items, 0, 10); } /** * Get moderator user profiles for a plate. */ private function get_plate_moderator_profiles(int $plate_id): array { $post = get_post($plate_id); $profiles = []; // Author = super moderator if ($post && $post->post_author > 0) { $user = get_userdata((int) $post->post_author); if ($user) { $profiles[] = [ 'id' => (int) $user->ID, 'name' => $user->display_name, 'avatar' => function_exists('aksrt_get_avatar_url') ? aksrt_get_avatar_url($user->ID, 40) : get_avatar_url($user->ID, ['size' => 40]), 'role' => 'plate_author', ]; } } $moderators = get_post_meta($plate_id, '_aksrt_moderators', true); if (!is_array($moderators)) { return $profiles; } $roles = get_post_meta($plate_id, '_aksrt_moderator_roles', true); if (!is_array($roles)) { $roles = []; } foreach ($moderators as $uid) { $uid = (int) $uid; if ($post && $uid === (int) $post->post_author) continue; $user = get_userdata($uid); if (!$user) continue; $profiles[] = [ 'id' => $uid, 'name' => $user->display_name, 'avatar' => function_exists('aksrt_get_avatar_url') ? aksrt_get_avatar_url($uid, 40) : get_avatar_url($uid, ['size' => 40]), 'role' => (string) ($roles[$uid] ?? 'moderator'), ]; } return $profiles; } }