Files
kate522andClaude Opus 5 9e8913ea0a fix(creator-studio): 固定公众号式创作工具栏
- 顶部改为 fixed 创作工具栏:左侧返回/品牌/模式,中间文章标题与保存状态,
  右侧撤销/重做、保存草稿、预览、发布
- 新增工作台级撤销/重做快照栈,连续编辑合并记录;支持 Ctrl/Cmd+Z / Shift+Z,
  不覆盖 RichText 输入框自身的编辑历史
- 移动端收敛工具栏布局,保留撤销/重做、预览、发布
- 先保存草稿再请求审核/直接发布;请求使用服务端更新后的文章与版本 ID
- 固定工具栏以独立 chrome 层布局,正文避开顶部工具栏高度
- 补充固定工具栏、历史操作与键盘快捷键契约

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-11-06 17:36:15 +08:00

316 lines
13 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Template Name: Jio - 写文章/投稿
* Template Post Type: page
*
* Front-end article submission page matching the reference newposts layout.
* 注意:page-newposts.php 是「最新文章」列表,不是投稿。
*
* @package Jio
*/
defined('ABSPATH') || exit;
$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;
}
// Invalid edit id → strip.
if (!empty($_GET['edit']) && empty($ctx['edit_post'])) {
wp_safe_redirect(remove_query_arg('edit'));
exit;
}
// Jio Creator Studio:登录用户 + 构建产物存在时使用沉浸式 Gutenberg 工作台。
// 否则保留原有 TinyMCE 表单作为降级路径(游客投稿、未构建、无权限)。
$studio = class_exists('\Jio\Core\CreatorStudio') ? \Jio\Core\CreatorStudio::get_instance() : null;
$use_studio = $studio
&& $studio::is_built()
&& get_current_user_id() > 0
&& !empty($ctx['is_can_new']);
if ($use_studio) {
// 沉浸式工作台:不渲染主题头部/底部挂件与导航,避免与工作台顶栏重复。
// header.php 里的 all_top_fluid 挂件区、页脚挂件、浮动入口都由 body class
// 与下方过滤器一并屏蔽。
add_filter('jio_render_header', '__return_false');
add_filter('jio_render_footer', '__return_false');
add_filter('jio_render_fluid_widgets', '__return_false');
add_filter('show_admin_bar', '__return_false', 99);
get_header();
?>
<main id="main-content" role="main" tabindex="-1" class="jio-studio-shell">
<div id="jio-creator-studio">
<div class="jio-studio-skeleton" aria-live="polite">
<i class="fa fas fa-spinner fa-spin" aria-hidden="true"></i>
<?php esc_html_e('正在加载创作工作台…', 'jio'); ?>
</div>
</div>
<?php
// 人机验证挂载点:由发布抽屉在提交时移入并展示,脚本扫描只执行一次。
if (function_exists('jio_render_captcha') && function_exists('jio_is_captcha_enabled_for') && jio_is_captcha_enabled_for('front_submit')) :
?>
<div id="jio-creator-captcha" hidden>
<?php jio_render_captcha('front_submit'); ?>
</div>
<?php
endif;
?>
</main>
<?php
get_footer();
return;
}
// wp_editor() is rendered after get_header(); queue its runtime before wp_head.
wp_enqueue_editor();
// Assets.
$css = get_template_directory() . '/assets/css/frontend/pages/submit.css';
if (file_exists($css)) {
wp_enqueue_style(
'jio-submit',
get_template_directory_uri() . '/assets/css/frontend/pages/submit.css',
['jio-public'],
(string) filemtime($css)
);
}
$js = get_template_directory() . '/assets/js/front-submit.js';
$editor_js = get_template_directory() . '/assets/js/front-submit-editor.js';
if (file_exists($editor_js)) {
wp_enqueue_script(
'jio-front-submit-editor',
get_template_directory_uri() . '/assets/js/front-submit-editor.js',
['editor'],
(string) filemtime($editor_js),
true
);
}
$pay_download_js = get_template_directory() . '/assets/js/front-submit-pay-download.js';
if (file_exists($pay_download_js)) {
wp_enqueue_script(
'jio-front-submit-pay-download',
get_template_directory_uri() . '/assets/js/front-submit-pay-download.js',
['jio-front-submit-editor'],
(string) filemtime($pay_download_js),
true
);
}
if (file_exists($js)) {
wp_enqueue_script(
'jio-front-submit',
get_template_directory_uri() . '/assets/js/front-submit.js',
['loader_js', 'jio-front-submit-editor', 'jio-front-submit-pay-download'],
(string) filemtime($js),
true
);
$upload_image_max_bytes = \Jio\Core\FrontSubmit::upload_limit_bytes('image');
$upload_video_max_bytes = \Jio\Core\FrontSubmit::upload_limit_bytes('video');
$upload_resource_max_bytes = \Jio\Core\FrontSubmit::upload_limit_bytes('resource');
$image_upload_multiple = class_exists('\Jio\Attachment\UploadLimits')
? \Jio\Attachment\UploadLimits::image_batch_limit()
: 6;
$system_tools = class_exists('\Jio\Settings\ExtensionRuntime')
? \Jio\Settings\ExtensionRuntime::get_system_tools_settings()
: [];
$float_scenes = is_array($system_tools['mce_float_toolbar'] ?? null) ? $system_tools['mce_float_toolbar'] : ['pc_s', 'm_s'];
$float_toolbar = wp_is_mobile() ? in_array('m_s', $float_scenes, true) : in_array('pc_s', $float_scenes, true);
wp_localize_script('jio-front-submit-editor', 'jioSubmitData', [
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('jio_submit'),
'restUrl' => esc_url_raw(rest_url('jio/v1')),
'restNonce' => wp_create_nonce('wp_rest'),
'postId' => (int) ($ctx['edit_id'] ?? 0),
'mediaUrl' => esc_url_raw(rest_url('jio/v1/front-submit/media')),
'media' => [
'image' => ['url' => esc_url_raw(rest_url('jio/v1/front-submit/media/image'))],
'video' => ['url' => esc_url_raw(rest_url('jio/v1/front-submit/media/video'))],
'file' => ['url' => esc_url_raw(rest_url('jio/v1/front-submit/media/file'))],
],
'splitUpload' => [
'enabled' => !empty($system_tools['split_upload_s']),
'minSize' => (int) ($system_tools['split_minimum_size'] ?? 20),
],
'imageUploadMultiple' => $image_upload_multiple,
'editor' => [
'canUploadImage' => !empty($ctx['can_upload_img']),
'canUploadVideo' => !empty($ctx['can_upload_video']),
'canEmbedVideo' => !empty($ctx['can_iframe']),
'canUploadFile' => !empty($ctx['can_upload_file']),
'canHide' => !empty($ctx['can_hide']),
'canPay' => !empty($ctx['can_pay']),
'floatToolbar' => $float_toolbar,
],
'uploads' => [
'image' => !empty($ctx['can_upload_img']) ? [
'url' => esc_url_raw(rest_url('jio/v1/front-submit/upload/image')),
'maxSize' => $upload_image_max_bytes,
] : null,
'video' => !empty($ctx['can_upload_video']) ? [
'url' => esc_url_raw(rest_url('jio/v1/front-submit/upload/video')),
'maxSize' => $upload_video_max_bytes,
] : null,
'resource' => !empty($ctx['can_upload_file']) ? [
'url' => esc_url_raw(rest_url('jio/v1/front-submit/upload/resource')),
'maxSize' => $upload_resource_max_bytes,
] : null,
],
'i18n' => [
'confirmLeave' => __('你有未保存的更改,确定要离开吗?', 'jio'),
'saving' => __('保存中...', 'jio'),
'saved' => __('草稿已保存', 'jio'),
'submitting' => __('提交中...', 'jio'),
'submitted' => __('投稿已提交', 'jio'),
'error' => __('操作失败,请重试', 'jio'),
'confirmDelete' => __('确定要删除这个草稿吗?', 'jio'),
'deleting' => __('正在删除草稿…', 'jio'),
'deleted' => __('草稿已删除', 'jio'),
'uploading' => __('正在上传…', 'jio'),
'uploaded' => __('上传成功', 'jio'),
'fileTooLarge' => __('文件大小超过限制', 'jio'),
'lastSaved' => __('最后保存:', 'jio'),
'preview' => __('预览文章', 'jio'),
'setVideoCoverTitle' => __('为视频设置封面', 'jio'),
'close' => __('关闭', 'jio'),
'insertImage' => __('插入图片', 'jio'),
'uploadImage' => __('上传图片', 'jio'),
'externalImage' => __('外链图片', 'jio'),
'insertVideo' => __('插入视频', 'jio'),
'uploadVideo' => __('上传视频', 'jio'),
'externalVideo' => __('外链视频', 'jio'),
'embedVideo' => __('嵌入视频', 'jio'),
'selectVideo' => __('选择或拖入视频', 'jio'),
'videoUploadHint' => __('上传完成后自动插入正文', 'jio'),
'externalVideoHint' => __('请填写视频文件地址:', 'jio'),
'embedVideoHint' => __('请输入嵌入地址或者直接粘贴 iframe 嵌入代码:', 'jio'),
'confirmEmbed' => __('确认嵌入', 'jio'),
'insertFile' => __('上传附件', 'jio'),
'uploadFile' => __('上传附件', 'jio'),
'externalFile' => __('输入地址', 'jio'),
'selectFile' => __('选择或拖入附件', 'jio'),
'fileUploadHint' => __('上传完成后自动插入正文', 'jio'),
'externalFileHint' => __('请填写附件地址:', 'jio'),
'selectImages' => __('选择或拖入图片', 'jio'),
'imageUploadHint' => __('可批量选择,上传完成后自动插入正文', 'jio'),
'selectedFiles' => __('已选择 %d 个文件', 'jio'),
'confirmUpload' => __('确认上传', 'jio'),
'externalImageHint' => __('每行填写一个图片地址:', 'jio'),
'confirmInsert' => __('确认插入', 'jio'),
'invalidMediaUrl' => __('请填写有效地址', 'jio'),
'invalidVideoUrl' => __('请填写有效的视频嵌入地址', 'jio'),
'invalidFile' => __('请选择有效文件', 'jio'),
'uploadUnavailable' => __('当前账号不能上传此类文件', 'jio'),
'mine' => __('我的媒体', 'jio'),
'mineImage' => __('我的图片', 'jio'),
'mineVideo' => __('我的视频', 'jio'),
'mineFile' => __('我的文件', 'jio'),
'search' => __('搜索', 'jio'),
'searchMedia' => __('搜索媒体(至少 2 个字符)', 'jio'),
'searchMin' => __('请输入至少 2 个字符进行搜索', 'jio'),
'clear' => __('清除', 'jio'),
'loadMore' => __('加载更多', 'jio'),
'loading' => __('加载中…', 'jio'),
'empty' => __('暂无媒体文件', 'jio'),
'emptyMedia' => __('暂无媒体文件', 'jio'),
'mediaError' => __('媒体加载失败', 'jio'),
'mediaUnavailable' => __('媒体库暂不可用', 'jio'),
'confirmSelected' => __('确认选择(%d)', 'jio'),
'maxItems' => __('最多选择 %d 个文件', 'jio'),
'maxMediaItems' => __('最多选择 %d 个文件', 'jio'),
'uploadingClose' => __('上传中,请稍候再关闭', 'jio'),
'mediaPickerUnavailable' => __('媒体选择器加载失败,请刷新页面后重试', 'jio'),
'download' => __('下载', 'jio'),
'quote' => __('引言', 'jio'),
'quoteGray' => __('灰色引言', 'jio'),
'quoteRed' => __('红色引言', 'jio'),
'quoteBlue' => __('蓝色引言', 'jio'),
'quoteGreen' => __('绿色引言', 'jio'),
'edit' => __('编辑', 'jio'),
'insert' => __('插入', 'jio'),
'highlightCode' => __('高亮代码', 'jio'),
'selectLanguage' => __('选择语言', 'jio'),
'codeGeneric' => __('通用高亮', 'jio'),
'codeTheme' => __('主题', 'jio'),
'codeFollowGlobal' => __('跟随全局设置', 'jio'),
'codeLabel' => __('代码:', 'jio'),
'hiddenContent' => __('隐藏内容', 'jio'),
'hiddenPlaceholder' => __('隐藏内容', 'jio'),
'replyVisible' => __('回复可见', 'jio'),
'loginVisible' => __('登录可见', 'jio'),
'vipVisible' => __('VIP 可见', 'jio'),
'payVisible' => __('付费可见', 'jio'),
'addResource' => __('添加资源', 'jio'),
'editResource' => __('编辑资源', 'jio'),
'downloadUrl' => __('下载地址', 'jio'),
'enterDownloadUrl' => __('请输入资源下载地址', 'jio'),
'pasteLinkHint' => __('支持直接粘贴网盘分享链接,并自动识别提取码', 'jio'),
'resourceNote' => __('资源备注', 'jio'),
'enterResourceNote' => __('请输入资源备注', 'jio'),
'resourceNoteHint' => __('例如提取码、解压密码等', 'jio'),
'clickCopy' => __('点击复制', 'jio'),
'copyName' => __('复制名称', 'jio'),
'copyContent' => __('复制内容', 'jio'),
'copyHint' => __('填写后,用户可点击资源备注复制内容', 'jio'),
'customButton' => __('自定义按钮', 'jio'),
'customButtonPlaceholder' => __('自定义下载按钮名称', 'jio'),
'selectButtonStyle' => __('选择按钮样式', 'jio'),
'extractCode' => __('提取码', 'jio'),
'extractCodeColon' => __('提取码:', 'jio'),
'confirm' => __('确认', 'jio'),
],
]);
}
get_header();
?>
<main id="main-content" role="main" class="container" tabindex="-1">
<form id="jio-submit-form" data-jio-submit-form data-dirty="false">
<div class="content-wrap newposts-wrap">
<div class="content-layout">
<?php
$main = get_template_directory() . '/templates/parts/submit/editor-main.php';
if (file_exists($main)) {
include $main;
}
?>
</div>
</div>
<div class="sidebar show-sidebar">
<?php \Jio\WidgetSystem\WidgetAreas::render('newposts_sidebar_top'); ?>
<?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;
}
?>
<?php \Jio\WidgetSystem\WidgetAreas::render('newposts_sidebar_bottom'); ?>
</div>
</form>
</main>
<?php
get_footer();