Merge pull request 'fix(frontend): close single 1:1 residuals without legacy paths' (#107) from worktree-frontend-single-true-1to1 into main
Reviewed-on: CYL/wordpress-theme-aksrt#107
This commit is contained in:
@@ -548,3 +548,17 @@ html.dark .post-drop-meta__item:hover {
|
||||
.article-tags .but.c-green { color: #5cb85c; }
|
||||
.article-tags .but.c-purple { color: #9c27b0; }
|
||||
.article-tags .but.c-red { color: #f44336; }
|
||||
|
||||
|
||||
/* ===== Zibll user-card after-box ===== */
|
||||
.user-card.author.zib-widget { margin-bottom: 16px; overflow: hidden; }
|
||||
.user-card.author .user-avatar { display: flex; justify-content: center; }
|
||||
.user-card.author .user-avatar .avatar,
|
||||
.user-card.author .user-avatar img { width: 80px; height: 80px; border-radius: 50%; object-fit: cover; }
|
||||
.user-card.author .user-name { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 4px; }
|
||||
.user-card.author .user-name .display-name { font-weight: 700; color: var(--key-color, inherit); }
|
||||
.user-card.author .user-desc { text-align: center; }
|
||||
.user-card.author .user-btns { text-align: center; }
|
||||
.user-card.author .user-card-posts ul { list-style: none; margin: 0; padding: 0; }
|
||||
.user-card.author .user-card-posts li { padding: 6px 0; border-bottom: 1px dashed var(--color-line, rgba(0,0,0,.06)); }
|
||||
.user-card.author .user-card-posts li:last-child { border-bottom: 0; }
|
||||
|
||||
@@ -273,3 +273,19 @@
|
||||
.article-avatar .display-name {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
|
||||
/* Slide cover overlay (injected by aksrt_render_slider overlay_html) */
|
||||
.aksrt-slider-wrapper--has-overlay,
|
||||
.aksrt-slider-wrapper--scale { position: relative; overflow: hidden; border-radius: var(--radius-2xl, 16px); }
|
||||
.aksrt-slider-wrapper--scale .new-swiper { position: absolute; inset: 0; width: 100%; height: 100%; }
|
||||
.aksrt-slider-wrapper--scale .swiper-slide img.fit-cover { width: 100%; height: 100%; object-fit: cover; }
|
||||
.aksrt-slider-overlay { position: absolute; inset: 0; z-index: 3; pointer-events: none; }
|
||||
.aksrt-slider-overlay .abs-center.left-bottom { pointer-events: none; padding: 18px 20px; color: #fff; }
|
||||
.aksrt-slider-overlay .article-title,
|
||||
.aksrt-slider-overlay .article-title.title-h-left { color: #fff; text-shadow: 0 1px 4px rgba(0,0,0,.45); }
|
||||
.aksrt-slider-overlay .breadcrumb a,
|
||||
.aksrt-slider-overlay .breadcrumb li { color: rgba(255,255,255,.9); }
|
||||
.aksrt-slider-overlay .single-metabox,
|
||||
.aksrt-slider-overlay .cover-meta { pointer-events: auto; }
|
||||
.aksrt-slider-overlay .post-drop-meta { pointer-events: auto; }
|
||||
|
||||
@@ -19,19 +19,31 @@
|
||||
var navContainer = document.querySelector('[data-nav]');
|
||||
if (!navContainer) return;
|
||||
|
||||
// Single-post TOC is owned by page-template.js (.posts-nav-box .toc-list).
|
||||
// Do not invent a second .aksrt-section-nav tree on article singles.
|
||||
if (document.querySelector('.posts-nav-box .toc-list, .posts-nav-box.toc-widget')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var headings = navContainer.querySelectorAll(HEADING_SEL);
|
||||
if (headings.length < MIN_HEADINGS) return;
|
||||
|
||||
var tocContainer = document.querySelector('.posts-nav-lists, .aksrt-section-nav');
|
||||
if (!tocContainer) {
|
||||
// Docs / page-navs only: create a dedicated container. Never inject into single sidebars.
|
||||
if (document.body.classList.contains('single') || document.body.classList.contains('single-post')) {
|
||||
return;
|
||||
}
|
||||
tocContainer = document.createElement('nav');
|
||||
tocContainer.className = 'aksrt-section-nav';
|
||||
tocContainer.setAttribute('aria-label', '文章目录');
|
||||
var sidebar = document.querySelector('.content-layout__aside, .sidebar, .widget-area');
|
||||
if (sidebar) {
|
||||
sidebar.insertBefore(tocContainer, sidebar.firstChild);
|
||||
} else {
|
||||
} else if (navContainer.parentNode) {
|
||||
navContainer.parentNode.insertBefore(tocContainer, navContainer);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -220,9 +220,16 @@
|
||||
if (!switchBtn) return;
|
||||
|
||||
var container = switchBtn.closest('.aksrt-video') || switchBtn.closest('[data-aksrt-video]');
|
||||
// Single cover: episodes sit in .single-video-footer; player is sibling under .single-video
|
||||
if (!container) {
|
||||
var singleVideo = switchBtn.closest('.single-video, .single-head-cover');
|
||||
if (singleVideo) {
|
||||
container = singleVideo.querySelector('.aksrt-video, [data-aksrt-video]');
|
||||
}
|
||||
}
|
||||
if (!container) return;
|
||||
|
||||
var src = switchBtn.dataset.src || switchBtn.getAttribute('href');
|
||||
var src = switchBtn.dataset.src || switchBtn.getAttribute('video-url') || switchBtn.getAttribute('href');
|
||||
if (!src) return;
|
||||
|
||||
e.preventDefault();
|
||||
@@ -237,9 +244,14 @@
|
||||
}
|
||||
|
||||
// 高亮当前集
|
||||
var siblings = container.parentElement ? container.parentElement.querySelectorAll('.switch-video, .switch-iframe') : [];
|
||||
siblings.forEach(function (btn) { btn.classList.remove('is-active'); });
|
||||
var scope = switchBtn.closest('.featured-video-episode') || (container.parentElement || document);
|
||||
var siblings = scope.querySelectorAll('.switch-video, .switch-iframe');
|
||||
siblings.forEach(function (btn) {
|
||||
btn.classList.remove('is-active');
|
||||
btn.classList.remove('active');
|
||||
});
|
||||
switchBtn.classList.add('is-active');
|
||||
switchBtn.classList.add('active');
|
||||
});
|
||||
|
||||
// ── Public API ──
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -77,9 +77,10 @@ class AKSRT_Domain_Model {
|
||||
// §3.8 文章扩展字段
|
||||
'thumbnail_url' => '',
|
||||
'featured_slide' => '',
|
||||
'featured_video' => '',
|
||||
'featured_video_episode' => 0,
|
||||
'subtitle' => '',
|
||||
'featured_video' => '',
|
||||
// Episode list: [ ['url' => string, 'title' => string], ... ]
|
||||
'featured_video_episode' => [],
|
||||
'subtitle' => '',
|
||||
'views' => 0,
|
||||
'likes' => 0,
|
||||
'maxheight' => 0,
|
||||
|
||||
+1102
-1183
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,12 @@ if (!function_exists('aksrt_get_the_subtitle')) :
|
||||
if ($post_id <= 0) {
|
||||
return '';
|
||||
}
|
||||
$subtitle = trim((string) get_post_meta($post_id, '_subtitle', true));
|
||||
// New route: subtitle lives in AKSRT_Post_Meta_Box aggregate meta.
|
||||
$subtitle = '';
|
||||
if (class_exists('AKSRT_Post_Meta_Box')) {
|
||||
$meta = AKSRT_Post_Meta_Box::get_meta($post_id);
|
||||
$subtitle = trim((string) ($meta['subtitle'] ?? ''));
|
||||
}
|
||||
if ($subtitle === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
+116
-90
@@ -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;
|
||||
|
||||
|
||||
@@ -65,6 +65,8 @@ function aksrt_render_slider(array $args = []): string {
|
||||
'slider_class' => '',
|
||||
'show_nav' => true,
|
||||
'show_pagination' => true,
|
||||
// Optional absolute overlay HTML (e.g. single cover title/meta). Not escaped.
|
||||
'overlay_html' => '',
|
||||
];
|
||||
|
||||
$args = wp_parse_args($args, $defaults);
|
||||
@@ -90,7 +92,13 @@ function aksrt_render_slider(array $args = []): string {
|
||||
// 高度样式
|
||||
$height_style = '';
|
||||
if ($args['scale_height']) {
|
||||
$height_style = sprintf('--pc-height:auto;--m-height:auto;--scale-ratio:%s;', esc_attr((string) $args['scale_ratio']));
|
||||
// padding-bottom percentage for ratio box (Zibll graphic cover style).
|
||||
$ratio_pct = max(10, min(100, (float) $args['scale_ratio'] * 100));
|
||||
$height_style = sprintf(
|
||||
'--pc-height:auto;--m-height:auto;--scale-ratio:%s;padding-bottom:%s%%;',
|
||||
esc_attr((string) $args['scale_ratio']),
|
||||
esc_attr((string) $ratio_pct)
|
||||
);
|
||||
} else {
|
||||
$height_style = sprintf('--pc-height:%s;--m-height:%s;', esc_attr($args['pc_height']), esc_attr($args['m_height']));
|
||||
}
|
||||
@@ -102,11 +110,13 @@ function aksrt_render_slider(array $args = []): string {
|
||||
|
||||
$wrapper_classes = ['aksrt-slider-wrapper'];
|
||||
if ($args['wrapper_class']) $wrapper_classes[] = $args['wrapper_class'];
|
||||
if ($args['scale_height']) $wrapper_classes[] = 'aksrt-slider-wrapper--scale';
|
||||
if ((string) $args['overlay_html'] !== '') $wrapper_classes[] = 'aksrt-slider-wrapper--has-overlay';
|
||||
$wrapper_class_str = implode(' ', $wrapper_classes);
|
||||
|
||||
// 渲染 HTML
|
||||
$html = '<div class="' . esc_attr($wrapper_class_str) . '">';
|
||||
$html .= '<div class="' . esc_attr($slider_class_str) . '" ' . implode(' ', $data_attrs) . ' style="' . esc_attr($height_style) . '">';
|
||||
$html = '<div class="' . esc_attr($wrapper_class_str) . '"' . ($args['scale_height'] ? ' style="position:relative;' . esc_attr(sprintf('padding-bottom:%s%%;', (string) max(10, min(100, (float) $args['scale_ratio'] * 100)))) . '"' : '') . '>';
|
||||
$html .= '<div class="' . esc_attr($slider_class_str) . '" ' . implode(' ', $data_attrs) . ' style="' . esc_attr($height_style) . ($args['scale_height'] ? 'position:absolute;inset:0;width:100%;height:100%;' : '') . '">';
|
||||
$html .= '<div class="swiper-wrapper">';
|
||||
|
||||
foreach ($slides as $slide) {
|
||||
@@ -116,7 +126,7 @@ function aksrt_render_slider(array $args = []): string {
|
||||
$desc = sanitize_text_field($slide['desc'] ?? '');
|
||||
$alt = sanitize_text_field($slide['alt'] ?? $title);
|
||||
$bg_color = sanitize_hex_color($slide['bg_color'] ?? '');
|
||||
$target = $slide['target'] === '_blank' ? '_blank' : '_self';
|
||||
$target = ($slide['target'] ?? '') === '_blank' ? '_blank' : '_self';
|
||||
|
||||
$slide_style = $bg_color ? 'background-color:' . esc_attr($bg_color) . ';' : '';
|
||||
|
||||
@@ -127,7 +137,7 @@ function aksrt_render_slider(array $args = []): string {
|
||||
}
|
||||
|
||||
if ($image) {
|
||||
$html .= '<img class="swiper-lazy" data-src="' . esc_url($image) . '" alt="' . esc_attr($alt) . '" loading="lazy">';
|
||||
$html .= '<img class="swiper-lazy fit-cover" data-src="' . esc_url($image) . '" src="' . esc_url($image) . '" alt="' . esc_attr($alt) . '" loading="lazy">';
|
||||
$html .= '<div class="swiper-lazy-preloader"></div>';
|
||||
}
|
||||
|
||||
@@ -158,6 +168,12 @@ function aksrt_render_slider(array $args = []): string {
|
||||
}
|
||||
|
||||
$html .= '</div>'; // .new-swiper
|
||||
|
||||
// Cover title/meta overlay (trusted HTML from single cover renderer).
|
||||
if ((string) $args['overlay_html'] !== '') {
|
||||
$html .= '<div class="aksrt-slider-overlay">' . $args['overlay_html'] . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
$html .= '</div>'; // .aksrt-slider-wrapper
|
||||
|
||||
return $html;
|
||||
|
||||
Reference in New Issue
Block a user