fix(frontend): close single 1:1 residuals without legacy paths

Unify TOC ownership, real video episode list, slider overlay, single-query related list, Zibll user-card after-box, and new-route subtitle/meta contracts.
This commit is contained in:
kate522 committed 2026-07-13 13:18:19 +08:00
1 parent 48f7436030
commit 5ce43c4326
10 files changed
+1373 -1298

No files matched your search

+66 -12
View File
@@ -43,8 +43,46 @@ class AKSRT_Post_Meta_Box {
public static function get_meta(int $post_id): array {
$defaults = AKSRT_Domain_Model::default_post_meta();
$saved = get_post_meta($post_id, self::META_KEY, true);
if (!is_array($saved)) $saved = [];
return array_merge($defaults, $saved);
if (!is_array($saved)) {
$saved = [];
}
$meta = array_merge($defaults, $saved);
// New route only: episodes is always a list of {url,title}.
$meta['featured_video_episode'] = self::sanitize_video_episodes($meta['featured_video_episode'] ?? []);
if (!in_array((string) ($meta['show_toc'] ?? ''), ['', '0', '1'], true)) {
$meta['show_toc'] = '';
}
return $meta;
}
/**
* Sanitize video episode list (new route: array of url/title rows).
*
* @param mixed $raw Raw form / meta value.
* @return array<int, array{url:string,title:string}>
*/
public static function sanitize_video_episodes($raw): array {
if (!is_array($raw)) {
return [];
}
$out = [];
foreach ($raw as $row) {
if (!is_array($row)) {
continue;
}
$url = esc_url_raw((string) ($row['url'] ?? ''));
if ($url === '') {
continue;
}
$out[] = [
'url' => $url,
'title' => sanitize_text_field((string) ($row['title'] ?? '')),
];
if (count($out) >= 50) {
break;
}
}
return $out;
}
/**
@@ -147,10 +185,12 @@ class AKSRT_Post_Meta_Box {
</select>
<p>
<label>
<input type="checkbox" name="aksrt_post_meta[show_toc]" value="1" <?php checked($meta['show_toc'] === '1'); ?>>
<?php esc_html_e('显示目录树', 'aksrt'); ?>
</label>
<label for="aksrt_show_toc"><strong><?php esc_html_e('目录树', 'aksrt'); ?></strong></label>
<select name="aksrt_post_meta[show_toc]" id="aksrt_show_toc" class="widefat">
<option value="" <?php selected((string) ($meta['show_toc'] ?? ''), ''); ?>><?php esc_html_e('跟随全局', 'aksrt'); ?></option>
<option value="1" <?php selected((string) ($meta['show_toc'] ?? ''), '1'); ?>><?php esc_html_e('强制开启', 'aksrt'); ?></option>
<option value="0" <?php selected((string) ($meta['show_toc'] ?? ''), '0'); ?>><?php esc_html_e('强制关闭', 'aksrt'); ?></option>
</select>
</p>
<p><strong><?php esc_html_e('单篇交互覆盖', 'aksrt'); ?></strong></p>
@@ -210,11 +250,24 @@ class AKSRT_Post_Meta_Box {
<small class="description"><?php esc_html_e('多张图片 URL 用逗号分隔,用于封面轮播。', 'aksrt'); ?></small>
<p><strong><?php esc_html_e('特色视频', 'aksrt'); ?></strong></p>
<input type="url" name="aksrt_post_meta[featured_video]" value="<?php echo esc_url($meta['featured_video']); ?>" class="widefat" placeholder="<?php esc_attr_e('视频 URL(嵌入/直链)', 'aksrt'); ?>">
<input type="url" name="aksrt_post_meta[featured_video]" value="<?php echo esc_url($meta['featured_video']); ?>" class="widefat" placeholder="<?php esc_attr_e('第 1 集视频 URL(直链)', 'aksrt'); ?>">
<p><strong><?php esc_html_e('视频集数', 'aksrt'); ?></strong></p>
<input type="number" name="aksrt_post_meta[featured_video_episode]" value="<?php echo esc_attr((string) (int) $meta['featured_video_episode']); ?>" class="widefat" min="0" max="9999" step="1">
<small class="description"><?php esc_html_e('0 表示非剧集,>0 表示第 N 集。', 'aksrt'); ?></small>
<p><strong><?php esc_html_e('视频剧集列表', 'aksrt'); ?></strong></p>
<?php
$episodes = is_array($meta['featured_video_episode'] ?? null) ? $meta['featured_video_episode'] : [];
// Always show at least 3 editable rows for new-route authoring.
$rows = max(3, count($episodes));
for ($i = 0; $i < $rows; $i++) :
$ep = is_array($episodes[$i] ?? null) ? $episodes[$i] : [];
$ep_url = (string) ($ep['url'] ?? '');
$ep_title = (string) ($ep['title'] ?? '');
?>
<p class="aksrt-episode-row">
<input type="text" name="aksrt_post_meta[featured_video_episode][<?php echo (int) $i; ?>][title]" value="<?php echo esc_attr($ep_title); ?>" class="widefat" placeholder="<?php echo esc_attr(sprintf(__('第 %d 集标题(可选)', 'aksrt'), $i + 1)); ?>" style="margin-bottom:4px;">
<input type="url" name="aksrt_post_meta[featured_video_episode][<?php echo (int) $i; ?>][url]" value="<?php echo esc_url($ep_url); ?>" class="widefat" placeholder="<?php echo esc_attr(sprintf(__('第 %d 集视频 URL', 'aksrt'), $i + 1)); ?>">
</p>
<?php endfor; ?>
<small class="description"><?php esc_html_e('新路线:每行一集(标题 + URL)。留空 URL 的行会被忽略。第 1 集可与上方「特色视频」二选一填写。', 'aksrt'); ?></small>
<hr>
<p><strong><?php esc_html_e('手动计数覆盖', 'aksrt'); ?></strong></p>
@@ -308,7 +361,8 @@ class AKSRT_Post_Meta_Box {
'custom_excerpt' => isset($raw['custom_excerpt']) ? sanitize_textarea_field($raw['custom_excerpt']) : '',
'layout_override' => isset($raw['layout_override']) ? sanitize_key($raw['layout_override']) : '',
'header_image_mode' => isset($raw['header_image_mode']) ? sanitize_key($raw['header_image_mode']) : '',
'show_toc' => !empty($raw['show_toc']) ? '1' : '',
// New route: '' follow | '1' force on | '0' force off.
'show_toc' => isset($raw['show_toc']) && in_array((string) $raw['show_toc'], ['', '0', '1'], true) ? (string) $raw['show_toc'] : '',
'show_reading_progress' => isset($raw['show_reading_progress']) ? sanitize_key((string) $raw['show_reading_progress']) : '',
'enable_lightbox' => isset($raw['enable_lightbox']) ? sanitize_key((string) $raw['enable_lightbox']) : '',
'show_related' => isset($raw['show_related']) ? sanitize_key((string) $raw['show_related']) : '',
@@ -324,7 +378,7 @@ class AKSRT_Post_Meta_Box {
'thumbnail_url' => isset($raw['thumbnail_url']) ? esc_url_raw((string) $raw['thumbnail_url']) : '',
'featured_slide' => isset($raw['featured_slide']) ? sanitize_text_field($raw['featured_slide']) : '',
'featured_video' => isset($raw['featured_video']) ? esc_url_raw((string) $raw['featured_video']) : '',
'featured_video_episode' => isset($raw['featured_video_episode']) ? max(0, (int) $raw['featured_video_episode']) : 0,
'featured_video_episode' => self::sanitize_video_episodes($raw['featured_video_episode'] ?? []),
'subtitle' => isset($raw['subtitle']) ? sanitize_text_field($raw['subtitle']) : '',
'views' => isset($raw['views']) ? max(0, (int) $raw['views']) : 0,
'likes' => isset($raw['likes']) ? max(0, (int) $raw['likes']) : 0,