roles; if (empty(array_intersect($user_roles, (array) $allowed_roles))) { // Also allow administrators if (!in_array('administrator', $user_roles, true)) { return false; } } } // Credits threshold: if configured, user must have enough credits $min_credits = (int) ($submit_settings['submit_min_credits'] ?? 0); if ($min_credits > 0 && class_exists('AKSRT_Credits')) { $balance = AKSRT_Credits::balance($user_id); if ($balance < $min_credits) { return false; } } // Default: allow users with edit_posts capability (or any logged-in user if submission is enabled) return user_can($user_id, 'edit_posts') || !empty($submit_settings['submit_enabled']); } /** * 提交文章 * * @param array $data 投稿数据 * @return array{success: bool, post_id?: int, message: string} */ public function submit(array $data): array { $user_id = get_current_user_id(); if (!$this->can_submit($user_id)) { return ['success' => false, 'message' => __('没有投稿权限', 'aksrt')]; } $title = sanitize_text_field((string) ($data['title'] ?? '')); $content = wp_kses_post((string) ($data['content'] ?? '')); $category = (int) ($data['category'] ?? 0); $tags = sanitize_text_field((string) ($data['tags'] ?? '')); $status = sanitize_key((string) ($data['status'] ?? 'pending')); if ($title === '') { return ['success' => false, 'message' => __('标题不能为空', 'aksrt')]; } $valid_statuses = ['pending', 'draft', 'publish']; if (!in_array($status, $valid_statuses, true)) { $status = 'pending'; } // Determine review policy: if review is not required and user can publish, auto-publish if ($status === 'pending') { $submit_settings = []; if (class_exists('AKSRT_Settings')) { $submit_settings = AKSRT_Settings::get_value('post_list.submit', []); } $review_required = $submit_settings['review_required'] ?? true; $skip_moderation = false; if (class_exists('AKSRT_Settings')) { $all = AKSRT_Settings::get_all(); $skip_moderation = !empty($all['skip_submit_moderation']); } if (!$review_required || $skip_moderation) { // Auto-publish if user has publish_posts capability if (user_can($user_id, 'publish_posts')) { $status = 'publish'; } } } $post_data = [ 'post_title' => $title, 'post_content' => $content, 'post_status' => $status, 'post_author' => $user_id, 'post_type' => 'post', ]; $post_id = wp_insert_post($post_data, true); if (is_wp_error($post_id)) { return ['success' => false, 'message' => $post_id->get_error_message()]; } // 设置分类 if ($category > 0) { wp_set_post_categories($post_id, [$category]); } // 设置标签 if ($tags !== '') { $tag_names = array_map('trim', explode(',', $tags)); wp_set_post_tags($post_id, $tag_names); } // 设置封面图 $thumbnail_url = sanitize_url((string) ($data['thumbnail'] ?? '')); if ($thumbnail_url !== '') { $this->set_thumbnail_from_url($post_id, $thumbnail_url); } // 设置专题 (topics taxonomy) $this->apply_topics_and_filters($post_id, $data); return [ 'success' => true, 'post_id' => $post_id, 'message' => $status === 'draft' ? __('草稿已保存', 'aksrt') : __('投稿已提交,等待审核', 'aksrt'), ]; } /** * 更新文章 */ public function update(int $post_id, array $data): array { $user_id = get_current_user_id(); if ($user_id <= 0) { return ['success' => false, 'message' => __('请先登录', 'aksrt')]; } $post = get_post($post_id); if (!$post || (int) $post->post_author !== $user_id) { return ['success' => false, 'message' => __('没有编辑权限', 'aksrt')]; } $update_data = ['ID' => $post_id]; if (isset($data['title'])) { $update_data['post_title'] = sanitize_text_field($data['title']); } if (isset($data['content'])) { $update_data['post_content'] = wp_kses_post($data['content']); } if (isset($data['status'])) { $update_data['post_status'] = sanitize_key($data['status']); } $result = wp_update_post($update_data, true); if (is_wp_error($result)) { return ['success' => false, 'message' => $result->get_error_message()]; } // Update category $category = (int) ($data['category'] ?? 0); if ($category > 0) { wp_set_post_categories($post_id, [$category]); } // Update tags if (isset($data['tags'])) { $tags = sanitize_text_field((string) $data['tags']); if ($tags !== '') { $tag_names = array_map('trim', explode(',', $tags)); wp_set_post_tags($post_id, $tag_names); } else { wp_set_post_tags($post_id, []); } } // Update thumbnail if (isset($data['thumbnail'])) { $thumbnail_url = sanitize_url((string) $data['thumbnail']); if ($thumbnail_url !== '') { $this->set_thumbnail_from_url($post_id, $thumbnail_url); } } // Update topics + custom filter meta $this->apply_topics_and_filters($post_id, $data); return ['success' => true, 'post_id' => $post_id, 'message' => __('已更新', 'aksrt')]; } /** * 从 URL 设置封面图 * * Downloads the image from the given URL, sideloads it into the media library, * and sets it as the post's featured image. * * @param int $post_id Post ID. * @param string $url Image URL. */ private function set_thumbnail_from_url(int $post_id, string $url): void { if ($url === '' || !filter_var($url, FILTER_VALIDATE_URL)) { return; } // Require media handling functions if (!function_exists('media_sideload_image')) { require_once ABSPATH . 'wp-admin/includes/media.php'; require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/image.php'; } // Check if URL is an image $ext = strtolower(pathinfo(parse_url($url, PHP_URL_PATH) ?? '', PATHINFO_EXTENSION)); $allowed_exts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg']; if ($ext !== '' && !in_array($ext, $allowed_exts, true)) { return; } // Sideload the image $attachment_id = media_sideload_image($url, $post_id, '', 'id'); if (!is_wp_error($attachment_id)) { set_post_thumbnail($post_id, $attachment_id); } elseif (defined('WP_DEBUG') && WP_DEBUG) { error_log(sprintf('[AKSRT Front Submit] Failed to set thumbnail for post %d: %s — %s', $post_id, $url, $attachment_id->get_error_message())); } } /** * AJAX 提交投稿 */ public function ajax_submit_post(): void { check_ajax_referer('aksrt_submit', '_nonce'); $data = $_POST; $result = $this->submit($data); if ($result['success']) { wp_send_json_success($result); } wp_send_json_error($result); } /** * AJAX 保存草稿 */ public function ajax_save_draft(): void { check_ajax_referer('aksrt_submit', '_nonce'); $_POST['status'] = 'draft'; $data = $_POST; $result = $this->submit($data); if ($result['success']) { wp_send_json_success($result); } wp_send_json_error($result); } /** * AJAX 更新文章 */ public function ajax_update_post(): void { check_ajax_referer('aksrt_submit', '_nonce'); $post_id = (int) ($_POST['post_id'] ?? 0); if ($post_id <= 0) { wp_send_json_error(['message' => __('文章 ID 无效', 'aksrt')]); } $result = $this->update($post_id, $_POST); if ($result['success']) { wp_send_json_success($result); } wp_send_json_error($result); } /** * AJAX handler: delete draft */ public function ajax_delete_draft(): void { check_ajax_referer('aksrt_submit', '_nonce'); $user_id = get_current_user_id(); if ($user_id <= 0) { wp_send_json_error(['message' => __('请先登录', 'aksrt')]); } // Fix: JS sends 'draft_id', not 'post_id' $post_id = (int) (($_POST['draft_id'] ?? $_POST['post_id']) ?? 0); if ($post_id <= 0) { wp_send_json_error(['message' => __('草稿 ID 无效', 'aksrt')]); } $post = get_post($post_id); if (!$post || (int) $post->post_author !== $user_id) { wp_send_json_error(['message' => __('没有删除权限', 'aksrt')]); } if ($post->post_status !== 'draft') { wp_send_json_error(['message' => __('只能删除草稿', 'aksrt')]); } $result = wp_delete_post($post_id, true); if ($result) { wp_send_json_success(['message' => __('草稿已删除', 'aksrt')]); } wp_send_json_error(['message' => __('删除失败', 'aksrt')]); } /** * Apply topics taxonomy terms and custom filter meta values to a post. * * Reads 'topics' array and 'cf_{dim_key}' scalar values from $data. * Custom filter keys are validated against the configured dimension definitions. * * @param int $post_id Post ID. * @param array $data Submission data ($_POST). */ private function apply_topics_and_filters(int $post_id, array $data): void { // Topics taxonomy if (isset($data['topics']) && is_array($data['topics']) && taxonomy_exists('topics')) { $topic_ids = array_map('absint', $data['topics']); $topic_ids = array_filter($topic_ids); wp_set_object_terms($post_id, array_values($topic_ids), 'topics'); } // Custom filter meta values (_aksrt_cf_{dim_key}) foreach ($data as $key => $val) { if (strpos($key, 'cf_') !== 0) { continue; } $dim_key = sanitize_key(substr($key, 3)); if ($dim_key === '') { continue; } $cf_val = sanitize_key((string) $val); $meta_key = '_aksrt_cf_' . $dim_key; if ($cf_val !== '') { update_post_meta($post_id, $meta_key, $cf_val); } else { delete_post_meta($post_id, $meta_key); } } } }