Make --radius-avatar the single source of truth for frontend user avatar shape. A new primitive in components-ext.css (img.avatar / avatar-mini / avatar-md / avatar-lg / .avatar-img img) forces a circle on every avatar image, which covers the bare avatar-mini in the ban and report modals, the classless img inside the VIP modal's .avatar-img, and the post-card meta avatar that previously depended on post-card.css being enqueued. The user center header frame, the public user home hero and the friends page avatar switch to the same token because route CSS loads after the public bundle and would otherwise override the primitive. The VIP icon no longer rides the avatar corner. The user card widget, the after-article author card and the sign-in modal stop opting into aksrt_get_avatar_box()'s corner badge, and the icon renders first in the badge/level row instead, since aksrt_get_user_name already orders name -> VIP -> auth -> medal -> level. The sign-in modal appends it after the display name and keeps the legacy precedence where a ban badge takes the VIP slot. The user card widget name row now shrinks its display name so the extra icon cannot overflow a narrow sidebar. Verified with the new frontend-avatar-shape-contract test plus post-visual, user-card-runtime, circular-button-icon-margin, user-features and page-display contracts, and php -l on every changed PHP file. The public bundle was rebuilt with tools/build-public-assets.mjs.
675 lines
26 KiB
PHP
675 lines
26 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'];
|
||
// 参考主题 article_image_cover_option.scale:20-200(%),标题预留 50px。
|
||
$img_scale = max(20, min(200, (int) $ctx['img_scale']));
|
||
// 参考主题 article_video_cover_option.scale:0-200,0 = 不固定长宽比 → 16:9 回退。
|
||
$video_scale = max(0, min(200, (int) $ctx['video_scale']));
|
||
$video_padding = $video_scale > 0 ? (string) $video_scale : '56.25';
|
||
$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') :
|
||
// 视频封面高度为裸比例(对齐参考主题 .dplayer-scale-height 契约):
|
||
// 标题/面包屑在下方 .single-video-footer,不叠加封面,无 +50px 补偿;
|
||
// 单图/多图封面标题叠加封面,才需要 calc(scale% + 50px)。
|
||
?>
|
||
<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($video_padding); ?>%;">
|
||
<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 echo $crumbs; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||
</div>
|
||
<h1 class="article-title">
|
||
<?php
|
||
$episodes = is_array($ctx['video_episodes'] ?? null) ? $ctx['video_episodes'] : [];
|
||
if ($episodes === []) {
|
||
$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 ?>
|
||
<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>
|
||
</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>';
|
||
// 幻灯片配置(参考主题 CFS_Module::slide 全字段,经 get_post_page_settings 归一化)。
|
||
$slide_option = is_array($ctx['settings']['article_slide_cover_option'] ?? null)
|
||
? $ctx['settings']['article_slide_cover_option']
|
||
: [];
|
||
$single_slide = count($slide_items) === 1;
|
||
$slider_args = [
|
||
'slides' => $slide_items,
|
||
'effect' => (string) ($slide_option['effect'] ?? 'slide'),
|
||
'direction' => (string) ($slide_option['direction'] ?? 'horizontal'),
|
||
'spaceBetween' => (int) ($slide_option['spacebetween'] ?? 15),
|
||
'speed' => (int) ($slide_option['speed'] ?? 0),
|
||
'loop' => !$single_slide && !empty($slide_option['loop']),
|
||
'autoplay' => !$single_slide && !empty($slide_option['autoplay']),
|
||
'interval' => max(1, (int) ($slide_option['interval'] ?? 4)) * 1000,
|
||
'show_nav' => !empty($slide_option['button']),
|
||
'show_pagination' => !empty($slide_option['pagination']),
|
||
'wrapper_class' => 'mb20 single-cover-slide imgbox-container',
|
||
'slider_class' => 'single-cover single-cover--slide',
|
||
'overlay_html' => $overlay,
|
||
];
|
||
if (!empty($slide_option['scale_height'])) {
|
||
// 固定高度模式:PC/移动端分别取 px 高度。
|
||
$slider_args['scale_height'] = false;
|
||
$slider_args['pc_height'] = ((int) ($slide_option['pc_height'] ?? 380)) . 'px';
|
||
$slider_args['m_height'] = ((int) ($slide_option['m_height'] ?? 180)) . 'px';
|
||
} else {
|
||
// 比例高度模式:scale% + 50px(标题预留,与单图封面同公式)。
|
||
$slider_args['scale_height'] = true;
|
||
$slider_args['scale_ratio'] = max(10, (int) ($slide_option['scale'] ?? 35)) / 100;
|
||
$slider_args['scale_extra'] = '50px';
|
||
}
|
||
$slider_html = aksrt_render_slider($slider_args);
|
||
?>
|
||
<div class="single-head-cover">
|
||
<?php echo $slider_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||
</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>
|
||
</div>
|
||
</div>
|
||
<?php
|
||
}
|
||
endif;
|
||
|
||
if (!function_exists('aksrt_single_render_box_header')) :
|
||
/**
|
||
* Title + author bar and the canonical article meta row.
|
||
*
|
||
* @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
|
||
get_template_part('templates/parts/single-author-bar', null, [
|
||
'show_time' => empty($settings['hide_time']),
|
||
'meta_html' => (string) ($ctx['meta_box_html'] ?? ''),
|
||
]);
|
||
?>
|
||
</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
|
||
$like_icon = function_exists('aksrt_icon_markup') ? aksrt_icon_markup('zibsvg-like') : '';
|
||
$like_label = function_exists('aksrt_format_number') ? aksrt_format_number($like_count) : number_format_i18n($like_count);
|
||
if (!empty($settings['like_enabled'])) : ?>
|
||
<button type="button" class="action action-like<?php echo $is_liked ? ' actived' : ''; ?>" id="action-like" data-post-id="<?php echo esc_attr((string) $post_id); ?>" data-initial-active="<?php echo $is_liked ? '1' : '0'; ?>">
|
||
<?php echo $like_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- fixed sprite markup. ?>
|
||
<text><?php esc_html_e('点赞', 'aksrt'); ?></text><count data-stat-count="likes" data-base-count="<?php echo esc_attr((string) $like_count); ?>"><?php echo esc_html((string) $like_label); ?></count>
|
||
</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_template_for_user($reward_author, $post_id);
|
||
}
|
||
?>
|
||
|
||
<?php if (!empty($settings['share_enabled']) && function_exists('aksrt_get_post_share_btn')) : ?>
|
||
<?php echo aksrt_get_post_share_btn($post_id, 'action action-share'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||
<?php endif; ?>
|
||
|
||
<?php
|
||
$favorite_icon = function_exists('aksrt_icon_markup') ? aksrt_icon_markup('zibsvg-favorite') : '';
|
||
$favorite_label = function_exists('aksrt_format_number') ? aksrt_format_number($favorite_count) : number_format_i18n($favorite_count);
|
||
if (!empty($settings['favorite_enabled'])) : ?>
|
||
<button type="button" class="action action-favorite<?php echo $is_favorited ? ' actived' : ''; ?>" id="action-favorite" data-post-id="<?php echo esc_attr((string) $post_id); ?>" data-initial-active="<?php echo $is_favorited ? '1' : '0'; ?>">
|
||
<?php echo $favorite_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- fixed sprite markup. ?>
|
||
<text><?php esc_html_e('收藏', 'aksrt'); ?></text><count data-stat-count="favorites" data-base-count="<?php echo esc_attr((string) $favorite_count); ?>"><?php echo esc_html((string) $favorite_label); ?></count>
|
||
</button>
|
||
<?php endif; ?>
|
||
</div>
|
||
</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')) :
|
||
/**
|
||
* After-article author card (legacy zib_get_user_card_box parity:
|
||
* show_button=false / show_img_bg=false / class="author").
|
||
*
|
||
* @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;
|
||
}
|
||
$settings = is_array($ctx['settings'] ?? null) ? $ctx['settings'] : [];
|
||
|
||
// 会员/禁封徽章不再挂头像角标,改由名字行的会员位出图(aksrt_get_user_name)。
|
||
$avatar_box = aksrt_get_avatar_box($author_id, 'avatar-img avatar-lg', true, false);
|
||
$name_html = aksrt_get_user_name([
|
||
'id' => $author_id,
|
||
'class' => 'flex1 flex ac',
|
||
'vip' => true,
|
||
'medal' => true,
|
||
'auth' => true,
|
||
'level' => true,
|
||
'follow' => true,
|
||
]);
|
||
$badges = aksrt_get_user_badges($author_id);
|
||
$desc = aksrt_get_user_description($author_id);
|
||
$card_style = sanitize_key((string) ($settings['author_card_style'] ?? 'card')) === 'card' ? 'card' : 'mini';
|
||
$posts_html = aksrt_render_user_card_posts([
|
||
'user_id' => $author_id,
|
||
'limit' => max(0, min(12, (int) ($settings['author_card_limit'] ?? 6))),
|
||
'orderby' => sanitize_key((string) ($settings['author_card_orderby'] ?? 'views')),
|
||
'post_type' => 'post',
|
||
'post_style' => $card_style,
|
||
'exclude' => (int) get_the_ID(),
|
||
]);
|
||
?>
|
||
<div class="user-card zib-widget author">
|
||
<div class="card-content mt10 relative">
|
||
<div class="user-content">
|
||
<div class="user-avatar"><?php echo $avatar_box; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
|
||
<div class="user-info mt20 mb10">
|
||
<div class="user-name flex jc"><?php echo $name_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
|
||
<div class="author-tag mt10 mini-scrollbar"><?php echo $badges; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
|
||
<div class="user-desc mt10 muted-2-color em09"><?php echo esc_html($desc); ?></div>
|
||
</div>
|
||
</div>
|
||
<?php echo $posts_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||
</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 zib_posts_related parity:
|
||
* category/topics/tag OR-terms, no current-post exclusion, meta 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 = [
|
||
'showposts' => $count,
|
||
'ignore_sticky_posts' => true,
|
||
'post_type' => 'post',
|
||
'post_status' => 'publish',
|
||
'order' => 'DESC',
|
||
'no_found_rows' => true,
|
||
];
|
||
|
||
// legacy: all three term clauses are always present (even when empty).
|
||
$tax_query = ['relation' => 'OR'];
|
||
foreach (['category', 'topics', 'post_tag'] as $taxonomy) {
|
||
$term_ids = wp_get_post_terms($post_id, $taxonomy, ['fields' => 'ids']);
|
||
$tax_query[] = [
|
||
'taxonomy' => $taxonomy,
|
||
'field' => 'term_id',
|
||
'terms' => array_map('intval', (array) $term_ids),
|
||
];
|
||
}
|
||
$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';
|
||
break;
|
||
case 'favorite':
|
||
$args['meta_key'] = '_aksrt_favorite_count';
|
||
$args['orderby'] = 'meta_value_num';
|
||
break;
|
||
case 'comment_count':
|
||
$args['orderby'] = 'comment_count';
|
||
break;
|
||
case 'views':
|
||
default:
|
||
$args['meta_key'] = 'aksrt_post_views';
|
||
$args['orderby'] = 'meta_value_num';
|
||
break;
|
||
}
|
||
|
||
return $args;
|
||
}
|
||
endif;
|
||
|
||
if (!function_exists('aksrt_single_render_related')) :
|
||
/**
|
||
* Related posts — legacy zib_posts_related parity (img/list/text).
|
||
*
|
||
* @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 = aksrt_single_related_query_args($post_id, $settings);
|
||
|
||
$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') {
|
||
$posts_html .= '<div class="swiper-slide mr10">' . aksrt_render_graphic_card_post($post) . '</div>';
|
||
} 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="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 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) {
|
||
echo aksrt_get_paid_box_html($post); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||
} elseif ($pay_type !== 'reading') {
|
||
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_nav):全局开关 + 每篇文章「不显示目录树」覆盖。
|
||
$article_nav_on = !isset($settings['article_nav']) || !empty($settings['article_nav']);
|
||
if ($article_nav_on) {
|
||
$extension_meta = get_post_meta(get_the_ID(), \AKSRT\Core\DomainModel::POST_META_KEY, true);
|
||
if (is_array($extension_meta) && !empty($extension_meta['no_article-navs'])) {
|
||
$article_nav_on = false;
|
||
}
|
||
}
|
||
?>
|
||
<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 echo $article_nav_on ? '' : ' no-nav'; ?>">
|
||
<?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 !empty($settings['p_indent']) ? ' p-indent' : ''; ?>"
|
||
<?php echo $article_nav_on ? 'data-nav="posts"' : ''; ?>
|
||
<?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 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',
|
||
]);
|
||
}
|
||
|
||
if (!empty($settings['authordesc'])) {
|
||
aksrt_single_render_author_card($ctx);
|
||
}
|
||
|
||
aksrt_single_render_prevnext($ctx);
|
||
aksrt_single_render_related($ctx);
|
||
}
|
||
endif;
|
||
|