feat(front-submit): 1:1 写文章/投稿页对齐 zibll newposts

Register AKSRT 写文章/投稿 page template, wire header publish entry to
front submit URL, rewrite page-submit as dual-column TinyMCE compose with
featured cover, multi-category/topics/filters, pay box, drafts, and
capability-gated FrontSubmit AJAX (pay/featured traits).
This commit is contained in:
kate522 committed 2026-07-18 13:29:27 +08:00
1 parent cd4b1a73dc
commit 518e825bdd
25 files changed
+3029 -982

No files matched your search

+105 -277
View File
@@ -1,310 +1,138 @@
<?php
/**
* Template Name: AKSRT - Submit
* Template Name: AKSRT - 写文章/投稿
* Template Post Type: page
*
* 前台投稿页面模板。
* 支持标题、摘要、分类、标签、正文、封面、草稿保存。
* 前台文章投稿页(对齐 zibll newposts:双栏 + TinyMCE + 封面 + 付费 + 草稿)。
* 注意:page-newposts.php 是「最新文章」列表,不是投稿。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
if (!is_user_logged_in()) {
wp_redirect(aksrt_get_login_url(get_permalink()));
$ctx = include get_template_directory() . '/templates/parts/submit/context.php';
if (!is_array($ctx)) {
$ctx = [];
}
// Feature disabled → soft 404.
if (empty($ctx['enabled'])) {
global $wp_query;
$wp_query->set_404();
status_header(404);
nocache_headers();
include get_query_template('404');
return;
}
// Guest without guest mode → login.
if (!empty($ctx['is_guest']) && empty($ctx['settings']['guest_enabled'])) {
$login = function_exists('aksrt_get_login_url') ? aksrt_get_login_url(get_permalink()) : wp_login_url(get_permalink());
wp_safe_redirect($login);
exit;
}
$user_id = get_current_user_id();
$can_submit = class_exists('\AKSRT\Core\FrontSubmit') && \AKSRT\Core\FrontSubmit::get_instance()->can_submit($user_id);
if (!$can_submit) {
wp_redirect(home_url('/'));
// Invalid edit id → strip.
if (!empty($_GET['edit']) && empty($ctx['edit_post'])) {
wp_safe_redirect(remove_query_arg('edit'));
exit;
}
// Check if editing existing draft
$edit_id = isset($_GET['edit']) ? absint($_GET['edit']) : 0;
$edit_post = null;
if ($edit_id > 0) {
$edit_post = get_post($edit_id);
if (!$edit_post || (int) $edit_post->post_author !== $user_id || $edit_post->post_status !== 'draft') {
$edit_post = null;
$edit_id = 0;
}
// Assets.
$css = get_template_directory() . '/assets/css/frontend/pages/submit.css';
if (file_exists($css)) {
wp_enqueue_style(
'aksrt-submit',
get_template_directory_uri() . '/assets/css/frontend/pages/submit.css',
[],
(string) filemtime($css)
);
}
get_header();
// Submit settings
$submit_settings = [];
if (class_exists('\AKSRT\Settings\Settings')) {
$all = \AKSRT\Settings\Settings::get_all();
$submit_settings = [
'enabled' => !empty($all['front_submit_enabled']),
'review_mode' => $all['front_submit_review_mode'] ?? 'pending',
'allow_cover' => !isset($all['front_submit_cover']) || !empty($all['front_submit_cover']),
'allow_excerpt' => !isset($all['front_submit_excerpt']) || !empty($all['front_submit_excerpt']),
'allow_category' => !isset($all['front_submit_category']) || !empty($all['front_submit_category']),
'allow_tags' => !isset($all['front_submit_tags']) || !empty($all['front_submit_tags']),
'max_images' => (int) ($all['front_submit_max_images'] ?? 5),
'allowed_cat_ids' => is_array($all['post_article_cat'] ?? null) ? array_map('intval', $all['post_article_cat']) : [],
'topics_s' => !empty($all['post_article_topics_s']),
'custom_filter_s' => !isset($all['post_article_custom_filter_s']) || !empty($all['post_article_custom_filter_s']),
];
}
// §3.7 topics: load topics taxonomy terms
$topics_terms = [];
if (!empty($submit_settings['topics_s']) && taxonomy_exists('topics')) {
$topics_terms = get_terms(['taxonomy' => 'topics', 'hide_empty' => false, 'orderby' => 'name', 'order' => 'ASC']);
if (is_wp_error($topics_terms)) $topics_terms = [];
}
$current_topics = [];
if ($edit_post && taxonomy_exists('topics')) {
$terms = wp_get_object_terms($edit_post->ID, 'topics', ['fields' => 'ids']);
if (!is_wp_error($terms)) $current_topics = $terms;
}
// §3.4 custom_filter: load dimension definitions for submit form
$custom_dimensions = [];
if (!empty($submit_settings['custom_filter_s']) && class_exists('\AKSRT\Settings\Settings')) {
$filter_all = \AKSRT\Settings\Settings::get_post_filter_settings();
$custom_dimensions = $filter_all['custom_filter'];
}
// §3.4 custom_filter: load current values for edit mode
$current_cf = [];
if ($edit_post && !empty($custom_dimensions)) {
foreach ($custom_dimensions as $dim) {
$current_cf[$dim['key']] = get_post_meta($edit_post->ID, '_aksrt_cf_' . $dim['key'], true);
}
}
// §3.7 post_article_cat: filter categories by allowed list
$cat_args = [
'taxonomy' => 'category',
'hide_empty' => false,
'orderby' => 'name',
'order' => 'ASC',
];
if (!empty($submit_settings['allowed_cat_ids'])) {
$cat_args['include'] = $submit_settings['allowed_cat_ids'];
}
$categories = get_categories($cat_args);
// Current values for edit mode
$current_title = $edit_post ? $edit_post->post_title : '';
$current_content = $edit_post ? $edit_post->post_content : '';
$current_excerpt = $edit_post ? $edit_post->post_excerpt : '';
$current_cats = $edit_post ? wp_get_post_categories($edit_post->ID) : [];
$current_tags = $edit_post ? implode(', ', wp_get_post_tags($edit_post->ID, ['fields' => 'names'])) : '';
$current_thumb = $edit_post ? get_the_post_thumbnail_url($edit_post->ID, 'large') : '';
?>
<main id="main-content" class="site-main content-shell content-shell--page" tabindex="-1">
<div class="aksrt-submit-page container">
<header class="aksrt-submit-page__header">
<h1 class="aksrt-submit-page__title">
<?php echo $edit_post ? esc_html__('编辑草稿', 'aksrt') : esc_html__('投稿', 'aksrt'); ?>
</h1>
<p class="aksrt-submit-page__desc">
<?php echo $edit_post
? esc_html__('继续编辑你的草稿,完成后提交审核。', 'aksrt')
: esc_html__('分享你的内容,提交后将进入审核流程。', 'aksrt'); ?>
</p>
</header>
<form class="aksrt-submit-form" id="aksrt-submit-form" data-aksrt-submit-form data-dirty="false">
<?php if ($edit_id > 0) : ?>
<input type="hidden" name="edit_id" value="<?php echo esc_attr((string) $edit_id); ?>">
<?php endif; ?>
<div class="aksrt-form-group">
<label for="submit-title"><?php esc_html_e('标题', 'aksrt'); ?> *</label>
<input type="text" id="submit-title" name="title" class="aksrt-form-input" required maxlength="200" placeholder="<?php esc_attr_e('请输入文章标题', 'aksrt'); ?>" value="<?php echo esc_attr($current_title); ?>">
</div>
<?php if (!empty($submit_settings['allow_excerpt'])) : ?>
<div class="aksrt-form-group">
<label for="submit-excerpt">
<?php esc_html_e('摘要', 'aksrt'); ?>
<button type="button" class="aksrt-ai-assist-btn" data-ai-action="summary" hidden><?php esc_html_e('AI 生成摘要', 'aksrt'); ?></button>
</label>
<textarea id="submit-excerpt" name="excerpt" class="aksrt-form-input" rows="3" maxlength="300" placeholder="<?php esc_attr_e('可选:文章摘要', 'aksrt'); ?>"><?php echo esc_textarea($current_excerpt); ?></textarea>
</div>
<?php endif; ?>
<div class="aksrt-submit-form__row">
<?php if (!empty($submit_settings['allow_category'])) : ?>
<div class="aksrt-form-group aksrt-submit-form__col">
<label for="submit-category"><?php esc_html_e('分类', 'aksrt'); ?></label>
<select id="submit-category" name="category" class="aksrt-form-input">
<option value=""><?php esc_html_e('选择分类', 'aksrt'); ?></option>
<?php foreach ($categories as $cat) : ?>
<option value="<?php echo esc_attr((string) $cat->term_id); ?>" <?php selected(in_array($cat->term_id, $current_cats)); ?>><?php echo esc_html($cat->name); ?></option>
<?php endforeach; ?>
</select>
</div>
<?php endif; ?>
<?php if (!empty($submit_settings['allow_tags'])) : ?>
<div class="aksrt-form-group aksrt-submit-form__col">
<label for="submit-tags">
<?php esc_html_e('标签', 'aksrt'); ?>
<button type="button" class="aksrt-ai-assist-btn" data-ai-action="tags" hidden><?php esc_html_e('AI 建议标签', 'aksrt'); ?></button>
</label>
<input type="text" id="submit-tags" name="tags" class="aksrt-form-input" placeholder="<?php esc_attr_e('多个标签用逗号分隔', 'aksrt'); ?>" value="<?php echo esc_attr($current_tags); ?>">
</div>
<?php endif; ?>
</div>
<?php if (!empty($submit_settings['topics_s']) && !empty($topics_terms)) : ?>
<div class="aksrt-form-group">
<label for="submit-topics"><?php esc_html_e('专题', 'aksrt'); ?></label>
<select id="submit-topics" name="topics[]" class="aksrt-form-input" multiple>
<?php foreach ($topics_terms as $topic) : ?>
<option value="<?php echo esc_attr((string) $topic->term_id); ?>" <?php selected(in_array($topic->term_id, $current_topics)); ?>><?php echo esc_html($topic->name); ?></option>
<?php endforeach; ?>
</select>
</div>
<?php endif; ?>
<?php if (!empty($submit_settings['custom_filter_s']) && !empty($custom_dimensions)) : ?>
<?php foreach ($custom_dimensions as $dim) : ?>
<div class="aksrt-form-group">
<label for="submit-cf-<?php echo esc_attr($dim['key']); ?>"><?php echo esc_html($dim['name']); ?></label>
<select id="submit-cf-<?php echo esc_attr($dim['key']); ?>" name="cf_<?php echo esc_attr($dim['key']); ?>" class="aksrt-form-input">
<option value=""><?php printf(esc_html__('选择%s', 'aksrt'), esc_html($dim['name'])); ?></option>
<?php foreach ($dim['vals'] as $val) : ?>
<option value="<?php echo esc_attr($val['key']); ?>" <?php selected(!empty($current_cf[$dim['key']]) && $current_cf[$dim['key']] === $val['key']); ?>><?php echo esc_html($val['name']); ?></option>
<?php endforeach; ?>
</select>
</div>
<?php endforeach; ?>
<?php endif; ?>
<div class="aksrt-form-group">
<label for="submit-content"><?php esc_html_e('内容', 'aksrt'); ?> *</label>
<div class="aksrt-submit-form__editor-toolbar">
<button type="button" class="aksrt-submit-form__toolbar-btn" data-action="bold" title="<?php esc_attr_e('加粗', 'aksrt'); ?>"><i class="fas fa-bold"></i></button>
<button type="button" class="aksrt-submit-form__toolbar-btn" data-action="italic" title="<?php esc_attr_e('斜体', 'aksrt'); ?>"><i class="fas fa-italic"></i></button>
<button type="button" class="aksrt-submit-form__toolbar-btn" data-action="code" title="<?php esc_attr_e('代码', 'aksrt'); ?>"><i class="fas fa-code"></i></button>
<button type="button" class="aksrt-submit-form__toolbar-btn" data-action="link" title="<?php esc_attr_e('链接', 'aksrt'); ?>"><i class="fas fa-link"></i></button>
<button type="button" class="aksrt-submit-form__toolbar-btn" data-action="image" title="<?php esc_attr_e('图片', 'aksrt'); ?>"><i class="fas fa-image"></i></button>
<button type="button" class="aksrt-submit-form__toolbar-btn" data-action="quote" title="<?php esc_attr_e('引用', 'aksrt'); ?>"><i class="fas fa-quote-left"></i></button>
<button type="button" class="aksrt-submit-form__toolbar-btn" data-action="heading" title="<?php esc_attr_e('标题', 'aksrt'); ?>"><i class="fas fa-heading"></i></button>
<button type="button" class="aksrt-submit-form__toolbar-btn" data-action="list" title="<?php esc_attr_e('列表', 'aksrt'); ?>"><i class="fas fa-list-ul"></i></button>
</div>
<textarea id="submit-content" name="content" class="aksrt-form-input aksrt-submit-form__textarea" rows="16" required placeholder="<?php esc_attr_e('请输入文章内容(支持 HTML)', 'aksrt'); ?>"><?php echo esc_textarea($current_content); ?></textarea>
<div class="aksrt-submit-form__word-count" id="submit-word-count"></div>
</div>
<?php if (!empty($submit_settings['allow_cover'])) : ?>
<div class="aksrt-form-group">
<label for="submit-thumbnail"><?php esc_html_e('封面图', 'aksrt'); ?></label>
<div class="aksrt-submit-form__cover-input">
<input type="url" id="submit-thumbnail" name="thumbnail" class="aksrt-form-input" placeholder="<?php esc_attr_e('输入封面图地址', 'aksrt'); ?>" value="<?php echo esc_url($current_thumb); ?>">
<button type="button" class="aksrt-submit-form__cover-btn" id="aksrt-pick-cover">
<i class="fas fa-image"></i>
<?php esc_html_e('选择图片', 'aksrt'); ?>
</button>
</div>
<div class="aksrt-submit-form__cover-preview" id="cover-preview" <?php echo $current_thumb ? '' : 'style="display:none"'; ?>>
<img id="cover-preview-img" src="<?php echo esc_url($current_thumb); ?>" alt="">
<button type="button" class="aksrt-submit-form__cover-remove" id="cover-remove" title="<?php esc_attr_e('移除封面', 'aksrt'); ?>">
<i class="fas fa-times"></i>
</button>
</div>
</div>
<?php endif; ?>
<div class="aksrt-submit-form__actions">
<button type="submit" class="aksrt-auth-submit" id="aksrt-submit-btn">
<i class="fas fa-paper-plane"></i>
<?php echo $edit_post ? esc_html__('更新投稿', 'aksrt') : esc_html__('提交投稿', 'aksrt'); ?>
</button>
<button type="button" class="btn-outline" id="aksrt-save-draft">
<i class="fas fa-save"></i>
<?php esc_html_e('保存草稿', 'aksrt'); ?>
</button>
</div>
<input type="hidden" name="_nonce" value="<?php echo esc_attr(wp_create_nonce('aksrt_submit')); ?>">
<input type="hidden" name="status" id="submit-status" value="pending">
</form>
<!-- Draft list -->
<?php
$user_drafts = get_posts([
'post_type' => 'post',
'post_status' => 'draft',
'author' => $user_id,
'numberposts' => 10,
'orderby' => 'modified',
'order' => 'DESC',
]);
if (!empty($user_drafts)) :
?>
<div class="aksrt-submit-drafts">
<h3 class="aksrt-submit-drafts__title">
<i class="fas fa-file-alt"></i>
<?php esc_html_e('我的草稿', 'aksrt'); ?>
</h3>
<div class="aksrt-submit-drafts__list">
<?php foreach ($user_drafts as $draft) : ?>
<div class="aksrt-submit-drafts__item">
<a href="<?php echo esc_url(add_query_arg('edit', $draft->ID, get_permalink())); ?>" class="aksrt-submit-drafts__link">
<span class="aksrt-submit-drafts__name"><?php echo esc_html($draft->post_title ?: __('无标题', 'aksrt')); ?></span>
<time class="aksrt-submit-drafts__date"><?php echo esc_html(get_the_date('Y-m-d H:i', $draft->ID)); ?></time>
</a>
<button type="button" class="aksrt-submit-drafts__delete" data-draft-id="<?php echo esc_attr((string) $draft->ID); ?>" title="<?php esc_attr_e('删除草稿', 'aksrt'); ?>">
<i class="fas fa-trash"></i>
</button>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>
</main>
<?php
// Enqueue the front-submit script
$front_submit_path = get_template_directory() . '/assets/js/front-submit.js';
if (file_exists($front_submit_path)) {
$js = get_template_directory() . '/assets/js/front-submit.js';
if (file_exists($js)) {
wp_enqueue_script(
'aksrt-front-submit',
get_template_directory_uri() . '/assets/js/front-submit.js',
['aksrt-script'],
(string) filemtime($front_submit_path),
['jquery'],
(string) filemtime($js),
true
);
$ai_available = class_exists('\AKSRT\Ai\AI') && \AKSRT\Ai\AI::get_instance()->is_available();
$mini = get_template_directory() . '/assets/js/mini-upload.js';
if (file_exists($mini)) {
wp_enqueue_script(
'aksrt-mini-upload',
get_template_directory_uri() . '/assets/js/mini-upload.js',
[],
(string) filemtime($mini),
true
);
}
if (!empty($ctx['can_upload_img']) || !empty($ctx['can_cover_image']) || !empty($ctx['can_cover_slide'])) {
wp_enqueue_media();
}
wp_localize_script('aksrt-front-submit', 'aksrtSubmitData', [
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('aksrt_submit_nonce'),
'nonce' => wp_create_nonce('aksrt_submit'),
'restUrl' => esc_url_raw(rest_url('aksrt/v1')),
'restNonce' => wp_create_nonce('wp_rest'),
'leaveWarning' => !empty($ctx['settings']['leave_warning']),
'ai' => [
'available' => $ai_available,
'summary' => $ai_available,
'tags' => $ai_available,
'available' => !empty($ctx['ai_available']),
'summary' => !empty($ctx['ai_available']),
'tags' => !empty($ctx['ai_available']),
],
'i18n' => [
'confirmLeave' => __('你有未保存的更改,确定要离开吗?', 'aksrt'),
'saving' => __('保存中...', 'aksrt'),
'saved' => __('草稿已保存', 'aksrt'),
'submitting' => __('提交中...', 'aksrt'),
'submitted' => __('投稿已提交', 'aksrt'),
'error' => __('操作失败,请重试', 'aksrt'),
'confirmDelete' => __('确定要删除这个草稿吗?', 'aksrt'),
'aiGenerating' => __('生成中…', 'aksrt'),
'aiFailed' => __('AI 生成失败', 'aksrt'),
'needContent' => __('请先填写正文内容', 'aksrt'),
'aiFilled' => __('已填入,可继续编辑', 'aksrt'),
'confirmLeave' => __('你有未保存的更改,确定要离开吗?', 'aksrt'),
'saving' => __('保存中...', 'aksrt'),
'saved' => __('草稿已保存', 'aksrt'),
'submitting' => __('提交中...', 'aksrt'),
'submitted' => __('投稿已提交', 'aksrt'),
'error' => __('操作失败,请重试', 'aksrt'),
'confirmDelete' => __('确定要删除这个草稿吗?', 'aksrt'),
'aiGenerating' => __('生成中…', 'aksrt'),
'aiFailed' => __('AI 生成失败', 'aksrt'),
'needContent' => __('请先填写正文内容', 'aksrt'),
'aiFilled' => __('已填入,可继续编辑', 'aksrt'),
'lastSaved' => __('最后保存:', 'aksrt'),
'preview' => __('预览文章', 'aksrt'),
'pickImage' => __('选择图片', 'aksrt'),
'pickVideo' => __('选择视频封面图', 'aksrt'),
'videoUrl' => __('请输入视频地址', 'aksrt'),
],
]);
}
get_header();
?>
<main id="main-content" class="site-main container aksrt-article-compose" tabindex="-1">
<form class="aksrt-submit-form aksrt-article-compose__form" id="aksrt-submit-form" data-aksrt-submit-form data-dirty="false" novalidate>
<div class="aksrt-article-compose__layout">
<?php
$main = get_template_directory() . '/templates/parts/submit/editor-main.php';
if (file_exists($main)) {
include $main;
}
?>
<aside class="aksrt-submit-sidebar">
<?php
$terms = get_template_directory() . '/templates/parts/submit/sidebar-terms.php';
if (file_exists($terms)) {
include $terms;
}
$pay = get_template_directory() . '/templates/parts/submit/sidebar-pay.php';
if (file_exists($pay)) {
include $pay;
}
$actions = get_template_directory() . '/templates/parts/submit/sidebar-submit.php';
if (file_exists($actions)) {
include $actions;
}
?>
</aside>
</div>
</form>
</main>
<?php
get_footer();