feat(admin): replicate reference post-page settings page 1:1

- rewrite post_list.page schema (JS+PHP 2.0.0) with the reference field set:
  three cover types with option fieldsets (image 20-200% / slide CFS_Module::slide
  full config with nested dependsOn / video 0-200%), hide-time, p_indent,
  breadcrumbs (home-text as switcher), prevnext, authordesc, maxheight slider,
  yiyan block, copyright, button toptext, front/after content, related posts
- migrate share_s to share page basic group and post_rewards_* to users
  features rewards group (runtime keys unchanged)
- remap get_post_page_settings() to reference keys, normalize slide cover
  option, hardcode non-reference runtime flags (reading progress, favorites,
  author card), drop dual-path legacy aliases
- wire slide option into aksrt_render_slider (effect/direction/spaceBetween/
  speed/interval/loop/autoplay/nav/pagination, scale vs fixed-height modes),
  clamp image scale 20-200 and video scale 0-200 with 16:9 fallback,
  wire p_indent container class + CSS, resync frontend-tags fallback and
  FacadeBulk fallback, remap PostMetaBox cover keys
- add post-page reference contract test; update stale cover-height and
  user-features contract tests

Validation: node --test 464/464 pass, audit-admin-contracts errors=0,
vite build ok, build-public-assets ok, php -l clean.
This commit is contained in:
2026-09-24 16:13:00 +08:00
parent a8060fa71d
commit 66f8bb933d
17 changed files with 797 additions and 267 deletions
+68 -35
View File
@@ -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 [