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

+116 -90
View File
@@ -114,6 +114,7 @@ if (!function_exists('aksrt_single_build_context')) :
'video_scale' => (int) ($settings['article_video_cover_scale'] ?? 56),
'slide_images' => array_values(array_filter((array) ($settings['slide_images'] ?? []))),
'episode_count' => max(0, (int) ($settings['episode_count'] ?? 0)),
'video_episodes' => is_array($settings['video_episodes'] ?? null) ? array_values($settings['video_episodes']) : [],
'video_poster' => (string) ($settings['video_poster'] ?? ''),
'meta_items' => $meta_items,
'meta_items_secondary' => $meta_items_secondary,
@@ -199,7 +200,7 @@ if (!function_exists('aksrt_single_render_cover')) :
<div class="single-head-cover">
<div class="single-video">
<div class="graphic single-cover single-cover--video imgbox-container" style="padding-bottom:<?php echo esc_attr((string) $video_scale); ?>%;">
<video src="<?php echo esc_url($ctx['cover_url']); ?>"<?php echo $poster !== '' ? ' poster="' . esc_url($poster) . '"' : ''; ?> controls playsinline class="fit-cover single-cover__video" data-aksrt-cover-video></video>
<div class="aksrt-video single-cover__player" data-aksrt-video data-src="<?php echo esc_url($ctx['cover_url']); ?>"<?php echo $poster !== '' ? ' data-poster="' . esc_url($poster) . '"' : ''; ?> style="position:absolute;inset:0;width:100%;height:100%;"></div>
</div>
<div class="single-video-footer dplayer-featured">
<div class="flex jsb at">
@@ -211,24 +212,34 @@ if (!function_exists('aksrt_single_render_cover')) :
<?php endif; ?>
</div>
<h1 class="article-title">
<?php if ($episode > 1) : ?>
<span class="badg badg-sm b-theme mr6"><i class="fa fa-play-circle mr3" aria-hidden="true"></i><?php printf(esc_html__('共%d集', 'aksrt'), $episode); ?></span>
<?php
$episodes = is_array($ctx['video_episodes'] ?? null) ? $ctx['video_episodes'] : [];
if ($episodes === [] && !empty($ctx['cover_url'])) {
$episodes = [['url' => (string) $ctx['cover_url'], 'title' => '']];
}
$ep_count = count($episodes);
if ($ep_count > 1) : ?>
<span class="badg badg-sm b-theme mr6"><i class="fa fa-play-circle mr3" aria-hidden="true"></i><?php printf(esc_html__('共%d集', 'aksrt'), $ep_count); ?></span>
<?php else : ?>
<i class="fa fa-play-circle-o mr6 opacity8" aria-hidden="true"></i>
<?php endif; ?>
<?php echo $title_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</h1>
<?php echo $badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php if ($episode > 0) : ?>
<?php if ($ep_count > 0) : ?>
<div class="featured-video-episode mt10">
<a href="javascript:;" class="switch-video text-ellipsis active" data-index="1" video-url="<?php echo esc_url($ctx['cover_url']); ?>">
<span class="mr6 badg badg-sm">1</span><i class="episode-active-icon"></i><?php printf(esc_html__('第%d集', 'aksrt'), 1); ?>
<?php foreach ($episodes as $idx => $ep) :
$ep_url = (string) ($ep['url'] ?? '');
if ($ep_url === '') { continue; }
$ep_title = trim((string) ($ep['title'] ?? ''));
if ($ep_title === '') {
$ep_title = sprintf(__('第%d集', 'aksrt'), $idx + 1);
}
?>
<a href="javascript:;" class="switch-video text-ellipsis<?php echo $idx === 0 ? ' active' : ''; ?>" data-index="<?php echo (int) ($idx + 1); ?>" data-src="<?php echo esc_url($ep_url); ?>">
<span class="mr6 badg badg-sm"><?php echo (int) ($idx + 1); ?></span><i class="episode-active-icon"></i><?php echo esc_html($ep_title); ?>
</a>
<?php for ($i = 2; $i <= $episode; $i++) : ?>
<a href="javascript:;" class="switch-video text-ellipsis" data-index="<?php echo (int) $i; ?>" video-url="<?php echo esc_url($ctx['cover_url']); ?>">
<span class="mr6 badg badg-sm"><?php echo (int) $i; ?></span><i class="episode-active-icon"></i><?php printf(esc_html__('第%d集', 'aksrt'), $i); ?>
</a>
<?php endfor; ?>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
@@ -264,15 +275,13 @@ if (!function_exists('aksrt_single_render_cover')) :
'show_pagination' => true,
'loop' => count($slide_items) > 1,
'autoplay' => count($slide_items) > 1,
'overlay_html' => $overlay,
]);
}
?>
<div class="single-head-cover">
<?php if ($slider_html !== '') : ?>
<div class="single-cover-slide-wrap" style="position:relative">
<?php echo $slider_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php echo $overlay; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<?php echo $slider_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php else : ?>
<div class="graphic mb20 single-cover single-cover--slide imgbox-container" style="padding-bottom:<?php echo esc_attr((string) $img_scale); ?>%;">
<?php foreach ($slides as $idx => $slide_url) : ?>
@@ -453,49 +462,66 @@ if (!function_exists('aksrt_single_render_author_card')) :
* @param array<string,mixed> $ctx Single context.
*/
function aksrt_single_render_author_card(array $ctx): void {
if (!function_exists('aksrt_get_user_dashboard_view')) {
$author_id = (int) get_the_author_meta('ID');
if ($author_id <= 0) {
return;
}
$author_id = (int) get_the_author_meta('ID');
$dashboard = aksrt_get_user_dashboard_view($author_id, ['viewer_id' => get_current_user_id()]);
$recent = is_array($dashboard['recent_posts'] ?? null) ? $dashboard['recent_posts'] : [];
$stats = [
['label' => __('文章', 'aksrt'), 'value' => (int) ($dashboard['post_count'] ?? 0)],
['label' => __('评论', 'aksrt'), 'value' => (int) ($dashboard['comment_count'] ?? 0)],
['label' => __('收藏', 'aksrt'), 'value' => (int) ($dashboard['favorite_count'] ?? 0)],
];
$author_url = get_author_posts_url($author_id);
$author_name = get_the_author();
$bio = trim((string) get_the_author_meta('description'));
$vip_badge = function_exists('aksrt_render_vip_badge') ? aksrt_render_vip_badge($author_id, 'sm') : '';
$signals = [];
if (class_exists('AKSRT_User_Home')) {
$signals = AKSRT_User_Home::get_instance()->get_profile_signals($author_id);
}
$avatar = function_exists('aksrt_get_avatar_html')
? aksrt_get_avatar_html($author_id, 80, ['class' => 'avatar avatar-lg'])
: get_avatar($author_id, 80, '', esc_attr($author_name), ['class' => 'avatar avatar-lg']);
$recent = get_posts([
'author' => $author_id,
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => 6,
'orderby' => 'date',
'order' => 'DESC',
'no_found_rows' => true,
]);
?>
<div class="author author-box zib-widget">
<div class="author-box__header">
<a href="<?php echo esc_url(get_author_posts_url($author_id)); ?>" class="author-box__avatar">
<?php echo get_avatar($author_id, 56, '', esc_attr(get_the_author())); ?>
</a>
<div class="author-box__info">
<a href="<?php echo esc_url(get_author_posts_url($author_id)); ?>" class="author-box__name"><?php echo esc_html(get_the_author()); ?></a>
<?php if ($bio !== '') : ?>
<p class="author-box__bio"><?php echo esc_html($bio); ?></p>
<?php endif; ?>
<div class="author-box__stats">
<?php foreach ($stats as $stat) : ?>
<span><strong><?php echo esc_html((string) $stat['value']); ?></strong> <?php echo esc_html($stat['label']); ?></span>
<?php endforeach; ?>
<div class="user-card author zib-widget">
<div class="card-content mt10 relative">
<div class="user-content">
<div class="user-avatar"><?php echo $avatar; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
<div class="user-info mt20 mb10">
<div class="user-name flex jc">
<a class="display-name" href="<?php echo esc_url($author_url); ?>"><?php echo esc_html($author_name); ?></a>
<?php echo $vip_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php foreach ($signals as $signal) : ?>
<span class="badg badg-sm ml3 aksrt-identity-chip aksrt-identity-chip--<?php echo esc_attr((string) ($signal['tone'] ?? 'info')); ?>">
<i class="fa fa-<?php echo esc_attr((string) ($signal['icon'] ?? 'check')); ?>" aria-hidden="true"></i>
<?php echo esc_html((string) ($signal['label'] ?? '')); ?>
</span>
<?php endforeach; ?>
</div>
<?php if ($bio !== '') : ?>
<div class="user-desc mt10 muted-2-color em09"><?php echo esc_html($bio); ?></div>
<?php endif; ?>
<div class="user-btns mt20">
<a class="but pw-1em mr6 jb-blue" href="<?php echo esc_url($author_url); ?>"><?php esc_html_e('个人主页', 'aksrt'); ?></a>
</div>
</div>
</div>
<?php if (!empty($recent)) : ?>
<div class="user-card-posts mt10">
<ul class="no-thumb">
<?php foreach ($recent as $rp) : ?>
<?php if ($rp instanceof WP_Post) : ?>
<li><a class="icon-circle" href="<?php echo esc_url(get_permalink($rp)); ?>"><?php echo esc_html(get_the_title($rp)); ?></a></li>
<?php endif; ?>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
</div>
<?php if (!empty($recent)) : ?>
<div class="author-box__posts">
<h3 class="author-box__posts-title"><?php esc_html_e('最新文章', 'aksrt'); ?></h3>
<ul>
<?php foreach ($recent as $recent_post) : ?>
<?php if ($recent_post instanceof WP_Post) : ?>
<li><a href="<?php echo esc_url(get_permalink($recent_post)); ?>"><?php echo esc_html(get_the_title($recent_post)); ?></a></li>
<?php endif; ?>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
</div>
<?php
}
@@ -682,7 +708,7 @@ if (!function_exists('aksrt_single_render_related')) :
$args = function_exists('aksrt_single_related_query_args')
? aksrt_single_related_query_args($post_id, $settings)
: [
'post_type' => get_post_type($post_id),
'post_type' => get_post_type($post_id) ?: 'post',
'post_status' => 'publish',
'post__not_in' => [$post_id],
'posts_per_page' => max(1, min(12, (int) ($settings['related_count'] ?? 6))),
@@ -695,21 +721,6 @@ if (!function_exists('aksrt_single_render_related')) :
return;
}
$posts_html = '';
while ($query->have_posts()) {
$query->the_post();
$p = get_post();
if (!($p instanceof WP_Post)) {
continue;
}
if ($style === 'img' && function_exists('aksrt_render_related_graphic_card')) {
$posts_html .= aksrt_render_related_graphic_card($p);
} else {
$posts_html .= '<li><a class="icon-circle" href="' . esc_url(get_permalink($p)) . '">' . esc_html(get_the_title($p)) . '</a></li>';
}
}
wp_reset_postdata();
$wrap_class = 'theme-box relates' . ($style === 'img' ? ' relates-thumb' : '');
?>
<div class="<?php echo esc_attr($wrap_class); ?>">
@@ -717,36 +728,51 @@ if (!function_exists('aksrt_single_render_related')) :
<div class="title-theme"><?php echo esc_html($title); ?></div>
</div>
<div class="zib-widget">
<?php if ($style === 'img') : ?>
<div class="swiper-container swiper-scroll">
<div class="swiper-wrapper">
<?php echo $posts_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
</div>
<?php elseif ($style === 'list') : ?>
<?php if ($style === 'list' && function_exists('aksrt_render_post_list')) : ?>
<?php
$list_query = new WP_Query($args);
if (function_exists('aksrt_render_post_list')) {
aksrt_render_post_list($list_query, [
'context' => 'related',
'mode' => 'mini',
'show_thumb' => true,
'show_meta' => true,
'show_number' => false,
]);
}
wp_reset_postdata();
// New route: single query only.
aksrt_render_post_list($query, [
'context' => 'related',
'mode' => 'mini',
'show_thumb' => true,
'show_meta' => true,
'show_number' => false,
]);
?>
<?php else : ?>
<ul class="no-thumb">
<?php echo $posts_html !== '' ? $posts_html : '<li>' . esc_html__('暂无相关文章', 'aksrt') . '</li>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</ul>
<?php
$posts_html = '';
while ($query->have_posts()) {
$query->the_post();
$p = get_post();
if (!($p instanceof WP_Post)) {
continue;
}
if ($style === 'img' && function_exists('aksrt_render_related_graphic_card')) {
$posts_html .= aksrt_render_related_graphic_card($p);
} else {
$posts_html .= '<li><a class="icon-circle" href="' . esc_url(get_permalink($p)) . '">' . esc_html(get_the_title($p)) . '</a></li>';
}
}
?>
<?php if ($style === 'img') : ?>
<div class="swiper-container swiper-scroll">
<div class="swiper-wrapper">
<?php echo $posts_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
</div>
<?php else : ?>
<ul class="no-thumb">
<?php echo $posts_html !== '' ? $posts_html : '<li>' . esc_html__('暂无相关文章', 'aksrt') . '</li>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</ul>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
<?php
wp_reset_postdata();
}
endif;