Files
wordpress-theme-aksrt/inc/helpers/aksrt-single-render.php
T
2026-08-26 15:11:25 +08:00

887 lines
35 KiB
PHP

<?php
/**
* Single post render helpers (legacy-style composition pieces).
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
if (!function_exists('aksrt_single_render_cover')) :
/**
* Cover / slide / video header (legacy single-head-cover).
*
* @param array<string,mixed> $ctx Single context.
*/
function aksrt_single_render_cover(array $ctx): void {
if (empty($ctx['show_cover']) || $ctx['cover_url'] === '') {
return;
}
$type = (string) $ctx['cover_type'];
$badge = (string) $ctx['status_badge'];
$crumbs = (string) $ctx['breadcrumbs_html'];
$meta_box = (string) ($ctx['meta_box_html'] ?? '');
$img_scale = max(10, min(100, (int) $ctx['img_scale']));
$video_scale = max(20, min(100, (int) $ctx['video_scale']));
$episode = (int) $ctx['episode_count'];
$slides = is_array($ctx['slide_images'] ?? null) ? $ctx['slide_images'] : [];
$poster = (string) ($ctx['video_poster'] ?? '');
$title_html = (string) ($ctx['title_html'] ?? esc_html(get_the_title()));
if ($type === 'video') :
?>
<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); ?>%;">
<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">
<?php if ($crumbs !== '') : ?>
<div class="post-breadcrumbs"><?php echo $crumbs; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
<?php endif; ?>
<?php if ($meta_box !== '') : ?>
<?php echo str_replace('abs-right', 'mb10 ml10 shrink0', $meta_box); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif; ?>
</div>
<h1 class="article-title">
<?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 fas fa-play-circle mr3" aria-hidden="true"></i><?php printf(esc_html__('共%d集', 'aksrt'), $ep_count); ?></span>
<?php else : ?>
<i class="fa fas fa-play-circle 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 ($ep_count > 0) : ?>
<div class="featured-video-episode mt10">
<?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 endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php
return;
endif;
if ($type === 'slide' && !empty($slides)) :
$slide_items = [];
foreach ($slides as $slide_url) {
$slide_items[] = [
'image' => (string) $slide_url,
'alt' => wp_strip_all_tags(get_the_title()),
];
}
$overlay = '<div class="abs-center left-bottom single-cover-con">'
. '<h1 class="article-title title-h-left">' . $title_html . '</h1>'
. ($crumbs !== '' ? $crumbs : '')
. $badge
. '</div>'
. ($meta_box !== '' ? str_replace('abs-right', 'cover-meta abs-right', $meta_box) : '');
$slider_html = '';
if (function_exists('aksrt_render_slider')) {
$slider_html = aksrt_render_slider([
'slides' => $slide_items,
'scale_height' => true,
'scale_ratio' => $img_scale / 100,
'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,
]);
}
?>
<div class="single-head-cover">
<?php if ($slider_html !== '') : ?>
<?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:calc(<?php echo esc_attr((string) $img_scale); ?>% + 50px);">
<?php foreach ($slides as $idx => $slide_url) : ?>
<img<?php echo aksrt_get_lazy_attr('lazy_sider', (string) $slide_url, 'fit-cover', aksrt_get_lazy_thumb('lg')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> alt="" style="<?php echo $idx === 0 ? '' : 'display:none;'; ?>">
<?php endforeach; ?>
<?php echo $overlay; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<?php endif; ?>
</div>
<?php
return;
endif;
// Image cover (default).
$img_attrs = aksrt_get_lazy_attr('lazy_cover', (string) $ctx['cover_url'], 'fit-cover', aksrt_get_lazy_thumb('lg'));
?>
<div class="single-head-cover">
<div class="graphic mb20 single-cover imgbox-container" style="padding-bottom:calc(<?php echo esc_attr((string) $img_scale); ?>% + 50px);">
<img<?php echo $img_attrs; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> alt="<?php echo esc_attr(wp_strip_all_tags(get_the_title())); ?>">
<div class="abs-center left-bottom single-cover-con">
<h1 class="article-title title-h-left"><?php echo $title_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></h1>
<?php if ($crumbs !== '') : ?>
<?php echo $crumbs; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif; ?>
<?php echo $badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<?php if ($meta_box !== '') : ?>
<?php echo str_replace('abs-right', 'cover-meta abs-right', $meta_box); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif; ?>
</div>
</div>
<?php
}
endif;
if (!function_exists('aksrt_single_render_box_header')) :
/**
* Title + author bar (when no cover).
*
* @param array<string,mixed> $ctx Single context.
*/
function aksrt_single_render_box_header(array $ctx): void {
$settings = is_array($ctx['settings'] ?? null) ? $ctx['settings'] : [];
?>
<div class="article-header theme-box clearfix relative">
<?php if (empty($ctx['show_cover'])) : ?>
<?php echo (string) $ctx['status_badge']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<h1 class="article-title"><a href="<?php the_permalink(); ?>"><?php echo (string) ($ctx['title_html'] ?? esc_html(get_the_title())); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></a></h1>
<?php endif; ?>
<?php
// Author bar always shows in header (legacy).
// `authordesc` only controls the after-box author card.
aksrt_single_render_author_bar([
'show_actions' => true,
'show_time' => empty($settings['hide_time']),
'meta_html' => empty($ctx['show_cover']) ? (string) ($ctx['meta_box_html'] ?? '') : '',
]);
$reading_meta = is_array($ctx['meta_items_secondary'] ?? null) ? $ctx['meta_items_secondary'] : [];
if ($reading_meta !== []) :
?>
<div class="article-reading-meta" aria-label="<?php esc_attr_e('阅读信息', 'aksrt'); ?>">
<?php foreach ($reading_meta as $item) : ?>
<span><i class="<?php echo esc_attr((string) ($item['icon'] ?? 'fa fas fa-file-alt')); ?>" aria-hidden="true"></i><?php echo esc_html((string) ($item['text'] ?? '')); ?></span>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<?php
}
endif;
if (!function_exists('aksrt_render_share_dropdown')) :
/**
* Shared #share-dropdown markup (actions row + tabbar fallback).
*
* @param array<string,mixed> $attrs Optional data-share-* overrides.
*/
function aksrt_render_share_dropdown(array $attrs = []): void {
static $printed = false;
if ($printed) {
return;
}
$printed = true;
$title = (string) ($attrs['title'] ?? get_the_title());
$url = (string) ($attrs['url'] ?? (get_permalink() ?: ''));
$image = (string) ($attrs['image'] ?? (get_the_post_thumbnail_url(null, 'large') ?: ''));
?>
<div class="share-dropdown" id="share-dropdown" aria-hidden="true"
data-share-title="<?php echo esc_attr($title); ?>"
data-share-url="<?php echo esc_url($url); ?>"
<?php if ($image !== '') : ?>data-share-image="<?php echo esc_url($image); ?>"<?php endif; ?>>
<div class="share-dropdown__grid">
<button type="button" class="share-item" data-platform="qq"><i class="fa fab fa-qq"></i><span>QQ</span></button>
<button type="button" class="share-item" data-platform="weibo"><i class="fa fab fa-weibo"></i><span>微博</span></button>
<button type="button" class="share-item" data-platform="twitter"><i class="fa fab fa-twitter"></i><span>X</span></button>
<button type="button" class="share-item" data-platform="wechat"><i class="fa fab fa-weixin"></i><span>微信</span></button>
<button type="button" class="share-item" data-platform="copy"><i class="fa fas fa-link"></i><span><?php esc_html_e('复制链接', 'aksrt'); ?></span></button>
<button type="button" class="share-item" data-platform="poster"><i class="fa fas fa-image"></i><span><?php esc_html_e('海报分享', 'aksrt'); ?></span></button>
</div>
<div id="share-qrcode-box" aria-hidden="true"><img id="share-qrcode-image" src="" alt="QR Code"></div>
</div>
<?php
}
endif;
if (!function_exists('aksrt_single_render_actions')) :
/**
* Like / reward / share / favorite row.
*
* @param array<string,mixed> $ctx Single context.
*/
function aksrt_single_render_actions(array $ctx): void {
$settings = is_array($ctx['settings'] ?? null) ? $ctx['settings'] : [];
$post_id = (int) $ctx['post_id'];
$like_count = (int) $ctx['like_count'];
$favorite_count = (int) $ctx['favorite_count'];
$is_liked = !empty($ctx['is_liked']);
$is_favorited = !empty($ctx['is_favorited']);
?>
<div class="text-center muted-3-color box-body em09"><?php echo esc_html($settings['button_toptext'] ?? __('喜欢就支持一下吧', 'aksrt')); ?></div>
<div class="post-actions-wrap relative">
<div class="text-center post-actions">
<?php if (!empty($settings['like_enabled'])) : ?>
<button type="button" class="action action-like<?php echo $is_liked ? ' is-active' : ''; ?>" id="action-like" data-post-id="<?php echo esc_attr((string) $post_id); ?>" data-initial-active="<?php echo $is_liked ? '1' : '0'; ?>">
<i class="<?php echo $is_liked ? 'fa' : 'fa'; ?> fa-heart" aria-hidden="true"></i>
<span class="action-btn__count" data-stat-count="likes" data-base-count="<?php echo esc_attr((string) $like_count); ?>"><?php echo esc_html((string) $like_count); ?></span>
</button>
<?php endif; ?>
<?php
$reward_author = (int) get_the_author_meta('ID');
$reward_btn = ($reward_author > 0 && !empty($settings['rewards_enabled']) && function_exists('aksrt_get_rewards_button'))
? aksrt_get_rewards_button($reward_author, 'action action-rewards', $post_id)
: '';
if ($reward_btn !== '') {
echo $reward_btn; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
aksrt_render_rewards_modal_for_user($reward_author, $post_id);
}
?>
<?php if (!empty($settings['share_enabled'])) : ?>
<button type="button" class="action action-share" id="action-share-trigger" aria-haspopup="true" aria-expanded="false">
<i class="fa fas fa-share-alt" aria-hidden="true"></i>
<span class="action-btn__label"><?php esc_html_e('分享', 'aksrt'); ?></span>
</button>
<?php endif; ?>
<?php if (!empty($settings['favorite_enabled'])) : ?>
<button type="button" class="action action-favorite<?php echo $is_favorited ? ' is-active' : ''; ?>" id="action-favorite" data-post-id="<?php echo esc_attr((string) $post_id); ?>" data-initial-active="<?php echo $is_favorited ? '1' : '0'; ?>">
<i class="<?php echo $is_favorited ? 'fa' : 'fa'; ?> fa-star" aria-hidden="true"></i>
<span class="action-btn__count" data-stat-count="favorites" data-base-count="<?php echo esc_attr((string) $favorite_count); ?>"><?php echo esc_html((string) $favorite_count); ?></span>
</button>
<?php endif; ?>
</div>
<?php if (!empty($settings['share_enabled']) && function_exists('aksrt_render_share_dropdown')) : ?>
<?php aksrt_render_share_dropdown();
?>
<?php endif; ?>
</div>
<?php
}
endif;
if (!function_exists('aksrt_single_render_content_footer')) :
/**
* Copyright + THE END + tags.
*
* @param array<string,mixed> $ctx Single context.
*/
function aksrt_single_render_content_footer(array $ctx): void {
$settings = is_array($ctx['settings'] ?? null) ? $ctx['settings'] : [];
$post_id = (int) $ctx['post_id'];
?>
<?php if (!empty($settings['yiyan_footer'])) : ?>
<?php
aksrt_render_yiyan_block([
'class' => 'article-yiyan yiyan-box aksrt-yiyan yiyan--footer',
]);
?>
<?php endif; ?>
<?php if (!empty($settings['copyright_enabled'])) : ?>
<div class="em09 muted-3-color">
<div><span>©</span> <?php esc_html_e('版权声明', 'aksrt'); ?></div>
<div class="posts-copyright"><?php echo esc_html((string) $ctx['copyright_text']); ?></div>
</div>
<?php endif; ?>
<div class="text-center theme-box muted-3-color box-body separator em09">THE END</div>
<?php
if (function_exists('aksrt_get_single_article_tags_html')) {
echo aksrt_get_single_article_tags_html($post_id); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
?>
<?php
}
endif;
if (!function_exists('aksrt_single_render_author_card')) :
/**
* Compact author card used in the after-box stack.
*
* @param array<string,mixed> $ctx Single context.
*/
function aksrt_single_render_author_card(array $ctx): void {
$author_id = (int) get_the_author_meta('ID');
if ($author_id <= 0) {
return;
}
$author_url = function_exists('aksrt_get_user_home_url') ? aksrt_get_user_home_url($author_id) : get_author_posts_url($author_id);
$author_name = get_the_author();
$bio = function_exists('aksrt_get_user_description') ? aksrt_get_user_description($author_id) : trim((string) get_the_author_meta('description'));
$name_html = function_exists('aksrt_get_user_name')
? aksrt_get_user_name([
'id' => $author_id,
'class' => 'user-name flex jc ac',
'vip' => true,
'medal' => true,
'auth' => true,
'level' => true,
'follow' => false,
])
: '';
$identity_chips = ($name_html === '' && function_exists('aksrt_render_user_identity_chips'))
? aksrt_render_user_identity_chips($author_id, 'card')
: '';
$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']);
$page_s = class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'get_post_page_settings')
? \AKSRT\Settings\Settings::get_post_page_settings()
: [];
$card_limit = max(0, min(12, (int) ($page_s['author_card_limit'] ?? 6)));
$card_orderby = sanitize_key((string) ($page_s['author_card_orderby'] ?? 'views'));
$card_style = sanitize_key((string) ($page_s['author_card_style'] ?? 'card'));
$recent = [];
if ($card_limit > 0) {
$q = [
'author' => $author_id,
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => $card_limit,
'ignore_sticky_posts' => true,
'no_found_rows' => true,
];
$cur = get_the_ID();
if ($cur) {
$q['post__not_in'] = [(int) $cur];
}
if ($card_orderby === 'views') {
$q['meta_query'] = [
'relation' => 'OR',
'views_value' => [
'key' => 'aksrt_post_views',
'compare' => 'EXISTS',
'type' => 'NUMERIC',
],
'views_missing' => [
'key' => 'aksrt_post_views',
'compare' => 'NOT EXISTS',
],
];
$q['orderby'] = ['views_value' => 'DESC', 'date' => 'DESC'];
} elseif ($card_orderby === 'comment_count') {
$q['orderby'] = 'comment_count';
$q['order'] = 'DESC';
} elseif ($card_orderby === 'modified') {
$q['orderby'] = 'modified';
$q['order'] = 'DESC';
} else {
$q['orderby'] = 'date';
$q['order'] = 'DESC';
}
$recent = get_posts($q);
}
$is_self = is_user_logged_in() && get_current_user_id() === $author_id;
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink());
$aksrt_post_settings = class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'get_post_page_settings')
? \AKSRT\Settings\Settings::get_post_page_settings()
: [];
$rewards_on = !empty($aksrt_post_settings['rewards_enabled'])
|| !empty($aksrt_post_settings['post_rewards_enabled'])
|| !empty($aksrt_post_settings['post_rewards_s']);
?>
<div class="user-card author aksrt-panel">
<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">
<?php if ($name_html !== '') : ?>
<?php echo $name_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php else : ?>
<div class="user-name flex jc ac">
<a class="display-name" href="<?php echo esc_url($author_url); ?>"><?php echo esc_html($author_name); ?></a>
<?php if ($identity_chips !== '') : ?>
<?php echo $identity_chips; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif; ?>
</div>
<?php endif; ?>
<?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">
<?php if ($is_self) : ?>
<?php
$submit = function_exists('aksrt_get_new_post_url')
? (string) aksrt_get_new_post_url()
: (function_exists('aksrt_get_page_url_by_template')
? (string) aksrt_get_page_url_by_template('templates/pages/page-submit.php')
: '');
$center = function_exists('aksrt_get_user_center_url')
? (string) aksrt_get_user_center_url()
: $author_url;
if ($submit !== '') :
?>
<a class="but pw-1em mr6 jb-pink" href="<?php echo esc_url($submit); ?>"><?php esc_html_e('发布文章', 'aksrt'); ?></a>
<?php endif; ?>
<a class="but pw-1em mr6 jb-blue" href="<?php echo esc_url($center); ?>"><?php esc_html_e('用户中心', 'aksrt'); ?></a>
<?php else : ?>
<?php if (is_user_logged_in()) : ?>
<button type="button" class="but pw-1em mr6 c-red follow" data-aksrt-follow="<?php echo (int) $author_id; ?>">
<i class="fa fas fa-heart em09" aria-hidden="true"></i> <span><?php esc_html_e('关注', 'aksrt'); ?></span>
</button>
<?php else : ?>
<a href="<?php echo esc_url($login_url); ?>" class="but pw-1em mr6 c-red follow">
<i class="fa fas fa-heart em09" aria-hidden="true"></i> <span><?php esc_html_e('关注', 'aksrt'); ?></span>
</a>
<?php endif; ?>
<?php
$tip = function_exists('aksrt_get_rewards_button') ? aksrt_get_rewards_button($author_id, 'but pw-1em mr6 c-yellow', get_the_ID()) : '';
if ($tip !== '') {
echo $tip; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
if (function_exists('aksrt_render_rewards_modal_for_user')) {
aksrt_render_rewards_modal_for_user($author_id, get_the_ID());
}
}
?>
<a class="but pw-1em mr6 jb-blue" href="<?php echo esc_url($author_url); ?>"><?php esc_html_e('个人主页', 'aksrt'); ?></a>
<?php endif; ?>
</div>
</div>
</div>
<?php if (!empty($recent)) : ?>
<div class="user-card-posts mt10">
<?php if ($card_style === 'card') : ?>
<div class="more-posts-mini flex hh">
<?php foreach ($recent as $rp) : ?>
<?php if ($rp instanceof WP_Post) :
$thumb = get_the_post_thumbnail_url($rp, 'medium');
?>
<a class="item mr10 mb10" href="<?php echo esc_url(get_permalink($rp)); ?>" style="display:block;width:46%;">
<?php if ($thumb) : ?>
<img<?php echo aksrt_get_lazy_attr('lazy_posts_thumb', (string) $thumb, 'fit-cover radius8'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> alt="" decoding="async" style="width:100%;aspect-ratio:16/10;object-fit:cover;">
<?php endif; ?>
<div class="em09 mt6 text-ellipsis"><?php echo esc_html(get_the_title($rp)); ?></div>
</a>
<?php endif; ?>
<?php endforeach; ?>
</div>
<?php else : ?>
<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>
<?php endif; ?>
</div>
<?php else : ?>
<div class="user-card-posts mt10 muted-2-color em09 text-center"><?php esc_html_e('暂无更多文章', 'aksrt'); ?></div>
<?php endif;
?> </div>
</div>
<?php
}
endif;
if (!function_exists('aksrt_single_render_prevnext')) :
/**
* Previous / next navigation (legacy .article-nav).
*
* @param array<string,mixed> $ctx Single context.
*/
function aksrt_single_render_prevnext(array $ctx = []): void {
$settings = is_array($ctx['settings'] ?? null) ? $ctx['settings'] : [];
$enabled = array_key_exists('prevnext', $settings)
? !empty($settings['prevnext'])
: (!array_key_exists('prevnext_s', $settings) || !empty($settings['prevnext_s']));
if (!$enabled) {
return;
}
// legacy: constrain to current category when available.
$in_same_term = !empty(get_the_category());
$prev = get_previous_post($in_same_term, '', 'category');
$next = get_next_post($in_same_term, '', 'category');
if (!$prev && !$next) {
// Fallback: any post.
$prev = get_previous_post();
$next = get_next_post();
}
if (!$prev && !$next) {
return;
}
$prev_title = $prev ? get_the_title($prev) : __('无更多文章', 'aksrt');
$next_title = $next ? get_the_title($next) : __('无更多文章', 'aksrt');
$prev_href = $prev ? get_permalink($prev) : 'javascript:;';
$next_href = $next ? get_permalink($next) : 'javascript:;';
?>
<div class="theme-box" style="height:99px">
<nav class="article-nav">
<div class="main-bg box-body radius8 main-shadow">
<a href="<?php echo $prev ? esc_url($prev_href) : 'javascript:;'; ?>">
<p class="muted-2-color"><i class="fa fas fa-angle-left em12" aria-hidden="true"></i><i class="fa fas fa-angle-left em12 mr6" aria-hidden="true"></i><?php esc_html_e('上一篇', 'aksrt'); ?></p>
<div class="text-ellipsis-2"><?php echo esc_html($prev_title); ?></div>
</a>
</div>
<div class="main-bg box-body radius8 main-shadow">
<a href="<?php echo $next ? esc_url($next_href) : 'javascript:;'; ?>">
<p class="muted-2-color"><?php esc_html_e('下一篇', 'aksrt'); ?><i class="fa fas fa-angle-right em12 ml6" aria-hidden="true"></i><i class="fa fas fa-angle-right em12" aria-hidden="true"></i></p>
<div class="text-ellipsis-2"><?php echo esc_html($next_title); ?></div>
</a>
</div>
</nav>
</div>
<?php
}
endif;
if (!function_exists('aksrt_single_related_query_args')) :
/**
* Build related-posts WP_Query args (legacy-like multi-tax OR + orderby).
*
* @param int $post_id Post ID.
* @param array<string,mixed> $settings Single display settings.
* @return array<string,mixed>
*/
function aksrt_single_related_query_args(int $post_id, array $settings): array {
$count = max(1, min(12, (int) ($settings['related_count'] ?? 6)));
$orderby = sanitize_key((string) ($settings['related_orderby'] ?? 'views'));
if (!in_array($orderby, ['modified', 'date', 'views', 'like', 'comment_count', 'favorite', 'rand'], true)) {
$orderby = 'views';
}
$args = [
'post_type' => get_post_type($post_id) ?: 'post',
'post_status' => 'publish',
'post__not_in' => [$post_id],
'posts_per_page' => $count,
'ignore_sticky_posts' => true,
'no_found_rows' => true,
'order' => 'DESC',
];
$tax_query = ['relation' => 'OR'];
$cat_ids = wp_get_post_terms($post_id, 'category', ['fields' => 'ids']);
if (!empty($cat_ids) && !is_wp_error($cat_ids)) {
$tax_query[] = [
'taxonomy' => 'category',
'field' => 'term_id',
'terms' => array_map('intval', $cat_ids),
];
}
$tag_ids = wp_get_post_terms($post_id, 'post_tag', ['fields' => 'ids']);
if (!empty($tag_ids) && !is_wp_error($tag_ids)) {
$tax_query[] = [
'taxonomy' => 'post_tag',
'field' => 'term_id',
'terms' => array_map('intval', $tag_ids),
];
}
if (count($tax_query) > 1) {
$args['tax_query'] = $tax_query;
}
switch ($orderby) {
case 'rand':
$args['orderby'] = 'rand';
break;
case 'date':
$args['orderby'] = 'date';
break;
case 'modified':
$args['orderby'] = 'modified';
break;
case 'like':
$args['meta_key'] = '_aksrt_like_count';
$args['orderby'] = ['meta_value_num' => 'DESC', 'date' => 'DESC'];
break;
case 'favorite':
$args['meta_key'] = '_aksrt_favorite_count';
$args['orderby'] = ['meta_value_num' => 'DESC', 'date' => 'DESC'];
break;
case 'comment_count':
$args['orderby'] = 'comment_count';
break;
case 'views':
$args['meta_query'] = [
'relation' => 'OR',
'views_value' => [
'key' => 'aksrt_post_views',
'compare' => 'EXISTS',
'type' => 'NUMERIC',
],
'views_missing' => [
'key' => 'aksrt_post_views',
'compare' => 'NOT EXISTS',
],
];
$args['orderby'] = ['views_value' => 'DESC', 'date' => 'DESC'];
break;
default:
$args['orderby'] = 'modified';
break;
}
return $args;
}
endif;
if (!function_exists('aksrt_single_render_related')) :
/**
* Related posts — legacy-like list + optional shared mini renderer.
*
* @param array<string,mixed> $ctx Single context.
*/
function aksrt_single_render_related(array $ctx): void {
$settings = is_array($ctx['settings'] ?? null) ? $ctx['settings'] : [];
if (empty($settings['related_enabled'])) {
return;
}
$post_id = (int) $ctx['post_id'];
$title = (string) ($settings['related_title'] ?? '');
if ($title === '') {
$title = __('相关推荐', 'aksrt');
}
$style = sanitize_key((string) ($settings['related_style'] ?? 'img'));
if (!in_array($style, ['img', 'list', 'text'], true)) {
$style = 'img';
}
$args = function_exists('aksrt_single_related_query_args')
? aksrt_single_related_query_args($post_id, $settings)
: [
'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))),
'ignore_sticky_posts' => true,
];
$query = new WP_Query($args);
$wrap_class = 'theme-box relates' . ($style === 'img' ? ' relates-thumb' : '');
$posts_html = '';
$mini_cfg = aksrt_renderer_config([
'type' => 'list',
'show_number' => false,
]);
while ($query->have_posts()) {
$query->the_post();
$post = get_post();
if (!($post instanceof WP_Post)) {
continue;
}
if ($style === 'list') {
$data = aksrt_renderer_collect_post_data($mini_cfg);
if ($data) {
$posts_html .= aksrt_renderer_mini($data, $mini_cfg);
}
} elseif ($style === 'img' && function_exists('aksrt_render_related_graphic_card')) {
$posts_html .= aksrt_render_related_graphic_card($post);
} else {
$subtitle = class_exists('\AKSRT\Core\PostMetaBox')
? (string) \AKSRT\Core\PostMetaBox::get_meta_value($post->ID, 'subtitle', '')
: '';
$posts_html .= '<li><a class="icon-circle" href="' . esc_url(get_permalink($post)) . '">' . esc_html(get_the_title($post) . $subtitle) . '</a></li>';
}
}
wp_reset_postdata();
if ($posts_html === '') {
$posts_html = '<li>' . esc_html__('暂无相关文章', 'aksrt') . '</li>';
}
?>
<div class="<?php echo esc_attr($wrap_class); ?>">
<div class="box-body notop">
<div class="title-theme"><?php echo esc_html($title); ?></div>
</div>
<div class="aksrt-panel">
<?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; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></ul>
<?php endif; ?>
</div>
</div>
<?php
}
endif;
if (!function_exists('aksrt_single_render_pay_box')) :
/**
* Unified single pay module (legacy aksrtpay_posts_pay_content equivalent).
*
* @param array<string,mixed> $ctx Single context.
*/
function aksrt_single_render_pay_box(array $ctx = []): void {
if ($ctx === []) {
$ctx = function_exists('aksrt_single_build_context') ? aksrt_single_build_context() : [];
}
if (empty($ctx['show_pay_box'])) {
return;
}
$allow = $ctx['allow_view'] ?? null;
if (is_array($allow) && empty($allow['allow'])) {
return;
}
$post_id = (int) ($ctx['post_id'] ?? get_the_ID());
$post = get_post($post_id);
if (!($post instanceof WP_Post)) {
return;
}
$pay_type = (string) ($ctx['pay_type'] ?? 'no');
if (!function_exists('aksrt_get_pay_meta')) {
return;
}
$has_access = function_exists('aksrt_has_paid_access') ? aksrt_has_paid_access($post) : false;
if ($has_access) {
if ($pay_type === 'download') {
get_template_part('templates/parts/resource-download-box');
return;
}
$order_type = $pay_type === 'gallery' ? 5 : ($pay_type === 'video' ? 6 : 4);
echo '<div class="zib-widget pay-box paid-box order-type-' . esc_attr((string) $order_type) . '" id="posts-pay">';
if ($pay_type === 'gallery') {
echo aksrt_render_paid_gallery($post); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} elseif ($pay_type === 'video') {
echo aksrt_render_paid_video($post); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
echo '<div class="box-body"><span class="badg c-green hollow badg-sm mr6">' . esc_html__('已解锁', 'aksrt') . '</span><b>' . esc_html(get_the_title($post)) . '</b></div>';
}
echo '</div>';
} else {
echo aksrt_get_paywall_html($post); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
endif;
if (!function_exists('aksrt_single_pay_box_matches')) :
/**
* @param array<string,mixed> $ctx
*/
function aksrt_single_pay_box_matches(array $ctx, string $slot): bool {
if (empty($ctx['show_pay_box'])) {
return false;
}
$allow = $ctx['allow_view'] ?? null;
if (is_array($allow) && empty($allow['allow'])) {
return false;
}
return (string) ($ctx['paid_box_position'] ?? 'top') === $slot;
}
endif;
if (!function_exists('aksrt_single_render_article')) :
/**
* Main article box (legacy order).
*
* @param array<string,mixed> $ctx Single context.
*/
function aksrt_single_render_article(array $ctx): void {
$settings = is_array($ctx['settings'] ?? null) ? $ctx['settings'] : [];
$maxheight = (int) $ctx['content_maxheight'];
?>
<article <?php post_class('article main-bg theme-box box-body radius8 main-shadow'); ?>>
<?php aksrt_single_render_box_header($ctx); ?>
<div class="article-content">
<?php if (!empty($settings['yiyan_header'])) : ?>
<?php
aksrt_render_yiyan_block([
'class' => 'article-yiyan yiyan-box aksrt-yiyan yiyan--header theme-box text-center radius8 main-shadow',
]);
?>
<?php endif; ?>
<?php do_action('aksrt_single_content_before', get_post()); ?>
<?php if (function_exists('aksrt_single_pay_box_matches') && aksrt_single_pay_box_matches($ctx, 'top')) : ?>
<?php aksrt_single_render_pay_box($ctx); ?>
<?php endif; ?>
<?php if ($ctx['front_content'] !== '') : ?>
<div class="post_front_content theme-box box-body"><?php echo wp_kses_post((string) $ctx['front_content']); ?></div>
<?php endif; ?>
<div class="theme-box wp-posts-content<?php echo $maxheight > 0 ? ' limit-height' : ''; ?>"
<?php echo $maxheight > 0 ? ' style="max-height:' . esc_attr((string) $maxheight) . 'px;"' : ''; ?>
data-maxheight="<?php echo esc_attr((string) ($maxheight > 0 ? max(0, $maxheight - 80) : 0)); ?>"
<?php echo !empty($settings['lightbox']) ? ' data-aksrt-lightbox="1"' : ''; ?>>
<?php the_content(); ?>
<?php
wp_link_pages([
'before' => '<p class="text-center post-nav-links radius8 padding-6">',
'after' => '</p>',
]);
?>
<?php if ($ctx['after_content'] !== '') : ?>
<?php echo wp_kses_post((string) $ctx['after_content']); ?>
<?php endif; ?>
</div>
<?php if (function_exists('aksrt_single_pay_box_matches') && aksrt_single_pay_box_matches($ctx, 'bottom')) : ?>
<?php aksrt_single_render_pay_box($ctx); ?>
<?php endif; ?>
<?php aksrt_single_render_content_footer($ctx); ?>
</div>
<?php aksrt_single_render_actions($ctx); ?>
</article>
<?php
}
endif;
if (!function_exists('aksrt_single_render_after_box')) :
/**
* After-article stack: yiyan box / author card / prevnext / related.
*
* @param array<string,mixed> $ctx Single context.
*/
function aksrt_single_render_after_box(array $ctx): void {
$settings = is_array($ctx['settings'] ?? null) ? $ctx['settings'] : [];
if (!empty($settings['yiyan_box'])) {
aksrt_render_yiyan_block([
'class' => 'yiyan-box main-bg theme-box text-center box-body radius8 main-shadow aksrt-yiyan',
]);
}
// Accept both `authordesc` (Settings DTO) and legacy `authordesc_s`.
$show_author = array_key_exists('authordesc', $settings)
? !empty($settings['authordesc'])
: !empty($settings['authordesc_s']);
if ($show_author) {
aksrt_single_render_author_card($ctx);
}
aksrt_single_render_prevnext($ctx);
aksrt_single_render_related($ctx);
}
endif;