';
- // 与单图封面同一高度公式:calc(比例% + 50px)。
- $slider_html = aksrt_render_slider([
+ // 幻灯片配置(参考主题 CFS_Module::slide 全字段,经 get_post_page_settings 归一化)。
+ $slide_option = is_array($ctx['settings']['article_slide_cover_option'] ?? null)
+ ? $ctx['settings']['article_slide_cover_option']
+ : [];
+ $single_slide = count($slide_items) === 1;
+ $slider_args = [
'slides' => $slide_items,
- 'scale_height' => true,
- 'scale_ratio' => $img_scale / 100,
- 'scale_extra' => '50px',
+ 'effect' => (string) ($slide_option['effect'] ?? 'slide'),
+ 'direction' => (string) ($slide_option['direction'] ?? 'horizontal'),
+ 'spaceBetween' => (int) ($slide_option['spacebetween'] ?? 15),
+ 'speed' => (int) ($slide_option['speed'] ?? 0),
+ 'loop' => !$single_slide && !empty($slide_option['loop']),
+ 'autoplay' => !$single_slide && !empty($slide_option['autoplay']),
+ 'interval' => max(1, (int) ($slide_option['interval'] ?? 4)) * 1000,
+ 'show_nav' => !empty($slide_option['button']),
+ 'show_pagination' => !empty($slide_option['pagination']),
'wrapper_class' => 'mb20 single-cover-slide imgbox-container',
'slider_class' => 'single-cover single-cover--slide',
- 'show_nav' => true,
- 'show_pagination' => true,
- 'loop' => count($slide_items) > 1,
- 'autoplay' => count($slide_items) > 1,
'overlay_html' => $overlay,
- ]);
+ ];
+ if (!empty($slide_option['scale_height'])) {
+ // 固定高度模式:PC/移动端分别取 px 高度。
+ $slider_args['scale_height'] = false;
+ $slider_args['pc_height'] = ((int) ($slide_option['pc_height'] ?? 380)) . 'px';
+ $slider_args['m_height'] = ((int) ($slide_option['m_height'] ?? 180)) . 'px';
+ } else {
+ // 比例高度模式:scale% + 50px(标题预留,与单图封面同公式)。
+ $slider_args['scale_height'] = true;
+ $slider_args['scale_ratio'] = max(10, (int) ($slide_option['scale'] ?? 35)) / 100;
+ $slider_args['scale_extra'] = '50px';
+ }
+ $slider_html = aksrt_render_slider($slider_args);
?>
@@ -633,7 +654,7 @@ if (!function_exists('aksrt_single_render_article')) :
-
0 ? ' style="max-height:' . esc_attr((string) $maxheight) . 'px;"' : ''; ?>
data-maxheight=" 0 ? max(0, $maxheight - 80) : 0)); ?>">
diff --git a/src/Admin/Schema/SchemaPostList.php b/src/Admin/Schema/SchemaPostList.php
index f61080a1..574fcd54 100644
--- a/src/Admin/Schema/SchemaPostList.php
+++ b/src/Admin/Schema/SchemaPostList.php
@@ -46,83 +46,83 @@ class SchemaPostList {
'page' => [
'key' => 'page',
'title' => '文章页面',
- 'version' => '1.0.0',
+ 'version' => '2.0.0',
'option_key' => 'aksrt_settings',
'groups' => [
- ['key' => 'navigation', 'title' => '文章导航', 'fields' => [
- ['key' => 'post_reading_progress', 'label' => '阅读进度条', 'type' => 'switch', 'default' => true],
- ['key' => 'breadcrumbs_single_s', 'label' => '面包屑导航', 'type' => 'switch', 'default' => true],
- ['key' => 'breadcrumbs_home_text', 'label' => '面包屑首页文字', 'type' => 'input', 'default' => '首页', 'depends_on' => 'breadcrumbs_single_s'],
- ['key' => 'breadcrumbs_single_text', 'label' => '末项显示「正文」', 'type' => 'switch', 'default' => true, 'depends_on' => 'breadcrumbs_single_s', 'description' => '关闭后末项显示文章标题'],
- ['key' => 'post_prevnext_s', 'label' => '上一篇/下一篇', 'type' => 'switch', 'default' => true],
- ['key' => 'post_authordesc_s', 'label' => '作者简介', 'type' => 'switch', 'default' => true],
- ['key' => 'post_author_card_limit', 'label' => '作者卡文章数', 'type' => 'number', 'default' => 6, 'min' => 0, 'max' => 12],
- ['key' => 'post_author_card_orderby', 'label' => '作者卡文章排序', 'type' => 'select', 'default' => 'views', 'options' => [
- ['label' => '浏览量', 'value' => 'views'],
- ['label' => '发布时间', 'value' => 'date'],
- ['label' => '更新时间', 'value' => 'modified'],
- ['label' => '评论数', 'value' => 'comment_count'],
+ ['key' => 'post_page', 'title' => '文章页面', 'fields' => [
+ ['key' => 'article_image_cover', 'label' => '图片封面', 'type' => 'switcher', 'default' => true, 'description' => '允许设置文章页顶部显示封面图'],
+ ['key' => 'article_image_cover_option', 'label' => ' ', 'type' => 'fieldset', 'default' => ['scale' => 35], 'dependsOn' => ['key' => 'article_image_cover', 'value' => true], 'description' => '图片封面配置', 'fields' => [
+ ['key' => 'scale', 'label' => '图片长宽比例', 'type' => 'number', 'default' => 35, 'min' => 20, 'max' => 200, 'step' => 5, 'unit' => '%'],
]],
- ['key' => 'post_author_card_style', 'label' => '作者卡文章样式', 'type' => 'radio', 'default' => 'card', 'options' => [
- ['label' => '卡片', 'value' => 'card'],
+ ['key' => 'article_slide_cover', 'label' => '幻灯片封面', 'type' => 'switcher', 'default' => true, 'description' => '允许设置幻灯片为文章封面'],
+ ['key' => 'article_slide_cover_option', 'label' => ' ', 'type' => 'fieldset', 'default' => [
+ 'direction' => 'horizontal', 'loop' => true, 'button' => false, 'pagination' => true, 'effect' => 'slide',
+ 'scale_height' => true, 'scale' => 35, 'auto_height' => false, 'max_height' => 500, 'min_height' => 180,
+ 'pc_height' => 380, 'm_height' => 180, 'spacebetween' => 15, 'speed' => 0, 'autoplay' => true, 'interval' => 4,
+ ], 'dependsOn' => ['key' => 'article_slide_cover', 'value' => true], 'fields' => [
+ ['key' => 'direction', 'label' => '幻灯片方向', 'type' => 'radio', 'default' => 'horizontal', 'options' => [
+ ['label' => '左右切换', 'value' => 'horizontal'],
+ ['label' => '上下切换', 'value' => 'vertical'],
+ ]],
+ ['key' => 'loop', 'label' => '循环切换', 'type' => 'switch', 'default' => true],
+ ['key' => 'button', 'label' => '显示翻页按钮', 'type' => 'switch', 'default' => false],
+ ['key' => 'pagination', 'label' => '显示指示器', 'type' => 'switch', 'default' => true],
+ ['key' => 'effect', 'label' => '切换动画', 'type' => 'select', 'default' => 'slide', 'options' => [
+ ['label' => '滑动', 'value' => 'slide'],
+ ['label' => '淡出淡入', 'value' => 'fade'],
+ ['label' => '3D方块', 'value' => 'cube'],
+ ['label' => '3D滑入', 'value' => 'coverflow'],
+ ['label' => '3D翻转', 'value' => 'flip'],
+ ]],
+ ['key' => 'scale_height', 'label' => '按比例自动高度', 'type' => 'switch', 'default' => true, 'dependsOn' => ['key' => 'direction', 'not' => 'vertical']],
+ ['key' => 'scale', 'label' => '长宽比例', 'type' => 'number', 'default' => 35, 'min' => 10, 'max' => 300, 'step' => 5, 'unit' => '%', 'dependsOn' => ['all' => [['key' => 'direction', 'not' => 'vertical'], ['key' => 'scale_height', 'value' => true]]]],
+ ['key' => 'auto_height', 'label' => '自动高度', 'type' => 'switch', 'default' => false, 'dependsOn' => ['all' => [['key' => 'direction', 'not' => 'vertical'], ['key' => 'scale_height', 'value' => false]]]],
+ ['key' => 'max_height', 'label' => '最大高度', 'type' => 'number', 'default' => 500, 'min' => 120, 'max' => 800, 'step' => 20, 'unit' => 'PX', 'dependsOn' => ['all' => [['key' => 'auto_height', 'value' => true], ['key' => 'direction', 'not' => 'vertical'], ['key' => 'scale_height', 'value' => false]]]],
+ ['key' => 'min_height', 'label' => '最小高度', 'type' => 'number', 'default' => 180, 'min' => 100, 'max' => 500, 'step' => 20, 'unit' => 'PX', 'dependsOn' => ['all' => [['key' => 'auto_height', 'value' => true], ['key' => 'direction', 'not' => 'vertical'], ['key' => 'scale_height', 'value' => false]]]],
+ ['key' => 'pc_height', 'label' => '电脑端高度', 'type' => 'number', 'default' => 380, 'min' => 120, 'max' => 800, 'step' => 20, 'unit' => 'PX'],
+ ['key' => 'm_height', 'label' => '移动端高度', 'type' => 'number', 'default' => 180, 'min' => 100, 'max' => 500, 'step' => 20, 'unit' => 'PX'],
+ ['key' => 'spacebetween', 'label' => '幻灯片间距', 'type' => 'number', 'default' => 15, 'min' => 0, 'max' => 500, 'step' => 5, 'unit' => 'PX'],
+ ['key' => 'speed', 'label' => '切换速度', 'type' => 'number', 'default' => 0, 'min' => 0, 'max' => 3000, 'step' => 100, 'unit' => '毫秒', 'description' => '切换过程的时间(越小越快),设置为0则为自动模式'],
+ ['key' => 'autoplay', 'label' => '自动播放', 'type' => 'switch', 'default' => true],
+ ['key' => 'interval', 'label' => '停顿时间', 'type' => 'number', 'default' => 4, 'min' => 0, 'max' => 20, 'step' => 1, 'unit' => '秒', 'dependsOn' => ['key' => 'autoplay', 'value' => true], 'description' => '自动切换的时间间隔(越小越快)'],
+ ]],
+ ['key' => 'article_video_cover', 'label' => '视频封面', 'type' => 'switcher', 'default' => true, 'description' => '允许设置视频为文章封面'],
+ ['key' => 'article_video_cover_option', 'label' => ' ', 'type' => 'fieldset', 'default' => ['scale' => 0], 'dependsOn' => ['key' => 'article_video_cover', 'value' => true], 'description' => '视频封面配置', 'fields' => [
+ ['key' => 'scale', 'label' => '固定长宽比例', 'type' => 'number', 'default' => 0, 'min' => 0, 'max' => 200, 'step' => 5, 'unit' => '%', 'description' => '为0则不固定长宽比例'],
+ ]],
+ ['key' => 'post_single_hide_time_s', 'label' => '隐藏时间', 'type' => 'switcher', 'default' => false, 'description' => '隐藏文章的更新及发布时间显示'],
+ ['key' => 'post_p_indent_s', 'label' => '内容段落缩进', 'type' => 'switcher', 'default' => false, 'description' => '开启后文章内容每一个段落首行将向右偏移2个文字距离'],
+ ['key' => 'breadcrumbs_single_s', 'label' => '面包屑导航', 'type' => 'switcher', 'default' => true],
+ ['key' => 'breadcrumbs_home_text', 'label' => '面包屑导航用“首页”替代网站名称', 'type' => 'switcher', 'default' => true, 'dependsOn' => ['key' => 'breadcrumbs_single_s', 'value' => true]],
+ ['key' => 'post_prevnext_s', 'label' => '上一页、下一页版块', 'type' => 'switcher', 'default' => true],
+ ['key' => 'post_authordesc_s', 'label' => '作者信息版块', 'type' => 'switcher', 'default' => true],
+ ['key' => 'article_maxheight_kg', 'label' => '内容高度限制', 'type' => 'switcher', 'default' => false, 'description' => '此处为全局设置,每篇文章可单独设置', 'help' => '全局开关'],
+ ['key' => 'article_maxheight', 'label' => '限制的最大高度', 'type' => 'slider', 'default' => 1000, 'min' => 600, 'max' => 3000, 'step' => 100, 'unit' => 'px', 'dependsOn' => ['key' => 'article_maxheight_kg', 'value' => true], 'description' => '开启后如果文章高度超过设定值则会显示展开阅读全文的按钮。每篇文章可单独开启此功能'],
+ ['key' => 'yiyan_single_content_header', 'label' => '文章内容头部显示一言', 'type' => 'switcher', 'default' => false],
+ ['key' => 'yiyan_single_content_footer', 'label' => '文章内容尾部显示一言', 'type' => 'switcher', 'default' => false],
+ ['key' => 'yiyan_single_box', 'label' => '文章页面下方独立一言版块', 'type' => 'switcher', 'default' => false],
+ ['key' => 'post_copyright_s', 'label' => '版权声明', 'type' => 'switcher', 'default' => true],
+ ['key' => 'post_copyright', 'label' => '版权提示内容', 'type' => 'textarea', 'default' => '文章版权归作者所有,未经允许请勿转载。', 'rows' => 2, 'dependsOn' => ['key' => 'post_copyright_s', 'value' => true], 'description' => '支持HTML代码,请注意代码规范及标签闭合'],
+ ['key' => 'post_button_toptext', 'label' => '文章页脚文案', 'type' => 'input', 'default' => '喜欢就支持一下吧', 'description' => '文章底部打赏、分享按钮上面的文字'],
+ ['key' => 'post_front_content', 'label' => '在文章内容前-插入内容', 'type' => 'textarea', 'default' => '', 'rows' => 3],
+ ['key' => 'post_after_content', 'label' => '在文章内容后-插入内容', 'type' => 'textarea', 'default' => '', 'rows' => 3, 'description' => '在每篇文章顶部和尾部插入内容,可以插入广告或者文章说明等内容。支持HTML代码,请注意代码规范及标签闭合'],
+ ['key' => 'post_related_s', 'label' => '相关文章版块', 'type' => 'switcher', 'default' => true],
+ ['key' => 'post_related_type', 'label' => '显示样式', 'type' => 'image_select', 'default' => 'img', 'dependsOn' => ['key' => 'post_related_s', 'value' => true], 'options' => [
+ ['label' => '图文卡片', 'value' => 'img'],
['label' => '列表', 'value' => 'list'],
+ ['label' => '纯文字', 'value' => 'text'],
+ ]],
+ ['key' => 'related_title', 'label' => '版块标题', 'type' => 'input', 'default' => '相关推荐', 'dependsOn' => ['key' => 'post_related_s', 'value' => true]],
+ ['key' => 'post_related_n', 'label' => '显示数量', 'type' => 'number', 'default' => 6, 'min' => 4, 'max' => 12, 'step' => 2, 'unit' => '篇', 'dependsOn' => ['key' => 'post_related_s', 'value' => true]],
+ ['key' => 'post_related_orderby', 'label' => '排序方式', 'type' => 'select', 'default' => 'views', 'dependsOn' => ['key' => 'post_related_s', 'value' => true], 'options' => [
+ ['label' => '更新时间', 'value' => 'modified'],
+ ['label' => '发布时间', 'value' => 'date'],
+ ['label' => '浏览量', 'value' => 'views'],
+ ['label' => '点赞量', 'value' => 'like'],
+ ['label' => '评论量', 'value' => 'comment_count'],
+ ['label' => '收藏数量', 'value' => 'favorite'],
+ ['label' => '随机', 'value' => 'rand'],
]],
- ['key' => 'post_single_hide_time_s', 'label' => '隐藏文章时间', 'type' => 'switch', 'default' => false],
- ]],
- ['key' => 'cover', 'title' => '封面展示', 'fields' => [
- ['key' => 'post_article_image_cover', 'label' => '图片封面', 'type' => 'switch', 'default' => true, 'description' => '文章有特色图时自动展示封面'],
- ['key' => 'post_article_image_cover_scale', 'label' => '封面高度比例(%)', 'type' => 'number', 'default' => 35, 'min' => 10, 'max' => 100],
- ['key' => 'post_article_slide_cover', 'label' => '幻灯片封面', 'type' => 'switch', 'default' => true, 'description' => '文章有图集时以轮播形式展示封面'],
- ['key' => 'post_article_video_cover', 'label' => '视频封面', 'type' => 'switch', 'default' => true, 'description' => '文章有视频时以视频播放器作为封面'],
- ['key' => 'post_article_video_cover_scale', 'label' => '视频封面高度比例(%)', 'type' => 'number', 'default' => 56, 'min' => 20, 'max' => 100],
- ]],
- ['key' => 'related', 'title' => '相关推荐', 'fields' => [
- ['key' => 'post_related_s', 'label' => '显示相关推荐', 'type' => 'switch', 'default' => true],
- ['key' => 'post_related_type', 'label' => '展示样式', 'type' => 'radio', 'default' => 'img', 'options' => [
- ['label' => '图文卡片', 'value' => 'img'],
- ['label' => '列表', 'value' => 'list'],
- ['label' => '纯文字', 'value' => 'text'],
- ]],
- ['key' => 'related_title', 'label' => '推荐标题', 'type' => 'input', 'default' => '相关推荐'],
- ['key' => 'post_related_n', 'label' => '推荐数量', 'type' => 'number', 'default' => 6, 'min' => 4, 'max' => 12, 'step' => 2],
- ['key' => 'post_related_orderby', 'label' => '推荐排序', 'type' => 'select', 'default' => 'views', 'options' => [
- ['label' => '更新时间', 'value' => 'modified'],
- ['label' => '发布时间', 'value' => 'date'],
- ['label' => '浏览量', 'value' => 'views'],
- ['label' => '点赞量', 'value' => 'like'],
- ['label' => '评论量', 'value' => 'comment_count'],
- ['label' => '收藏数量', 'value' => 'favorite'],
- ['label' => '随机', 'value' => 'rand'],
- ]],
- ]],
- ['key' => 'interactions', 'title' => '互动功能', 'fields' => [
- ['key' => 'post_favorite_enabled', 'label' => '收藏', 'type' => 'switch', 'default' => true],
- ['key' => 'share_s', 'label' => '分享按钮', 'type' => 'switch', 'default' => true],
- ['key' => 'post_copyright_enabled', 'label' => '版权信息', 'type' => 'switch', 'default' => true],
- ['key' => 'post_copyright_text', 'label' => '版权默认文案', 'type' => 'textarea', 'default' => '', 'description' => '留空则自动生成"本文由 XX 原创发布"'],
- ['key' => 'post_wechat_share', 'label' => '微信分享', 'type' => 'switch', 'default' => false],
- ['key' => 'post_rewards_s', 'label' => '用户打赏', 'type' => 'switch', 'default' => false],
- ['key' => 'post_rewards_text', 'label' => '打赏按钮文案', 'type' => 'input', 'default' => '赞赏'],
- ['key' => 'post_rewards_amounts', 'label' => '快捷金额', 'type' => 'input', 'default' => '5,10,20,50,100', 'description' => '英文逗号分隔,赞赏直接进入作者站内余额'],
- ['key' => 'post_rewards_custom', 'label' => '允许自定义金额', 'type' => 'switch', 'default' => true],
- ['key' => 'post_rewards_min', 'label' => '最低金额', 'type' => 'input', 'sanitize_type' => 'money_decimal', 'default' => '1.00', 'min' => 0.01, 'max' => 1000, 'step' => 0.01],
- ['key' => 'post_rewards_max', 'label' => '最高金额', 'type' => 'input', 'sanitize_type' => 'money_decimal', 'default' => '1000.00', 'min' => 1, 'max' => 100000, 'step' => 0.01],
- ['key' => 'post_button_toptext', 'label' => '按钮上方文案', 'type' => 'input', 'default' => '喜欢就支持一下吧'],
- ]],
- ['key' => 'content', 'title' => '内容处理', 'fields' => [
- ['key' => 'post_p_indent', 'label' => '段落缩进', 'type' => 'switch', 'default' => false],
- ['key' => 'post_maxheight_kg', 'label' => '内容高度限制', 'type' => 'switch', 'default' => false],
- ['key' => 'post_maxheight', 'label' => '最大高度', 'type' => 'number', 'default' => 1000, 'min' => 600, 'max' => 3000],
- ['key' => 'post_front_content', 'label' => '文章头部内容', 'type' => 'textarea', 'default' => ''],
- ['key' => 'post_after_content', 'label' => '文章底部内容', 'type' => 'textarea', 'default' => ''],
- ]],
- ['key' => 'yiyan', 'title' => '一言', 'fields' => [
- ['key' => 'post_yiyan_header', 'label' => '文章头部一言', 'type' => 'switch', 'default' => false],
- ['key' => 'post_yiyan_footer', 'label' => '文章底部一言', 'type' => 'switch', 'default' => false],
- ['key' => 'post_yiyan_box', 'label' => '一言盒子', 'type' => 'switch', 'default' => false],
- ]],
- ['key' => 'meta', 'title' => 'Meta设置', 'fields' => [
- ['key' => 'post_views_s', 'label' => '浏览统计', 'type' => 'switch', 'default' => true],
]],
],
],
@@ -286,7 +286,10 @@ class SchemaPostList {
'version' => '1.0.0',
'option_key' => 'aksrt_settings',
'groups' => [
- ['key' => 'basic', 'title' => '分享设置', 'fields' => [
+ ['key' => 'basic', 'title' => '分享按钮', 'fields' => [
+ ['key' => 'share_s', 'label' => '分享按钮', 'type' => 'switcher', 'default' => true, 'help' => '关闭后,在文章页面将不显示分享按钮,论坛及商城不受影响'],
+ ]],
+ ['key' => 'wechat', 'title' => '微信分享', 'fields' => [
['key' => 'share_wechat_s', 'label' => '微信分享', 'type' => 'switch', 'default' => false],
['key' => 'share_wechat_appid', 'label' => 'AppID', 'type' => 'input', 'default' => ''],
['key' => 'share_wechat_app_secret', 'label' => 'AppSecret', 'type' => 'input', 'default' => ''],
diff --git a/src/Admin/Schema/SchemaUsers.php b/src/Admin/Schema/SchemaUsers.php
index d21604fc..5216401a 100644
--- a/src/Admin/Schema/SchemaUsers.php
+++ b/src/Admin/Schema/SchemaUsers.php
@@ -525,6 +525,14 @@ class SchemaUsers {
['key' => 'user_cover_img', 'label' => '用户默认封面', 'type' => 'media', 'default' => get_theme_file_uri('assets/img/user_t.jpg'), 'help' => '建议尺寸 1000×400;用户未设置封面时显示此图。'],
['key' => 'user_desc_std', 'label' => '用户默认签名', 'type' => 'input', 'default' => '这家伙很懒,什么都没有写...', 'help' => '用户未设置个人简介时显示的默认文案。'],
]],
+ ['key' => 'rewards', 'title' => '打赏', 'fields' => [
+ ['key' => 'post_rewards_s', 'label' => '用户打赏功能', 'type' => 'switcher', 'default' => true, 'help' => '开启后,登录用户可使用站内余额赞赏作者,金额直接进入作者余额。'],
+ ['key' => 'post_rewards_text', 'label' => '打赏按钮文案', 'type' => 'input', 'default' => '赞赏', 'dependsOn' => ['key' => 'post_rewards_s', 'value' => true], 'help' => '自定义打赏按钮文字'],
+ ['key' => 'post_rewards_amounts', 'label' => '快捷金额', 'type' => 'input', 'default' => '5,10,20,50,100', 'dependsOn' => ['key' => 'post_rewards_s', 'value' => true], 'help' => '使用英文逗号分隔'],
+ ['key' => 'post_rewards_custom', 'label' => '允许自定义金额', 'type' => 'switcher', 'default' => true, 'dependsOn' => ['key' => 'post_rewards_s', 'value' => true]],
+ ['key' => 'post_rewards_min', 'label' => '最低金额', 'type' => 'input', 'sanitize_type' => 'money_decimal', 'default' => '1.00', 'min' => 0.01, 'max' => 1000, 'step' => 0.01, 'dependsOn' => ['key' => 'post_rewards_s', 'value' => true]],
+ ['key' => 'post_rewards_max', 'label' => '最高金额', 'type' => 'input', 'sanitize_type' => 'money_decimal', 'default' => '1000.00', 'min' => 1, 'max' => 100000, 'step' => 0.01, 'dependsOn' => ['key' => 'post_rewards_s', 'value' => true]],
+ ]],
],
],
'verify' => [
diff --git a/src/Core/PostMetaBox.php b/src/Core/PostMetaBox.php
index 2d6dfd21..ee26ae85 100644
--- a/src/Core/PostMetaBox.php
+++ b/src/Core/PostMetaBox.php
@@ -109,13 +109,13 @@ class PostMetaBox {
public function render_meta_box(\WP_Post $post): void {
$meta = self::get_meta($post->ID);
$settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : [];
- $show_image = !array_key_exists('post_article_image_cover', $settings) || !empty($settings['post_article_image_cover']);
+ $show_image = !array_key_exists('article_image_cover', $settings) || !empty($settings['article_image_cover']);
$show_slide = !array_key_exists('list_thumb_slides_s', $settings)
|| !empty($settings['list_thumb_slides_s'])
- || !empty($settings['post_article_slide_cover']);
+ || !empty($settings['article_slide_cover']);
$show_video = !array_key_exists('list_thumb_video_s', $settings)
|| !empty($settings['list_thumb_video_s'])
- || !empty($settings['post_article_video_cover']);
+ || !empty($settings['article_video_cover']);
wp_nonce_field('aksrt_post_extension_save', 'aksrt_post_extension_nonce');
?>
diff --git a/src/Settings/FacadeBulk.php b/src/Settings/FacadeBulk.php
index 7ab850b0..003c620b 100644
--- a/src/Settings/FacadeBulk.php
+++ b/src/Settings/FacadeBulk.php
@@ -165,10 +165,20 @@ trait FacadeBulk {
}
$settings = self::get_all();
+ // 参考主题语义:switcher 开 = 面包屑用“首页”替代网站名称。
+ $breadcrumbs_home_replace = !isset($settings['breadcrumbs_home_text']) || !empty($settings['breadcrumbs_home_text']);
return [
- 'reading_progress' => !isset($settings['post_reading_progress']) || !empty($settings['post_reading_progress']),
- 'breadcrumbs' => !isset($settings['breadcrumbs_single_s']) || !empty($settings['breadcrumbs_single_s']),
- 'breadcrumbs_home' => trim((string) ($settings['breadcrumbs_home_text'] ?? '')) ?: __('首页', 'aksrt'),
+ 'reading_progress' => true,
+ 'breadcrumbs' => !isset($settings['breadcrumbs_single_s']) || !empty($settings['breadcrumbs_single_s']),
+ 'breadcrumbs_home' => $breadcrumbs_home_replace ? __('首页', 'aksrt') : (string) get_bloginfo('name'),
+ 'breadcrumbs_home_replace' => $breadcrumbs_home_replace,
+ 'p_indent' => !empty($settings['post_p_indent_s']),
+ 'article_image_cover' => !isset($settings['article_image_cover']) || !empty($settings['article_image_cover']),
+ 'article_image_cover_scale' => 35,
+ 'article_slide_cover' => !isset($settings['article_slide_cover']) || !empty($settings['article_slide_cover']),
+ 'article_slide_cover_option'=> [],
+ 'article_video_cover' => !isset($settings['article_video_cover']) || !empty($settings['article_video_cover']),
+ 'article_video_cover_scale' => 0,
'related_enabled' => !isset($settings['post_related_s']) || !empty($settings['post_related_s']),
'related_count' => max(4, min(12, (int) ($settings['post_related_n'] ?? 6))),
'related_style' => in_array(($settings['post_related_type'] ?? 'img'), ['img', 'list', 'text'], true) ? (string) $settings['post_related_type'] : 'img',
diff --git a/src/Settings/PostDisplay.php b/src/Settings/PostDisplay.php
index 829517f9..00081a4e 100644
--- a/src/Settings/PostDisplay.php
+++ b/src/Settings/PostDisplay.php
@@ -152,9 +152,9 @@ class PostDisplay {
// Canonical render keys (used by aksrt_single_*). Legacy *_s aliases kept for older callers.
$breadcrumbs = !isset($settings['breadcrumbs_single_s']) || !empty($settings['breadcrumbs_single_s']);
- $breadcrumbs_home = sanitize_text_field((string) ($settings['breadcrumbs_home_text'] ?? '首页'));
- // legacy breadcrumbs_single_text: true → last crumb is "正文"; false → full title.
- $breadcrumbs_single_text = !isset($settings['breadcrumbs_single_text']) || !empty($settings['breadcrumbs_single_text']);
+ // 参考主题语义:switcher 开 = 面包屑用“首页”替代网站名称。
+ $breadcrumbs_home_replace = !isset($settings['breadcrumbs_home_text']) || !empty($settings['breadcrumbs_home_text']);
+ $breadcrumbs_home = $breadcrumbs_home_replace ? __('首页', 'aksrt') : (string) get_bloginfo('name');
$prevnext = !isset($settings['post_prevnext_s']) || !empty($settings['post_prevnext_s']);
$authordesc = !isset($settings['post_authordesc_s']) || !empty($settings['post_authordesc_s']);
$hide_time = !empty($settings['post_single_hide_time_s']);
@@ -195,23 +195,22 @@ class PostDisplay {
}
$rewards_min = $rewards_min_amount->decimal();
$rewards_max = $rewards_max_amount->decimal();
- $author_card_limit = max(0, min(12, (int) ($settings['post_author_card_limit'] ?? 6)));
- $author_card_orderby = sanitize_key((string) ($settings['post_author_card_orderby'] ?? 'views'));
- if (!in_array($author_card_orderby, ['views', 'date', 'modified', 'comment_count'], true)) {
- $author_card_orderby = 'views';
- }
- $author_card_style = sanitize_key((string) ($settings['post_author_card_style'] ?? 'card'));
- if (!in_array($author_card_style, ['card', 'list'], true)) {
- $author_card_style = 'card';
- }
- $maxheight_enabled = !empty($settings['article_maxheight_kg'])
- || !empty($settings['post_article_maxheight_kg'])
- || !empty($settings['post_maxheight_kg']);
- $maxheight = max(0, (int) ($settings['article_maxheight'] ?? $settings['post_article_maxheight'] ?? $settings['post_maxheight'] ?? 1000));
+ // 内容高度限制(参考键 article_maxheight_kg / article_maxheight)。
+ $maxheight_enabled = !empty($settings['article_maxheight_kg']);
+ $maxheight = max(0, (int) ($settings['article_maxheight'] ?? 1000));
if ($maxheight <= 0) {
$maxheight = 1000;
}
+ // 封面(参考键 article_*_cover + option fieldset)。
+ $image_cover_option = is_array($settings['article_image_cover_option'] ?? null) ? $settings['article_image_cover_option'] : [];
+ $image_cover_scale = max(20, min(200, (int) ($image_cover_option['scale'] ?? 35)));
+ $video_cover_option = is_array($settings['article_video_cover_option'] ?? null) ? $settings['article_video_cover_option'] : [];
+ $video_cover_scale = max(0, min(200, (int) ($video_cover_option['scale'] ?? 0)));
+
+ $slide_option_raw = is_array($settings['article_slide_cover_option'] ?? null) ? $settings['article_slide_cover_option'] : [];
+ $slide_option = self::normalize_slide_cover_option($slide_option_raw);
+
// 图片灯箱(参考主题 imagelightbox*):pc_s/m_s 复选框归并为单一开关。
$lightbox_type = sanitize_key((string) ($settings['imagelightbox_type'] ?? 'group'));
if (!in_array($lightbox_type, ['group', 'alone'], true)) {
@@ -245,24 +244,28 @@ class PostDisplay {
}
return [
- 'reading_progress' => !isset($settings['post_reading_progress']) || !empty($settings['post_reading_progress']),
+ // 参考主题无阅读进度条配置:功能常开(字段已随 1:1 复刻移除)。
+ 'reading_progress' => true,
// Canonical + legacy breadcrumbs.
'breadcrumbs' => $breadcrumbs,
'breadcrumbs_home' => $breadcrumbs_home,
+ 'breadcrumbs_home_replace' => $breadcrumbs_home_replace,
'breadcrumbs_single_s' => $breadcrumbs,
- 'breadcrumbs_home_text' => $breadcrumbs_home,
- 'breadcrumbs_single_text' => $breadcrumbs_single_text,
+ 'breadcrumbs_home_text' => $breadcrumbs_home_replace,
+ 'breadcrumbs_single_text' => true,
// Canonical + legacy prev/next / time; canonical author card flag.
'prevnext' => $prevnext,
'prevnext_s' => $prevnext,
'authordesc' => $authordesc,
'hide_time' => $hide_time,
'single_hide_time_s' => $hide_time,
- 'article_image_cover' => !isset($settings['post_article_image_cover']) || !empty($settings['post_article_image_cover']),
- 'article_image_cover_scale'=> max(10, min(100, (int) ($settings['post_article_image_cover_scale'] ?? 35))),
- 'article_slide_cover' => !isset($settings['post_article_slide_cover']) || !empty($settings['post_article_slide_cover']),
- 'article_video_cover' => !isset($settings['post_article_video_cover']) || !empty($settings['post_article_video_cover']),
- 'article_video_cover_scale'=> max(20, min(100, (int) ($settings['post_article_video_cover_scale'] ?? 56))),
+ 'p_indent' => !empty($settings['post_p_indent_s']),
+ 'article_image_cover' => !isset($settings['article_image_cover']) || !empty($settings['article_image_cover']),
+ 'article_image_cover_scale'=> $image_cover_scale,
+ 'article_slide_cover' => !isset($settings['article_slide_cover']) || !empty($settings['article_slide_cover']),
+ 'article_slide_cover_option' => $slide_option,
+ 'article_video_cover' => !isset($settings['article_video_cover']) || !empty($settings['article_video_cover']),
+ 'article_video_cover_scale'=> $video_cover_scale,
'lightbox' => !isset($settings['imagelightbox']) || !empty($settings['imagelightbox']),
'lightbox_type' => $lightbox_type,
'lightbox_thumbs' => $lightbox_thumbs,
@@ -276,15 +279,16 @@ class PostDisplay {
'related_orderby' => $related_orderby,
'related_title' => sanitize_text_field((string) ($settings['related_title'] ?? '相关推荐')),
'like_enabled' => !isset($settings['post_like_s']) || !empty($settings['post_like_s']),
- 'favorite_enabled' => !isset($settings['post_favorite_enabled']) || !empty($settings['post_favorite_enabled']),
+ // 参考主题无收藏开关:收藏按钮常显(字段已随 1:1 复刻移除)。
+ 'favorite_enabled' => true,
// Canonical + legacy share.
'share_enabled' => $share_enabled,
'share_s' => $share_enabled,
- // Author card posts (legacy user_card_box defaults).
- 'author_card_limit' => $author_card_limit,
- 'author_card_orderby' => $author_card_orderby,
- 'author_card_style' => $author_card_style,
- // On-site wallet tipping.
+ // Author card posts(参考主题无独立配置:保持默认)。
+ 'author_card_limit' => 6,
+ 'author_card_orderby' => 'views',
+ 'author_card_style' => 'card',
+ // On-site wallet tipping(配置位于用户功能页,参考主题同位置)。
'rewards_enabled' => $rewards_enabled,
'post_rewards_s' => $rewards_enabled,
'rewards_text' => $rewards_text,
@@ -296,14 +300,14 @@ class PostDisplay {
// Content max height (legacy article_maxheight_kg / article_maxheight).
'maxheight_enabled' => $maxheight_enabled,
'maxheight' => $maxheight,
- 'copyright_enabled' => !isset($settings['post_copyright_enabled']) || !empty($settings['post_copyright_enabled']),
- 'copyright_text' => (string) ($settings['post_copyright_text'] ?? ''),
+ 'copyright_enabled' => !isset($settings['post_copyright_s']) || !empty($settings['post_copyright_s']),
+ 'copyright_text' => (string) ($settings['post_copyright'] ?? '文章版权归作者所有,未经允许请勿转载。'),
'button_toptext' => sanitize_text_field((string) ($settings['post_button_toptext'] ?? '喜欢就支持一下吧')),
'front_content' => (string) ($settings['post_front_content'] ?? ''),
'after_content' => (string) ($settings['post_after_content'] ?? ''),
- 'yiyan_header' => !empty($settings['post_yiyan_header']),
- 'yiyan_footer' => !empty($settings['post_yiyan_footer']),
- 'yiyan_box' => !empty($settings['post_yiyan_box']),
+ 'yiyan_header' => !empty($settings['yiyan_single_content_header']),
+ 'yiyan_footer' => !empty($settings['yiyan_single_content_footer']),
+ 'yiyan_box' => !empty($settings['yiyan_single_box']),
'default_views_min' => max(0, min(9999, (int) ($views_range['min'] ?? 20))),
'default_views_max' => max(1, min(9999, (int) ($views_range['max'] ?? 50))),
'default_likes_min' => max(0, min(9999, (int) ($like_range['min'] ?? 5))),
@@ -313,6 +317,35 @@ class PostDisplay {
];
}
+ /**
+ * 幻灯片封面配置归一化(参考主题 CFS_Module::slide 全字段)。
+ */
+ private static function normalize_slide_cover_option(array $raw): array {
+ $effect = sanitize_key((string) ($raw['effect'] ?? 'slide'));
+ if (!in_array($effect, ['slide', 'fade', 'cube', 'coverflow', 'flip'], true)) {
+ $effect = 'slide';
+ }
+ $direction = sanitize_key((string) ($raw['direction'] ?? 'horizontal')) === 'vertical' ? 'vertical' : 'horizontal';
+ return [
+ 'direction' => $direction,
+ 'loop' => !empty($raw['loop']),
+ 'button' => !empty($raw['button']),
+ 'pagination' => !empty($raw['pagination']),
+ 'effect' => $effect,
+ 'scale_height' => !empty($raw['scale_height']),
+ 'scale' => max(10, min(300, (int) ($raw['scale'] ?? 35))),
+ 'auto_height' => !empty($raw['auto_height']),
+ 'max_height' => max(120, min(800, (int) ($raw['max_height'] ?? 500))),
+ 'min_height' => max(100, min(500, (int) ($raw['min_height'] ?? 180))),
+ 'pc_height' => max(120, min(800, (int) ($raw['pc_height'] ?? 380))),
+ 'm_height' => max(100, min(500, (int) ($raw['m_height'] ?? 180))),
+ 'spacebetween' => max(0, min(500, (int) ($raw['spacebetween'] ?? 15))),
+ 'speed' => max(0, min(3000, (int) ($raw['speed'] ?? 0))),
+ 'autoplay' => !empty($raw['autoplay']),
+ 'interval' => max(0, min(20, (int) ($raw['interval'] ?? 4))),
+ ];
+ }
+
public static function get_share_settings(): array {
$settings = Settings::get_all();
return [
diff --git a/tests/admin/post-page-settings-reference-contract.test.cjs b/tests/admin/post-page-settings-reference-contract.test.cjs
new file mode 100644
index 00000000..2fb405f0
--- /dev/null
+++ b/tests/admin/post-page-settings-reference-contract.test.cjs
@@ -0,0 +1,341 @@
+'use strict';
+
+/**
+ * 「文章页面」后台设置页契约(对齐参考主题 zibll 1:1):
+ * 1) PHP schema(SchemaPostList.php page section 2.0.0)字段键、顺序、默认值与
+ * JS schema(assets/admin/src/schema/sections/post-list/page.js)一致;
+ * 2) 参考主题字段全集存在:三类封面(article_image_cover / article_slide_cover /
+ * article_video_cover + option fieldset)、post_single_hide_time_s、
+ * post_p_indent_s、breadcrumbs_single_s、breadcrumbs_home_text(switcher)、
+ * post_prevnext_s、post_authordesc_s、article_maxheight_kg/maxheight、
+ * 一言三项、post_copyright_s/post_copyright、post_button_toptext、
+ * post_front_content/after_content、post_related_*;
+ * 3) 幻灯片 fieldset 全字段及嵌套依赖(direction/scale_height/auto_height 组合)
+ * 对齐参考主题 CFS_Module::slide;
+ * 4) 旧 AKSRT 私有/异名字段(阅读进度条、面包屑末项文本、作者卡片配置、收藏开关、
+ * share_s 与 post_rewards_*、旧封面键)已彻底移除,无双路兼容;
+ * 5) 运行时(PostDisplay::get_post_page_settings)只读取新键:幻灯片配置归一化并
+ * 接线 aksrt_render_slider,p_indent 接线容器类,breadcrumbs_home_text 为
+ * switcher 布尔语义(true = 用「首页」替代网站名称)。
+ */
+
+const assert = require('node:assert/strict');
+const fs = require('node:fs');
+const path = require('node:path');
+const test = require('node:test');
+
+const root = path.resolve(__dirname, '..', '..');
+const php = fs.readFileSync(path.join(root, 'src/Admin/Schema/SchemaPostList.php'), 'utf8');
+const js = fs.readFileSync(path.join(root, 'assets/admin/src/schema/sections/post-list/page.js'), 'utf8');
+const runtime = fs.readFileSync(path.join(root, 'src/Settings/PostDisplay.php'), 'utf8');
+const facadeBulk = fs.readFileSync(path.join(root, 'src/Settings/FacadeBulk.php'), 'utf8');
+const metaBox = fs.readFileSync(path.join(root, 'src/Core/PostMetaBox.php'), 'utf8');
+const frontendTags = fs.readFileSync(path.join(root, 'inc/helpers/aksrt-frontend-tags.php'), 'utf8');
+const singleRender = fs.readFileSync(path.join(root, 'inc/helpers/aksrt-single-render.php'), 'utf8');
+const contentCss = fs.readFileSync(path.join(root, 'assets/css/frontend/single-post/content.css'), 'utf8');
+const shareJs = fs.readFileSync(path.join(root, 'assets/admin/src/schema/sections/post-list/share.js'), 'utf8');
+const sharePhp = php;
+const featuresJs = fs.readFileSync(path.join(root, 'assets/admin/src/schema/sections/users/features.js'), 'utf8');
+const usersPhp = fs.readFileSync(path.join(root, 'src/Admin/Schema/SchemaUsers.php'), 'utf8');
+
+/** Extract a PHP section body by its section key (balanced brackets). */
+function phpSection(src, sectionKey) {
+ const start = src.indexOf(`'${sectionKey}' => [`);
+ assert.notEqual(start, -1, `PHP schema has a ${sectionKey} section`);
+ const open = src.indexOf('[', start);
+ let depth = 0;
+ for (let i = open; i < src.length; i++) {
+ const ch = src[i];
+ if (ch === "'" || ch === '"') {
+ const quote = ch;
+ i++;
+ while (i < src.length) {
+ if (src[i] === '\\') { i += 2; continue; }
+ if (src[i] === quote) break;
+ i++;
+ }
+ continue;
+ }
+ if (ch === '[') depth++;
+ else if (ch === ']') {
+ depth--;
+ if (depth === 0) return src.slice(open, i + 1);
+ }
+ }
+ throw new Error(`unbalanced ${sectionKey} section`);
+}
+
+const pagePhp = phpSection(php, 'page');
+
+// 参考主题 admin-options.php 4934–5249 的字段顺序(含 fieldset 父字段与子字段)。
+const PAGE_FIELD_SEQUENCE = [
+ 'article_image_cover',
+ 'article_image_cover_option',
+ 'scale', // article_image_cover_option.scale
+ 'article_slide_cover',
+ 'article_slide_cover_option',
+ // article_slide_cover_option(CFS_Module::slide 全字段)
+ 'direction', 'loop', 'button', 'pagination', 'effect', 'scale_height', 'scale',
+ 'auto_height', 'max_height', 'min_height', 'pc_height', 'm_height',
+ 'spacebetween', 'speed', 'autoplay', 'interval',
+ 'article_video_cover',
+ 'article_video_cover_option',
+ 'scale', // article_video_cover_option.scale
+ 'post_single_hide_time_s',
+ 'post_p_indent_s',
+ 'breadcrumbs_single_s',
+ 'breadcrumbs_home_text',
+ 'post_prevnext_s',
+ 'post_authordesc_s',
+ 'article_maxheight_kg',
+ 'article_maxheight',
+ 'yiyan_single_content_header',
+ 'yiyan_single_content_footer',
+ 'yiyan_single_box',
+ 'post_copyright_s',
+ 'post_copyright',
+ 'post_button_toptext',
+ 'post_front_content',
+ 'post_after_content',
+ 'post_related_s',
+ 'post_related_type',
+ 'related_title',
+ 'post_related_n',
+ 'post_related_orderby',
+];
+
+// 旧 AKSRT 私有/异名字段:已迁移(share_s / post_rewards_*)或无参考对应项被移除。
+const LEGACY_KEYS = [
+ 'post_reading_progress',
+ 'post_author_card_limit',
+ 'post_author_card_orderby',
+ 'post_author_card_style',
+ 'post_favorite_enabled',
+ 'post_wechat_share',
+ 'post_article_image_cover',
+ 'post_article_slide_cover',
+ 'post_article_video_cover',
+];
+
+test('PHP page section 字段键与参考主题一致且顺序相同', () => {
+ // 字段定义均为 ['key' => '...', 'label' => ...] 开头;dependsOn 内的
+ // ['key' => '...', 'value'/'not' => ...] 不是字段定义,须排除。
+ const found = [];
+ const re = /'key'\s*=>\s*'([a-z0-9_]+)',\s*'label'/g;
+ let m;
+ while ((m = re.exec(pagePhp)) !== null) found.push(m[1]);
+ assert.deepEqual(found, PAGE_FIELD_SEQUENCE);
+});
+
+test('PHP page section 版本升级至 2.0.0 且为单一 post_page 组', () => {
+ assert.match(pagePhp, /'version'\s*=>\s*'2\.0\.0'/);
+ assert.match(pagePhp, /'groups'\s*=>\s*\[\s*\['key'\s*=>\s*'post_page',\s*'title'\s*=>\s*'文章页面'/);
+});
+
+test('PHP page section 默认值对齐参考主题', () => {
+ // 三类封面开关。
+ assert.match(pagePhp, /'article_image_cover',\s*'label'\s*=>\s*'图片封面',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*true/);
+ assert.match(pagePhp, /'article_slide_cover',\s*'label'\s*=>\s*'幻灯片封面',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*true/);
+ assert.match(pagePhp, /'article_video_cover',\s*'label'\s*=>\s*'视频封面',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*true/);
+ // 图片封面比例:20-200 step5,默认 35。
+ assert.match(pagePhp, /'scale',\s*'label'\s*=>\s*'图片长宽比例',\s*'type'\s*=>\s*'number',\s*'default'\s*=>\s*35,\s*'min'\s*=>\s*20,\s*'max'\s*=>\s*200,\s*'step'\s*=>\s*5/);
+ // 视频封面比例:0-200,默认 0(为 0 则不固定长宽比例)。
+ assert.match(pagePhp, /'scale',\s*'label'\s*=>\s*'固定长宽比例',\s*'type'\s*=>\s*'number',\s*'default'\s*=>\s*0,\s*'min'\s*=>\s*0,\s*'max'\s*=>\s*200/);
+ // 面包屑「首页」替代开关(switcher,默认 true)。
+ assert.match(pagePhp, /'breadcrumbs_home_text',\s*'label'\s*=>\s*'面包屑导航用“首页”替代网站名称',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*true/);
+ // 段落缩进 / 隐藏时间。
+ assert.match(pagePhp, /'post_p_indent_s',\s*'label'\s*=>\s*'内容段落缩进',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*false/);
+ assert.match(pagePhp, /'post_single_hide_time_s',\s*'label'\s*=>\s*'隐藏时间',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*false/);
+ // 内容高度限制:slider 600-3000 step100。
+ assert.match(pagePhp, /'article_maxheight_kg',\s*'label'\s*=>\s*'内容高度限制',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*false/);
+ assert.match(pagePhp, /'article_maxheight',\s*'label'\s*=>\s*'限制的最大高度',\s*'type'\s*=>\s*'slider',\s*'default'\s*=>\s*1000,\s*'min'\s*=>\s*600,\s*'max'\s*=>\s*3000,\s*'step'\s*=>\s*100/);
+ // 版权声明。
+ assert.match(pagePhp, /'post_copyright_s',\s*'label'\s*=>\s*'版权声明',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*true/);
+ assert.match(pagePhp, /'post_copyright',\s*'label'\s*=>\s*'版权提示内容',\s*'type'\s*=>\s*'textarea',\s*'default'\s*=>\s*'文章版权归作者所有,未经允许请勿转载。'/);
+ assert.match(pagePhp, /'post_button_toptext',\s*'label'\s*=>\s*'文章页脚文案',\s*'type'\s*=>\s*'input',\s*'default'\s*=>\s*'喜欢就支持一下吧'/);
+ // 相关文章:image_select 三样式 + 数量 4-12 step2 + 排序默认 views。
+ assert.match(pagePhp, /'post_related_type',\s*'label'\s*=>\s*'显示样式',\s*'type'\s*=>\s*'image_select',\s*'default'\s*=>\s*'img'/);
+ assert.match(pagePhp, /'post_related_n',\s*'label'\s*=>\s*'显示数量',\s*'type'\s*=>\s*'number',\s*'default'\s*=>\s*6,\s*'min'\s*=>\s*4,\s*'max'\s*=>\s*12,\s*'step'\s*=>\s*2/);
+ assert.match(pagePhp, /'post_related_orderby',\s*'label'\s*=>\s*'排序方式',\s*'type'\s*=>\s*'select',\s*'default'\s*=>\s*'views'/);
+});
+
+test('PHP slide fieldset 全字段及嵌套依赖对齐 CFS_Module::slide', () => {
+ // 幻灯片默认值(pagination: true, pc_height: 380, m_height: 180 为参考主题默认)。
+ assert.match(pagePhp, /'direction'\s*=>\s*'horizontal',\s*'loop'\s*=>\s*true,\s*'button'\s*=>\s*false,\s*'pagination'\s*=>\s*true,\s*'effect'\s*=>\s*'slide'/);
+ assert.match(pagePhp, /'pc_height'\s*=>\s*380,\s*'m_height'\s*=>\s*180,\s*'spacebetween'\s*=>\s*15,\s*'speed'\s*=>\s*0,\s*'autoplay'\s*=>\s*true,\s*'interval'\s*=>\s*4/);
+ // 嵌套依赖:scale_height 需要 direction != vertical。
+ assert.match(pagePhp, /'scale_height',\s*'label'\s*=>\s*'按比例自动高度',\s*'type'\s*=>\s*'switch',\s*'default'\s*=>\s*true,\s*'dependsOn'\s*=>\s*\['key'\s*=>\s*'direction',\s*'not'\s*=>\s*'vertical'\]/);
+ // scale 需要 direction != vertical 且 scale_height 开启。
+ assert.match(pagePhp, /'scale',\s*'label'\s*=>\s*'长宽比例'[\s\S]*?'dependsOn'\s*=>\s*\['all'\s*=>\s*\[\['key'\s*=>\s*'direction',\s*'not'\s*=>\s*'vertical'\],\s*\['key'\s*=>\s*'scale_height',\s*'value'\s*=>\s*true\]\]\]/);
+ // auto_height 需要 direction != vertical 且 scale_height 关闭。
+ assert.match(pagePhp, /'auto_height',\s*'label'\s*=>\s*'自动高度'[\s\S]*?'dependsOn'\s*=>\s*\['all'\s*=>\s*\[\['key'\s*=>\s*'direction',\s*'not'\s*=>\s*'vertical'\],\s*\['key'\s*=>\s*'scale_height',\s*'value'\s*=>\s*false\]\]\]/);
+ // max/min_height 需要 auto_height 开启 + direction != vertical + scale_height 关闭。
+ assert.match(pagePhp, /'max_height',\s*'label'\s*=>\s*'最大高度'[\s\S]*?'dependsOn'\s*=>\s*\['all'\s*=>\s*\[\['key'\s*=>\s*'auto_height',\s*'value'\s*=>\s*true\],\s*\['key'\s*=>\s*'direction',\s*'not'\s*=>\s*'vertical'\],\s*\['key'\s*=>\s*'scale_height',\s*'value'\s*=>\s*false\]\]\]/);
+ // interval 依赖 autoplay。
+ assert.match(pagePhp, /'interval',\s*'label'\s*=>\s*'停顿时间'[\s\S]*?'dependsOn'\s*=>\s*\['key'\s*=>\s*'autoplay',\s*'value'\s*=>\s*true\]/);
+ // 五种切换动画。
+ for (const effect of ['slide', 'fade', 'cube', 'coverflow', 'flip']) {
+ assert.ok(pagePhp.includes(`'value' => '${effect}'`), `缺少切换动画 ${effect}`);
+ }
+});
+
+test('PHP page section 不含旧私有/异名字段(无双路)', () => {
+ for (const legacy of LEGACY_KEYS) {
+ assert.ok(!pagePhp.includes(`'${legacy}'`), `page section 残留旧字段: ${legacy}`);
+ }
+ // share_s / post_rewards_* 已迁移至分享设置页与用户功能页。
+ assert.ok(!pagePhp.includes(`'share_s'`), 'page section 残留 share_s');
+ assert.ok(!pagePhp.includes(`'post_rewards_`), 'page section 残留 post_rewards_*');
+});
+
+test('JS schema 与 PHP 字段键一致(ui_only 一言行除外)', () => {
+ const keys = [];
+ const re = /key:\s*'([a-z0-9_]+)',\s*label:/g;
+ let m;
+ while ((m = re.exec(js)) !== null) keys.push(m[1]);
+ assert.deepEqual(keys, PAGE_FIELD_SEQUENCE);
+ for (const legacy of LEGACY_KEYS) {
+ assert.ok(!keys.includes(legacy), `JS schema 残留旧字段: ${legacy}`);
+ }
+ assert.ok(!js.includes(`key: 'share_s'`), 'JS page schema 残留 share_s');
+ assert.ok(!/key:\s*'post_rewards_/.test(js), 'JS page schema 残留 post_rewards_*');
+ // 参考主题视频封面配置教程链接以 ui_only description 承载。
+ assert.match(js, /ui_only:\s*true/);
+ assert.match(js, /https:\/\/www\.zibll\.com\/1900\.html/);
+});
+
+test('JS schema 默认值与依赖对齐参考主题', () => {
+ assert.match(js, /article_image_cover:\s*true/);
+ assert.match(js, /article_image_cover_option:\s*\{\s*scale:\s*35\s*\}/);
+ assert.match(js, /article_slide_cover:\s*true/);
+ assert.match(js, /article_video_cover:\s*true/);
+ assert.match(js, /article_video_cover_option:\s*\{\s*scale:\s*0\s*\}/);
+ assert.match(js, /post_single_hide_time_s:\s*false/);
+ assert.match(js, /post_p_indent_s:\s*false/);
+ assert.match(js, /breadcrumbs_single_s:\s*true/);
+ assert.match(js, /breadcrumbs_home_text:\s*true/);
+ assert.match(js, /post_prevnext_s:\s*true/);
+ assert.match(js, /post_authordesc_s:\s*true/);
+ assert.match(js, /article_maxheight_kg:\s*false/);
+ assert.match(js, /article_maxheight:\s*1000/);
+ assert.match(js, /post_copyright_s:\s*true/);
+ assert.match(js, /post_copyright:\s*'文章版权归作者所有,未经允许请勿转载。'/);
+ assert.match(js, /post_button_toptext:\s*'喜欢就支持一下吧'/);
+ assert.match(js, /post_related_s:\s*true/);
+ assert.match(js, /post_related_type:\s*'img'/);
+ assert.match(js, /related_title:\s*'相关推荐'/);
+ assert.match(js, /post_related_n:\s*6/);
+ assert.match(js, /post_related_orderby:\s*'views'/);
+ // 幻灯片嵌套依赖(dependsOn {key, not} / {all: [...]} 形式)。
+ assert.match(js, /key:\s*'scale_height',[\s\S]*?dependsOn:\s*\{\s*key:\s*'direction',\s*not:\s*'vertical'\s*\}/);
+ assert.match(js, /key:\s*'scale',[\s\S]*?dependsOn:\s*\{\s*all:\s*\[\{\s*key:\s*'direction',\s*not:\s*'vertical'\s*\},\s*\{\s*key:\s*'scale_height',\s*value:\s*true\s*\}\]\s*\}/);
+ assert.match(js, /key:\s*'auto_height',[\s\S]*?dependsOn:\s*\{\s*all:\s*\[\{\s*key:\s*'direction',\s*not:\s*'vertical'\s*\},\s*\{\s*key:\s*'scale_height',\s*value:\s*false\s*\}\]\s*\}/);
+ assert.match(js, /key:\s*'interval',[\s\S]*?dependsOn:\s*\{\s*key:\s*'autoplay',\s*value:\s*true\s*\}/);
+});
+
+test('share_s 迁移至分享设置页 basic 组(键不变,无双路)', () => {
+ assert.match(sharePhp, /\['key' => 'basic', 'title' => '分享按钮', 'fields' => \[\s*\['key' => 'share_s', 'label' => '分享按钮', 'type' => 'switcher', 'default' => true, 'help' => '关闭后,在文章页面将不显示分享按钮,论坛及商城不受影响'\]/);
+ assert.match(shareJs, /key: 'basic',/);
+ assert.match(shareJs, /share_s: true/);
+ assert.match(shareJs, /key: 'share_s', label: '分享按钮', type: 'switch', default: true, help: '关闭后,在文章页面将不显示分享按钮,论坛及商城不受影响'/);
+});
+
+test('post_rewards_* 迁移至用户功能页 rewards 组(对齐参考主题 8977 行)', () => {
+ assert.match(usersPhp, /\['key' => 'rewards', 'title' => '打赏', 'fields' => \[/);
+ assert.match(usersPhp, /'post_rewards_s', 'label' => '用户打赏功能', 'type' => 'switcher', 'default' => true/);
+ assert.match(usersPhp, /'post_rewards_text', 'label' => '打赏按钮文案', 'type' => 'input', 'default' => '赞赏'/);
+ assert.match(featuresJs, /post_rewards_s:\s*true/);
+ assert.match(featuresJs, /post_rewards_text:\s*'赞赏'/);
+ assert.match(featuresJs, /key: 'post_rewards_s', label: '用户打赏功能', type: 'switch', default: true/);
+});
+
+test('旧字段在 schema 与运行时中均已移除(无双路)', () => {
+ for (const legacy of LEGACY_KEYS) {
+ assert.ok(!php.includes(legacy), `PHP schema 残留旧字段: ${legacy}`);
+ assert.ok(!runtime.includes(legacy), `PostDisplay 残留旧字段: ${legacy}`);
+ assert.ok(!js.includes(legacy), `page.js 残留旧字段: ${legacy}`);
+ assert.ok(!metaBox.includes(legacy), `PostMetaBox 残留旧字段: ${legacy}`);
+ }
+ // 面包屑末项正文无参考配置:schema 字段移除,运行时硬编码为常开。
+ assert.ok(!pagePhp.includes(`'breadcrumbs_single_text'`), 'page section 残留 breadcrumbs_single_text');
+ assert.ok(!js.includes(`key: 'breadcrumbs_single_text'`), 'page.js 残留 breadcrumbs_single_text');
+ assert.match(runtime, /'breadcrumbs_single_text'\s*=>\s*true/);
+ // aksrt-frontend-tags 不再有 breadcrumbs_home_text 文本兼容(switcher 布尔语义)。
+ assert.ok(!frontendTags.includes("breadcrumbs_home'] = (string) $settings['breadcrumbs_home_text'"), 'frontend-tags 残留 breadcrumbs_home_text 文本兼容');
+});
+
+test('运行时只读取新键并接线封面/缩进/版权/一言', () => {
+ // 三类封面 + option fieldset。
+ assert.match(runtime, /\$settings\['article_image_cover'\]/);
+ assert.match(runtime, /\$settings\['article_image_cover_option'\]/);
+ assert.match(runtime, /\$settings\['article_slide_cover'\]/);
+ assert.match(runtime, /\$settings\['article_slide_cover_option'\]/);
+ assert.match(runtime, /\$settings\['article_video_cover'\]/);
+ assert.match(runtime, /\$settings\['article_video_cover_option'\]/);
+ // 图片封面比例 clamp 20-200(参考主题默认 35)。
+ assert.match(runtime, /max\(20, min\(200, \(int\) \(\$image_cover_option\['scale'\] \?\? 35\)\)\)/);
+ // 视频封面比例 clamp 0-200(0 = 不固定长宽比例)。
+ assert.match(runtime, /max\(0, min\(200, \(int\) \(\$video_cover_option\['scale'\] \?\? 0\)\)\)/);
+ // 幻灯片配置归一化(CFS_Module::slide 全字段)。
+ assert.match(runtime, /normalize_slide_cover_option\(\$slide_option_raw\)/);
+ for (const key of ['direction', 'loop', 'button', 'pagination', 'effect', 'scale_height', 'scale', 'auto_height', 'max_height', 'min_height', 'pc_height', 'm_height', 'spacebetween', 'speed', 'autoplay', 'interval']) {
+ assert.ok(runtime.includes(`'${key}'`), `normalize_slide_cover_option 缺少 ${key}`);
+ }
+ // 面包屑「首页」替代语义:true → 「首页」,false → 网站名称。
+ assert.match(runtime, /!isset\(\$settings\['breadcrumbs_home_text'\]\) \|\| !empty\(\$settings\['breadcrumbs_home_text'\]\)/);
+ assert.match(runtime, /get_bloginfo\('name'\)/);
+ // 段落缩进 / 高度限制 / 版权 / 页脚文案 / 插入内容 / 一言 / 相关文章。
+ assert.match(runtime, /\$settings\['post_p_indent_s'\]/);
+ assert.match(runtime, /\$settings\['article_maxheight_kg'\]/);
+ assert.match(runtime, /\$settings\['article_maxheight'\]/);
+ assert.match(runtime, /\$settings\['post_copyright_s'\]/);
+ assert.match(runtime, /\$settings\['post_copyright'\]/);
+ assert.match(runtime, /\$settings\['post_button_toptext'\]/);
+ assert.match(runtime, /\$settings\['post_front_content'\]/);
+ assert.match(runtime, /\$settings\['post_after_content'\]/);
+ assert.match(runtime, /\$settings\['yiyan_single_content_header'\]/);
+ assert.match(runtime, /\$settings\['yiyan_single_content_footer'\]/);
+ assert.match(runtime, /\$settings\['yiyan_single_box'\]/);
+ assert.match(runtime, /\$settings\['post_related_s'\]/);
+ assert.match(runtime, /\$settings\['post_related_type'\]/);
+ assert.match(runtime, /\$settings\['post_related_n'\]/);
+ assert.match(runtime, /\$settings\['post_related_orderby'\]/);
+ // 阅读进度/收藏/作者卡片无参考配置:常量默认(无双路)。
+ assert.match(runtime, /'reading_progress'\s*=>\s*true/);
+ assert.match(runtime, /'favorite_enabled'\s*=>\s*true/);
+ assert.match(runtime, /'author_card_limit'\s*=>\s*6/);
+});
+
+test('前台链路:幻灯片配置接线渲染器,p_indent 接线容器与 CSS', () => {
+ // 幻灯片配置接入 aksrt_render_slider(effect/direction/spaceBetween/speed/interval)。
+ assert.match(singleRender, /article_slide_cover_option/);
+ assert.match(singleRender, /'effect'\s*=>\s*\(string\) \(\$slide_option\['effect'\] \?\? 'slide'\)/);
+ assert.match(singleRender, /'direction'\s*=>\s*\(string\) \(\$slide_option\['direction'\] \?\? 'horizontal'\)/);
+ assert.match(singleRender, /'spaceBetween'\s*=>\s*\(int\) \(\$slide_option\['spacebetween'\] \?\? 15\)/);
+ assert.match(singleRender, /'interval'\s*=>\s*max\(1, \(int\) \(\$slide_option\['interval'\] \?\? 4\)\) \* 1000/);
+ assert.match(singleRender, /'show_nav'\s*=>\s*!empty\(\$slide_option\['button'\]\)/);
+ assert.match(singleRender, /'show_pagination'\s*=>\s*!empty\(\$slide_option\['pagination'\]\)/);
+ // 固定高度模式(scale_height)与比例高度模式(scale + 50px 标题预留)双分支。
+ assert.match(singleRender, /!empty\(\$slide_option\['scale_height'\]\)/);
+ assert.match(singleRender, /\$slider_args\['pc_height'\] = \(\(int\) \(\$slide_option\['pc_height'\] \?\? 380\)\) \. 'px'/);
+ assert.match(singleRender, /\$slider_args\['scale_ratio'\] = max\(10, \(int\) \(\$slide_option\['scale'\] \?\? 35\)\) \/ 100/);
+ // 图片封面比例 clamp 20-200;视频封面 0 → 16:9(56.25)回退。
+ assert.match(singleRender, /max\(20, min\(200, \(int\) \$ctx\['img_scale'\]\)\)/);
+ assert.match(singleRender, /max\(0, min\(200, \(int\) \$ctx\['video_scale'\]\)\)/);
+ assert.match(singleRender, /\$video_scale > 0 \? \(string\) \$video_scale : '56\.25'/);
+ // p_indent 容器类 + CSS 规则(首行缩进 2em)。
+ assert.match(singleRender, /p-indent/);
+ assert.match(contentCss, /\.wp-posts-content\.p-indent p\s*\{\s*text-indent:\s*2em;/);
+ // 前端回退数组包含新键。
+ assert.match(frontendTags, /'p_indent'\s*=>\s*false/);
+ assert.match(frontendTags, /'article_image_cover_scale'\s*=>\s*35/);
+ assert.match(frontendTags, /'article_slide_cover_option'\s*=>\s*\[\]/);
+ assert.match(frontendTags, /'article_video_cover_scale'\s*=>\s*0/);
+ // PostMetaBox 读取新封面键(文章级封面字段可见性)。
+ assert.match(metaBox, /!empty\(\$settings\['article_image_cover'\]\)/);
+ assert.match(metaBox, /!empty\(\$settings\['article_slide_cover'\]\)/);
+ assert.match(metaBox, /!empty\(\$settings\['article_video_cover'\]\)/);
+ // FacadeBulk 独立回退与新键同步。
+ assert.match(facadeBulk, /'breadcrumbs_home_replace'/);
+ assert.match(facadeBulk, /'article_slide_cover_option'/);
+});
diff --git a/tests/widget-system/single-cover-height-contract.test.cjs b/tests/widget-system/single-cover-height-contract.test.cjs
index fbeb835a..e0f932f0 100644
--- a/tests/widget-system/single-cover-height-contract.test.cjs
+++ b/tests/widget-system/single-cover-height-contract.test.cjs
@@ -25,10 +25,16 @@ test('single cover: image mode keeps the calc(scale% + 50px) height', () => {
});
test('single cover: video mode uses the bare ratio height like the reference dplayer box', () => {
+ // 参考主题 article_video_cover_option.scale:0-200,0 = 不固定长宽比 → 16:9 回退。
assert.match(
singleRender,
- /single-cover--video imgbox-container" style="padding-bottom:<\?php echo esc_attr\(\(string\) \$video_scale\); \?>%;"/,
- '视频封面必须输出裸 video_scale% 高度(标题在下方 footer,无叠加补偿)'
+ /\$video_padding = \$video_scale > 0 \? \(string\) \$video_scale : '56\.25';/,
+ '视频封面 scale=0 必须回退 16:9(56.25%),非 0 输出裸比例'
+ );
+ assert.match(
+ singleRender,
+ /single-cover--video imgbox-container" style="padding-bottom:<\?php echo esc_attr\(\$video_padding\); \?>%;"/,
+ '视频封面必须输出裸比例高度(标题在下方 footer,无叠加补偿)'
);
// +50px 是标题叠加封面的补偿量,仅单图/多图封面适用;视频封面
// 对齐参考主题 .dplayer-scale-height,不得继承该公式。
@@ -40,10 +46,17 @@ test('single cover: video mode uses the bare ratio height like the reference dpl
});
test('single cover: slide mode reserves the same +50px via the slider helper', () => {
+ // 比例高度模式(scale_height 关闭分支)向 slider helper 传 scale_extra=50px;
+ // 固定高度模式(scale_height 开启分支)改用参考主题 pc/m_height 定高。
assert.match(
singleRender,
- /'scale_extra'\s*=>\s*'50px'/,
- '多图封面必须向 aksrt_render_slider 传入 scale_extra=50px'
+ /\$slider_args\['scale_extra'\] = '50px';/,
+ '比例高度分支必须向 aksrt_render_slider 传 scale_extra=50px'
+ );
+ assert.match(
+ singleRender,
+ /\$slider_args\['pc_height'\] = \(\(int\) \(\$slide_option\['pc_height'\] \?\? 380\)\) \. 'px';/,
+ '固定高度分支必须使用参考主题 pc_height 定高'
);
assert.match(
sliderHelper,
diff --git a/tests/widget-system/user-features-contract.test.cjs b/tests/widget-system/user-features-contract.test.cjs
index eeb78a8d..536d3fc3 100644
--- a/tests/widget-system/user-features-contract.test.cjs
+++ b/tests/widget-system/user-features-contract.test.cjs
@@ -14,11 +14,18 @@ const expectedFields = [
'user_cover_upload_s',
'user_cover_img',
'user_desc_std',
+ // 打赏组(参考主题 8977 行:post_rewards_* 迁移至用户功能页)。
+ 'post_rewards_s',
+ 'post_rewards_text',
+ 'post_rewards_amounts',
+ 'post_rewards_custom',
+ 'post_rewards_min',
+ 'post_rewards_max',
];
const externalSources = ['', 'gravatar', 'weavatar', 'cravatar', 'geekzu', 'loli', 'sep', 'v2ex'];
-test('user features schemas expose exactly the six canonical fields', () => {
+test('user features schemas expose the canonical fields plus the migrated rewards group', () => {
const js = read('assets/admin/src/schema/sections/users/features.js');
const phpAll = read('src/Admin/Schema/SchemaUsers.php');
const php = phpAll.slice(phpAll.indexOf("'features' => ["), phpAll.indexOf("'verify' => ["));
@@ -36,6 +43,11 @@ test('user features schemas expose exactly the six canonical fields', () => {
assert.match(js, /user_desc_std:\s*'这家伙很懒,什么都没有写\.\.\.'/);
assert.match(php, /avatar_default_img[^\n]+get_theme_file_uri\('assets\/img\/avatar-default\.png'\)/);
assert.match(php, /user_cover_img[^\n]+get_theme_file_uri\('assets\/img\/user_t\.jpg'\)/);
+ // 打赏组:开关默认开(对齐参考主题),依赖收敛于 post_rewards_s。
+ assert.match(js, /post_rewards_s:\s*true/);
+ assert.match(js, /post_rewards_text:\s*'赞赏'/);
+ assert.match(php, /\['key' => 'rewards', 'title' => '打赏', 'fields' => \[/);
+ assert.match(php, /'post_rewards_s', 'label' => '用户打赏功能', 'type' => 'switcher', 'default' => true/);
const jsSources = [...js.matchAll(/\{ label: '[^']+', value: '([^']*)' \}/g)].map((match) => match[1]);
const phpSources = [...php.matchAll(/\['label' => '[^']+', 'value' => '([^']*)'\]/g)].map((match) => match[1]);
diff --git a/tools/admin-contract-report.json b/tools/admin-contract-report.json
index 96993d94..b5f26170 100644
--- a/tools/admin-contract-report.json
+++ b/tools/admin-contract-report.json
@@ -1,5 +1,5 @@
{
- "generated_at": "2026-09-24T07:38:16+00:00",
+ "generated_at": "2026-09-24T08:11:53+00:00",
"errors": [],
"warnings": [
"customPage tools-update section 'tools/update' not in PHP schema (ok if pure operational page)",
@@ -10,6 +10,8 @@
"enum incomplete parse for 'bind_reminder' (php subset of js): php=[email] js=[email,phone,email_phone,close]",
"enum incomplete parse for 'content_audit_on_failure' (php subset of js): php=[allow] js=[allow,block]",
"enum incomplete parse for 'content_audit_provider' (php subset of js): php=[baidu] js=[baidu,null]",
+ "enum incomplete parse for 'direction' (php subset of js): php=[horizontal] js=[horizontal,vertical]",
+ "enum incomplete parse for 'effect' (php subset of js): php=[slide] js=[slide,fade,cube,coverflow,flip]",
"enum incomplete parse for 'epay_device' (php subset of js): php=[auto] js=[auto,pc,mobile]",
"enum incomplete parse for 'font_family' (php subset of js): php=[system] js=[system,noto-sans-sc,noto-serif-sc,custom]",
"enum incomplete parse for 'i18n_default_lang' (php subset of js): php=[zh-CN] js=[zh-CN,en,zh-TW,ja,ko,fr,de,es,ru]",
@@ -37,11 +39,7 @@
"enum incomplete parse for 'pay_box_position' (php subset of js): php=[box_top] js=[box_top,top,bottom,box_bottom]",
"enum incomplete parse for 'pay_modo_default' (php subset of js): php=[0] js=[0,points]",
"enum incomplete parse for 'pay_wechat_sdk_options' (php subset of js): php=[official_wechat] js=[official_wechat,epay,null]",
- "enum incomplete parse for 'post_author_card_orderby' (php subset of js): php=[views] js=[views,date,modified,comment_count]",
- "enum incomplete parse for 'post_author_card_style' (php subset of js): php=[card] js=[card,list]",
"enum incomplete parse for 'post_pay_box_position' (php subset of js): php=[content] js=[content,mask]",
- "enum incomplete parse for 'post_related_orderby' (php subset of js): php=[modified] js=[modified,date,views,like,comment_count,favorite,rand]",
- "enum incomplete parse for 'post_related_type' (php subset of js): php=[img] js=[img,list,text]",
"enum incomplete parse for 'post_time_source' (php subset of js): php=[date] js=[date,modified]",
"enum incomplete parse for 'referral_attribution_model' (php subset of js): php=[first_touch] js=[first_touch,last_touch]",
"enum incomplete parse for 'search_type_in' (php subset of js): php=[post] js=[post,user]",