Merge branch 'main' of ssh://gitea.rutua.cn:222/kate522/wordpress-theme-aksrt

This commit is contained in:
2026-08-31 19:17:24 +08:00
19 changed files with 261 additions and 615 deletions
+11 -11
View File
@@ -984,17 +984,6 @@ strong, b {
.but a { color: inherit; }
/* 关注控件(.follow 且无 .but):纯文字色样式,无背景色;
清除原生 button 的 UA 背景/边框,并保持 .but 同款可点反馈 */
.follow:not(.but) {
background: transparent;
border: none;
padding: 0;
cursor: pointer;
transition: 0.15s;
}
.follow:not(.but):hover { opacity: 0.8; }
.but .icon,
.but > .fa:not(.fa-angle-right),
.but > i[class*="fa-"]:not(.fa-angle-right) { margin-right: 0.25em; }
@@ -2697,6 +2686,17 @@ main.container,
flex-wrap: nowrap;
white-space: nowrap;
}
.user-card.author .user-name > name {
overflow: visible;
}
.user-card.author .user-name > name > .display-name {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.user-card.author .user-name > name > .follow {
flex-shrink: 0;
}
.aksrt-author-hero__name,
.uc-header__name {
display: inline-flex;
+1 -1
View File
File diff suppressed because one or more lines are too long
-134
View File
@@ -738,58 +738,6 @@ article.article.box-body,
}
/* source: assets/css/frontend/single-post/author-actions.css */
/* ===== Author Bar (inside article card) ===== */
.article-author-bar {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 16px;
}
.article-author-bar__avatar {
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
flex-shrink: 0;
}
.article-author-bar__info {
flex: 1;
min-width: 0;
}
.article-author-bar__name {
font-weight: 600;
font-size: 14px;
color: var(--color-ink);
}
.article-author-bar__name:hover {
color: var(--color-primary);
}
.article-author-bar__meta {
display: flex;
flex-wrap: wrap;
gap: 10px;
font-size: 12px;
color: var(--color-ink-4, var(--color-ink-4));
margin-top: 2px;
}
.article-author-bar__meta span {
display: inline-flex;
align-items: center;
gap: 3px;
}
.article-author-bar__line {
height: 1px;
background: var(--color-line);
margin-bottom: 20px;
}
/* ===== Content Footer ===== */
.article-footer {
margin-top: 32px;
@@ -1210,88 +1158,6 @@ html.dark .post-drop-meta__item:hover {
text-align: center;
}
.author-box__header {
display: flex;
align-items: flex-start;
gap: 14px;
}
.author-box__avatar {
width: 56px;
height: 56px;
border-radius: 50%;
object-fit: cover;
flex-shrink: 0;
border: 3px solid var(--color-primary-soft, rgba(240, 68, 148, 0.2));
}
.author-box__info {
flex: 1;
min-width: 0;
}
.author-box__name {
font-size: 1rem;
font-weight: 700;
color: var(--color-ink);
display: block;
margin-bottom: 4px;
}
.author-box__name:hover { color: var(--color-primary); }
.author-box__bio {
font-size: 13px;
color: var(--color-ink-3, var(--color-ink-4));
line-height: 1.5;
margin: 0 0 6px;
}
.author-box__stats {
font-size: 12px;
color: var(--color-ink-4, var(--color-ink-4));
}
.author-box__stats strong {
color: var(--color-ink);
margin-right: 2px;
}
.author-box__posts {
margin-top: 14px;
padding-top: 12px;
border-top: 1px solid var(--color-line);
}
.author-box__posts-title {
font-size: 12px;
font-weight: 600;
color: var(--color-ink-3, var(--color-ink-4));
margin: 0 0 8px;
}
.author-box__posts ul {
list-style: none;
margin: 0;
padding: 0;
}
.author-box__posts li { padding: 3px 0; }
.author-box__posts a {
font-size: 13px;
color: var(--color-ink);
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
}
.author-box__posts a:hover { color: var(--color-primary); }
.author-box__posts a::before { content: '\2022'; color: var(--color-primary); }
/* ===== legacy single-metabox / author bar ===== */
.article-avatar .user-info {
width: 100%;
+1 -1
View File
File diff suppressed because one or more lines are too long
-11
View File
@@ -178,17 +178,6 @@
.but a { color: inherit; }
/* 关注控件(.follow 且无 .but):纯文字色样式,无背景色;
清除原生 button 的 UA 背景/边框,并保持 .but 同款可点反馈 */
.follow:not(.but) {
background: transparent;
border: none;
padding: 0;
cursor: pointer;
transition: 0.15s;
}
.follow:not(.but):hover { opacity: 0.8; }
.but .icon,
.but > .fa:not(.fa-angle-right),
.but > i[class*="fa-"]:not(.fa-angle-right) { margin-right: 0.25em; }
+11
View File
@@ -259,6 +259,17 @@ main.container,
flex-wrap: nowrap;
white-space: nowrap;
}
.user-card.author .user-name > name {
overflow: visible;
}
.user-card.author .user-name > name > .display-name {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.user-card.author .user-name > name > .follow {
flex-shrink: 0;
}
.aksrt-author-hero__name,
.uc-header__name {
display: inline-flex;
@@ -1,55 +1,3 @@
/* ===== Author Bar (inside article card) ===== */
.article-author-bar {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 16px;
}
.article-author-bar__avatar {
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
flex-shrink: 0;
}
.article-author-bar__info {
flex: 1;
min-width: 0;
}
.article-author-bar__name {
font-weight: 600;
font-size: 14px;
color: var(--color-ink);
}
.article-author-bar__name:hover {
color: var(--color-primary);
}
.article-author-bar__meta {
display: flex;
flex-wrap: wrap;
gap: 10px;
font-size: 12px;
color: var(--color-ink-4, var(--color-ink-4));
margin-top: 2px;
}
.article-author-bar__meta span {
display: inline-flex;
align-items: center;
gap: 3px;
}
.article-author-bar__line {
height: 1px;
background: var(--color-line);
margin-bottom: 20px;
}
/* ===== Content Footer ===== */
.article-footer {
margin-top: 32px;
@@ -470,88 +418,6 @@ html.dark .post-drop-meta__item:hover {
text-align: center;
}
.author-box__header {
display: flex;
align-items: flex-start;
gap: 14px;
}
.author-box__avatar {
width: 56px;
height: 56px;
border-radius: 50%;
object-fit: cover;
flex-shrink: 0;
border: 3px solid var(--color-primary-soft, rgba(240, 68, 148, 0.2));
}
.author-box__info {
flex: 1;
min-width: 0;
}
.author-box__name {
font-size: 1rem;
font-weight: 700;
color: var(--color-ink);
display: block;
margin-bottom: 4px;
}
.author-box__name:hover { color: var(--color-primary); }
.author-box__bio {
font-size: 13px;
color: var(--color-ink-3, var(--color-ink-4));
line-height: 1.5;
margin: 0 0 6px;
}
.author-box__stats {
font-size: 12px;
color: var(--color-ink-4, var(--color-ink-4));
}
.author-box__stats strong {
color: var(--color-ink);
margin-right: 2px;
}
.author-box__posts {
margin-top: 14px;
padding-top: 12px;
border-top: 1px solid var(--color-line);
}
.author-box__posts-title {
font-size: 12px;
font-weight: 600;
color: var(--color-ink-3, var(--color-ink-4));
margin: 0 0 8px;
}
.author-box__posts ul {
list-style: none;
margin: 0;
padding: 0;
}
.author-box__posts li { padding: 3px 0; }
.author-box__posts a {
font-size: 13px;
color: var(--color-ink);
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
}
.author-box__posts a:hover { color: var(--color-primary); }
.author-box__posts a::before { content: '\2022'; color: var(--color-primary); }
/* ===== legacy single-metabox / author bar ===== */
.article-avatar .user-info {
width: 100%;
+57 -58
View File
@@ -4508,31 +4508,60 @@ tbquire.config({
/**
* AKSRT Follow — 前台关注交互
*
* 监听 [data-aksrt-follow] 按钮点击,切换关注状态。
*
* @package AKSRT
*/
(function () {
'use strict';
var API_ROOT = (function () {
var d = window.aksrtData || {};
var raw = d.apiUrl || d.restUrl || d.rest_url || '/index.php?rest_route=/aksrt/v1';
var data = window.aksrtData || {};
var raw = data.apiUrl || data.restUrl || data.rest_url || '/index.php?rest_route=/aksrt/v1';
return String(raw).replace(/\/$/, '');
})();
var NONCE = (window.aksrtData && window.aksrtData.restNonce) || '';
// 初始关注状态由服务端渲染(is-following class + 文案),无需逐按钮查询。
function buttonsFor(userId) {
return Array.prototype.slice.call(document.querySelectorAll('[data-aksrt-follow="' + userId + '"]'));
}
function setLoading(buttons, loading) {
buttons.forEach(function (button) {
button.disabled = loading;
button.classList.toggle('is-loading', loading);
});
}
function updateButton(button, isFollowing) {
var icon = button.querySelector('i');
var label = button.querySelector('[data-aksrt-follow-label]');
if (!label) return;
button.classList.toggle('is-following', isFollowing);
if (icon) {
icon.className = isFollowing ? 'fa fas fa-heart em09' : 'fa far fa-heart em09';
}
label.textContent = isFollowing ? '已关注' : '关注';
button.title = isFollowing ? '点击取消关注' : '关注该用户';
}
function updateFollowerCount(count) {
var countEl = document.querySelector('.aksrt-user-home__stat-value[data-followers]');
if (countEl && count !== undefined) {
countEl.textContent = count;
}
}
function initFollowButtons() {
document.addEventListener('click', function (e) {
var btn = e.target.closest('[data-aksrt-follow]');
if (!btn) return;
e.preventDefault();
document.addEventListener('click', function (event) {
var button = event.target.closest('[data-aksrt-follow]');
if (!button) return;
event.preventDefault();
var targetUserId = parseInt(btn.getAttribute('data-aksrt-follow'));
if (!targetUserId) return;
var targetUserId = parseInt(button.getAttribute('data-aksrt-follow'), 10);
if (!targetUserId || button.disabled) return;
btn.disabled = true;
var matchingButtons = buttonsFor(targetUserId);
setLoading(matchingButtons, true);
fetch(API_ROOT + '/follow/toggle', {
method: 'POST',
@@ -4542,64 +4571,34 @@ tbquire.config({
},
body: JSON.stringify({ target_user_id: targetUserId }),
})
.then(function (res) { return res.json(); })
.then(function (response) { return response.json(); })
.then(function (result) {
if (result.success && result.data) {
var isFollowing = result.data.is_following;
updateFollowButton(btn, isFollowing, result.data.follower_count);
notyf(isFollowing ? '关注成功' : '已取消关注', 'success');
} else {
if (!result.success || !result.data) {
notyf(result.message || '操作失败', 'danger');
return;
}
var isFollowing = Boolean(result.data.is_following);
matchingButtons.forEach(function (matchingButton) {
updateButton(matchingButton, isFollowing);
});
updateFollowerCount(result.data.follower_count);
if (!isFollowing) {
var managementItem = button.closest('.uc-follow-management__item');
if (managementItem) managementItem.remove();
}
notyf(isFollowing ? '关注成功' : '已取消关注', 'success');
})
.catch(function () {
notyf('网络错误,请重试', 'danger');
})
.finally(function () {
btn.disabled = false;
setLoading(matchingButtons, false);
});
});
}
function updateFollowButton(btn, isFollowing, count) {
var icon = btn.querySelector('i');
var text = btn.querySelector('span');
function setLabel(label) {
if (text) {
text.textContent = label;
return;
}
// Fallback when markup has bare text nodes next to the icon.
var nodes = Array.prototype.slice.call(btn.childNodes);
var textNode = nodes.filter(function (n) { return n.nodeType === 3 && n.textContent.trim() !== ''; })[0];
if (textNode) {
textNode.textContent = ' ' + label + ' ';
}
}
if (isFollowing) {
btn.classList.add('is-following');
if (icon) icon.className = 'fa fas fa-heart';
setLabel('已关注');
btn.title = '点击取消关注';
} else {
btn.classList.remove('is-following');
if (icon) icon.className = 'fa fas fa-heart';
setLabel('关注');
btn.title = '关注该用户';
}
if (!isFollowing && btn.closest('.uc-follow-management__item')) {
btn.closest('.uc-follow-management__item').remove();
}
// Update follower count display if exists
var countEl = document.querySelector('.aksrt-user-home__stat-value[data-followers]');
if (countEl && count !== undefined) {
countEl.textContent = count;
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initFollowButtons);
} else {
+57 -58
View File
@@ -1,31 +1,60 @@
/**
* AKSRT Follow — 前台关注交互
*
* 监听 [data-aksrt-follow] 按钮点击,切换关注状态。
*
* @package AKSRT
*/
(function () {
'use strict';
var API_ROOT = (function () {
var d = window.aksrtData || {};
var raw = d.apiUrl || d.restUrl || d.rest_url || '/index.php?rest_route=/aksrt/v1';
var data = window.aksrtData || {};
var raw = data.apiUrl || data.restUrl || data.rest_url || '/index.php?rest_route=/aksrt/v1';
return String(raw).replace(/\/$/, '');
})();
var NONCE = (window.aksrtData && window.aksrtData.restNonce) || '';
// 初始关注状态由服务端渲染(is-following class + 文案),无需逐按钮查询。
function buttonsFor(userId) {
return Array.prototype.slice.call(document.querySelectorAll('[data-aksrt-follow="' + userId + '"]'));
}
function setLoading(buttons, loading) {
buttons.forEach(function (button) {
button.disabled = loading;
button.classList.toggle('is-loading', loading);
});
}
function updateButton(button, isFollowing) {
var icon = button.querySelector('i');
var label = button.querySelector('[data-aksrt-follow-label]');
if (!label) return;
button.classList.toggle('is-following', isFollowing);
if (icon) {
icon.className = isFollowing ? 'fa fas fa-heart em09' : 'fa far fa-heart em09';
}
label.textContent = isFollowing ? '已关注' : '关注';
button.title = isFollowing ? '点击取消关注' : '关注该用户';
}
function updateFollowerCount(count) {
var countEl = document.querySelector('.aksrt-user-home__stat-value[data-followers]');
if (countEl && count !== undefined) {
countEl.textContent = count;
}
}
function initFollowButtons() {
document.addEventListener('click', function (e) {
var btn = e.target.closest('[data-aksrt-follow]');
if (!btn) return;
e.preventDefault();
document.addEventListener('click', function (event) {
var button = event.target.closest('[data-aksrt-follow]');
if (!button) return;
event.preventDefault();
var targetUserId = parseInt(btn.getAttribute('data-aksrt-follow'));
if (!targetUserId) return;
var targetUserId = parseInt(button.getAttribute('data-aksrt-follow'), 10);
if (!targetUserId || button.disabled) return;
btn.disabled = true;
var matchingButtons = buttonsFor(targetUserId);
setLoading(matchingButtons, true);
fetch(API_ROOT + '/follow/toggle', {
method: 'POST',
@@ -35,64 +64,34 @@
},
body: JSON.stringify({ target_user_id: targetUserId }),
})
.then(function (res) { return res.json(); })
.then(function (response) { return response.json(); })
.then(function (result) {
if (result.success && result.data) {
var isFollowing = result.data.is_following;
updateFollowButton(btn, isFollowing, result.data.follower_count);
notyf(isFollowing ? '关注成功' : '已取消关注', 'success');
} else {
if (!result.success || !result.data) {
notyf(result.message || '操作失败', 'danger');
return;
}
var isFollowing = Boolean(result.data.is_following);
matchingButtons.forEach(function (matchingButton) {
updateButton(matchingButton, isFollowing);
});
updateFollowerCount(result.data.follower_count);
if (!isFollowing) {
var managementItem = button.closest('.uc-follow-management__item');
if (managementItem) managementItem.remove();
}
notyf(isFollowing ? '关注成功' : '已取消关注', 'success');
})
.catch(function () {
notyf('网络错误,请重试', 'danger');
})
.finally(function () {
btn.disabled = false;
setLoading(matchingButtons, false);
});
});
}
function updateFollowButton(btn, isFollowing, count) {
var icon = btn.querySelector('i');
var text = btn.querySelector('span');
function setLabel(label) {
if (text) {
text.textContent = label;
return;
}
// Fallback when markup has bare text nodes next to the icon.
var nodes = Array.prototype.slice.call(btn.childNodes);
var textNode = nodes.filter(function (n) { return n.nodeType === 3 && n.textContent.trim() !== ''; })[0];
if (textNode) {
textNode.textContent = ' ' + label + ' ';
}
}
if (isFollowing) {
btn.classList.add('is-following');
if (icon) icon.className = 'fa fas fa-heart';
setLabel('已关注');
btn.title = '点击取消关注';
} else {
btn.classList.remove('is-following');
if (icon) icon.className = 'fa fas fa-heart';
setLabel('关注');
btn.title = '关注该用户';
}
if (!isFollowing && btn.closest('.uc-follow-management__item')) {
btn.closest('.uc-follow-management__item').remove();
}
// Update follower count display if exists
var countEl = document.querySelector('.aksrt-user-home__stat-value[data-followers]');
if (countEl && count !== undefined) {
countEl.textContent = count;
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initFollowButtons);
} else {
-3
View File
@@ -71,9 +71,6 @@ if (!function_exists('aksrt_resolve_single_post_display_settings')) :
if (!array_key_exists('prevnext', $settings) && array_key_exists('prevnext_s', $settings)) {
$settings['prevnext'] = !empty($settings['prevnext_s']);
}
if (!array_key_exists('authordesc', $settings) && array_key_exists('authordesc_s', $settings)) {
$settings['authordesc'] = !empty($settings['authordesc_s']);
}
$meta = class_exists('\AKSRT\Core\PostMetaBox') ? \AKSRT\Core\PostMetaBox::get_meta($post->ID) : [];
if (!empty($meta['article_maxheight_xz'])) {
$settings['maxheight_enabled'] = true;
+3 -18
View File
@@ -242,24 +242,9 @@ if (!function_exists('aksrt_author_minicard')) :
: '<a href="' . esc_url($home) . '">' . esc_html($user->display_name) . '</a>';
// Follow control (separate from name, legacy places both in dt).
// 服务端直接输出真实关注状态(legacy parity: zib_get_user_follow 恒渲染当前态)。
$follow = '';
$current = get_current_user_id();
if ($current !== $user_id) {
$is_following = false;
if (is_user_logged_in() && class_exists('\AKSRT\User\Follow')) {
$is_following = \AKSRT\User\Follow::get_instance()->is_following($current, $user_id);
}
$follow_label = $is_following ? __('已关注', 'aksrt') : __('关注', 'aksrt');
$follow_title = $is_following ? __('点击取消关注', 'aksrt') : __('关注该用户', 'aksrt');
$follow_state = $is_following ? ' is-following' : '';
if (is_user_logged_in()) {
$follow = '<button type="button" class="focus-color px12 ml10 follow flex0 but' . $follow_state . '" data-aksrt-follow="' . (int) $user_id . '" title="' . esc_attr($follow_title) . '"><i class="fa fas fa-heart em09" aria-hidden="true"></i> ' . esc_html($follow_label) . '</button>';
} else {
$login = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url();
$follow = '<a class="focus-color px12 ml10 follow flex0 but" href="' . esc_url($login) . '"><i class="fa fas fa-heart em09" aria-hidden="true"></i> ' . esc_html__('关注', 'aksrt') . '</a>';
}
}
$follow = function_exists('aksrt_get_user_follow')
? aksrt_get_user_follow('focus-color px12 ml10 follow flex0 but', $user_id)
: '';
$desc = function_exists('aksrt_get_user_description') ? aksrt_get_user_description($user_id) : trim((string) get_user_meta($user_id, 'description', true));
+37 -15
View File
@@ -119,6 +119,37 @@ if (!function_exists('aksrt_render_user_identity_chips')) {
}
}
if (!function_exists('aksrt_get_user_follow')) {
/**
* Render the canonical user follow control.
*/
function aksrt_get_user_follow(string $class, int $follow_id, ?string $text = null, ?string $ok_text = null, ?string $guest_text = null): string {
$current_user_id = get_current_user_id();
if ($follow_id <= 0 || $current_user_id === $follow_id) {
return '';
}
$text = $text ?? __('关注', 'aksrt');
$ok_text = $ok_text ?? __('已关注', 'aksrt');
$guest_text = $guest_text ?? $text;
$class = trim($class);
if (!is_user_logged_in()) {
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url();
return '<a class="' . esc_attr($class) . '" href="' . esc_url($login_url) . '"><i class="fa far fa-heart em09" aria-hidden="true"></i> <span>' . esc_html($guest_text) . '</span></a>';
}
$is_following = class_exists('\AKSRT\User\Follow')
&& \AKSRT\User\Follow::get_instance()->is_following($current_user_id, $follow_id);
$state_class = $is_following ? ' is-following' : '';
$title = $is_following ? __('点击取消关注', 'aksrt') : __('关注该用户', 'aksrt');
$label = $is_following ? $ok_text : $text;
$icon_class = $is_following ? 'fa fas fa-heart em09' : 'fa far fa-heart em09';
return '<a href="javascript:;" class="' . esc_attr($class . $state_class) . '" data-aksrt-follow="' . $follow_id . '" title="' . esc_attr($title) . '"><i class="' . esc_attr($icon_class) . '" aria-hidden="true"></i> <span data-aksrt-follow-label>' . esc_html($label) . '</span></a>';
}
}
if (!function_exists('aksrt_get_user_name')) {
/**
* legacy parity: name link + auth + wear medal + level + optional follow.
@@ -155,7 +186,9 @@ if (!function_exists('aksrt_get_user_name')) {
return '';
}
$home = function_exists('get_author_posts_url') ? get_author_posts_url($user_id) : '';
$home = function_exists('aksrt_get_user_home_url')
? aksrt_get_user_home_url($user_id)
: get_author_posts_url($user_id);
$display = $user->display_name;
$vip_html = '';
if (!empty($args['vip']) && function_exists('aksrt_render_vip_badge')) {
@@ -220,20 +253,9 @@ if (!function_exists('aksrt_get_user_name')) {
}
}
$follow_html = '';
if (!empty($args['follow']) && get_current_user_id() !== $user_id) {
if (is_user_logged_in()) {
// 服务端直接输出真实关注状态(legacy parity: zib_get_user_follow 恒渲染当前态)。
$is_following = class_exists('\AKSRT\User\Follow') && \AKSRT\User\Follow::get_instance()->is_following(get_current_user_id(), $user_id);
$follow_label = $is_following ? __('已关注', 'aksrt') : __('关注', 'aksrt');
$follow_title = $is_following ? __('点击取消关注', 'aksrt') : __('关注该用户', 'aksrt');
$follow_state = $is_following ? ' is-following' : '';
$follow_html = '<button type="button" class="focus-color ml10 follow flex0' . $follow_state . '" data-aksrt-follow="' . (int) $user_id . '" title="' . esc_attr($follow_title) . '"><i class="fa fas fa-heart em09" aria-hidden="true"></i> ' . esc_html($follow_label) . '</button>';
} else {
$login = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url();
$follow_html = '<a class="focus-color ml10 follow flex0" href="' . esc_url($login) . '"><i class="fa fas fa-heart em09" aria-hidden="true"></i> ' . esc_html__('关注', 'aksrt') . '</a>';
}
}
$follow_html = !empty($args['follow'])
? aksrt_get_user_follow('focus-color ml10 follow flex0', $user_id)
: '';
// Order: name → auth → wear medal → level → follow (legacy).
$html = '<name class="' . esc_attr((string) $args['class']) . '">' . $name_link . $vip_html . $auth_html . $medal_html . $level_html . $follow_html . '</name>';
+2 -9
View File
@@ -156,10 +156,7 @@ if (!function_exists('aksrt_single_render_box_header')) :
<?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,
get_template_part('templates/parts/single-author-bar', null, [
'show_time' => empty($settings['hide_time']),
'meta_html' => (string) ($ctx['meta_box_html'] ?? ''),
]);
@@ -681,11 +678,7 @@ if (!function_exists('aksrt_single_render_after_box')) :
]);
}
// 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) {
if (!empty($settings['authordesc'])) {
aksrt_single_render_author_card($ctx);
}
-17
View File
@@ -137,23 +137,6 @@ if (!function_exists('aksrt_single_is_docs_page')) :
}
endif;
if (!function_exists('aksrt_single_render_author_bar')) :
/**
* Render author bar from a real args array (no ambient query vars).
*
* @param array<string,mixed> $args Partial args.
*/
function aksrt_single_render_author_bar(array $args = []): void {
$args = wp_parse_args($args, [
'extra_class' => '',
'show_actions' => false,
'show_bio' => false,
'meta_items' => [],
]);
get_template_part('templates/parts/single-author-bar', null, $args);
}
endif;
if (!function_exists('aksrt_single')) :
/**
* Compose header + article + after-box (legacy equivalent).
@@ -47,7 +47,7 @@ $result = $follow
<div class="uc-follow-management__identity"><a href="<?php echo esc_url($home_url); ?>"><?php echo esc_html((string) ($item['display_name'] ?? '')); ?></a><span><?php echo esc_html(sprintf(__('关注于 %s', 'aksrt'), get_date_from_gmt((string) ($item['created_at'] ?? ''), get_option('date_format')))); ?></span></div>
<div class="uc-follow-management__actions">
<a class="btn-ghost btn-sm" href="<?php echo esc_url($home_url); ?>"><?php esc_html_e('主页', 'aksrt'); ?></a>
<?php if ($active_list === 'following') : ?><button type="button" class="btn-outline btn-sm is-following" data-aksrt-follow="<?php echo esc_attr((string) $member_id); ?>"><i class="fa fas fa-heart" aria-hidden="true"></i><span><?php esc_html_e('已关注', 'aksrt'); ?></span></button><?php endif; ?>
<?php if ($active_list === 'following') : ?><button type="button" class="btn-outline btn-sm is-following" data-aksrt-follow="<?php echo esc_attr((string) $member_id); ?>"><i class="fa fas fa-heart" aria-hidden="true"></i><span data-aksrt-follow-label><?php esc_html_e('已关注', 'aksrt'); ?></span></button><?php endif; ?>
</div>
</div>
<?php endforeach; ?>
+1 -2
View File
@@ -218,11 +218,10 @@ class PostDisplay {
'breadcrumbs_single_s' => $breadcrumbs,
'breadcrumbs_home_text' => $breadcrumbs_home,
'breadcrumbs_single_text' => $breadcrumbs_single_text,
// Canonical + legacy prev/next / author / time.
// Canonical + legacy prev/next / time; canonical author card flag.
'prevnext' => $prevnext,
'prevnext_s' => $prevnext,
'authordesc' => $authordesc,
'authordesc_s' => $authordesc,
'hide_time' => $hide_time,
'single_hide_time_s' => $hide_time,
'article_image_cover' => !isset($settings['post_article_image_cover']) || !empty($settings['post_article_image_cover']),
+19 -88
View File
@@ -1,14 +1,10 @@
<?php
/**
* Shared author info bar for single post content (legacy article-avatar / user-info).
* Article header author bar.
*
* Contract: get_template_part(..., null, $args)
* show_actions bool Follow / PM buttons + meta row
* show_bio bool Append bio after time
* show_time bool Relative publish/update time (default true)
* meta_html string Prebuilt legacy single-metabox HTML
* meta_items array Legacy text meta items (fallback)
* extra_class string
* meta_html string Prebuilt single-metabox HTML
*
* @package AKSRT
*/
@@ -16,40 +12,25 @@
defined('ABSPATH') || exit;
$args = is_array($args ?? null) ? $args : [];
$extra_class = (string) ($args['extra_class'] ?? '');
$show_actions = !empty($args['show_actions']);
$show_bio = !empty($args['show_bio']);
$show_time = !array_key_exists('show_time', $args) || !empty($args['show_time']);
$meta_html = (string) ($args['meta_html'] ?? '');
$meta_items = is_array($args['meta_items'] ?? null) ? array_values(array_filter($args['meta_items'], static function ($meta_item) {
if (is_array($meta_item)) {
return !empty($meta_item['text']);
}
return is_string($meta_item) && $meta_item !== '';
})) : [];
$author_id = (int) get_the_author_meta('ID');
$author_name = get_the_author();
$author_url = function_exists('aksrt_get_user_home_url') ? aksrt_get_user_home_url($author_id) : get_author_posts_url($author_id);
$modified_ts = (int) get_the_modified_time('U');
$published_ts = (int) get_the_time('U');
$now = (int) current_time('timestamp');
$use_modified = $modified_ts > $published_ts;
$rel_base = $use_modified ? $modified_ts : $published_ts;
$rel_label = $use_modified ? __('更新', 'aksrt') : __('发布', 'aksrt');
$abs_fmt = $use_modified
? sprintf(__('%s更新', 'aksrt'), get_the_modified_time('Y年m月d日 H:i'))
: sprintf(__('%s发布', 'aksrt'), get_the_time('Y年m月d日 H:i'));
$time_text = human_time_diff($rel_base, $now) . $rel_label;
$classes = trim('article-avatar ' . $extra_class);
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink());
$is_self = is_user_logged_in() && get_current_user_id() === $author_id;
// Identity: aksrt_get_user_name is the canonical renderer (legacy order).
$name_html = '';
if (function_exists('aksrt_get_user_name')) {
$name_html = aksrt_get_user_name([
$time_text = human_time_diff($rel_base, (int) current_time('timestamp')) . $rel_label;
$description = function_exists('aksrt_get_user_description')
? aksrt_get_user_description($author_id)
: trim((string) get_the_author_meta('description'));
$name_html = aksrt_get_user_name([
'id' => $author_id,
'class' => 'name flex ac flex1',
'name_class' => '',
@@ -58,11 +39,14 @@ if (function_exists('aksrt_get_user_name')) {
'auth' => true,
'level' => true,
'follow' => false,
]);
}
]);
$follow_html = aksrt_get_user_follow('px12-sm ml10 follow but c-red', $author_id);
$reward_button = get_current_user_id() !== $author_id && function_exists('aksrt_get_rewards_button')
? aksrt_get_rewards_button($author_id, 'ml6 but c-yellow px12-sm', get_the_ID())
: '';
?>
<div class="<?php echo esc_attr($classes); ?>">
<div class="article-avatar">
<div class="user-info flex ac article-avatar">
<a href="<?php echo esc_url($author_url); ?>">
<span class="avatar-img">
@@ -75,19 +59,14 @@ if (function_exists('aksrt_get_user_name')) {
</a>
<div class="user-right flex flex1 ac jsb ml10">
<div class="flex1">
<?php if ($name_html !== '') : ?>
<?php echo $name_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php else : ?>
<span class="name flex ac flex1">
<a class="display-name text-ellipsis" href="<?php echo esc_url($author_url); ?>"><?php echo esc_html($author_name); ?></a>
</span>
<?php endif; ?>
<div class="px12-sm muted-2-color text-ellipsis">
<?php if ($show_time) : ?>
<span class="icon-circle" title="<?php echo esc_attr($abs_fmt); ?>" data-toggle="tooltip"><?php echo esc_html($time_text); ?></span>
<?php elseif ($description !== '') : ?>
<span class="icon-circle"><?php echo esc_html($description); ?></span>
<?php endif; ?>
<?php
// Article detail IP 归属地 (extension display: post).
$post_city = function_exists('aksrt_get_post_author_city_badge_html')
? aksrt_get_post_author_city_badge_html($author_id, 'badg badg-sm')
: '';
@@ -95,68 +74,20 @@ if (function_exists('aksrt_get_user_name')) {
echo $post_city; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
?>
<?php if ($show_bio) : ?>
<?php $bio = function_exists('aksrt_get_user_description') ? aksrt_get_user_description($author_id) : trim((string) get_the_author_meta('description')); ?>
<?php if ($bio !== '') : ?>
<span class="icon-circle"><?php echo esc_html($bio); ?></span>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
<?php if ($show_actions) : ?>
<div class="flex0 user-action">
<?php if (!$is_self) : ?>
<?php if (is_user_logged_in()) : ?>
<?php
// 服务端直接输出真实关注状态(legacy parity: zib_get_user_follow 恒渲染当前态)。
$is_following = class_exists('\AKSRT\User\Follow') && \AKSRT\User\Follow::get_instance()->is_following(get_current_user_id(), $author_id);
$follow_label = $is_following ? __('已关注', 'aksrt') : __('关注', 'aksrt');
$follow_title = $is_following ? __('点击取消关注', 'aksrt') : __('关注该用户', 'aksrt');
?>
<button type="button" class="px12-sm ml10 follow c-red<?php echo $is_following ? ' is-following' : ''; ?>" data-aksrt-follow="<?php echo (int) $author_id; ?>" title="<?php echo esc_attr($follow_title); ?>">
<i class="fa fas fa-heart em09" aria-hidden="true"></i> <span><?php echo esc_html($follow_label); ?></span>
</button>
<?php
$reward_button = function_exists('aksrt_get_rewards_button')
? aksrt_get_rewards_button($author_id, 'ml6 but c-yellow px12-sm', get_the_ID())
: '';
if ($reward_button !== '') {
echo $reward_button; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
aksrt_render_rewards_modal_for_user($author_id, get_the_ID());
}
?>
<?php else : ?>
<a href="<?php echo esc_url($login_url); ?>" class="px12-sm ml10 follow c-red">
<i class="fa fas fa-heart em09" aria-hidden="true"></i> <?php esc_html_e('关注', 'aksrt'); ?>
</a>
<?php endif; ?>
<?php echo $follow_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php if ($reward_button !== '') : ?>
<?php echo $reward_button; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php aksrt_render_rewards_modal_for_user($author_id, get_the_ID()); ?>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
</div>
<?php if ($show_actions) : ?>
<div class="single-meta-row">
<i class="line-form-line" aria-hidden="true"></i>
<?php if ($meta_html !== '') : ?>
<?php echo $meta_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php elseif (!empty($meta_items)) : ?>
<div class="flex ac single-metabox">
<div class="post-metas">
<?php foreach ($meta_items as $meta_item) : ?>
<?php
$meta_text = is_array($meta_item) ? (string) ($meta_item['text'] ?? '') : (string) $meta_item;
$meta_icon = is_array($meta_item) ? (string) ($meta_item['icon'] ?? '') : '';
if ($meta_text === '') {
continue;
}
?>
<span class="item meta-view"><?php if ($meta_icon !== '') : ?><i class="<?php echo esc_attr($meta_icon); ?>" aria-hidden="true"></i> <?php endif; ?><?php echo esc_html($meta_text); ?></span>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
+1 -16
View File
@@ -157,22 +157,7 @@ $unread_count = $is_own ? aksrt_get_user_unread_notification_count($user_id) : 0
<?php endif; ?>
<?php else : ?>
<?php if ($show_follow) : ?>
<?php if (is_user_logged_in()) : ?>
<?php
$hero_following = class_exists('\AKSRT\User\Follow')
? \AKSRT\User\Follow::get_instance()->is_following(get_current_user_id(), (int) $user_id)
: false;
?>
<button type="button" class="btn-outline btn-sm aksrt-author-hero__follow-btn<?php echo $hero_following ? ' is-following' : ''; ?>" data-aksrt-follow="<?php echo (int) $user_id; ?>" title="<?php echo $hero_following ? esc_attr__('点击取消关注', 'aksrt') : esc_attr__('关注该用户', 'aksrt'); ?>">
<i class="fa fas fa-heart" aria-hidden="true"></i>
<span><?php echo esc_html($hero_following ? __('已关注', 'aksrt') : __('关注', 'aksrt')); ?></span>
</button>
<?php else : ?>
<a href="<?php echo esc_url(aksrt_get_login_url($home_url)); ?>" class="btn-outline btn-sm aksrt-author-hero__follow-btn">
<i class="fa fas fa-heart" aria-hidden="true"></i>
<span><?php esc_html_e('登录后关注', 'aksrt'); ?></span>
</a>
<?php endif; ?>
<?php echo aksrt_get_user_follow('btn-outline btn-sm aksrt-author-hero__follow-btn', (int) $user_id, null, null, __('登录后关注', 'aksrt')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif; ?>
<?php endif; ?>
@@ -259,6 +259,12 @@ test('after-box author card mirrors legacy user-card structure', () => {
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');
const base = read('assets/css/frontend/base.css');
const follow = read('assets/js/follow.js');
const authorBar = read('templates/parts/single-author-bar.php');
const single = read('inc/helpers/aksrt-single.php');
const settings = read('src/Settings/PostDisplay.php');
const frontendTags = read('inc/helpers/aksrt-frontend-tags.php');
assert.match(render, /avatar-img avatar-lg/);
assert.match(render, /'vip'\s+=>\s+false/);
@@ -280,6 +286,21 @@ test('after-box author card mirrors legacy user-card structure', () => {
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/);
assert.match(identity, /function aksrt_get_user_follow\(/);
assert.match(identity, /data-aksrt-follow-label/);
assert.match(identity, /aksrt_get_user_follow\('focus-color ml10 follow flex0', \$user_id\)/);
assert.match(authorBar, /aksrt_get_user_follow\('px12-sm ml10 follow but c-red', \$author_id\)/);
assert.doesNotMatch(authorBar, /Follow::get_instance|is_following|show_actions|show_bio|extra_class|meta_items/);
assert.doesNotMatch(single, /aksrt_single_render_author_bar/);
assert.match(base, /\.user-card\.author \.user-name > name\s*\{[^}]*overflow:\s*visible;/s);
assert.match(base, /\.user-card\.author \.user-name > name > \.follow\s*\{[^}]*flex-shrink:\s*0;/s);
assert.match(follow, /querySelectorAll\('\[data-aksrt-follow="' \+ userId/);
assert.match(follow, /matchingButtons\.forEach/);
assert.match(follow, /\[data-aksrt-follow-label\]/);
assert.doesNotMatch(follow, /childNodes|textNode|Fallback when markup/);
assert.doesNotMatch(settings, /'authordesc_s'\s*=>/);
assert.doesNotMatch(frontendTags, /array_key_exists\('authordesc_s'/);
assert.doesNotMatch(authorActions, /article-author-bar__|author-box__/);
});
test('AJAX pagination can recover, caps auto loading and rehydrates appended media', () => {