$data Request payload. * @param string $action posts_save|posts_draft * @return array{success:bool,message:string,post_id?:int,url?:string,goto?:string,reload?:bool,time?:string,posts_id?:int} */ public function persist(array $data, string $action = 'posts_save'): array { $user_id = get_current_user_id(); $settings = $this->settings(); $posts_id = absint($data['posts_id'] ?? $data['edit_id'] ?? $data['post_id'] ?? 0); $is_guest = $user_id <= 0; $is_edit = $posts_id > 0; $is_draft = $action === 'posts_draft'; if (empty($settings['enabled'])) { return ['success' => false, 'message' => __('投稿功能已关闭', 'aksrt')]; } if ($is_guest) { if ((int) $settings['default_user_id'] <= 0) { return ['success' => false, 'message' => __('请先登录', 'aksrt')]; } if ($is_draft) { return ['success' => false, 'message' => __('未登录无法保存草稿', 'aksrt')]; } if ($is_edit) { return ['success' => false, 'message' => __('未登录无法编辑文章', 'aksrt')]; } } // 小黑屋(suspend):剥夺发布权限,发帖/草稿/编辑均拦截(对齐参考主题)。 if (!$is_guest && \AKSRT\User\Restriction::get_instance()->is_suspended($user_id)) { $suspend = \AKSRT\User\Restriction::get_instance()->get_active_suspend($user_id); $message = __('您已被关入小黑屋,暂时无法发布内容', 'aksrt'); if (!empty($suspend['reason'])) { $message .= ',原因:' . $suspend['reason']; } return ['success' => false, 'message' => $message]; } if ($is_edit) { if (!$this->can_edit_post($posts_id, $user_id)) { return ['success' => false, 'message' => __('没有编辑权限', 'aksrt')]; } } elseif (!$this->can_submit($is_guest ? 0 : $user_id)) { return ['success' => false, 'message' => __('没有投稿权限', 'aksrt')]; } $title = sanitize_text_field((string) ($data['post_title'] ?? $data['title'] ?? '')); $content = (string) ($data['post_content'] ?? $data['content'] ?? ''); $excerpt = sanitize_textarea_field((string) ($data['excerpt'] ?? $data['post_excerpt'] ?? '')); if ($title === '') { return ['success' => false, 'message' => __('请填写文章标题', 'aksrt')]; } if (trim(wp_strip_all_tags($content)) === '') { return ['success' => false, 'message' => __('还未填写任何内容', 'aksrt')]; } $categories = $this->normalize_category_ids($data); $tags_raw = (string) ($data['tags'] ?? ''); if (!$is_draft) { $title_len = $this->text_length($title); if ($title_len < (int) $settings['title_min']) { return ['success' => false, 'message' => __('标题太短!', 'aksrt')]; } if ($title_len > (int) $settings['title_max']) { return [ 'success' => false, 'message' => sprintf( /* translators: %d: max title length */ __('标题太长了,不能超过%d个字', 'aksrt'), (int) $settings['title_max'] ), ]; } if ($this->text_length(wp_strip_all_tags($content)) < 10) { return ['success' => false, 'message' => __('文章内容过少', 'aksrt')]; } if (empty($categories)) { return ['success' => false, 'message' => __('请选择文章分类', 'aksrt')]; } } // Allowed category whitelist. $allowed = $settings['allowed_cat_ids'] ?? []; if (!empty($allowed) && !empty($categories)) { $categories = array_values(array_intersect($categories, $allowed)); if (!$is_draft && empty($categories)) { return ['success' => false, 'message' => __('所选分类不可用', 'aksrt')]; } } $author_id = $user_id; if ($is_guest) { $guest_name = sanitize_text_field((string) ($data['user_name'] ?? '')); if ($guest_name === '') { return ['success' => false, 'message' => __('请输入昵称', 'aksrt')]; } $contact = sanitize_text_field((string) ($data['contact_details'] ?? '')); $suffix = $contact !== '' ? sprintf(__('[投稿-姓名:%1$s,联系:%2$s]', 'aksrt'), $guest_name, $contact) : sprintf(__('[投稿-姓名:%s]', 'aksrt'), $guest_name); $title = $title . $suffix; $author_id = (int) $settings['default_user_id']; } $status = 'draft'; if (!$is_draft) { $status = $this->resolve_publish_status($user_id); } $can_iframe = $this->user_can_cap('new_post_iframe_video', $user_id); if ($can_iframe) { add_filter('wp_kses_allowed_html', [$this, 'allow_iframe_kses'], 99, 2); } $content = wp_kses_post($content); if ($can_iframe) { remove_filter('wp_kses_allowed_html', [$this, 'allow_iframe_kses'], 99); } $postarr = [ 'post_type' => 'post', 'post_title' => $title, 'post_content' => $content, 'post_status' => $status, 'comment_status' => 'open', ]; if ($excerpt !== '' || array_key_exists('excerpt', $data) || array_key_exists('post_excerpt', $data)) { $postarr['post_excerpt'] = $excerpt; } if (!empty($settings['allow_tags']) && $tags_raw !== '') { $tag_names = preg_split('/[,,\n]+/u', $tags_raw) ?: []; $tag_names = array_values(array_filter(array_map('trim', $tag_names))); $postarr['tags_input'] = $tag_names; } if (!empty($categories)) { $postarr['post_category'] = $categories; } if ($is_edit) { $postarr['ID'] = $posts_id; $in_id = wp_update_post($postarr, true); } else { $postarr['post_author'] = $author_id; $in_id = wp_insert_post($postarr, true); } if (is_wp_error($in_id)) { return ['success' => false, 'message' => $in_id->get_error_message()]; } if (!$in_id) { return ['success' => false, 'message' => __('文章保存失败,请稍后再试', 'aksrt')]; } // Re-apply categories when empty update should clear (logged-in only). if (isset($data['category'])) { wp_set_post_categories((int) $in_id, $categories); } if (!empty($settings['allow_tags']) && array_key_exists('tags', $data) && $tags_raw === '') { wp_set_post_tags((int) $in_id, []); } $this->apply_custom_filters((int) $in_id, $data, $settings); $pay_result = $this->save_pay_meta((int) $in_id, $data, $user_id, $settings); if (!$pay_result['success']) { $pay_result['post_id'] = (int) $in_id; $pay_result['posts_id'] = (int) $in_id; return $pay_result; } $this->save_featured_meta((int) $in_id, $data, $user_id); $post = get_post((int) $in_id); if ($post instanceof \WP_Post) { do_action($is_edit ? 'aksrt_new_edit_posts' : 'aksrt_new_add_posts', $post); if ($status === 'pending') { do_action('aksrt_new_posts_pending', $post); } } $url = get_permalink((int) $in_id) ?: ''; $send = [ 'success' => true, 'post_id' => (int) $in_id, 'posts_id' => (int) $in_id, 'url' => $url, ]; switch ($status) { case 'pending': $send['message'] = __('内容已提交,正在等待审核', 'aksrt'); if ($user_id > 0 && function_exists('aksrt_get_user_home_url')) { $send['reload'] = true; $send['goto'] = add_query_arg( ['tab' => 'post', 'post_status' => 'pending'], aksrt_get_user_home_url($user_id) ); } break; case 'draft': $send['message'] = __('草稿已保存', 'aksrt'); $send['time'] = current_time('mysql'); break; default: $send['message'] = __('文章已发布', 'aksrt'); $send['reload'] = true; $send['goto'] = $url; break; } return $send; } /** * Publish status for a non-draft submission. * * Mirrors the reference theme: pending unless the user holds the * "publish without review" capability. */ private function resolve_publish_status(int $user_id): string { if ($this->user_can_cap('new_post_audit_no', $user_id)) { return 'publish'; } return 'pending'; } /** * @param array $data * @return int[] */ private function normalize_category_ids(array $data): array { $raw = $data['category'] ?? $data['categories'] ?? []; if (!is_array($raw)) { $raw = $raw === '' || $raw === null ? [] : [$raw]; } $ids = array_values(array_unique(array_filter(array_map('absint', $raw)))); return $ids; } private function text_length(string $text): int { $text = trim(wp_strip_all_tags($text)); if (function_exists('mb_strlen')) { return (int) mb_strlen($text); } return strlen($text); } /** * @param array $data * @param array $settings */ private function apply_custom_filters(int $post_id, array $data, array $settings): void { if (empty($settings['custom_filter_s'])) { return; } $dims = []; if (class_exists('\AKSRT\Settings\Settings')) { $filter_all = \AKSRT\Settings\Settings::get_post_filter_settings(); $dims = is_array($filter_all['custom_filter'] ?? null) ? $filter_all['custom_filter'] : []; } $allowed_keys = []; foreach ($dims as $dim) { if (!empty($dim['key'])) { $allowed_keys[] = sanitize_key((string) $dim['key']); } } foreach ($data as $key => $val) { $dim_key = ''; if (strpos((string) $key, 'cf_') === 0) { $dim_key = sanitize_key(substr((string) $key, 3)); } elseif (strpos((string) $key, '_aksrt_cf_') === 0) { $dim_key = sanitize_key(substr((string) $key, 10)); } elseif (in_array(sanitize_key((string) $key), $allowed_keys, true)) { $dim_key = sanitize_key((string) $key); } if ($dim_key === '' || (!empty($allowed_keys) && !in_array($dim_key, $allowed_keys, true))) { continue; } $meta_key = '_aksrt_cf_' . $dim_key; if (is_array($val)) { $vals = array_values(array_filter(array_map('sanitize_key', $val))); if ($vals) { update_post_meta($post_id, $meta_key, $vals); } else { delete_post_meta($post_id, $meta_key); } } else { $cf_val = sanitize_key((string) $val); if ($cf_val !== '') { update_post_meta($post_id, $meta_key, $cf_val); } else { delete_post_meta($post_id, $meta_key); } } } } /** * @param array $tags * @return array */ public function allow_iframe_kses(array $tags, $context = ''): array { if ($context !== 'post' && $context !== '') { return $tags; } $tags['iframe'] = [ 'src' => true, 'width' => true, 'height' => true, 'frameborder' => true, 'allowfullscreen' => true, 'allow' => true, 'style' => true, 'class' => true, 'id' => true, 'scrolling' => true, ]; return $tags; } }