Wire Zibll-style board sort tabs, shared topic rows, page-template URLs, forum asset enqueue on home/plate, home synthesis load-more, and topic last_reply meta for last-reply ordering. No dual-read.
411 lines
17 KiB
PHP
411 lines
17 KiB
PHP
<?php
|
|
/**
|
|
* Forum draft management service.
|
|
*
|
|
* Extracted from AKSRT_Forum to isolate draft CRUD, loading,
|
|
* and AJAX handlers into a cohesive unit.
|
|
*
|
|
* @package AKSRT
|
|
*/
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
class AKSRT_Forum_Draft_Service {
|
|
|
|
/** @var AKSRT_Forum */
|
|
private $forum;
|
|
|
|
public function __construct(AKSRT_Forum $forum) {
|
|
$this->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<int,array{id:int,title:string,date:string,board_name:string}>
|
|
*/
|
|
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()),
|
|
];
|
|
}
|
|
}
|