forum = $forum; } public function save_draft(int $user_id, array $data) { if ($user_id <= 0) { return new WP_Error('not_logged_in', __('请先登录', 'aksrt')); } $draft_id = (int) ($data['draft_id'] ?? 0); $post_arr = [ 'post_type' => 'aksrt_forum_topic', 'post_status' => 'draft', 'post_author' => $user_id, 'post_title' => sanitize_text_field((string) ($data['title'] ?? '')), 'post_content' => wp_kses_post((string) ($data['content'] ?? '')), ]; if ($draft_id > 0) { $existing = get_post($draft_id); if (!$existing || $existing->post_type !== 'aksrt_forum_topic' || (int) $existing->post_author !== $user_id || $existing->post_status !== 'draft') { return new WP_Error('invalid_draft', __('草稿不存在或无权编辑', 'aksrt')); } $post_arr['ID'] = $draft_id; $result = wp_update_post($post_arr, true); } else { $result = wp_insert_post($post_arr, true); } if (is_wp_error($result)) { return $result; } $post_id = (int) $result; $board_id = (int) ($data['board_id'] ?? 0); if ($board_id > 0) { if ($this->forum->is_plate_cpt_active()) { update_post_meta($post_id, '_aksrt_plate_id', $board_id); } else { wp_set_object_terms($post_id, [$board_id], 'aksrt_forum_board'); } } $tags = is_array($data['tags'] ?? null) ? $data['tags'] : []; if (!empty($tags)) { $clean_tags = array_slice(array_values(array_filter(array_map('sanitize_text_field', $tags))), 0, 5); wp_set_object_terms($post_id, $clean_tags, 'aksrt_forum_tag'); } $meta_fields = [ 'visibility' => '_aksrt_visibility', 'bbs_type' => '_aksrt_bbs_type', 'special_topic' => '_aksrt_special_topic', 'cover_type' => '_aksrt_cover_type', ]; foreach ($meta_fields as $key => $meta_key) { if (isset($data[$key])) { update_post_meta($post_id, $meta_key, sanitize_key((string) $data[$key])); } } if (isset($data['credits_cost'])) { update_post_meta($post_id, '_aksrt_credits_cost', max(0, (int) $data['credits_cost'])); } if (isset($data['pay_price'])) { update_post_meta($post_id, '_aksrt_pay_price', sanitize_text_field((string) $data['pay_price'])); } if (isset($data['visibility_roles']) && is_array($data['visibility_roles'])) { update_post_meta($post_id, '_aksrt_visibility_roles', array_map('sanitize_key', $data['visibility_roles'])); } $this->forum->save_cover_meta($post_id, $data); if (!empty($data['vote_enabled'])) { $vote_meta = $this->forum->prepare_vote_meta($data); $this->forum->save_vote_meta($post_id, $vote_meta); } if (isset($data['topic_cat'])) { $topic_cat = sanitize_text_field((string) $data['topic_cat']); if ($topic_cat !== '' && taxonomy_exists('aksrt_forum_topic_cat')) { wp_set_object_terms($post_id, [$topic_cat], 'aksrt_forum_topic_cat'); } } return [ 'post_id' => $post_id, 'message' => __('草稿已保存', 'aksrt'), ]; } public function get_user_draft_count(int $user_id): int { if ($user_id <= 0) { return 0; } global $wpdb; return (int) $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = 'aksrt_forum_topic' AND post_status = 'draft' AND post_author = %d", $user_id )); } /** * @return array */ public function get_user_drafts(int $user_id, int $limit = 20): array { if ($user_id <= 0) { return []; } $query = new WP_Query([ 'post_type' => 'aksrt_forum_topic', 'post_status' => 'draft', 'author' => $user_id, 'posts_per_page' => $limit, 'orderby' => 'modified', 'order' => 'DESC', 'no_found_rows' => true, ]); $drafts = []; while ($query->have_posts()) { $query->the_post(); $post = get_post(); if (!$post) { continue; } $board_name = ''; if ($this->forum->is_plate_cpt_active()) { $plate_id = (int) get_post_meta($post->ID, '_aksrt_plate_id', true); if ($plate_id > 0) { $board_name = get_the_title($plate_id); } } else { $terms = wp_get_post_terms($post->ID, 'aksrt_forum_board', ['fields' => 'names']); if (!is_wp_error($terms) && !empty($terms)) { $board_name = $terms[0]; } } $drafts[] = [ 'id' => $post->ID, 'title' => $post->post_title ?: __('(无标题)', 'aksrt'), 'date' => $post->post_modified, 'board_name' => $board_name, ]; } wp_reset_postdata(); return $drafts; } public function delete_draft(int $user_id, int $draft_id): bool { if ($user_id <= 0 || $draft_id <= 0) { return false; } $post = get_post($draft_id); if (!$post || $post->post_type !== 'aksrt_forum_topic' || (int) $post->post_author !== $user_id || $post->post_status !== 'draft') { return false; } return (bool) wp_delete_post($draft_id, true); } /** * Load a draft's full data for the compose form. */ public function load_draft_data(int $user_id, int $draft_id): ?array { if ($user_id <= 0 || $draft_id <= 0) { return null; } $post = get_post($draft_id); if (!$post || $post->post_type !== 'aksrt_forum_topic' || (int) $post->post_author !== $user_id || $post->post_status !== 'draft') { return null; } $board_id = 0; if ($this->forum->is_plate_cpt_active()) { $board_id = (int) get_post_meta($draft_id, '_aksrt_plate_id', true); } else { $terms = wp_get_post_terms($draft_id, 'aksrt_forum_board', ['fields' => 'ids']); if (!is_wp_error($terms) && !empty($terms)) { $board_id = (int) $terms[0]; } } $tags = wp_get_post_terms($draft_id, 'aksrt_forum_tag', ['fields' => 'names']); if (is_wp_error($tags)) { $tags = []; } $topic_cat = ''; if (taxonomy_exists('aksrt_forum_topic_cat')) { $cats = wp_get_post_terms($draft_id, 'aksrt_forum_topic_cat', ['fields' => 'names']); if (!is_wp_error($cats) && !empty($cats)) { $topic_cat = $cats[0]; } } $slide_raw = get_post_meta($draft_id, '_aksrt_featured_slide', true); return [ 'draft_id' => $draft_id, 'title' => $post->post_title, 'content' => $post->post_content, 'board_id' => $board_id, 'tags' => implode(', ', $tags), 'visibility' => get_post_meta($draft_id, '_aksrt_visibility', true) ?: AKSRT_Forum::VISIBILITY_PUBLIC, 'bbs_type' => get_post_meta($draft_id, '_aksrt_bbs_type', true) ?: AKSRT_Forum::BBS_TYPE_STANDARD, 'special_topic' => get_post_meta($draft_id, '_aksrt_special_topic', true) ?: '', 'cover_type' => get_post_meta($draft_id, '_aksrt_cover_type', true) ?: '', 'cover_image' => get_post_meta($draft_id, '_aksrt_cover_image', true) ?: '', 'featured_slide' => is_array($slide_raw) ? $slide_raw : [], 'featured_video' => get_post_meta($draft_id, '_aksrt_featured_video', true) ?: '', 'credits_cost' => (int) get_post_meta($draft_id, '_aksrt_credits_cost', true), 'pay_price' => get_post_meta($draft_id, '_aksrt_pay_price', true) ?: '', 'visibility_roles' => get_post_meta($draft_id, '_aksrt_visibility_roles', true) ?: [], 'topic_cat' => $topic_cat, 'vote_enabled' => get_post_meta($draft_id, '_aksrt_vote_enabled', true) === '1', 'vote_title' => get_post_meta($draft_id, '_aksrt_vote_title', true) ?: '', 'vote_multiple' => get_post_meta($draft_id, '_aksrt_vote_multiple', true) === '1', 'vote_type' => get_post_meta($draft_id, '_aksrt_vote_type', true) ?: 'standard', 'vote_expiry_days' => (int) get_post_meta($draft_id, '_aksrt_vote_expiry_days', true), 'vote_options' => $this->get_vote_options_text($draft_id), ]; } private function get_vote_options_text(int $post_id): string { $options = get_post_meta($post_id, '_aksrt_vote_options', true); if (!is_array($options)) { return ''; } return implode("\n", array_column($options, 'label')); } // ────────────────────────────────────────────────────────── // AJAX handlers // ────────────────────────────────────────────────────────── public function ajax_save_draft(): void { check_ajax_referer('aksrt_forum_compose', '_nonce'); $user_id = get_current_user_id(); if ($user_id <= 0) { wp_send_json_error(['message' => __('请先登录', 'aksrt')]); } $data = [ 'draft_id' => (int) ($_POST['draft_id'] ?? 0), 'board_id' => (int) ($_POST['board_id'] ?? 0), 'title' => sanitize_text_field((string) ($_POST['title'] ?? '')), 'content' => wp_kses_post((string) ($_POST['content'] ?? '')), 'tags' => $this->forum->split_tags((string) ($_POST['tags'] ?? '')), 'special_topic' => sanitize_text_field((string) ($_POST['special_topic'] ?? '')), 'visibility' => sanitize_key((string) ($_POST['visibility'] ?? AKSRT_Forum::VISIBILITY_PUBLIC)), 'bbs_type' => sanitize_key((string) ($_POST['bbs_type'] ?? AKSRT_Forum::BBS_TYPE_STANDARD)), 'topic_cat' => sanitize_text_field((string) ($_POST['topic_cat'] ?? '')), 'vote_enabled' => !empty($_POST['vote_enabled']), 'vote_title' => sanitize_text_field((string) ($_POST['vote_title'] ?? '')), 'vote_options_text' => sanitize_textarea_field((string) ($_POST['vote_options'] ?? '')), 'vote_multiple' => !empty($_POST['vote_multiple']), 'vote_type' => sanitize_key((string) ($_POST['vote_type'] ?? 'standard')), 'vote_expiry_days' => (int) ($_POST['vote_expiry_days'] ?? 0), 'cover_type' => sanitize_key((string) ($_POST['cover_type'] ?? '')), 'cover_image' => esc_url_raw((string) ($_POST['cover_image'] ?? '')), 'featured_slide' => (string) ($_POST['featured_slide'] ?? ''), 'featured_video' => esc_url_raw((string) ($_POST['featured_video'] ?? '')), 'credits_cost' => (int) ($_POST['credits_cost'] ?? 0), 'pay_price' => sanitize_text_field((string) ($_POST['pay_price'] ?? '')), 'visibility_roles' => isset($_POST['visibility_roles']) && is_array($_POST['visibility_roles']) ? array_map('sanitize_key', $_POST['visibility_roles']) : [], ]; $result = $this->save_draft($user_id, $data); if (is_wp_error($result)) { wp_send_json_error(['message' => $result->get_error_message()]); } wp_send_json_success($result); } public function ajax_load_draft(): void { check_ajax_referer('aksrt_forum_compose', '_nonce'); $user_id = get_current_user_id(); $draft_id = (int) ($_GET['draft_id'] ?? $_POST['draft_id'] ?? 0); if ($user_id <= 0) { wp_send_json_error(['message' => __('请先登录', 'aksrt')]); } $data = $this->load_draft_data($user_id, $draft_id); if (!$data) { wp_send_json_error(['message' => __('草稿不存在', 'aksrt')]); } wp_send_json_success($data); } public function ajax_delete_draft(): void { check_ajax_referer('aksrt_forum_compose', '_nonce'); $user_id = get_current_user_id(); $draft_id = (int) ($_POST['draft_id'] ?? 0); if ($user_id <= 0) { wp_send_json_error(['message' => __('请先登录', 'aksrt')]); } if (!$this->delete_draft($user_id, $draft_id)) { wp_send_json_error(['message' => __('删除失败', 'aksrt')]); } wp_send_json_success(['message' => __('草稿已删除', 'aksrt')]); } // ────────────────────────────────────────────────────────── // Publish draft (transition from draft → publish/pending) // ────────────────────────────────────────────────────────── public function publish_draft(int $user_id, int $draft_id, array $data) { if ($user_id <= 0) { return new WP_Error('not_logged_in', __('请先登录', 'aksrt')); } $post = get_post($draft_id); if (!$post || $post->post_type !== 'aksrt_forum_topic' || (int) $post->post_author !== $user_id || $post->post_status !== 'draft') { return new WP_Error('invalid_draft', __('草稿不存在', 'aksrt')); } $data['draft_id'] = $draft_id; $save_result = $this->save_draft($user_id, $data); if (is_wp_error($save_result)) { return $save_result; } $board_id = (int) ($data['board_id'] ?? 0); $title = sanitize_text_field((string) ($data['title'] ?? '')); $content = wp_kses_post((string) ($data['content'] ?? '')); if ($board_id <= 0 || $title === '' || $content === '') { return new WP_Error('invalid_payload', __('请填写版块、标题和内容', 'aksrt')); } $can_publish = $this->forum->can_user_publish_directly($user_id, $board_id); $post_status = $can_publish ? 'publish' : 'pending'; $review_status = $can_publish ? AKSRT_Forum::REVIEW_APPROVED : AKSRT_Forum::REVIEW_PENDING; wp_update_post([ 'ID' => $draft_id, 'post_status' => $post_status, ]); update_post_meta($draft_id, '_aksrt_review_status', $review_status); update_post_meta($draft_id, '_aksrt_sticky', AKSRT_Forum::STICKY_NONE); update_post_meta($draft_id, '_aksrt_featured', '0'); update_post_meta($draft_id, '_aksrt_locked', '0'); update_post_meta($draft_id, '_aksrt_views', 0); update_post_meta($draft_id, '_aksrt_last_reply', (string) get_post_field('post_date', $draft_id)); $bbs_type = get_post_meta($draft_id, '_aksrt_bbs_type', true) ?: AKSRT_Forum::BBS_TYPE_STANDARD; if ($bbs_type === AKSRT_Forum::BBS_TYPE_QUESTION) { update_post_meta($draft_id, '_aksrt_question_status', AKSRT_Forum::QUESTION_UNSOLVED); } if ($post_status === 'publish') { $this->forum->sync_board_count($board_id); if (class_exists('AKSRT_Credits_Earn')) { do_action('aksrt_forum_topic_created', $user_id, $draft_id); } } if ($post_status === 'pending') { $this->forum->notify_topic_pending($draft_id, $user_id); } return [ 'post_id' => $draft_id, 'message' => $post_status === 'publish' ? __('主题已发布', 'aksrt') : __('主题已提交审核,审核通过后对外展示', 'aksrt'), 'redirect' => $post_status === 'publish' ? get_permalink($draft_id) : add_query_arg('compose_status', 'pending', aksrt_get_forum_home_url()), ]; } }