修复了一些小问题

This commit is contained in:
2026-08-30 13:49:54 +08:00
parent 1b4928d69b
commit ed914d2eec
12 changed files with 339 additions and 349 deletions
@@ -646,17 +646,3 @@ 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; }
/* ===== legacy user-card after-box ===== */
.user-card.author { 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: nowrap; 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; }
+13 -48
View File
@@ -385,61 +385,26 @@ html.dark .article-end-separator span {
/* ===== Related list (legacy-like) ===== */
.relates .title-theme,
.related-box .title-theme {
font-size: 14px;
font-weight: 700;
margin: 0 0 8px;
/* ===== Zibll .zib-widget base (正文下方卡片堆栈依赖) ===== */
.zib-widget {
clear: both;
background-color: var(--main-bg-color);
padding: 15px;
box-shadow: 0 0 10px var(--main-shadow);
border-radius: var(--main-radius);
margin-bottom: 20px;
}
.related-list,
.relates ul.no-thumb {
list-style: none;
margin: 0;
padding: 4px 0 8px;
}
.related-list li,
.relates ul.no-thumb li {
padding: 8px 0;
border-bottom: 1px solid var(--color-line);
}
.related-list li:last-child,
.relates ul.no-thumb li:last-child {
border-bottom: none;
}
.related-list a,
.relates ul.no-thumb a {
color: var(--color-ink);
text-decoration: none;
font-size: 14px;
}
.related-list a:hover,
.relates ul.no-thumb a:hover {
color: var(--color-primary);
.zib-widget.nobottom.notop {
padding-top: 0;
padding-bottom: 0;
}
/* ===== Related (legacy .relates / .relates-thumb) ===== */
.post-nav-box.is-empty,
.post-nav-box__item.is-empty {
visibility: hidden;
}
/* ===== Related (legacy .relates / .relates-thumb) ===== */
.relates .title-theme { margin-bottom: 0; }
.relates .aksrt-panel { padding: 12px 15px; }
.relates ul.no-thumb { list-style: none; margin: 0; padding: 0; }
.relates ul.no-thumb li { padding: 6px 0; border-bottom: 1px dashed var(--color-line, rgba(0,0,0,.06)); }
.relates ul.no-thumb li:last-child { border-bottom: 0; }
.relates ul.no-thumb a.icon-circle { color: inherit; text-decoration: none; }
.relates ul.no-thumb a.icon-circle:hover { color: var(--focus-color, #f04494); }
.relates-thumb .swiper-scroll { padding-bottom: 4px; }
.relates-thumb .swiper-slide { width: 200px; max-width: 70vw; }
.relates-thumb .graphic.style-3 { position: relative; overflow: hidden; border-radius: 8px; }
.relates-thumb .graphic.style-3 .graphic-img { position: relative; overflow: hidden; background: var(--surface-2, #f3f4f6); }
.relates-thumb .graphic.style-3 img.fit-cover { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.relates-thumb .graphic.style-3 .graphic-text { padding: 10px 12px; width: 100%; background: linear-gradient(transparent, rgba(0,0,0,.55)); color: #fff; }
.relates-thumb .graphic.style-3 .text1 { font-size: 13px; font-weight: 600; color: #fff; }
.relates-thumb .graphic.style-3 .text3 { opacity: .9; margin-top: 4px; }
/* Colored tag pills on single */
.article-tags .but { display: inline-flex; align-items: center; gap: 4px; margin: 0 6px 6px 0; }
.article-tags .but.ml6 { margin-left: 0; }
+37
View File
@@ -222,6 +222,43 @@ add_filter('get_avatar_data', static function (array $args, $subject): array {
return $args;
}, 20, 2);
if (!function_exists('aksrt_get_avatar_box')) {
/**
* legacy parity: 头像框(span.avatar-img + VIP 角标 + 可选主页链接)。
*
* @param mixed $id_or_email 用户 ID / WP_User / 邮箱。
* @param string $class 外层 span 类(avatar-img avatar-lg 等)。
* @param bool $link 是否包一层用户主页链接。
* @param bool $vip 是否叠加 VIP 角标图。
*/
function aksrt_get_avatar_box($id_or_email, string $class = 'avatar-img', bool $link = true, bool $vip = true): string {
$user_id = aksrt_resolve_avatar_user_id($id_or_email);
$subject = $user_id > 0 ? $user_id : $id_or_email;
$img = aksrt_get_avatar_html($subject, 80, ['class' => 'avatar']);
$badge = '';
if ($vip && $user_id > 0 && function_exists('aksrt_get_user_vip_level') && aksrt_get_user_vip_level($user_id) > 0) {
$icon_url = '';
if (class_exists('\AKSRT\Settings\Settings')) {
$vip_settings = \AKSRT\Settings\Settings::get_payment_vip_settings();
$level = aksrt_get_user_vip_level($user_id);
$icon_url = esc_url((string) ($vip_settings['levels'][$level]['img_icon'] ?? ''));
}
if ($icon_url !== '') {
$badge_text = function_exists('aksrt_get_vip_badge_text') ? aksrt_get_vip_badge_text($user_id) : 'VIP';
$badge = '<img class="avatar-badge" src="' . $icon_url . '" title="' . esc_attr($badge_text) . '" alt="' . esc_attr($badge_text) . '">';
}
}
$html = '<span class="' . esc_attr($class) . '">' . $img . $badge . '</span>';
if ($link && $user_id > 0) {
$home = function_exists('aksrt_get_user_home_url') ? aksrt_get_user_home_url($user_id) : get_author_posts_url($user_id);
$html = '<a href="' . esc_url($home) . '">' . $html . '</a>';
}
return $html;
}
}
add_action('show_user_profile', 'aksrt_render_admin_avatar_field');
add_action('edit_user_profile', 'aksrt_render_admin_avatar_field');
add_action('user_edit_form_tag', static function (): void {
+26
View File
@@ -297,6 +297,32 @@ if (! function_exists('aksrt_format_number')) :
return (string) number_format_i18n($num);
}
endif;
if (! function_exists('aksrt_cut_count')) :
/**
* legacy parity: 万级数字缩写(_cut_count)。
*/
function aksrt_cut_count($number): string {
$number = (float) $number;
if ($number > 999999) {
return round($number / 10000, 0) . 'W+';
}
if ($number > 9999) {
return round($number / 10000, 1) . 'W+';
}
return (string) $number;
}
endif;
if (! function_exists('aksrt_str_cut')) :
/**
* legacy parity: 按显示宽度截断标题(ASCII 记 1、CJK 记 2,zib_str_cut)。
*/
function aksrt_str_cut(string $str, int $start = 0, int $width = 100, string $trimmarker = '...'): string {
if ($str === '' || $width <= 0) {
return $str;
}
return mb_strimwidth($str, max(0, $start), $width * 2, $trimmarker, 'UTF-8');
}
endif;
if (!function_exists('aksrt_render_mini_post_item')) :
/**
* Render a compact sidebar post item for homepage widgets.
+74
View File
@@ -243,6 +243,80 @@ if (!function_exists('aksrt_get_user_name')) {
}
}
if (!function_exists('aksrt_get_user_posts_meta_sum')) {
/**
* 求和用户已发布文章的某个 meta(legacy get_user_posts_meta_sum parity)。
*
* @param int $user_id 用户 ID。
* @param string $meta_key 文章 meta 键。
*/
function aksrt_get_user_posts_meta_sum(int $user_id, string $meta_key): int {
global $wpdb;
if ($user_id <= 0 || $meta_key === '') {
return 0;
}
$cache_group = 'aksrt_user_posts_' . $meta_key . '_sum';
$cached = wp_cache_get($user_id, $cache_group);
if (false !== $cached) {
return (int) $cached;
}
$sum = $wpdb->get_var($wpdb->prepare(
"SELECT SUM(_m.meta_value + 0) FROM {$wpdb->posts} _p INNER JOIN {$wpdb->postmeta} _m ON _p.ID = _m.post_id"
. " WHERE _p.post_author = %d AND _m.meta_key = %s AND _p.post_status = 'publish'",
$user_id,
$meta_key
));
$sum = $sum ? (int) $sum : 0;
wp_cache_set($user_id, $sum, $cache_group, 43200);
return $sum;
}
}
if (!function_exists('aksrt_get_user_badges')) {
/**
* legacy parity: 作者资料卡统计徽章行(文章 / 评论 / 粉丝|点赞 / 人气值)。
*
* @param int $user_id 用户 ID。
*/
function aksrt_get_user_badges(int $user_id): string {
if ($user_id <= 0) {
return '';
}
$home = aksrt_get_user_home_url($user_id);
$post_n = aksrt_cut_count(count_user_posts($user_id, 'post', true));
$com_n = aksrt_cut_count((int) get_comments([
'user_id' => $user_id,
'status' => 'approve',
'count' => true,
'post_type' => ['post'],
]));
$followed_n = class_exists('\AKSRT\User\Follow')
? aksrt_cut_count((int) \AKSRT\User\Follow::get_instance()->get_follower_count($user_id))
: 0;
$like_n = aksrt_cut_count(aksrt_get_user_posts_meta_sum($user_id, '_aksrt_like_count'));
$view_n = aksrt_cut_count(aksrt_get_user_posts_meta_sum($user_id, 'aksrt_post_views'));
$html = '';
$html .= '<a class="but c-blue tag-posts" data-toggle="tooltip" title="' . esc_attr(sprintf(__('共%s篇文章', 'aksrt'), $post_n)) . '" href="' . esc_url($home) . '">' . aksrt_renderer_svg_icon('post') . $post_n . '</a>';
$html .= '<a class="but c-green tag-comment" data-toggle="tooltip" title="' . esc_attr(sprintf(__('共%s条评论', 'aksrt'), $com_n)) . '" href="' . esc_url($home) . '">' . aksrt_renderer_svg_icon('comment') . $com_n . '</a>';
if ($followed_n) {
$html .= '<a class="but c-yellow tag-follow" data-toggle="tooltip" title="' . esc_attr(sprintf(__('共%s个粉丝', 'aksrt'), $followed_n)) . '" href="' . esc_url($home) . '"><i class="fa fa-heart em09" aria-hidden="true"></i>' . $followed_n . '</a>';
} elseif ($like_n) {
$html .= '<span class="badg c-yellow tag-like" data-toggle="tooltip" title="' . esc_attr(sprintf(__('获得%s个点赞', 'aksrt'), $like_n)) . '">' . aksrt_renderer_svg_icon('like') . $like_n . '</span>';
}
$html .= '<span class="badg c-red tag-view" data-toggle="tooltip" title="' . esc_attr(sprintf(__('人气值 %s', 'aksrt'), $view_n)) . '">' . aksrt_renderer_svg_icon('hot') . $view_n . '</span>';
/**
* Filter the author-card count badges row.
*
* @param string $html Badges HTML.
* @param int $user_id User ID.
*/
return (string) apply_filters('aksrt_user_count_badges', $html, $user_id);
}
}
if (!function_exists('aksrt_render_user_medal_entry')) {
/**
* Render the compact legacy-style badge overview entry.
+35 -20
View File
@@ -475,34 +475,49 @@ endif;
// Register gate once helpers load.
add_filter('the_content', 'aksrt_single_content_allow_view', 8);
if (!function_exists('aksrt_render_related_graphic_card')) :
if (!function_exists('aksrt_render_graphic_card_post')) :
/**
* Minimal legacy style-3 related thumb card.
* legacy parity: style-3 图文卡(相关推荐 / 作者卡文章条共用)。
*
* @param WP_Post $post 文章对象。
* @param array<string,mixed> $args {
* @type string $class .graphic 附加类(mb10 / em09)。
* @type int $cut 悬停第二行标题截断宽度。
* }
*/
function aksrt_render_related_graphic_card(WP_Post $post): string {
function aksrt_render_graphic_card_post(WP_Post $post, array $args = []): string {
$args = wp_parse_args($args, [
'class' => 'mb10',
'cut' => 45,
]);
$subtitle = class_exists('\AKSRT\Core\PostMetaBox')
? (string) \AKSRT\Core\PostMetaBox::get_meta_value($post->ID, 'subtitle', '')
: '';
$title = get_the_title($post) . $subtitle;
$url = get_permalink($post);
$media = aksrt_renderer_resolve_images($post->ID, aksrt_renderer_config(['type' => 'card']));
$thumb = (string) $media['primary'];
$time = human_time_diff((int) get_the_time('U', $post), (int) current_time('timestamp'));
$img = (string) ($media['primary'] ?? '');
if ($img === '') {
$img = aksrt_get_lazy_thumb('lg');
}
$time = aksrt_time_ago((string) get_the_time('Y-m-d H:i:s', $post));
$views = function_exists('aksrt_get_post_views') ? (int) aksrt_get_post_views($post->ID) : 0;
$views_label = function_exists('aksrt_format_number') ? aksrt_format_number($views) : number_format_i18n($views);
$info = '<item>' . esc_html($time) . '</item>'
. '<item class="pull-right">' . aksrt_renderer_svg_icon('view') . ' ' . esc_html(aksrt_cut_count($views)) . '</item>';
$img_attrs = aksrt_get_lazy_attr('lazy_cover', (string) $thumb, 'fit-cover', aksrt_get_lazy_thumb('lg'));
$html = '<div class="swiper-slide mr10">';
$html .= '<div class="graphic style-3 mb10">';
$html .= '<a href="' . esc_url((string) $url) . '" class="radius8" title="' . esc_attr($title) . '">';
$html .= '<div class="graphic-img" style="padding-bottom:70%">';
$html .= '<img' . $img_attrs . ' alt="' . esc_attr($title) . '">';
$html .= '<div class="abs-center left-bottom graphic-text">';
$html .= '<div class="text1 text-ellipsis">' . esc_html($title) . '</div>';
$html .= '<div class="text3 muted-2-color em09"><span class="item">' . esc_html($time) . '</span>';
$html .= '<span class="item pull-right"><i class="fa fas fa-eye" aria-hidden="true"></i> ' . esc_html((string) $views_label) . '</span></div>';
$html .= '</div></div></a></div></div>';
return $html;
return aksrt_graphic_card([
'type' => 'style-3',
'class' => (string) $args['class'],
'img' => $img,
'alt' => $title,
'link' => [
'url' => get_permalink($post),
'target' => '',
],
'text1' => esc_html($title),
'text2' => esc_html(aksrt_str_cut($title, 0, (int) $args['cut'], '...')),
'text3' => $info,
'lazy' => true,
'height_scale' => 70,
]);
}
endif;
+65 -223
View File
@@ -300,182 +300,55 @@ endif;
if (!function_exists('aksrt_single_render_author_card')) :
/**
* Compact author card used in the after-box stack.
* 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;
}
$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
$author_id = (int) get_the_author_meta('ID');
if ($author_id <= 0) {
return;
}
$settings = is_array($ctx['settings'] ?? null) ? $ctx['settings'] : [];
$avatar_box = aksrt_get_avatar_box($author_id, 'avatar-img avatar-lg');
$name_html = aksrt_get_user_name([
'id' => $author_id,
'class' => 'flex1 flex ac',
'vip' => false,
'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;
@@ -534,7 +407,8 @@ endif;
if (!function_exists('aksrt_single_related_query_args')) :
/**
* Build related-posts WP_Query args (legacy-like multi-tax OR + orderby).
* 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.
@@ -548,36 +422,25 @@ if (!function_exists('aksrt_single_related_query_args')) :
}
$args = [
'post_type' => get_post_type($post_id) ?: 'post',
'post_status' => 'publish',
'post__not_in' => [$post_id],
'posts_per_page' => $count,
'showposts' => $count,
'ignore_sticky_posts' => true,
'no_found_rows' => 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'];
$cat_ids = wp_get_post_terms($post_id, 'category', ['fields' => 'ids']);
if (!empty($cat_ids) && !is_wp_error($cat_ids)) {
foreach (['category', 'topics', 'post_tag'] as $taxonomy) {
$term_ids = wp_get_post_terms($post_id, $taxonomy, ['fields' => 'ids']);
$tax_query[] = [
'taxonomy' => 'category',
'taxonomy' => $taxonomy,
'field' => 'term_id',
'terms' => array_map('intval', $cat_ids),
'terms' => array_map('intval', (array) $term_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;
}
$args['tax_query'] = $tax_query;
switch ($orderby) {
case 'rand':
@@ -591,32 +454,19 @@ if (!function_exists('aksrt_single_related_query_args')) :
break;
case 'like':
$args['meta_key'] = '_aksrt_like_count';
$args['orderby'] = ['meta_value_num' => 'DESC', 'date' => 'DESC'];
$args['orderby'] = 'meta_value_num';
break;
case 'favorite':
$args['meta_key'] = '_aksrt_favorite_count';
$args['orderby'] = ['meta_value_num' => 'DESC', 'date' => 'DESC'];
$args['orderby'] = 'meta_value_num';
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';
$args['meta_key'] = 'aksrt_post_views';
$args['orderby'] = 'meta_value_num';
break;
}
@@ -626,7 +476,7 @@ endif;
if (!function_exists('aksrt_single_render_related')) :
/**
* Related posts — legacy-like list + optional shared mini renderer.
* Related posts — legacy zib_posts_related parity (img/list/text).
*
* @param array<string,mixed> $ctx Single context.
*/
@@ -646,15 +496,7 @@ if (!function_exists('aksrt_single_render_related')) :
$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,
];
$args = aksrt_single_related_query_args($post_id, $settings);
$query = new WP_Query($args);
$wrap_class = 'theme-box relates' . ($style === 'img' ? ' relates-thumb' : '');
@@ -674,8 +516,8 @@ if (!function_exists('aksrt_single_render_related')) :
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);
} 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', '')
@@ -692,7 +534,7 @@ if (!function_exists('aksrt_single_render_related')) :
<div class="box-body notop">
<div class="title-theme"><?php echo esc_html($title); ?></div>
</div>
<div class="aksrt-panel">
<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>
+4 -28
View File
@@ -100,41 +100,18 @@ if (!function_exists('aksrt_render_user_card_posts')) :
$style = 'mini';
}
$posts = aksrt_user_card_query_posts($args);
$show_empty = !empty($args['show_empty']);
if (!$posts) {
if (!$show_empty) {
return '';
}
return '<div class="user-card-posts mt10 muted-2-color em09 text-center">' .
esc_html__('暂无更多文章', 'aksrt') .
'</div>';
return '';
}
ob_start();
echo '<div class="user-card-posts mt10">';
if ($style === 'card') {
echo '<div class="swiper-container more-posts swiper-scroll"><div class="swiper-wrapper">';
foreach ($posts as $rp) {
$thumb = get_the_post_thumbnail_url($rp, 'medium');
$title = get_the_title($rp);
$time = function_exists('aksrt_time_ago')
? aksrt_time_ago((string) $rp->post_date)
: human_time_diff((int) get_the_time('U', $rp), (int) current_time('timestamp')) . __('前', 'aksrt');
$views = function_exists('aksrt_get_post_views') ? (int) aksrt_get_post_views($rp->ID) : 0;
echo '<div class="swiper-slide mr10">';
echo '<a class="aksrt-more-post-card" href="' . esc_url(get_permalink($rp)) . '">';
if ($thumb) {
echo '<span class="aksrt-more-post-card__thumb"><img' . aksrt_get_lazy_attr('lazy_posts_thumb', (string) $thumb, 'fit-cover') . ' alt="" decoding="async"></span>';
}
echo '<span class="aksrt-more-post-card__body">';
echo '<span class="aksrt-more-post-card__title text-ellipsis">' . esc_html($title) . '</span>';
echo '<span class="aksrt-more-post-card__meta muted-2-color em09">';
echo '<item>' . esc_html($time) . '</item>';
if ($views > 0) {
echo '<item class="pull-right"><i class="fa fas fa-eye" aria-hidden="true"></i> ' . esc_html((string) $views) . '</item>';
}
echo '</span></span></a></div>';
echo '<div class="swiper-slide mr10">' .
aksrt_render_graphic_card_post($rp, ['class' => 'em09', 'cut' => 32]) .
'</div>';
}
echo '</div><div class="swiper-button-prev"></div><div class="swiper-button-next"></div></div>';
} else {
@@ -146,7 +123,6 @@ if (!function_exists('aksrt_render_user_card_posts')) :
}
echo '</div>';
}
echo '</div>';
return (string) ob_get_clean();
}
endif;
+40 -10
View File
@@ -470,22 +470,52 @@ endif;
if (!function_exists('aksrt_time_ago')) :
/**
* 将日期转为相对时间描述(中文)
* 将日期转为相对时间描述(中文,legacy zib_get_time_ago parity)。
*
* @param string $date 日期字符串(MySQL datetime 格式)
* @return string 例如 "19天前"、"3个月前"
*/
function aksrt_time_ago(string $date): string {
$diff = time() - strtotime($date);
$time = (int) strtotime($date);
$ctime = (int) strtotime(current_time('mysql'));
$t = $ctime - $time;
if ($diff < 0) return __('刚刚', 'aksrt');
if ($diff < 60) return __('刚刚', 'aksrt');
if ($diff < 3600) return sprintf(__('%d分钟前', 'aksrt'), (int) floor($diff / 60));
if ($diff < 86400) return sprintf(__('%d小时前', 'aksrt'), (int) floor($diff / 3600));
if ($diff < 2592000) return sprintf(__('%d天前', 'aksrt'), (int) floor($diff / 86400));
if ($diff < 31536000) return sprintf(__('%d个月前', 'aksrt'), (int) floor($diff / 2592000));
return sprintf(__('%d年前', 'aksrt'), (int) floor($diff / 31536000));
if ($t < 0) {
return date('Y-m-d H:i', $time);
}
$y = (int) date('Y', $ctime) - (int) date('Y', $time);
if (0 === $t) {
return __('刚刚', 'aksrt');
}
if ($t < 60) {
return sprintf(__('%d秒前', 'aksrt'), $t);
}
if ($t < 3600) {
return sprintf(__('%d分钟前', 'aksrt'), (int) floor($t / 60));
}
if ($t < 86400) {
return sprintf(__('%d小时前', 'aksrt'), (int) floor($t / 3600));
}
if ($t < 2592000) {
if ($time > (int) strtotime(date('Ymd', (int) strtotime('-1 day')))) {
return __('昨天', 'aksrt');
}
if ($time > (int) strtotime(date('Ymd', (int) strtotime('-2 days')))) {
return __('前天', 'aksrt');
}
return sprintf(__('%d天前', 'aksrt'), (int) floor($t / 86400));
}
if ($t < 31536000 && 0 === $y) {
$m = (int) date('m', $ctime) - (int) date('m', $time) - 1;
if (0 === $m) {
return sprintf(__('%d天前', 'aksrt'), (int) floor($t / 86400));
}
return sprintf(__('%d个月前', 'aksrt'), $m);
}
if ($t < 31536000 && $y > 0) {
return sprintf(__('%d个月前', 'aksrt'), 12 - (int) date('m', $time) + (int) date('m', $ctime));
}
return sprintf(__('%d年前', 'aksrt'), (int) date('Y', $ctime) - (int) date('Y', $time));
}
endif;
+1 -1
View File
@@ -106,7 +106,7 @@ final class UserRenderer {
}
$html .= '</div>';
if (!empty($i['show_posts']) && function_exists('aksrt_render_user_card_posts')) {
$html .= '<div class="aksrt-user-card__posts">' . aksrt_render_user_card_posts(['user_id' => $user_id, 'count' => 4, 'post_style' => 'mini', 'show_empty' => false]) . '</div>';
$html .= '<div class="aksrt-user-card__posts">' . aksrt_render_user_card_posts(['user_id' => $user_id, 'limit' => 4, 'post_style' => 'mini']) . '</div>';
}
return $html . '</article>';
}
+3 -1
View File
@@ -51,7 +51,9 @@ if (!$query->have_posts()) {
$query->the_post();
$post = get_post();
if ($post instanceof WP_Post) {
echo aksrt_render_related_graphic_card($post); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '<div class="swiper-slide mr10">' .
aksrt_render_graphic_card_post($post) . // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'</div>';
}
}
wp_reset_postdata();
@@ -82,7 +82,8 @@ test('settings use legacy list modes, grouped options, and destructive related k
for (const key of legacyKeys) {
assert.equal(source.includes(key), false, `legacy article key remains: ${key}`);
}
assert.doesNotMatch(source, /list_card_topics|is_topics|\btopics\b/);
// bare `topics` is legal again: the related-posts query mirrors the legacy taxonomy OR.
assert.doesNotMatch(source, /list_card_topics|is_topics/);
});
test('forced mode wins and thumb_if_has ignores spare and lazy placeholders', () => {
@@ -226,12 +227,18 @@ test('related posts, author page and widgets use their dedicated renderers', ()
assert.match(related, /\['img', 'list', 'text'\]/);
assert.match(related, /aksrt_renderer_mini\(\$data, \$mini_cfg\)/);
assert.match(related, /aksrt_render_related_graphic_card\(\$post\)/);
assert.match(related, /\<div class="swiper-slide mr10"\>' \. aksrt_render_graphic_card_post\(\$post\)/);
assert.match(related, /<li><a class="icon-circle"/);
assert.doesNotMatch(related, /topics|post_related_source/);
assert.match(related, /\['category', 'topics', 'post_tag'\]/);
assert.match(related, /'terms'\s+=>\s+array_map\('intval', \(array\) \$term_ids\)/);
assert.match(related, /class=\"zib-widget\"/);
assert.doesNotMatch(related, /post_related_source/);
assert.doesNotMatch(related, /post__not_in/);
assert.match(related, /\$args\['meta_key'\]\s*=\s*'aksrt_post_views'/);
assert.match(relatedCard, /aksrt_renderer_resolve_images/);
assert.match(relatedCard, /'text2'\s+=>\s+esc_html\(aksrt_str_cut\(\$title, 0, \(int\) \$args\['cut'\], '\.\.\.'\)\)/);
assert.match(resourceRelated, /swiper-container swiper-scroll/);
assert.match(resourceRelated, /aksrt_render_related_graphic_card/);
assert.match(resourceRelated, /aksrt_render_graphic_card_post/);
assert.doesNotMatch(resourceRelated, /aksrt_render_post_list/);
assert.match(author, /aksrt_render_post_list/);
assert.doesNotMatch(author, /'type'\s*=>\s*'list'/);
@@ -245,6 +252,36 @@ test('related posts, author page and widgets use their dedicated renderers', ()
assert.match(widgets, /swiper-container swiper-scroll/);
});
test('after-box author card mirrors legacy user-card structure', () => {
const render = read('inc/helpers/aksrt-single-render.php');
const identity = read('inc/helpers/aksrt-identity.php');
const avatar = read('inc/helpers/aksrt-avatar.php');
const userCard = read('inc/helpers/aksrt-user-card.php');
const css = read('assets/css/frontend/single-post/related-toc.css');
const authorActions = read('assets/css/frontend/single-post/author-actions.css');
assert.match(render, /avatar-img avatar-lg/);
assert.match(render, /'vip'\s+=>\s+false/);
assert.match(render, /'follow'\s+=>\s+true/);
assert.match(render, /<div class="author-tag mt10 mini-scrollbar">/);
assert.match(render, /<div class="user-desc mt10 muted-2-color em09">/);
assert.doesNotMatch(render, /user-btns/);
assert.doesNotMatch(render, /发布文章.*用户中心/s);
assert.match(identity, /tag-posts/);
assert.match(identity, /tag-comment/);
assert.match(identity, /tag-follow/);
assert.match(identity, /tag-like/);
assert.match(identity, /tag-view/);
assert.match(identity, /get_follower_count/);
assert.match(avatar, /class="avatar-badge"/);
assert.match(userCard, /swiper-container more-posts swiper-scroll/);
assert.match(userCard, /'class'\s+=>\s+'em09', 'cut'\s+=>\s+32/);
assert.doesNotMatch(userCard, /aksrt-more-post-card/);
assert.match(css, /^\.zib-widget \{/m);
assert.doesNotMatch(css, /relates-thumb \.graphic\.style-3 \.graphic-img/);
assert.doesNotMatch(authorActions, /\.user-card-author|\.user-card \.author|user-card-posts/);
});
test('AJAX pagination can recover, caps auto loading and rehydrates appended media', () => {
const php = read('inc/helpers/template-tags/pagination-permissions.php');
const homepage = read('inc/helpers/homepage/content.php');