refactor: flatten src/AKSRT → src (drop theme-identifier dir)

Move all domain folders from src/AKSRT/{Domain} to src/{Domain}; remap
Composer PSR-4 "AKSRT\\": "src/"; update audit script + README + ADR
paths; remove dead tools/build-psr4-map.php (scanned deleted inc/classes).
This commit is contained in:
2026-08-02 22:06:06 +08:00
parent 56f1a8175f
commit 8c5b1555c9
285 changed files with 26 additions and 3600 deletions
+354
View File
@@ -0,0 +1,354 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Core;
defined('ABSPATH') || exit;
/**
* Create / update / status machine for front-end article submit.
*/
trait FrontSubmitSave {
/**
* Persist a front-end submission (new or edit).
*
* @param array<string, mixed> $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 (empty($settings['guest_enabled']) || (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')];
}
}
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($settings['allow_category']) && empty($categories)) {
return ['success' => false, 'message' => __('请选择文章分类', 'aksrt')];
}
if (!empty($settings['cover_required']) && empty($data['featured_data']) && empty($data['thumbnail'])) {
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) && !empty($settings['allow_category'])) {
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, $settings);
}
$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 (!empty($settings['allow_category']) && 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_topics_and_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);
// Legacy thumbnail URL fallback.
$thumbnail_url = esc_url_raw((string) ($data['thumbnail'] ?? ''));
if ($thumbnail_url !== '' && empty($data['featured_data'])) {
$this->set_thumbnail_from_url((int) $in_id, $thumbnail_url);
}
$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;
}
/**
* @param array<string, mixed> $settings
*/
private function resolve_publish_status(int $user_id, array $settings): string {
$mode = (string) ($settings['review_mode'] ?? 'pending');
if ($mode === 'draft') {
return 'draft';
}
if ($mode === 'publish') {
return 'publish';
}
// pending mode: skip audit when capability grants it.
if ($this->user_can_cap('new_post_audit_no', $user_id)) {
return 'publish';
}
return 'pending';
}
/**
* @param array<string, mixed> $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<string,mixed> $data
* @param array<string,mixed> $settings
*/
private function apply_topics_and_filters(int $post_id, array $data, array $settings): void {
if (!empty($settings['topics_s']) && taxonomy_exists('topics')) {
$topics = $data['topics'] ?? [];
if (!is_array($topics)) {
$topics = $topics === '' || $topics === null ? [] : [$topics];
}
$topic_ids = array_values(array_filter(array_map('absint', $topics)));
wp_set_object_terms($post_id, $topic_ids, 'topics');
}
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<string,string> $tags
* @return array<string,string>
*/
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;
}
}