- Forum compose/save/draft: VIP prices, partial hide, coupons + shortcode tools - Rainbow clogin aggregate login (rewrite + schema + CLoginClient) - Native ban appeals tab: approve/reject + lift ban + notify - WidgetPostsNavs + register missing forum widgets - Submit editor shortcodes: login/reply/vip/payshow
442 lines
19 KiB
PHP
442 lines
19 KiB
PHP
<?php
|
|
/**
|
|
* @package AKSRT
|
|
*/
|
|
|
|
namespace AKSRT\Forum;
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
class DraftService {
|
|
|
|
/** @var Forum */
|
|
private $forum;
|
|
|
|
public function __construct(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'])) {
|
|
$cost = max(0, (int) $data['credits_cost']);
|
|
update_post_meta($post_id, '_aksrt_credits_cost', $cost);
|
|
update_post_meta($post_id, '_aksrt_pay_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['credits_vip1']) && (string) $data['credits_vip1'] !== '') {
|
|
update_post_meta($post_id, '_aksrt_credits_vip1', max(0, (int) $data['credits_vip1']));
|
|
}
|
|
if (isset($data['credits_vip2']) && (string) $data['credits_vip2'] !== '') {
|
|
update_post_meta($post_id, '_aksrt_credits_vip2', max(0, (int) $data['credits_vip2']));
|
|
}
|
|
if (isset($data['pay_vip1_price']) && (string) $data['pay_vip1_price'] !== '') {
|
|
$v1 = max(0, (float) $data['pay_vip1_price']);
|
|
update_post_meta($post_id, '_aksrt_pay_vip1_price', $v1);
|
|
update_post_meta($post_id, '_aksrt_pay_vip_price', $v1);
|
|
}
|
|
if (isset($data['pay_vip2_price']) && (string) $data['pay_vip2_price'] !== '') {
|
|
update_post_meta($post_id, '_aksrt_pay_vip2_price', max(0, (float) $data['pay_vip2_price']));
|
|
}
|
|
if (array_key_exists('pay_hide_part', $data)) {
|
|
update_post_meta($post_id, '_aksrt_pay_hide_part', !empty($data['pay_hide_part']) ? 1 : 0);
|
|
}
|
|
if (array_key_exists('topic_coupon_s', $data)) {
|
|
update_post_meta($post_id, '_aksrt_topic_coupon_s', !empty($data['topic_coupon_s']) ? 1 : 0);
|
|
}
|
|
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) ?: Forum::VISIBILITY_PUBLIC,
|
|
'bbs_type' => get_post_meta($draft_id, '_aksrt_bbs_type', true) ?: 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) ?: '',
|
|
'credits_vip1' => get_post_meta($draft_id, '_aksrt_credits_vip1', true),
|
|
'credits_vip2' => get_post_meta($draft_id, '_aksrt_credits_vip2', true),
|
|
'pay_vip1_price' => get_post_meta($draft_id, '_aksrt_pay_vip1_price', true),
|
|
'pay_vip2_price' => get_post_meta($draft_id, '_aksrt_pay_vip2_price', true),
|
|
'pay_hide_part' => (int) get_post_meta($draft_id, '_aksrt_pay_hide_part', true) === 1,
|
|
'topic_coupon_s' => (int) get_post_meta($draft_id, '_aksrt_topic_coupon_s', true) === 1,
|
|
'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'] ?? Forum::VISIBILITY_PUBLIC)),
|
|
'bbs_type' => sanitize_key((string) ($_POST['bbs_type'] ?? 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'] ?? '')),
|
|
'credits_vip1' => isset($_POST['credits_vip1']) ? sanitize_text_field((string) $_POST['credits_vip1']) : '',
|
|
'credits_vip2' => isset($_POST['credits_vip2']) ? sanitize_text_field((string) $_POST['credits_vip2']) : '',
|
|
'pay_vip1_price' => isset($_POST['pay_vip1_price']) ? sanitize_text_field((string) $_POST['pay_vip1_price']) : '',
|
|
'pay_vip2_price' => isset($_POST['pay_vip2_price']) ? sanitize_text_field((string) $_POST['pay_vip2_price']) : '',
|
|
'pay_hide_part' => !empty($_POST['pay_hide_part']),
|
|
'topic_coupon_s' => !empty($_POST['topic_coupon_s']),
|
|
'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 ? Forum::REVIEW_APPROVED : 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', 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) ?: Forum::BBS_TYPE_STANDARD;
|
|
if ($bbs_type === Forum::BBS_TYPE_QUESTION) {
|
|
update_post_meta($draft_id, '_aksrt_question_status', 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()),
|
|
];
|
|
}
|
|
}
|