feat(frontend): deepen single post 1:1 parity with Zibll

Split related style/source settings, add subtitle/meta/allow-view helpers, align cover/author/tags/pay shell markup and CSS with Zibll single.
This commit is contained in:
2026-07-13 12:43:07 +08:00
parent 72ea0828bc
commit 48f7436030
14 changed files with 2156 additions and 1434 deletions
@@ -21,9 +21,13 @@ export const pageSchema = {
post_lightbox_play: false,
post_related_enabled: true,
post_related_count: 6,
// Display style (Zibll img/list/text). Legacy post_related_type may still hold source or style.
post_related_style: 'img',
post_related_source: 'category',
post_related_type: 'category',
post_related_orderby: 'rand',
post_related_orderby: 'views',
post_related_title: '相关推荐',
breadcrumbs_single_text: true,
post_copyright_enabled: true,
post_copyright_text: '',
share_s: true,
@@ -64,6 +68,7 @@ export const pageSchema = {
{ key: 'post_reading_progress', label: '阅读进度条', type: 'switch', default: true },
{ key: 'breadcrumbs_single_s', label: '面包屑导航', type: 'switch', default: true },
{ key: 'breadcrumbs_home_text', label: '面包屑首页文字', type: 'input', default: '首页', dependsOn: { key: 'breadcrumbs_single_s', value: true } },
{ key: 'breadcrumbs_single_text', label: '末项显示「正文」', type: 'switch', default: true, dependsOn: { key: 'breadcrumbs_single_s', value: true }, help: '关闭后末项显示文章标题' },
{ key: 'post_prevnext_s', label: '上一篇/下一篇', type: 'switch', default: true },
{ key: 'post_authordesc_s', label: '作者简介', type: 'switch', default: true },
{ key: 'post_single_hide_time_s', label: '隐藏文章时间', type: 'switch', default: false },
@@ -101,16 +106,21 @@ export const pageSchema = {
fields: [
{ key: 'post_related_enabled', label: '显示相关推荐', type: 'switch', default: true },
{ key: 'post_related_count', label: '推荐数量', type: 'number', default: 6, min: 1, max: 20, dependsOn: { key: 'post_related_enabled', value: true } },
{ key: 'post_related_type', label: '推荐方式', type: 'radio', default: 'category', options: [
{ label: '同分类', value: 'category' },
{ key: 'post_related_style', label: '展示样式', type: 'radio', default: 'img', options: [
{ label: '图文卡片', value: 'img' },
{ label: '列表', value: 'list' },
{ label: '纯文字', value: 'text' },
], dependsOn: { key: 'post_related_enabled', value: true }, help: '对齐 Zibll 相关文章三种样式' },
{ key: 'post_related_source', label: '推荐来源', type: 'radio', default: 'category', options: [
{ label: '同分类/标签/专题', value: 'category' },
{ label: '同标签', value: 'tag' },
{ label: '随机', value: 'random' },
], dependsOn: { key: 'post_related_enabled', value: true } },
{ key: 'post_related_orderby', label: '推荐排序', type: 'select', default: 'rand', options: [
{ key: 'post_related_orderby', label: '推荐排序', type: 'select', default: 'views', options: [
{ label: '浏览量', value: 'views' },
{ label: '随机', value: 'rand' },
{ label: '相关度', value: 'relevance' },
{ label: '时间', value: 'date' },
{ label: '浏览量', value: 'views' },
{ label: '更新时间', value: 'modified' },
], dependsOn: { key: 'post_related_enabled', value: true } },
{ key: 'post_related_title', label: '推荐标题', type: 'input', default: '相关推荐', dependsOn: { key: 'post_related_enabled', value: true } },
+46 -16
View File
@@ -1,5 +1,7 @@
/* ========== 面包屑导航 — 对齐 zibll breadcrumb ========== */
.breadcrumb-nav {
.breadcrumb-nav,
ul.breadcrumb,
.breadcrumbs-wrap {
font-size: 13px;
padding: 0 8px;
margin-bottom: 12px;
@@ -8,7 +10,9 @@
}
.breadcrumb-nav ol,
.breadcrumb-nav ul {
.breadcrumb-nav ul,
ul.breadcrumb,
.breadcrumbs.breadcrumb {
list-style: none;
margin: 0;
padding: 0;
@@ -18,59 +22,85 @@
gap: 0;
}
.breadcrumb-nav li {
.breadcrumb-nav li,
ul.breadcrumb > li,
.breadcrumbs.breadcrumb > li {
display: inline-flex;
align-items: center;
}
.breadcrumb-nav li + li::before {
/* Zibll flat ul.breadcrumb inserts parents with " </li><li> " — separators via content */
.breadcrumb-nav li + li::before,
ul.breadcrumb > li + li::before {
content: '/';
padding: 0 0 0 6px;
padding: 0 6px 0 6px;
opacity: 0.6;
}
.breadcrumb-nav a {
.breadcrumbs.breadcrumb .breadcrumbs__sep {
padding: 0 6px;
opacity: 0.6;
}
.breadcrumb-nav a,
ul.breadcrumb a,
.breadcrumbs a {
color: var(--color-ink-4, #999);
text-decoration: none;
transition: color 0.2s;
}
.breadcrumb-nav a:hover {
.breadcrumb-nav a:hover,
ul.breadcrumb a:hover,
.breadcrumbs a:hover {
color: var(--color-primary, #f04494);
}
.breadcrumb-nav .current {
.breadcrumb-nav .current,
ul.breadcrumb > li:last-child,
.breadcrumbs__item.is-current {
color: var(--color-ink-2, #4e5358);
font-weight: 500;
}
.single-cover-con .breadcrumb-nav,
.single-cover-con .breadcrumb {
.single-cover-con .breadcrumb,
.single-cover-con ul.breadcrumb,
.post-breadcrumbs .breadcrumb {
font-size: 13px;
margin: 5px 0 0 -6px;
opacity: unset;
padding: 0;
}
/* 封面内面包屑 */
.single-cover-con .breadcrumb-nav {
color: rgba(255, 255, 255, 0.7);
.single-cover-con .breadcrumb-nav,
.single-cover-con ul.breadcrumb,
.single-cover-con .breadcrumb {
color: rgba(255, 255, 255, 0.85);
}
.single-cover-con .breadcrumb-nav a {
color: rgba(255, 255, 255, 0.7);
.single-cover-con .breadcrumb-nav a,
.single-cover-con ul.breadcrumb a,
.single-cover-con .breadcrumb a {
color: rgba(255, 255, 255, 0.85);
}
.single-cover-con .breadcrumb-nav a:hover {
.single-cover-con .breadcrumb-nav a:hover,
.single-cover-con ul.breadcrumb a:hover {
color: #fff;
}
.single-cover-con .breadcrumb-nav .current {
.single-cover-con .breadcrumb-nav .current,
.single-cover-con ul.breadcrumb > li:last-child {
color: #fff;
}
/* 移动端 */
@media (max-width: 767px) {
.breadcrumb-nav {
.breadcrumb-nav,
ul.breadcrumb,
.breadcrumbs-wrap {
font-size: 12px;
}
}
@@ -540,3 +540,11 @@ html.dark .post-drop-meta__item:hover {
.post-actions .action i {
font-size: 16px;
}
.article-tags br { flex-basis: 100%; width: 100%; content: ""; display: block; height: 0; }
.article-tags .but.c-blue { color: #2196f3; }
.article-tags .but.c-yellow { color: #f0ad4e; }
.article-tags .but.c-green { color: #5cb85c; }
.article-tags .but.c-purple { color: #9c27b0; }
.article-tags .but.c-red { color: #f44336; }
+62 -2
View File
@@ -211,5 +211,65 @@
.single-head-cover .cover-meta.single-metabox {
display: flex; flex-wrap: wrap; gap: 8px 12px; font-size: 12px; color: var(--color-ink-4);
}
.aksrt-single-pay-box { margin: 16px 0 20px; }
.aksrt-single-pay-box .pay-box, .aksrt-single-pay-box .aksrt-paywall { margin: 0; }
/* Pay box root — Zibll #posts-pay.zib-widget.pay-box */
#posts-pay.zib-widget,
#posts-pay.pay-box,
.aksrt-single-pay-box {
margin: 16px 0 20px;
}
#posts-pay .aksrt-paywall,
.aksrt-single-pay-box .pay-box,
.aksrt-single-pay-box .aksrt-paywall {
margin: 0;
}
/* Cover meta pills + title-h-left */
.single-cover-con .article-title.title-h-left,
.single-cover .article-title.title-h-left {
color: #fff;
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.45);
margin: 0;
}
.single-metabox.cover-meta,
.cover-meta.abs-right {
position: absolute;
top: 12px;
right: 12px;
z-index: 3;
color: #fff;
}
.single-metabox .post-metas item,
.single-metabox .post-metas item a {
color: inherit;
opacity: 0.92;
margin-left: 10px;
font-size: 12px;
}
.single-metabox .post-metas item .fa,
.single-metabox .post-metas item i {
margin-right: 3px;
opacity: 0.9;
}
.single-cover-slide-wrap {
position: relative;
overflow: hidden;
border-radius: var(--radius-2xl, 16px);
}
.single-cover-slide-wrap .abs-center.left-bottom {
z-index: 2;
padding: 18px 20px;
color: #fff;
}
.article-avatar name.flex {
gap: 4px;
flex-wrap: wrap;
}
.article-avatar .aksrt-vip-badge,
.article-avatar .aksrt-identity-chip {
font-size: 12px;
line-height: 1.2;
vertical-align: middle;
}
.article-avatar .display-name {
margin-right: 2px;
}
@@ -549,3 +549,26 @@ html.dark .article-end-separator span {
.post-nav-box__item.is-empty {
visibility: hidden;
}
/* ===== Related (Zibll .relates / .relates-thumb) ===== */
.relates .title-theme { margin-bottom: 0; }
.relates .zib-widget { 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; }
.allow-view-deny { margin: 20px 0; }
+1
View File
@@ -27,6 +27,7 @@ require_once AKSRT_DIR . '/inc/helpers/aksrt-template-tags.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-frontend-tags.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-flow.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-homepage.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-single-components.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-single.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-archive.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-archive-filters.php';
@@ -72,6 +72,7 @@ class AKSRT_Admin_Schema_Post_List {
['key' => 'post_reading_progress', 'label' => '阅读进度条', 'type' => 'switch', 'default' => true],
['key' => 'breadcrumbs_single_s', 'label' => '面包屑导航', 'type' => 'switch', 'default' => true],
['key' => 'breadcrumbs_home_text', 'label' => '面包屑首页文字', 'type' => 'input', 'default' => '首页', 'depends_on' => 'breadcrumbs_single_s'],
['key' => 'breadcrumbs_single_text', 'label' => '末项显示「正文」', 'type' => 'switch', 'default' => true, 'depends_on' => 'breadcrumbs_single_s', 'description' => '关闭后末项显示文章标题'],
['key' => 'post_prevnext_s', 'label' => '上一篇/下一篇', 'type' => 'switch', 'default' => true],
['key' => 'post_authordesc_s', 'label' => '作者简介', 'type' => 'switch', 'default' => true],
['key' => 'post_single_hide_time_s', 'label' => '隐藏文章时间', 'type' => 'switch', 'default' => false],
@@ -94,16 +95,27 @@ class AKSRT_Admin_Schema_Post_List {
['key' => 'related', 'title' => '相关推荐', 'fields' => [
['key' => 'post_related_enabled', 'label' => '显示相关推荐', 'type' => 'switch', 'default' => true],
['key' => 'post_related_count', 'label' => '推荐数量', 'type' => 'number', 'default' => 6, 'min' => 1, 'max' => 20],
['key' => 'post_related_type', 'label' => '推荐方式', 'type' => 'radio', 'default' => 'category', 'options' => [
['key' => 'post_related_style', 'label' => '展示样式', 'type' => 'radio', 'default' => 'img', 'options' => [
['label' => '图文卡片', 'value' => 'img'],
['label' => '列表', 'value' => 'list'],
['label' => '纯文字', 'value' => 'text'],
], 'description' => '对齐 Zibll 相关文章三种样式'],
['key' => 'post_related_source', 'label' => '推荐来源', 'type' => 'radio', 'default' => 'category', 'options' => [
['label' => '同分类/标签/专题', 'value' => 'category'],
['label' => '同标签', 'value' => 'tag'],
['label' => '随机', 'value' => 'random'],
]],
// Legacy alias kept so old saved values still round-trip.
['key' => 'post_related_type', 'label' => '推荐方式(兼容)', 'type' => 'radio', 'default' => 'category', 'options' => [
['label' => '同分类', 'value' => 'category'],
['label' => '同标签', 'value' => 'tag'],
['label' => '随机', 'value' => 'random'],
]],
['key' => 'post_related_orderby', 'label' => '推荐排序', 'type' => 'select', 'default' => 'rand', 'options' => [
['key' => 'post_related_orderby', 'label' => '推荐排序', 'type' => 'select', 'default' => 'views', 'options' => [
['label' => '浏览量', 'value' => 'views'],
['label' => '随机', 'value' => 'rand'],
['label' => '相关度', 'value' => 'relevance'],
['label' => '时间', 'value' => 'date'],
['label' => '浏览量', 'value' => 'views'],
['label' => '更新时间', 'value' => 'modified'],
]],
['key' => 'post_related_title', 'label' => '推荐标题', 'type' => 'input', 'default' => '相关推荐'],
+10 -53
View File
@@ -896,68 +896,25 @@ class AKSRT_Settings {
* @return array{toc_enabled:bool,toc_depth:int,reading_progress:bool,lightbox:bool,related_enabled:bool,related_count:int,like_enabled:bool,favorite_enabled:bool,share_enabled:bool,copyright_enabled:bool,print_enabled:bool,pdf_export_enabled:bool,word_count_enabled:bool,reading_time_enabled:bool}
*/
public static function get_post_page_settings(): array {
// Canonical assembly lives in the post-display helper (related style/source split, aliases).
if (class_exists('AKSRT_Settings_Post_Display')) {
return AKSRT_Settings_Post_Display::get_post_page_settings();
}
$settings = self::get_all();
$related_type = sanitize_key((string) ($settings['post_related_type'] ?? 'category'));
if (!in_array($related_type, ['category', 'tag', 'random'], true)) {
$related_type = 'category';
}
$related_orderby = sanitize_key((string) ($settings['post_related_orderby'] ?? 'rand'));
if (!in_array($related_orderby, ['rand', 'relevance', 'date', 'views', 'modified'], true)) {
$related_orderby = 'rand';
}
return [
'toc_enabled' => !isset($settings['post_toc_enabled']) || !empty($settings['post_toc_enabled']),
'toc_depth' => max(1, min(6, (int) ($settings['post_toc_depth'] ?? 3))),
'reading_progress' => !isset($settings['post_reading_progress']) || !empty($settings['post_reading_progress']),
'breadcrumbs' => !isset($settings['breadcrumbs_single_s']) || !empty($settings['breadcrumbs_single_s']),
'breadcrumbs_home' => trim((string) ($settings['breadcrumbs_home_text'] ?? '')) ?: __('首页', 'aksrt'),
'prevnext' => !isset($settings['post_prevnext_s']) || !empty($settings['post_prevnext_s']),
'authordesc' => !isset($settings['post_authordesc_s']) || !empty($settings['post_authordesc_s']),
'hide_time' => !empty($settings['post_single_hide_time_s']),
'lightbox' => !isset($settings['post_lightbox']) || !empty($settings['post_lightbox']),
'related_enabled' => !isset($settings['post_related_enabled']) || !empty($settings['post_related_enabled']),
'related_count' => max(1, min(20, (int) ($settings['post_related_count'] ?? 6))),
'related_type' => $related_type,
'related_orderby' => $related_orderby,
'related_title' => sanitize_text_field((string) ($settings['post_related_title'] ?? '')),
'like_enabled' => !isset($settings['post_like_enabled']) || !empty($settings['post_like_enabled']),
'favorite_enabled' => !isset($settings['post_favorite_enabled']) || !empty($settings['post_favorite_enabled']),
'share_enabled' => !isset($settings['share_s']) || !empty($settings['share_s']) || !empty($settings['post_share_enabled']),
'rewards_enabled' => !empty($settings['post_rewards_s']) || !empty($settings['post_rewards_enabled']),
'rewards_text' => trim((string) ($settings['post_rewards_text'] ?? '')) ?: __('赞赏支持', 'aksrt'),
'rewards_image' => esc_url_raw((string) ($settings['post_rewards_img'] ?? '')),
'rewards_btn_text' => trim((string) ($settings['post_rewards_btn_text'] ?? '')) ?: __('赞赏', 'aksrt'),
'copyright_enabled' => !isset($settings['post_copyright_enabled']) || !empty($settings['post_copyright_enabled']),
'copyright_text' => trim((string) ($settings['post_copyright_text'] ?? '')),
'front_content' => trim((string) ($settings['post_front_content'] ?? '')),
'after_content' => trim((string) ($settings['post_after_content'] ?? '')),
'maxheight_enabled' => !empty($settings['post_maxheight_kg']),
'maxheight' => max(0, (int) ($settings['post_maxheight'] ?? 1000)),
'yiyan_header' => !empty($settings['post_yiyan_header']),
'yiyan_footer' => !empty($settings['post_yiyan_footer']),
'yiyan_box' => !empty($settings['post_yiyan_box']),
'default_views_min' => max(0, (int) ($settings['post_default_views_min'] ?? 20)),
'default_views_max' => max(1, (int) ($settings['post_default_views_max'] ?? 50)),
'default_likes_min' => max(0, (int) ($settings['post_default_likes_min'] ?? 5)),
'default_likes_max' => max(1, (int) ($settings['post_default_likes_max'] ?? 15)),
'print_enabled' => !isset($settings['feature_print']) || !empty($settings['feature_print']),
'pdf_export_enabled' => !empty($settings['feature_pdf_export']),
'word_count_enabled' => !isset($settings['feature_word_count']) || !empty($settings['feature_word_count']),
'reading_time_enabled' => !isset($settings['feature_reading_time']) || !empty($settings['feature_reading_time']),
'article_image_cover' => !isset($settings['post_article_image_cover']) || !empty($settings['post_article_image_cover']),
'article_image_cover_scale' => max(10, min(100, (int) ($settings['post_article_image_cover_scale'] ?? 35))),
'article_slide_cover' => !isset($settings['post_article_slide_cover']) || !empty($settings['post_article_slide_cover']),
'article_video_cover' => !isset($settings['post_article_video_cover']) || !empty($settings['post_article_video_cover']),
'article_video_cover_scale' => max(20, min(100, (int) ($settings['post_article_video_cover_scale'] ?? 56))),
'button_toptext' => trim((string) ($settings['post_button_toptext'] ?? '')) ?: __('喜欢就支持一下吧', 'aksrt'),
'lightbox_thumbs' => !isset($settings['post_lightbox_thumbs']) || !empty($settings['post_lightbox_thumbs']),
'lightbox_zoom' => !isset($settings['post_lightbox_zoom']) || !empty($settings['post_lightbox_zoom']),
'lightbox_full' => !isset($settings['post_lightbox_full']) || !empty($settings['post_lightbox_full']),
'lightbox_down' => !empty($settings['post_lightbox_down']),
'lightbox_play' => !empty($settings['post_lightbox_play']),
'mobile_nav_s' => !isset($settings['article_nav_mobile_nav_s']) || !empty($settings['article_nav_mobile_nav_s']),
'related_style' => 'img',
'related_source' => 'category',
'related_type' => 'category',
'related_orderby' => 'views',
'related_title' => sanitize_text_field((string) ($settings['post_related_title'] ?? '相关推荐')),
];
}
@@ -231,30 +231,61 @@ class AKSRT_Settings_Post_Display {
// Canonical render keys (used by aksrt_single_*). Legacy *_s aliases kept for older callers.
$breadcrumbs = !isset($settings['breadcrumbs_single_s']) || !empty($settings['breadcrumbs_single_s']);
$breadcrumbs_home = sanitize_text_field((string) ($settings['breadcrumbs_home_text'] ?? '首页'));
// Zibll breadcrumbs_single_text: true → last crumb is "正文"; false → full title.
$breadcrumbs_single_text = !isset($settings['breadcrumbs_single_text']) || !empty($settings['breadcrumbs_single_text']);
$prevnext = !isset($settings['post_prevnext_s']) || !empty($settings['post_prevnext_s']);
$authordesc = !isset($settings['post_authordesc_s']) || !empty($settings['post_authordesc_s']);
$hide_time = !empty($settings['post_single_hide_time_s']);
$share_enabled = !isset($settings['share_s']) || !empty($settings['share_s']);
$rewards_enabled = !empty($settings['post_rewards_enabled']) || !empty($settings['post_rewards_s']);
$rewards_image = (string) ($settings['post_rewards_image'] ?? $settings['post_rewards_qrcode'] ?? '');
$rewards_image = (string) ($settings['post_rewards_image'] ?? $settings['post_rewards_qrcode'] ?? $settings['post_rewards_img'] ?? '');
$rewards_text = sanitize_text_field((string) ($settings['post_rewards_text'] ?? ''));
$rewards_btn_text = sanitize_text_field((string) ($settings['post_rewards_btn_text'] ?? '赞赏'));
$maxheight_enabled = !empty($settings['article_maxheight_kg']) || !empty($settings['post_article_maxheight_kg']);
$maxheight = max(0, (int) ($settings['article_maxheight'] ?? $settings['post_article_maxheight'] ?? 1000));
$maxheight_enabled = !empty($settings['article_maxheight_kg'])
|| !empty($settings['post_article_maxheight_kg'])
|| !empty($settings['post_maxheight_kg']);
$maxheight = max(0, (int) ($settings['article_maxheight'] ?? $settings['post_article_maxheight'] ?? $settings['post_maxheight'] ?? 1000));
if ($maxheight <= 0) {
$maxheight = 1000;
}
// Related: split Zibll display style (img/list/text) from query source (category/tag/random).
// Legacy post_related_type held either style (img|list|text) or source (category|tag|random).
$raw_related_type = sanitize_key((string) ($settings['post_related_type'] ?? ''));
$raw_related_style = sanitize_key((string) ($settings['post_related_style'] ?? ''));
$raw_related_source = sanitize_key((string) ($settings['post_related_source'] ?? ''));
$related_style = 'img';
if (in_array($raw_related_style, ['img', 'list', 'text'], true)) {
$related_style = $raw_related_style;
} elseif (in_array($raw_related_type, ['img', 'list', 'text'], true)) {
$related_style = $raw_related_type;
}
$related_source = 'category';
if (in_array($raw_related_source, ['category', 'tag', 'random'], true)) {
$related_source = $raw_related_source;
} elseif (in_array($raw_related_type, ['category', 'tag', 'random'], true)) {
$related_source = $raw_related_type;
}
$related_orderby = sanitize_key((string) ($settings['post_related_orderby'] ?? 'views'));
if (!in_array($related_orderby, ['rand', 'relevance', 'date', 'views', 'modified'], true)) {
$related_orderby = 'views';
}
return [
'toc_enabled' => !isset($settings['post_toc_enabled']) || !empty($settings['post_toc_enabled']),
'toc_depth' => max(1, min(6, (int) ($settings['post_toc_depth'] ?? 3))),
'article_nav_mobile_nav_s' => !isset($settings['article_nav_mobile_nav_s']) || !empty($settings['article_nav_mobile_nav_s']),
'mobile_nav_s' => !isset($settings['article_nav_mobile_nav_s']) || !empty($settings['article_nav_mobile_nav_s']),
'reading_progress' => !isset($settings['post_reading_progress']) || !empty($settings['post_reading_progress']),
// Canonical + legacy breadcrumbs.
'breadcrumbs' => $breadcrumbs,
'breadcrumbs_home' => $breadcrumbs_home,
'breadcrumbs_single_s' => $breadcrumbs,
'breadcrumbs_home_text' => $breadcrumbs_home,
'breadcrumbs_single_text' => $breadcrumbs_single_text,
// Canonical + legacy prev/next / author / time.
'prevnext' => $prevnext,
'prevnext_s' => $prevnext,
@@ -275,8 +306,12 @@ class AKSRT_Settings_Post_Display {
'lightbox_play' => !empty($settings['post_lightbox_play']),
'related_enabled' => !isset($settings['post_related_enabled']) || !empty($settings['post_related_enabled']),
'related_count' => max(1, min(20, (int) ($settings['post_related_count'] ?? 6))),
'related_type' => in_array(sanitize_key((string) ($settings['post_related_type'] ?? 'category')), ['category', 'tag', 'random'], true) ? sanitize_key((string) ($settings['post_related_type'] ?? 'category')) : 'category',
'related_orderby' => in_array(sanitize_key((string) ($settings['post_related_orderby'] ?? 'rand')), ['rand', 'relevance', 'date', 'views', 'modified'], true) ? sanitize_key((string) ($settings['post_related_orderby'] ?? 'rand')) : 'rand',
// Display style (Zibll img/list/text) + query source (category/tag/random).
'related_style' => $related_style,
'related_source' => $related_source,
// Legacy alias: older code treated related_type as source.
'related_type' => $related_source,
'related_orderby' => $related_orderby,
'related_title' => sanitize_text_field((string) ($settings['post_related_title'] ?? '相关推荐')),
'like_enabled' => !isset($settings['post_like_enabled']) || !empty($settings['post_like_enabled']),
'favorite_enabled' => !isset($settings['post_favorite_enabled']) || !empty($settings['post_favorite_enabled']),
@@ -305,6 +340,8 @@ class AKSRT_Settings_Post_Display {
'default_likes_max' => max(1, min(9999, (int) ($settings['post_default_likes_max'] ?? 15))),
'word_count_enabled' => !isset($settings['post_word_count_enabled']) || !empty($settings['post_word_count_enabled']),
'reading_time_enabled' => !isset($settings['post_reading_time_enabled']) || !empty($settings['post_reading_time_enabled']),
'print_enabled' => !isset($settings['feature_print']) || !empty($settings['feature_print']),
'pdf_export_enabled' => !empty($settings['feature_pdf_export']),
];
}
+46 -17
View File
@@ -186,9 +186,12 @@ if (!function_exists('aksrt_resolve_single_post_display_settings')) :
'lightbox' => true,
'related_enabled' => true,
'related_count' => 6,
'related_style' => 'img',
'related_source' => 'category',
'related_type' => 'category',
'related_orderby' => 'rand',
'related_orderby' => 'views',
'related_title' => '',
'breadcrumbs_single_text' => true,
'like_enabled' => true,
'favorite_enabled' => true,
'share_enabled' => true,
@@ -235,6 +238,17 @@ if (!function_exists('aksrt_resolve_single_post_display_settings')) :
if (!array_key_exists('authordesc', $settings) && array_key_exists('authordesc_s', $settings)) {
$settings['authordesc'] = !empty($settings['authordesc_s']);
}
// related_style / related_source (legacy related_type held either).
if (!array_key_exists('related_style', $settings) || !in_array((string) $settings['related_style'], ['img', 'list', 'text'], true)) {
$legacy = sanitize_key((string) ($settings['related_type'] ?? ''));
$settings['related_style'] = in_array($legacy, ['img', 'list', 'text'], true) ? $legacy : 'img';
}
if (!array_key_exists('related_source', $settings) || !in_array((string) $settings['related_source'], ['category', 'tag', 'random'], true)) {
$legacy = sanitize_key((string) ($settings['related_type'] ?? ''));
$settings['related_source'] = in_array($legacy, ['category', 'tag', 'random'], true) ? $legacy : 'category';
}
// Keep related_type as source alias for older callers.
$settings['related_type'] = (string) $settings['related_source'];
$meta = class_exists('AKSRT_Post_Meta_Box') ? AKSRT_Post_Meta_Box::get_meta($post->ID) : [];
if (($meta['show_toc'] ?? '') !== '') {
@@ -365,9 +379,14 @@ endif;
if (!function_exists('aksrt_breadcrumbs')) :
/**
* 输出面包屑 HTML
* 输出面包屑 HTML(single 对齐 Zibll `ul.breadcrumb`)。
*
* @param string $home_text Home label.
* @param array $args {
* @type bool $single_text When true on single post, last crumb is 「正文」.
* }
*/
function aksrt_breadcrumbs(string $home_text = '') {
function aksrt_breadcrumbs(string $home_text = '', array $args = []) {
if (is_front_page()) {
return;
}
@@ -376,6 +395,30 @@ if (!function_exists('aksrt_breadcrumbs')) :
$home_text = __('首页', 'aksrt');
}
$single_text = array_key_exists('single_text', $args)
? !empty($args['single_text'])
: true;
// Zibll single breadcrumb: flat ul.breadcrumb without sep nodes.
if (is_singular('post')) {
$cats = get_the_category();
if (empty($cats)) {
return;
}
$category = $cats[0];
$last = $single_text ? __('正文', 'aksrt') : get_the_title();
// get_category_parents returns trailing separator; strip and rebuild like Zibll.
$parents = get_category_parents($category->term_id, true, ' </li><li> ');
if (is_wp_error($parents) || !is_string($parents) || $parents === '') {
$parents = '<a href="' . esc_url(get_category_link($category->term_id)) . '">' . esc_html($category->name) . '</a> </li><li> ';
}
$html = '<ul class="breadcrumb">'
. '<li><a href="' . esc_url(home_url('/')) . '"><i class="fa fa-map-marker"></i> ' . esc_html($home_text) . '</a></li><li>'
. $parents . esc_html($last) . '</li></ul>';
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return;
}
$items = [];
$items[] = '<li class="breadcrumbs__item breadcrumbs__item--home"><a href="' . esc_url(home_url('/')) . '"><i class="fa fa-map-marker" aria-hidden="true"></i><span>' . esc_html($home_text) . '</span></a></li>';
@@ -400,20 +443,6 @@ if (!function_exists('aksrt_breadcrumbs')) :
$items[] = '<li class="breadcrumbs__item is-current"><span>' . esc_html($search_query !== '' ? sprintf(__('搜索:%s', 'aksrt'), $search_query) : __('搜索', 'aksrt')) . '</span></li>';
} elseif (function_exists('aksrt_is_resource_archive') && aksrt_is_resource_archive()) {
$items[] = '<li class="breadcrumbs__item is-current"><span>' . esc_html__('资源', 'aksrt') . '</span></li>';
} elseif (is_singular('post')) {
$cats = get_the_category();
if (!empty($cats)) {
$category = $cats[0];
$ancestors = array_reverse(get_ancestors($category->term_id, 'category'));
foreach ($ancestors as $ancestor_id) {
$ancestor = get_term($ancestor_id, 'category');
if ($ancestor instanceof WP_Term && !is_wp_error($ancestor)) {
$items[] = '<li class="breadcrumbs__item"><a href="' . esc_url(get_category_link($ancestor->term_id)) . '">' . esc_html($ancestor->name) . '</a></li>';
}
}
$items[] = '<li class="breadcrumbs__item"><a href="' . esc_url(get_category_link($category->term_id)) . '">' . esc_html($category->name) . '</a></li>';
}
$items[] = '<li class="breadcrumbs__item is-current"><span>' . esc_html(get_the_title()) . '</span></li>';
} elseif (is_page()) {
$items[] = '<li class="breadcrumbs__item is-current"><span>' . esc_html(get_the_title()) . '</span></li>';
} elseif (is_archive()) {
+4 -4
View File
@@ -426,12 +426,12 @@ if (!function_exists('aksrt_get_paywall_html')) {
<?php endif; ?>
</div>
<?php if (!$is_logged_in) : ?>
<a href="<?php echo esc_url($login_url); ?>" class="btn-primary btn-lg aksrt-paywall__btn">
<i class="fas fa-sign-in-alt"></i> <?php esc_html_e('登录后购买', 'aksrt'); ?>
<a href="<?php echo esc_url($login_url); ?>" class="but jb-red padding-lg cashier-link aksrt-paywall__btn">
<i class="fa fa-sign-in" aria-hidden="true"></i> <?php esc_html_e('登录后购买', 'aksrt'); ?>
</a>
<?php elseif ($price > 0) : ?>
<button type="button" class="btn-primary btn-lg aksrt-paywall__btn" data-aksrt-pay-content="<?php echo esc_attr((string) $post->ID); ?>">
<i class="fas fa-shopping-cart"></i> <?php printf(esc_html__('立即购买(%s)', 'aksrt'), aksrt_format_price($price) . ' ' . $currency); ?>
<button type="button" class="but jb-red padding-lg cashier-link aksrt-paywall__btn" data-aksrt-pay-content="<?php echo esc_attr((string) $post->ID); ?>">
<i class="fa fa-shopping-cart" aria-hidden="true"></i> <?php printf(esc_html__('立即购买(%s)', 'aksrt'), aksrt_format_price($price) . ' ' . $currency); ?>
</button>
<?php endif; ?>
<p class="aksrt-paywall__hint">
+507
View File
@@ -0,0 +1,507 @@
<?php
/**
* Single-post component helpers (Zibll parity).
*
* Meta box, subtitle, status badge, allow-view gate, colored tags, related cards.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
if (!function_exists('aksrt_get_the_subtitle')) :
/**
* Post subtitle (meta `_subtitle`), Zibll get_the_subtitle style.
*
* @param bool $span Wrap in focus-color span.
* @param string $class Extra class on the span.
* @param int|null $post_id Post ID.
*/
function aksrt_get_the_subtitle(bool $span = true, string $class = '', $post_id = null): string {
$post_id = $post_id ? (int) $post_id : (int) get_the_ID();
if ($post_id <= 0) {
return '';
}
$subtitle = trim((string) get_post_meta($post_id, '_subtitle', true));
if ($subtitle === '') {
return '';
}
if (!$span) {
return $subtitle;
}
$cls = trim('focus-color ' . $class);
return ' <span class="' . esc_attr($cls) . '">' . esc_html($subtitle) . '</span>';
}
endif;
if (!function_exists('aksrt_get_single_title_html')) :
/**
* Title + optional subtitle for single headers.
*/
function aksrt_get_single_title_html($post_id = null): string {
$post_id = $post_id ? (int) $post_id : (int) get_the_ID();
$title = get_the_title($post_id);
$sub = function_exists('aksrt_get_the_subtitle') ? aksrt_get_the_subtitle(true, '', $post_id) : '';
return esc_html($title) . $sub;
}
endif;
if (!function_exists('aksrt_get_posts_meta_items_html')) :
/**
* Zibll-like post meta pills: comment + views + likes.
*
* @param int|null $post_id Post ID.
*/
function aksrt_get_posts_meta_items_html($post_id = null): string {
$post = get_post($post_id);
if (!($post instanceof WP_Post)) {
return '';
}
$post_id = (int) $post->ID;
$html = '';
$comments_closed = function_exists('aksrt_are_comments_closed')
? aksrt_are_comments_closed()
: (!comments_open($post_id));
if (comments_open($post_id) && !$comments_closed) {
$href = is_single($post_id)
? 'javascript:(typeof scrollTopTo===\'function\'?scrollTopTo(\'#comments\'):void(0));'
: get_comments_link($post_id);
$count = (int) get_comments_number($post_id);
$html .= '<item class="meta-comm"><a rel="nofollow" href="' . esc_attr($href) . '" title="' . esc_attr__('去评论', 'aksrt') . '">'
. '<i class="fa fa-comment-o" aria-hidden="true"></i> ' . esc_html((string) $count) . '</a></item>';
}
$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);
$html .= '<item class="meta-view"><i class="fa fa-eye" aria-hidden="true"></i> ' . esc_html((string) $views_label) . '</item>';
$like_on = true;
if (class_exists('AKSRT_Settings')) {
$page = AKSRT_Settings::get_post_page_settings();
$like_on = !isset($page['like_enabled']) || !empty($page['like_enabled']);
}
if ($like_on && $post->post_type === 'post') {
$likes = function_exists('aksrt_get_post_likes') ? (int) aksrt_get_post_likes($post_id) : 0;
$html .= '<item class="meta-like"><i class="fa fa-heart-o" aria-hidden="true"></i> ' . esc_html((string) $likes) . '</item>';
}
return $html;
}
endif;
if (!function_exists('aksrt_get_single_meta_box')) :
/**
* Zibll zib_get_single_meta_box equivalent.
*
* @param string $class Extra class on wrapper.
* @param string $direction Unused (kept for API parity).
* @param int|null $post_id Post ID.
*/
function aksrt_get_single_meta_box(string $class = 'abs-right', string $direction = 'down', $post_id = null): string {
if (!is_single() && !is_singular()) {
return '';
}
$post_id = $post_id ? (int) $post_id : (int) get_the_ID();
$meta = function_exists('aksrt_get_posts_meta_items_html') ? aksrt_get_posts_meta_items_html($post_id) : '';
$more = '';
$edit_link = get_edit_post_link($post_id);
$permalink = get_permalink($post_id);
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url($permalink ?: home_url('/'));
$more .= '<div class="clearfix ml6 post-drop-meta">';
$more .= '<button type="button" class="but cir post-drop-meta__toggle" aria-haspopup="true" aria-expanded="false" aria-label="' . esc_attr__('更多', 'aksrt') . '">';
$more .= '<i class="fa fa-ellipsis-h" aria-hidden="true"></i></button>';
$more .= '<div class="post-drop-meta__menu" aria-hidden="true">';
if ($edit_link && current_user_can('edit_post', $post_id)) {
$more .= '<a href="' . esc_url($edit_link) . '" class="post-drop-meta__item"><i class="fa fa-edit" aria-hidden="true"></i><span>' . esc_html__('编辑文章', 'aksrt') . '</span></a>';
}
$more .= '<button type="button" class="post-drop-meta__item" data-share-copy data-share-url="' . esc_url((string) $permalink) . '"><i class="fa fa-link" aria-hidden="true"></i><span>' . esc_html__('复制链接', 'aksrt') . '</span></button>';
if (is_user_logged_in()) {
$more .= '<button type="button" class="post-drop-meta__item" data-aksrt-report=\'{"target_type":"post","target_id":' . (int) $post_id . '}\'><i class="fa fa-flag" aria-hidden="true"></i><span>' . esc_html__('举报', 'aksrt') . '</span></button>';
} else {
$more .= '<a href="' . esc_url($login_url) . '" class="post-drop-meta__item"><i class="fa fa-flag" aria-hidden="true"></i><span>' . esc_html__('登录后举报', 'aksrt') . '</span></a>';
}
$more .= '</div></div>';
return '<div class="flex ac single-metabox ' . esc_attr($class) . '"><div class="post-metas">' . $meta . '</div>' . $more . '</div>';
}
endif;
if (!function_exists('aksrt_get_post_status_badge')) :
/**
* Non-publish status badge (text badge, Zibll-compatible classes).
*/
function aksrt_get_post_status_badge($post = null): string {
$post = get_post($post);
if (!($post instanceof WP_Post)) {
return '';
}
$map = [
'draft' => ['label' => __('草稿', 'aksrt'), 'class' => 'jb-yellow'],
'pending' => ['label' => __('待审核', 'aksrt'), 'class' => 'jb-red'],
'private' => ['label' => __('私密', 'aksrt'), 'class' => 'b-black opacity8'],
'future' => ['label' => __('定时发布', 'aksrt'), 'class' => 'c-blue'],
'trash' => ['label' => __('回收站', 'aksrt'), 'class' => 'jb-red'],
];
$status = $post->post_status;
if (!isset($map[$status])) {
return '';
}
return '<badge class="img-badge right ' . esc_attr($map[$status]['class']) . '">'
. esc_html($map[$status]['label']) . '</badge>';
}
endif;
if (!function_exists('aksrt_get_colored_term_tags')) :
/**
* Colored but-links for terms (Zibll cat/topics palette).
*
* @param WP_Term[] $terms Terms.
* @param string $class Base class.
* @param string $before Prefix HTML inside link.
* @param string[] $palette Color classes.
*/
function aksrt_get_colored_term_tags(array $terms, string $class = 'but ml6 radius', string $before = '', array $palette = []): string {
if ($palette === []) {
$palette = ['c-blue', 'c-yellow', 'c-green', 'c-purple', 'c-red'];
}
$html = '';
$i = 0;
foreach ($terms as $term) {
if (!($term instanceof WP_Term)) {
continue;
}
$color = $palette[$i % count($palette)];
$i++;
$url = get_term_link($term);
if (is_wp_error($url)) {
continue;
}
$html .= '<a class="' . esc_attr(trim($class . ' ' . $color)) . '" href="' . esc_url($url) . '">'
. $before . esc_html($term->name) . '</a>';
}
return $html;
}
endif;
if (!function_exists('aksrt_get_single_article_tags_html')) :
/**
* topics → categories → tags (Zibll footer order).
*/
function aksrt_get_single_article_tags_html(int $post_id): string {
$topics_html = '';
if (taxonomy_exists('topics')) {
$topics = wp_get_post_terms($post_id, 'topics');
if (!is_wp_error($topics) && !empty($topics)) {
$topics_html = aksrt_get_colored_term_tags(
array_slice($topics, 0, 6),
'but ml6 radius',
'<i class="fa fa-cube" aria-hidden="true"></i> ',
['c-yellow', 'c-green', 'c-purple', 'c-red', 'c-blue']
);
}
}
$cats = get_the_category($post_id) ?: [];
$cat_html = aksrt_get_colored_term_tags(
$cats,
'but ml6 radius',
'<i class="fa fa-folder-open-o" aria-hidden="true"></i> ',
['c-blue', 'c-yellow', 'c-green', 'c-purple', 'c-red']
);
$tags_html = '';
$tags = get_the_tags($post_id) ?: [];
if (!empty($tags) && is_array($tags)) {
// Sort by count desc like Zibll.
usort($tags, static function ($a, $b) {
return (int) $b->count <=> (int) $a->count;
});
foreach ($tags as $tag) {
if (!($tag instanceof WP_Term)) {
continue;
}
$tags_html .= '<a href="' . esc_url(get_tag_link($tag->term_id)) . '" class="but ml6 radius"># ' . esc_html($tag->name) . '</a>';
}
}
$top = $topics_html . $cat_html;
if ($top === '' && $tags_html === '') {
return '';
}
return '<div class="theme-box article-tags">' . $top . ($tags_html !== '' ? '<br>' . $tags_html : '') . '</div>';
}
endif;
if (!function_exists('aksrt_get_post_allow_view_data')) :
/**
* Category/post reading permission gate (Zibll zib_get_post_allow_view_data subset).
*
* Supported term meta keys (first match wins, walks parents):
* - term meta `post_cat_config` array with `allow_view` = signin|login|roles
* - term meta `aksrt_allow_view` string
* - post meta `_aksrt_allow_view` string (public|login|signin|roles)
*
* @return array{allow:bool,type:string,not_html:string,allow_reason:string,html:string}
*/
function aksrt_get_post_allow_view_data($post = null): array {
static $cache = [];
$data = [
'allow' => true,
'type' => '',
'not_html' => '',
'allow_reason' => '',
'html' => '',
];
$post = get_post($post);
if (!($post instanceof WP_Post) || $post->post_type !== 'post') {
return $data;
}
if (isset($cache[$post->ID])) {
return $cache[$post->ID];
}
if (current_user_can('manage_options')) {
$data['allow_reason'] = __('您是管理员,可查看所有内容', 'aksrt');
$cache[$post->ID] = $data;
return $data;
}
// Post-level override.
$post_allow = sanitize_key((string) get_post_meta($post->ID, '_aksrt_allow_view', true));
if ($post_allow !== '' && $post_allow !== 'public') {
$resolved = aksrt_resolve_allow_view_rule($post_allow, [], $post);
$cache[$post->ID] = $resolved;
return $resolved;
}
$cats = get_the_category($post->ID);
if (empty($cats)) {
$cache[$post->ID] = $data;
return $data;
}
foreach ($cats as $cat) {
if (!($cat instanceof WP_Term)) {
continue;
}
$rule = aksrt_get_term_allow_view_rule((int) $cat->term_id);
if ($rule['type'] === '' || $rule['type'] === 'public') {
continue;
}
$resolved = aksrt_resolve_allow_view_rule($rule['type'], $rule['roles'], $post);
if (!$resolved['allow']) {
$cache[$post->ID] = $resolved;
return $resolved;
}
}
$cache[$post->ID] = $data;
return $data;
}
endif;
if (!function_exists('aksrt_get_term_allow_view_rule')) :
/**
* Walk category ancestors for allow_view config.
*
* @return array{type:string,roles:array<string,mixed>}
*/
function aksrt_get_term_allow_view_rule(int $term_id): array {
static $term_cache = [];
if (isset($term_cache[$term_id])) {
return $term_cache[$term_id];
}
$empty = ['type' => '', 'roles' => []];
$chain = array_merge([$term_id], get_ancestors($term_id, 'category'));
foreach ($chain as $id) {
$id = (int) $id;
$config = get_term_meta($id, 'post_cat_config', true);
if (is_array($config) && !empty($config['allow_view'])) {
$rule = [
'type' => sanitize_key((string) $config['allow_view']),
'roles' => is_array($config['allow_view_roles'] ?? null) ? $config['allow_view_roles'] : [],
];
$term_cache[$term_id] = $rule;
return $rule;
}
$flat = sanitize_key((string) get_term_meta($id, 'aksrt_allow_view', true));
if ($flat !== '' && $flat !== 'public') {
$rule = ['type' => $flat, 'roles' => []];
$term_cache[$term_id] = $rule;
return $rule;
}
}
$term_cache[$term_id] = $empty;
return $empty;
}
endif;
if (!function_exists('aksrt_resolve_allow_view_rule')) :
/**
* @param array<string,mixed> $roles Role constraints for type=roles.
* @return array{allow:bool,type:string,not_html:string,allow_reason:string,html:string}
*/
function aksrt_resolve_allow_view_rule(string $type, array $roles, WP_Post $post): array {
$type = sanitize_key($type);
if (in_array($type, ['login', 'signin'], true)) {
$type = 'signin';
}
$data = [
'allow' => true,
'type' => $type,
'not_html' => '',
'allow_reason' => '',
'html' => '',
];
$user_id = get_current_user_id();
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink($post));
if ($type === 'signin') {
$data['html'] = '<p class="separator muted-3-color em09">' . esc_html__('登录后可查看', 'aksrt') . '</p>';
if ($user_id <= 0) {
$data['allow'] = false;
$data['not_html'] = aksrt_get_allow_view_deny_shell(
__('内容已隐藏,请登录后查看', 'aksrt'),
'<div class="text-center em09 mt20"><p class="separator muted-3-color mb20">' . esc_html__('登录后继续查看', 'aksrt') . '</p>'
. '<p><a href="' . esc_url($login_url) . '" class="but jb-blue padding-lg"><i class="fa fa-fw fa-sign-in" aria-hidden="true"></i>' . esc_html__('登录', 'aksrt') . '</a></p></div>'
);
} else {
$data['allow_reason'] = __('您已登录,可查看此内容', 'aksrt');
}
return $data;
}
if ($type === 'roles') {
$need_vip = isset($roles['vip']) ? (int) $roles['vip'] : 0;
$need_level = isset($roles['level']) ? (int) $roles['level'] : 0;
$need_auth = !empty($roles['auth']);
$badges = '';
if ($need_vip > 0) {
$badges .= '<span class="badg mm3">' . esc_html(sprintf(__('VIP%d 及以上', 'aksrt'), $need_vip)) . '</span>';
}
if ($need_level > 0) {
$badges .= '<span class="badg mm3">' . esc_html(sprintf(__('等级 %d 及以上', 'aksrt'), $need_level)) . '</span>';
}
if ($need_auth) {
$badges .= '<span class="badg mm3">' . esc_html__('认证用户', 'aksrt') . '</span>';
}
$data['html'] = '<div class="text-center em09"><p class="separator muted-3-color">' . esc_html__('以下用户组可查看', 'aksrt') . '</p>' . $badges . '</div>';
$ok = false;
if ($user_id > 0) {
if ($need_vip > 0 && function_exists('aksrt_get_user_vip_level') && aksrt_get_user_vip_level($user_id) >= $need_vip) {
$ok = true;
}
if (!$ok && $need_level > 0) {
$user_level = (int) get_user_meta($user_id, 'aksrt_user_level', true);
if ($user_level >= $need_level) {
$ok = true;
}
}
if (!$ok && $need_auth) {
$verify = sanitize_key((string) get_user_meta($user_id, 'aksrt_user_verify_status', true));
if (in_array($verify, ['verified', '1', 'yes'], true)) {
$ok = true;
}
}
// If no specific role constraints, any logged-in user passes.
if (!$ok && $need_vip <= 0 && $need_level <= 0 && !$need_auth) {
$ok = true;
}
}
if (!$ok) {
$data['allow'] = false;
$body = $data['html'];
if ($user_id <= 0) {
$body .= '<p class="text-center mt10"><a href="' . esc_url($login_url) . '" class="but jb-blue padding-lg">' . esc_html__('登录', 'aksrt') . '</a></p>';
}
$data['not_html'] = aksrt_get_allow_view_deny_shell(__('内容已隐藏', 'aksrt'), $body);
} else {
$data['allow_reason'] = __('您满足查看条件', 'aksrt');
}
return $data;
}
// Unknown type → allow.
$data['type'] = '';
return $data;
}
endif;
if (!function_exists('aksrt_get_allow_view_deny_shell')) :
function aksrt_get_allow_view_deny_shell(string $title, string $body_html): string {
return '<div class="zib-widget text-center allow-view-deny">'
. '<div class="box-body">'
. '<p class="em12 muted-2-color mb10"><i class="fa fa-lock fa-2x" aria-hidden="true"></i></p>'
. '<h3 class="em12">' . esc_html($title) . '</h3>'
. $body_html
. '</div></div>';
}
endif;
if (!function_exists('aksrt_single_content_allow_view')) :
/**
* the_content filter — hide body when category/post allow_view denies.
*/
function aksrt_single_content_allow_view($content) {
if (is_admin() || !is_singular('post') || !in_the_loop() || !is_main_query()) {
return $content;
}
$data = aksrt_get_post_allow_view_data(get_post());
if (!empty($data['allow'])) {
return $content;
}
return (string) ($data['not_html'] ?? '');
}
endif;
// Register gate once helpers load.
add_filter('the_content', 'aksrt_single_content_allow_view', 8);
if (!function_exists('aksrt_render_related_graphic_card')) :
/**
* Minimal Zibll style-3 related thumb card.
*/
function aksrt_render_related_graphic_card(WP_Post $post): string {
$title = get_the_title($post);
$url = get_permalink($post);
$thumb = get_the_post_thumbnail_url($post, 'medium_large');
if (!$thumb) {
$thumb = get_theme_file_uri('assets/img/thumbnail-lg.svg');
if (!file_exists(get_theme_file_path('assets/img/thumbnail-lg.svg'))) {
$thumb = includes_url('images/media/default.png');
}
}
$time = human_time_diff((int) get_the_time('U', $post), (int) current_time('timestamp'));
$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);
$img_attrs = function_exists('aksrt_lazy_img_attrs')
? aksrt_lazy_img_attrs($thumb, 'cover', 'fit-cover')
: 'class="fit-cover" src="' . esc_url($thumb) . '" loading="lazy"';
$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"><item>' . esc_html($time) . '</item>';
$html .= '<item class="pull-right"><i class="fa fa-eye" aria-hidden="true"></i> ' . esc_html((string) $views_label) . '</item></div>';
$html .= '</div></div></a></div></div>';
return $html;
}
endif;
+169 -129
View File
@@ -33,32 +33,20 @@ if (!function_exists('aksrt_single_build_context')) :
? aksrt_reading_minutes($post_id)
: 0;
// Zibll primary meta = comment/views/likes via meta_box_html.
$meta_items = [];
if (empty($settings['hide_time'])) {
$meta_items[] = [
'icon' => 'far fa-calendar-alt',
'text' => get_the_date('', $post_id),
];
}
$meta_items[] = [
'icon' => 'far fa-eye',
'text' => sprintf(
__('浏览 %s', 'aksrt'),
function_exists('aksrt_format_number') ? aksrt_format_number($views) : number_format_i18n($views)
),
];
$meta_items[] = [
'icon' => 'far fa-comment-dots',
'text' => sprintf(__('评论 %s', 'aksrt'), number_format_i18n($comment_count)),
];
$meta_box_html = function_exists('aksrt_get_single_meta_box')
? aksrt_get_single_meta_box('abs-right', 'down', $post_id)
: '';
$meta_items_secondary = [];
if ($word_count > 0) {
$meta_items[] = [
$meta_items_secondary[] = [
'icon' => 'far fa-file-word',
'text' => sprintf(__('约 %s 字', 'aksrt'), number_format_i18n($word_count)),
];
}
if ($reading_minutes > 0) {
$meta_items[] = [
$meta_items_secondary[] = [
'icon' => 'far fa-hourglass',
'text' => sprintf(_n('约 %d 分钟', '约 %d 分钟', $reading_minutes, 'aksrt'), $reading_minutes),
];
@@ -92,23 +80,23 @@ if (!function_exists('aksrt_single_build_context')) :
$breadcrumbs_html = '';
if (!empty($settings['breadcrumbs']) && function_exists('aksrt_breadcrumbs')) {
ob_start();
aksrt_breadcrumbs($settings['breadcrumbs_home'] ?? ($settings['breadcrumbs_home_text'] ?? ''));
aksrt_breadcrumbs(
(string) ($settings['breadcrumbs_home'] ?? ($settings['breadcrumbs_home_text'] ?? '')),
['single_text' => !isset($settings['breadcrumbs_single_text']) || !empty($settings['breadcrumbs_single_text'])]
);
$breadcrumbs_html = trim((string) ob_get_clean());
}
$status_badge = '';
$post_status = get_post_status($post_id);
if ($post_status && $post_status !== 'publish') {
$status_map = [
'draft' => ['label' => __('草稿', 'aksrt'), 'class' => 'jb-yellow'],
'pending' => ['label' => __('待审核', 'aksrt'), 'class' => 'jb-red'],
'private' => ['label' => __('私密', 'aksrt'), 'class' => 'b-black opacity8'],
'future' => ['label' => __('定时发布', 'aksrt'), 'class' => 'c-blue'],
];
if (isset($status_map[$post_status])) {
$status_badge = '<badge class="img-badge right ' . esc_attr($status_map[$post_status]['class']) . '">'
. esc_html($status_map[$post_status]['label']) . '</badge>';
}
$status_badge = function_exists('aksrt_get_post_status_badge')
? aksrt_get_post_status_badge($post_id)
: '';
$allow_view = function_exists('aksrt_get_post_allow_view_data')
? aksrt_get_post_allow_view_data($post_id)
: ['allow' => true];
if (empty($allow_view['allow'])) {
// Zibll: pay box is skipped when allow_view denies.
$show_pay_box = false;
}
$aside_widgets = is_active_sidebar('sidebar-article')
@@ -128,6 +116,10 @@ if (!function_exists('aksrt_single_build_context')) :
'episode_count' => max(0, (int) ($settings['episode_count'] ?? 0)),
'video_poster' => (string) ($settings['video_poster'] ?? ''),
'meta_items' => $meta_items,
'meta_items_secondary' => $meta_items_secondary,
'meta_box_html' => $meta_box_html,
'title_html' => function_exists('aksrt_get_single_title_html') ? aksrt_get_single_title_html($post_id) : esc_html(get_the_title($post_id)),
'allow_view' => $allow_view,
'views' => $views,
'comment_count' => $comment_count,
'like_count' => $like_count,
@@ -194,31 +186,29 @@ if (!function_exists('aksrt_single_render_cover')) :
$type = (string) $ctx['cover_type'];
$badge = (string) $ctx['status_badge'];
$crumbs = (string) $ctx['breadcrumbs_html'];
$meta_items = is_array($ctx['meta_items'] ?? null) ? $ctx['meta_items'] : [];
$meta_box = (string) ($ctx['meta_box_html'] ?? '');
$img_scale = max(10, min(100, (int) $ctx['img_scale']));
$video_scale = max(20, min(100, (int) $ctx['video_scale']));
$episode = (int) $ctx['episode_count'];
$slides = is_array($ctx['slide_images'] ?? null) ? $ctx['slide_images'] : [];
$poster = (string) ($ctx['video_poster'] ?? '');
$title = get_the_title();
$title_html = (string) ($ctx['title_html'] ?? esc_html(get_the_title()));
if ($type === 'video') :
?>
<div class="single-head-cover">
<div class="single-video single-cover single-cover--video graphic imgbox-container" style="padding-bottom:<?php echo esc_attr((string) $video_scale); ?>%;">
<video src="<?php echo esc_url($ctx['cover_url']); ?>"<?php echo $poster !== '' ? ' poster="' . esc_url($poster) . '"' : ''; ?> autoplay muted loop playsinline controls class="single-cover__video fit-cover"></video>
<div class="single-cover__overlay"></div>
<div class="single-video">
<div class="graphic single-cover single-cover--video imgbox-container" style="padding-bottom:<?php echo esc_attr((string) $video_scale); ?>%;">
<video src="<?php echo esc_url($ctx['cover_url']); ?>"<?php echo $poster !== '' ? ' poster="' . esc_url($poster) . '"' : ''; ?> controls playsinline class="fit-cover single-cover__video" data-aksrt-cover-video></video>
</div>
<div class="single-video-footer dplayer-featured">
<div class="flex jsb at">
<?php if ($crumbs !== '') : ?>
<div class="post-breadcrumbs"><?php echo $crumbs; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
<?php endif; ?>
<div class="cover-meta single-metabox">
<?php foreach ($meta_items as $item) : ?>
<item class="meta-view"><?php echo esc_html((string) ($item['text'] ?? '')); ?></item>
<?php endforeach; ?>
</div>
<?php if ($meta_box !== '') : ?>
<?php echo str_replace('abs-right', 'mb10 ml10 shrink0', $meta_box); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif; ?>
</div>
<h1 class="article-title">
<?php if ($episode > 1) : ?>
@@ -226,71 +216,94 @@ if (!function_exists('aksrt_single_render_cover')) :
<?php else : ?>
<i class="fa fa-play-circle-o mr6 opacity8" aria-hidden="true"></i>
<?php endif; ?>
<?php echo esc_html($title); ?>
<?php echo $title_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</h1>
<?php echo $badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php if ($episode > 0) : ?>
<div class="featured-video-episode mt10">
<span class="switch-video text-ellipsis active"><span class="mr6 badg badg-sm">1</span><i class="episode-active-icon"></i><?php printf(esc_html__('第%d集', 'aksrt'), 1); ?></span>
<a href="javascript:;" class="switch-video text-ellipsis active" data-index="1" video-url="<?php echo esc_url($ctx['cover_url']); ?>">
<span class="mr6 badg badg-sm">1</span><i class="episode-active-icon"></i><?php printf(esc_html__('第%d集', 'aksrt'), 1); ?>
</a>
<?php for ($i = 2; $i <= $episode; $i++) : ?>
<a href="javascript:;" class="switch-video text-ellipsis" data-index="<?php echo (int) $i; ?>" video-url="<?php echo esc_url($ctx['cover_url']); ?>">
<span class="mr6 badg badg-sm"><?php echo (int) $i; ?></span><i class="episode-active-icon"></i><?php printf(esc_html__('第%d集', 'aksrt'), $i); ?>
</a>
<?php endfor; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php
return;
endif;
if ($type === 'slide' && !empty($slides)) :
$slide_items = [];
foreach ($slides as $slide_url) {
$slide_items[] = [
'image' => (string) $slide_url,
'alt' => wp_strip_all_tags(get_the_title()),
];
}
$overlay = '<div class="abs-center left-bottom single-cover-con">'
. '<h1 class="article-title title-h-left">' . $title_html . '</h1>'
. ($crumbs !== '' ? $crumbs : '')
. $badge
. '</div>'
. ($meta_box !== '' ? str_replace('abs-right', 'cover-meta abs-right', $meta_box) : '');
$slider_html = '';
if (function_exists('aksrt_render_slider')) {
$slider_html = aksrt_render_slider([
'slides' => $slide_items,
'scale_height' => true,
'scale_ratio' => $img_scale / 100,
'wrapper_class' => 'mb20 single-cover-slide imgbox-container',
'slider_class' => 'single-cover single-cover--slide',
'show_nav' => true,
'show_pagination' => true,
'loop' => count($slide_items) > 1,
'autoplay' => count($slide_items) > 1,
]);
}
?>
<div class="single-head-cover">
<div class="single-cover single-cover--slide graphic imgbox-container mb20 single-cover-slide" style="padding-bottom:<?php echo esc_attr((string) $img_scale); ?>%;">
<div class="single-cover__slides">
<?php if ($slider_html !== '') : ?>
<div class="single-cover-slide-wrap" style="position:relative">
<?php echo $slider_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php echo $overlay; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<?php else : ?>
<div class="graphic mb20 single-cover single-cover--slide imgbox-container" style="padding-bottom:<?php echo esc_attr((string) $img_scale); ?>%;">
<?php foreach ($slides as $idx => $slide_url) : ?>
<div class="single-cover__slide<?php echo $idx === 0 ? ' is-active' : ''; ?>">
<img src="<?php echo esc_url($slide_url); ?>" alt="<?php echo esc_attr($title); ?>" class="fit-cover" loading="<?php echo $idx === 0 ? 'eager' : 'lazy'; ?>">
</div>
<img src="<?php echo esc_url($slide_url); ?>" alt="" class="fit-cover" style="<?php echo $idx === 0 ? '' : 'display:none;'; ?>" loading="<?php echo $idx === 0 ? 'eager' : 'lazy'; ?>">
<?php endforeach; ?>
<?php echo $overlay; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<div class="single-cover__overlay"></div>
<div class="abs-center left-bottom single-cover-con single-cover__content">
<?php if ($crumbs !== '') : ?>
<div class="post-breadcrumbs"><?php echo $crumbs; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
<?php endif; ?>
<h1 class="article-title title-h-left"><?php echo esc_html($title); ?></h1>
<?php echo $badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<div class="cover-meta abs-right">
<?php foreach ($meta_items as $item) : ?>
<item class="meta-view"><?php echo esc_html((string) ($item['text'] ?? '')); ?></item>
<?php endforeach; ?>
</div>
<div class="single-cover__dots" aria-hidden="true">
<?php foreach ($slides as $idx => $_slide) : ?>
<span class="single-cover__dot<?php echo $idx === 0 ? ' is-active' : ''; ?>"></span>
<?php endforeach; ?>
</div>
</div>
</div>
<?php
return;
endif;
// Image cover (default).
$img_attrs = function_exists('aksrt_lazy_img_attrs')
? aksrt_lazy_img_attrs((string) $ctx['cover_url'], 'cover', 'fit-cover')
: 'class="fit-cover" src="' . esc_url((string) $ctx['cover_url']) . '" loading="eager"';
?>
<div class="single-head-cover">
<div class="graphic mb20 single-cover imgbox-container" style="padding-bottom:<?php echo esc_attr((string) $img_scale); ?>%;">
<img src="<?php echo esc_url($ctx['cover_url']); ?>" alt="<?php echo esc_attr($title); ?>" class="fit-cover" loading="eager">
<div class="single-cover__overlay"></div>
<div class="abs-center left-bottom single-cover-con single-cover__content">
<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 !== '') : ?>
<div class="post-breadcrumbs"><?php echo $crumbs; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
<?php echo $crumbs; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif; ?>
<h1 class="article-title title-h-left"><?php echo esc_html($title); ?></h1>
<?php echo $badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<div class="cover-meta abs-right">
<?php foreach ($meta_items as $item) : ?>
<item class="meta-view"><?php echo esc_html((string) ($item['text'] ?? '')); ?></item>
<?php endforeach; ?>
</div>
<?php if ($meta_box !== '') : ?>
<?php echo str_replace('abs-right', 'cover-meta abs-right', $meta_box); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif; ?>
</div>
</div>
<?php
@@ -309,22 +322,18 @@ if (!function_exists('aksrt_single_render_box_header')) :
<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"><?php the_title(); ?></h1>
<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
$show_author_bar = array_key_exists('authordesc', $settings)
? !empty($settings['authordesc'])
: !empty($settings['authordesc_s']);
if ($show_author_bar) :
?>
<?php
// Author bar always shows in header (Zibll zib_single_box_header).
// `authordesc` only controls the after-box author card.
aksrt_single_render_author_bar([
'show_actions' => true,
'meta_items' => empty($ctx['show_cover']) ? (array) $ctx['meta_items'] : [],
'show_time' => empty($settings['hide_time']),
'meta_html' => empty($ctx['show_cover']) ? (string) ($ctx['meta_box_html'] ?? '') : '',
]);
?>
<?php endif; ?>
</div>
<?php
}
@@ -428,25 +437,11 @@ if (!function_exists('aksrt_single_render_content_footer')) :
<?php endif; ?>
<div class="text-center theme-box muted-3-color box-body separator em09">THE END</div>
<div class="theme-box article-tags">
<?php foreach (get_the_category() ?: [] as $cat) : ?>
<a href="<?php echo esc_url(get_category_link($cat->term_id)); ?>" class="but"><i class="far fa-folder-open" aria-hidden="true"></i> <?php echo esc_html($cat->name); ?></a>
<?php endforeach; ?>
<?php
$topics = taxonomy_exists('topics') ? wp_get_post_terms($post_id, 'topics') : [];
if (!is_wp_error($topics) && !empty($topics)) :
foreach (array_slice($topics, 0, 2) as $topic) :
if (function_exists('aksrt_get_single_article_tags_html')) {
echo aksrt_get_single_article_tags_html($post_id); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
?>
<a href="<?php echo esc_url(get_term_link($topic)); ?>" class="but"><i class="fas fa-cube" aria-hidden="true"></i> <?php echo esc_html($topic->name); ?></a>
<?php
endforeach;
endif;
?>
<br>
<?php foreach (get_the_tags() ?: [] as $tag) : ?>
<a href="<?php echo esc_url(get_tag_link($tag->term_id)); ?>" class="but">#<?php echo esc_html($tag->name); ?></a>
<?php endforeach; ?>
</div>
<?php
}
endif;
@@ -472,7 +467,7 @@ if (!function_exists('aksrt_single_render_author_card')) :
];
$bio = trim((string) get_the_author_meta('description'));
?>
<div class="author author-box">
<div class="author author-box zib-widget">
<div class="author-box__header">
<a href="<?php echo esc_url(get_author_posts_url($author_id)); ?>" class="author-box__avatar">
<?php echo get_avatar($author_id, 56, '', esc_attr(get_the_author())); ?>
@@ -569,7 +564,7 @@ if (!function_exists('aksrt_single_related_query_args')) :
*/
function aksrt_single_related_query_args(int $post_id, array $settings): array {
$count = max(1, min(12, (int) ($settings['related_count'] ?? 6)));
$type = sanitize_key((string) ($settings['related_type'] ?? 'category'));
$type = sanitize_key((string) ($settings['related_source'] ?? ($settings['related_type'] ?? 'category')));
if (!in_array($type, ['category', 'tag', 'random'], true)) {
$type = 'category';
}
@@ -677,7 +672,11 @@ if (!function_exists('aksrt_single_render_related')) :
$post_id = (int) $ctx['post_id'];
$title = (string) ($settings['related_title'] ?? '');
if ($title === '') {
$title = __('相关文章', 'aksrt');
$title = __('相关推荐', 'aksrt');
}
$style = sanitize_key((string) ($settings['related_style'] ?? 'img'));
if (!in_array($style, ['img', 'list', 'text'], true)) {
$style = 'img';
}
$args = function_exists('aksrt_single_related_query_args')
@@ -695,30 +694,59 @@ if (!function_exists('aksrt_single_render_related')) :
wp_reset_postdata();
return;
}
?>
<div class="theme-box relates related-box">
<div class="box-body notop">
<div class="title-theme related-box__title"><?php echo esc_html($title); ?></div>
</div>
<div class="zib-widget related-box__list">
<?php if (function_exists('aksrt_render_post_list')) : ?>
<?php aksrt_render_post_list($query, ['context' => 'related', 'mode' => 'mini', 'show_thumb' => true, 'show_meta' => true]); ?>
<?php else : ?>
<ul class="no-thumb related-list">
<?php
while ($query->have_posts()) :
$posts_html = '';
while ($query->have_posts()) {
$query->the_post();
$p = get_post();
if (!($p instanceof WP_Post)) {
continue;
}
if ($style === 'img' && function_exists('aksrt_render_related_graphic_card')) {
$posts_html .= aksrt_render_related_graphic_card($p);
} else {
$posts_html .= '<li><a class="icon-circle" href="' . esc_url(get_permalink($p)) . '">' . esc_html(get_the_title($p)) . '</a></li>';
}
}
wp_reset_postdata();
$wrap_class = 'theme-box relates' . ($style === 'img' ? ' relates-thumb' : '');
?>
<li>
<a class="icon-circle" href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
</li>
<?php endwhile; ?>
<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 elseif ($style === 'list') : ?>
<?php
$list_query = new WP_Query($args);
if (function_exists('aksrt_render_post_list')) {
aksrt_render_post_list($list_query, [
'context' => 'related',
'mode' => 'mini',
'show_thumb' => true,
'show_meta' => true,
'show_number' => false,
]);
}
wp_reset_postdata();
?>
<?php else : ?>
<ul class="no-thumb">
<?php echo $posts_html !== '' ? $posts_html : '<li>' . esc_html__('暂无相关文章', 'aksrt') . '</li>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</ul>
<?php endif; ?>
</div>
</div>
<?php
wp_reset_postdata();
}
endif;
@@ -736,6 +764,10 @@ if (!function_exists('aksrt_single_render_pay_box')) :
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);
@@ -745,7 +777,7 @@ if (!function_exists('aksrt_single_render_pay_box')) :
$pay_type = (string) ($ctx['pay_type'] ?? 'no');
if (!empty($ctx['has_download']) || $pay_type === 'download') {
echo '<div class="aksrt-single-pay-box aksrt-single-pay-box--download" id="posts-pay">';
echo '<div class="zib-widget pay-box pay-type-download" id="posts-pay">';
get_template_part('templates/parts/resource-download-box');
echo '</div>';
return;
@@ -756,14 +788,15 @@ if (!function_exists('aksrt_single_render_pay_box')) :
}
$has_access = function_exists('aksrt_has_paid_access') ? aksrt_has_paid_access($post) : false;
echo '<div class="aksrt-single-pay-box aksrt-single-pay-box--' . esc_attr($pay_type) . '" id="posts-pay">';
$paid_class = $has_access ? 'paid-box' : 'pay-box';
echo '<div class="zib-widget ' . esc_attr($paid_class) . ' pay-type-' . esc_attr($pay_type) . '" id="posts-pay">';
if ($has_access) {
if ($pay_type === 'gallery' && function_exists('aksrt_render_paid_gallery')) {
echo aksrt_render_paid_gallery($post); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} elseif ($pay_type === 'video' && function_exists('aksrt_render_paid_video')) {
echo aksrt_render_paid_video($post); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
echo '<div class="zib-widget pay-box paid-box"><div class="box-body"><span class="badg c-green hollow badg-sm mr6">' . esc_html__('已解锁', 'aksrt') . '</span><b>' . esc_html(get_the_title($post)) . '</b></div></div>';
echo '<div class="box-body"><span class="badg c-green hollow badg-sm mr6">' . esc_html__('已解锁', 'aksrt') . '</span><b>' . esc_html(get_the_title($post)) . '</b></div>';
}
} elseif (function_exists('aksrt_get_paywall_html')) {
echo aksrt_get_paywall_html($post); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
@@ -780,6 +813,10 @@ if (!function_exists('aksrt_single_pay_box_matches')) :
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;
@@ -814,7 +851,7 @@ if (!function_exists('aksrt_single_render_article')) :
<div class="theme-box wp-posts-content<?php echo $maxheight > 0 ? ' limit-height' : ''; ?>"
<?php echo $maxheight > 0 ? ' style="max-height:' . esc_attr((string) $maxheight) . 'px;"' : ''; ?>
data-nav="posts"
<?php echo !empty($ctx['show_toc']) ? ' data-nav="posts"' : ''; ?>
data-maxheight="<?php echo esc_attr((string) ($maxheight > 0 ? max(0, $maxheight - 80) : 0)); ?>"
data-aksrt-toc="<?php echo !empty($ctx['show_toc']) ? '1' : '0'; ?>"
data-aksrt-toc-depth="<?php echo esc_attr((string) ((int) ($settings['toc_depth'] ?? 3))); ?>"
@@ -925,7 +962,10 @@ if (!function_exists('aksrt_single')) :
echo $ctx['breadcrumbs_html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} elseif (function_exists('aksrt_breadcrumbs')) {
$settings = is_array($ctx['settings'] ?? null) ? $ctx['settings'] : [];
aksrt_breadcrumbs($settings['breadcrumbs_home'] ?? ($settings['breadcrumbs_home_text'] ?? ''));
aksrt_breadcrumbs(
(string) ($settings['breadcrumbs_home'] ?? ($settings['breadcrumbs_home_text'] ?? '')),
['single_text' => !isset($settings['breadcrumbs_single_text']) || !empty($settings['breadcrumbs_single_text'])]
);
}
} else {
aksrt_single_render_cover($ctx);
+37 -29
View File
@@ -3,6 +3,12 @@
* Shared author info bar for single post content (Zibll article-avatar / user-info).
*
* 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 Zibll single-metabox HTML
* meta_items array Legacy text meta items (fallback)
* extra_class string
*
* @package AKSRT
*/
@@ -13,6 +19,8 @@ $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']);
@@ -39,18 +47,44 @@ $aksrt_pm_on = class_exists('AKSRT_Settings') && !empty(AKSRT_Settings::private_
$aksrt_msg_url = function_exists('aksrt_get_messages_page_url') ? aksrt_get_messages_page_url() : '';
$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 chips: VIP + verify/level/badges.
$vip_badge = function_exists('aksrt_render_vip_badge') ? aksrt_render_vip_badge($author_id, 'sm') : '';
$signals = [];
if (class_exists('AKSRT_User_Home')) {
$signals = AKSRT_User_Home::get_instance()->get_profile_signals($author_id);
}
?>
<div class="<?php echo esc_attr($classes); ?>">
<div class="user-info flex ac">
<?php if (function_exists('aksrt_get_avatar_html')) : ?>
<a class="avatar-img" href="<?php echo esc_url($author_url); ?>">
<?php echo aksrt_get_avatar_html($author_id, 52, ['class' => 'avatar avatar-id-' . $author_id]); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</a>
<?php else : ?>
<a class="avatar-img" href="<?php echo esc_url($author_url); ?>">
<?php echo get_avatar($author_id, 52, '', esc_attr($author_name), ['class' => 'avatar avatar-id-' . $author_id]); ?>
</a>
<?php endif; ?>
<div class="user-right flex flex1 ac jsb ml10">
<div class="flex1">
<name class="flex ac flex1">
<a class="display-name text-ellipsis" href="<?php echo esc_url($author_url); ?>"><?php echo esc_html($author_name); ?></a>
<?php if ($vip_badge !== '') : ?>
<?php echo $vip_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif; ?>
<?php foreach ($signals as $signal) : ?>
<span class="badg badg-sm ml3 aksrt-identity-chip aksrt-identity-chip--<?php echo esc_attr((string) ($signal['tone'] ?? 'info')); ?>">
<i class="fa fa-<?php echo esc_attr((string) ($signal['icon'] ?? 'check')); ?>" aria-hidden="true"></i>
<?php echo esc_html((string) ($signal['label'] ?? '')); ?>
</span>
<?php endforeach; ?>
</name>
<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 endif; ?>
<?php if ($show_bio) : ?>
<?php $bio = trim((string) get_the_author_meta('description')); ?>
<?php if ($bio !== '') : ?>
@@ -85,7 +119,9 @@ $is_self = is_user_logged_in() && get_current_user_id() === $author_id;
<?php if ($show_actions) : ?>
<div class="relative">
<i class="line-form-line" aria-hidden="true"></i>
<?php if (!empty($meta_items)) : ?>
<?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 abs-right">
<div class="post-metas">
<?php foreach ($meta_items as $meta_item) : ?>
@@ -99,34 +135,6 @@ $is_self = is_user_logged_in() && get_current_user_id() === $author_id;
<item class="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); ?></item>
<?php endforeach; ?>
</div>
<div class="clearfix ml6 post-drop-meta">
<button type="button" class="but cir post-drop-meta__toggle" aria-haspopup="true" aria-expanded="false" aria-label="<?php esc_attr_e('更多', 'aksrt'); ?>">
<i class="fa fa-ellipsis-h" aria-hidden="true"></i>
</button>
<div class="post-drop-meta__menu" aria-hidden="true">
<?php if (current_user_can('edit_post', get_the_ID())) : ?>
<a href="<?php echo esc_url(get_edit_post_link(get_the_ID())); ?>" class="post-drop-meta__item">
<i class="fa fa-edit" aria-hidden="true"></i>
<span><?php esc_html_e('编辑文章', 'aksrt'); ?></span>
</a>
<?php endif; ?>
<button type="button" class="post-drop-meta__item" data-share-copy data-share-url="<?php echo esc_url(get_permalink()); ?>">
<i class="fa fa-link" aria-hidden="true"></i>
<span><?php esc_html_e('复制链接', 'aksrt'); ?></span>
</button>
<?php if (is_user_logged_in()) : ?>
<button type="button" class="post-drop-meta__item" data-aksrt-report='{"target_type":"post","target_id":<?php echo (int) get_the_ID(); ?>}'>
<i class="fa fa-flag" aria-hidden="true"></i>
<span><?php esc_html_e('举报', 'aksrt'); ?></span>
</button>
<?php else : ?>
<a href="<?php echo esc_url($login_url); ?>" class="post-drop-meta__item">
<i class="fa fa-flag" aria-hidden="true"></i>
<span><?php esc_html_e('登录后举报', 'aksrt'); ?></span>
</a>
<?php endif; ?>
</div>
</div>
</div>
<?php endif; ?>
</div>