Merge pull request 'refactor(resource): drop aksrt_resource CPT residue, post+meta only' (#113) from worktree-hero-banner-chain into main

Reviewed-on: CYL/wordpress-theme-aksrt#113
This commit is contained in:
2026-07-13 23:53:29 +08:00
22 changed files with 362 additions and 986 deletions
-191
View File
@@ -1,191 +0,0 @@
<?php
/**
* Resource Archive Template
*
* 统一资源归档页(/resources)。
* 渲染资源虚拟归档 (post + _aksrt_is_resource) 与历史 aksrt_resource CPT 内容。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
$current_orderby = isset($_GET['orderby']) ? sanitize_key((string) $_GET['orderby']) : 'date';
$allowed_orderby = ['date', 'downloads', 'views'];
if (!in_array($current_orderby, $allowed_orderby, true)) $current_orderby = 'date';
$current_price = isset($_GET['price']) ? sanitize_key((string) $_GET['price']) : 'all';
if (!in_array($current_price, ['all', 'free', 'paid'], true)) $current_price = 'all';
$price_min = isset($_GET['price_min']) && $_GET['price_min'] !== '' ? (float) $_GET['price_min'] : '';
$price_max = isset($_GET['price_max']) && $_GET['price_max'] !== '' ? (float) $_GET['price_max'] : '';
$archive_url = function_exists('aksrt_resource_archive_url') ? aksrt_resource_archive_url() : home_url('/resources/');
$sort_options = [
'date' => __('最新', 'aksrt'),
'downloads' => __('热门下载', 'aksrt'),
'views' => __('阅读最多', 'aksrt'),
];
$price_options = [
'all' => __('全部', 'aksrt'),
'free' => __('免费', 'aksrt'),
'paid' => __('付费', 'aksrt'),
];
// 给筛选链接保留其它已有 query
$preserve = [];
if ($current_orderby !== 'date') $preserve['orderby'] = $current_orderby;
if ($current_price !== 'all') $preserve['price'] = $current_price;
if ($price_min !== '' && $price_min > 0) $preserve['price_min'] = $price_min;
if ($price_max !== '') $preserve['price_max'] = $price_max;
$has_filter = $current_orderby !== 'date' || $current_price !== 'all' || $price_min !== '' || $price_max !== '';
?>
<main id="main-content" class="site-main container" tabindex="-1">
<header class="resource-archive-header page-cover">
<div class="container">
<?php if (function_exists('aksrt_breadcrumbs')) : ?>
<nav class="resource-archive-header__breadcrumbs" aria-label="<?php esc_attr_e('面包屑', 'aksrt'); ?>">
<?php aksrt_breadcrumbs(); ?>
</nav>
<?php endif; ?>
<h1 class="resource-archive-header__title"><?php
if (function_exists('post_type_exists') && post_type_exists('aksrt_resource')
&& function_exists('is_post_type_archive') && is_post_type_archive('aksrt_resource')) {
post_type_archive_title();
} else {
esc_html_e('资源', 'aksrt');
}
?></h1>
<p class="resource-archive-header__sub">
<?php
global $wp_query;
/* translators: %s: 资源总数 */
printf(esc_html__('共 %s 个资源', 'aksrt'), esc_html(number_format_i18n((int) $wp_query->found_posts)));
?>
</p>
</div>
</header>
<div class="content-wrap">
<div class="content-layout content-layout--no-sidebar">
<div class="content-layout__primary resource-archive">
<nav class="resource-archive__toolbar" aria-label="<?php esc_attr_e('筛选', 'aksrt'); ?>">
<!-- 排序 -->
<div class="resource-archive__filter-group">
<span class="resource-archive__filter-label"><?php esc_html_e('排序', 'aksrt'); ?></span>
<ul class="resource-archive__sort">
<?php foreach ($sort_options as $key => $label) :
$args = $preserve;
if ($key === 'date') {
unset($args['orderby']);
} else {
$args['orderby'] = $key;
}
$url = $args ? add_query_arg($args, $archive_url) : $archive_url;
$active = $current_orderby === $key;
?>
<li>
<a href="<?php echo esc_url($url); ?>"
class="resource-archive__sort-link<?php echo $active ? ' is-active' : ''; ?>"
<?php echo $active ? 'aria-current="true"' : ''; ?>>
<?php echo esc_html($label); ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</div>
<!-- 付费状态 -->
<div class="resource-archive__filter-group">
<span class="resource-archive__filter-label"><?php esc_html_e('价格', 'aksrt'); ?></span>
<ul class="resource-archive__sort">
<?php foreach ($price_options as $key => $label) :
$args = $preserve;
if ($key === 'all') {
unset($args['price']);
} else {
$args['price'] = $key;
}
// 切换付费状态时重置区间
unset($args['price_min'], $args['price_max']);
$url = $args ? add_query_arg($args, $archive_url) : $archive_url;
$active = $current_price === $key;
?>
<li>
<a href="<?php echo esc_url($url); ?>"
class="resource-archive__sort-link<?php echo $active ? ' is-active' : ''; ?>"
<?php echo $active ? 'aria-current="true"' : ''; ?>>
<?php echo esc_html($label); ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</div>
<!-- 价格区间表单 -->
<form class="resource-archive__price-range" method="get" action="<?php echo esc_url($archive_url); ?>">
<?php
// 保留 orderby / price
if ($current_orderby !== 'date') {
echo '<input type="hidden" name="orderby" value="' . esc_attr($current_orderby) . '">';
}
if ($current_price !== 'all') {
echo '<input type="hidden" name="price" value="' . esc_attr($current_price) . '">';
}
?>
<label class="resource-archive__price-input">
<span><?php esc_html_e('最低', 'aksrt'); ?></span>
<input type="number" name="price_min" min="0" step="1" value="<?php echo esc_attr((string) $price_min); ?>">
</label>
<span class="resource-archive__price-sep">—</span>
<label class="resource-archive__price-input">
<span><?php esc_html_e('最高', 'aksrt'); ?></span>
<input type="number" name="price_max" min="0" step="1" value="<?php echo esc_attr((string) $price_max); ?>">
</label>
<button type="submit" class="btn-primary btn-sm"><?php esc_html_e('应用', 'aksrt'); ?></button>
</form>
<?php if ($has_filter) : ?>
<a class="resource-archive__reset" href="<?php echo esc_url($archive_url); ?>">
<i class="fas fa-times-circle" aria-hidden="true"></i>
<?php esc_html_e('清除筛选', 'aksrt'); ?>
</a>
<?php endif; ?>
</nav>
<?php if (have_posts()) : ?>
<div class="posts-row ajaxpager tab-pane">
<?php
set_query_var('aksrt_post_card_layout', 'card');
while (have_posts()) :
the_post();
get_template_part('templates/parts/content', 'post-card');
endwhile;
set_query_var('aksrt_post_card_layout', '');
?>
</div>
<?php aksrt_render_pagination(); ?>
<?php else : ?>
<div class="no-results">
<p><?php esc_html_e('暂无资源。', 'aksrt'); ?></p>
<?php if ($has_filter) : ?>
<p><a class="btn-outline btn-sm" href="<?php echo esc_url($archive_url); ?>"><?php esc_html_e('清除筛选条件', 'aksrt'); ?></a></p>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
</main>
<?php
get_footer();
+188
View File
@@ -0,0 +1,188 @@
<?php
/**
* Resource Archive Template
*
* Virtual list at /resources/ — ordinary posts marked with _aksrt_is_resource.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
$current_orderby = isset($_GET['orderby']) ? sanitize_key((string) $_GET['orderby']) : 'date';
$allowed_orderby = ['date', 'downloads', 'views'];
if (!in_array($current_orderby, $allowed_orderby, true)) {
$current_orderby = 'date';
}
$current_price = isset($_GET['price']) ? sanitize_key((string) $_GET['price']) : 'all';
if (!in_array($current_price, ['all', 'free', 'paid'], true)) {
$current_price = 'all';
}
$price_min = isset($_GET['price_min']) && $_GET['price_min'] !== '' ? (float) $_GET['price_min'] : '';
$price_max = isset($_GET['price_max']) && $_GET['price_max'] !== '' ? (float) $_GET['price_max'] : '';
$archive_url = function_exists('aksrt_resource_archive_url') ? aksrt_resource_archive_url() : home_url('/resources/');
$sort_options = [
'date' => __('最新', 'aksrt'),
'downloads' => __('热门下载', 'aksrt'),
'views' => __('阅读最多', 'aksrt'),
];
$price_options = [
'all' => __('全部', 'aksrt'),
'free' => __('免费', 'aksrt'),
'paid' => __('付费', 'aksrt'),
];
$preserve = [];
if ($current_orderby !== 'date') {
$preserve['orderby'] = $current_orderby;
}
if ($current_price !== 'all') {
$preserve['price'] = $current_price;
}
if ($price_min !== '' && $price_min > 0) {
$preserve['price_min'] = $price_min;
}
if ($price_max !== '') {
$preserve['price_max'] = $price_max;
}
$has_filter = $current_orderby !== 'date' || $current_price !== 'all' || $price_min !== '' || $price_max !== '';
?>
<main id="main-content" class="site-main container" tabindex="-1">
<header class="resource-archive-header page-cover">
<div class="container">
<?php if (function_exists('aksrt_breadcrumbs')) : ?>
<nav class="resource-archive-header__breadcrumbs" aria-label="<?php esc_attr_e('面包屑', 'aksrt'); ?>">
<?php aksrt_breadcrumbs(); ?>
</nav>
<?php endif; ?>
<h1 class="resource-archive-header__title"><?php esc_html_e('资源', 'aksrt'); ?></h1>
<p class="resource-archive-header__sub">
<?php
global $wp_query;
/* translators: %s: resource count */
printf(esc_html__('共 %s 个资源', 'aksrt'), esc_html(number_format_i18n((int) $wp_query->found_posts)));
?>
</p>
</div>
</header>
<div class="content-wrap">
<div class="content-layout content-layout--no-sidebar">
<div class="content-layout__primary resource-archive">
<nav class="resource-archive__toolbar" aria-label="<?php esc_attr_e('筛选', 'aksrt'); ?>">
<div class="resource-archive__filter-group">
<span class="resource-archive__filter-label"><?php esc_html_e('排序', 'aksrt'); ?></span>
<ul class="resource-archive__sort">
<?php foreach ($sort_options as $key => $label) :
$args = $preserve;
if ($key === 'date') {
unset($args['orderby']);
} else {
$args['orderby'] = $key;
}
$url = $args ? add_query_arg($args, $archive_url) : $archive_url;
$active = $current_orderby === $key;
?>
<li>
<a href="<?php echo esc_url($url); ?>"
class="resource-archive__sort-link<?php echo $active ? ' is-active' : ''; ?>"
<?php echo $active ? 'aria-current="true"' : ''; ?>>
<?php echo esc_html($label); ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</div>
<div class="resource-archive__filter-group">
<span class="resource-archive__filter-label"><?php esc_html_e('价格', 'aksrt'); ?></span>
<ul class="resource-archive__sort">
<?php foreach ($price_options as $key => $label) :
$args = $preserve;
if ($key === 'all') {
unset($args['price']);
} else {
$args['price'] = $key;
}
unset($args['price_min'], $args['price_max']);
$url = $args ? add_query_arg($args, $archive_url) : $archive_url;
$active = $current_price === $key;
?>
<li>
<a href="<?php echo esc_url($url); ?>"
class="resource-archive__sort-link<?php echo $active ? ' is-active' : ''; ?>"
<?php echo $active ? 'aria-current="true"' : ''; ?>>
<?php echo esc_html($label); ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</div>
<form class="resource-archive__price-range" method="get" action="<?php echo esc_url($archive_url); ?>">
<?php
if ($current_orderby !== 'date') {
echo '<input type="hidden" name="orderby" value="' . esc_attr($current_orderby) . '">';
}
if ($current_price !== 'all') {
echo '<input type="hidden" name="price" value="' . esc_attr($current_price) . '">';
}
?>
<label class="resource-archive__price-input">
<span><?php esc_html_e('最低', 'aksrt'); ?></span>
<input type="number" name="price_min" min="0" step="1" value="<?php echo esc_attr((string) $price_min); ?>">
</label>
<span class="resource-archive__price-sep">—</span>
<label class="resource-archive__price-input">
<span><?php esc_html_e('最高', 'aksrt'); ?></span>
<input type="number" name="price_max" min="0" step="1" value="<?php echo esc_attr((string) $price_max); ?>">
</label>
<button type="submit" class="btn-primary btn-sm"><?php esc_html_e('应用', 'aksrt'); ?></button>
</form>
<?php if ($has_filter) : ?>
<a class="resource-archive__reset" href="<?php echo esc_url($archive_url); ?>">
<i class="fas fa-times-circle" aria-hidden="true"></i>
<?php esc_html_e('清除筛选', 'aksrt'); ?>
</a>
<?php endif; ?>
</nav>
<?php if (have_posts()) : ?>
<div class="posts-row ajaxpager tab-pane">
<?php
set_query_var('aksrt_post_card_layout', 'card');
while (have_posts()) :
the_post();
get_template_part('templates/parts/content', 'post-card');
endwhile;
set_query_var('aksrt_post_card_layout', '');
?>
</div>
<?php aksrt_render_pagination(); ?>
<?php else : ?>
<div class="no-results">
<p><?php esc_html_e('暂无资源。', 'aksrt'); ?></p>
<?php if ($has_filter) : ?>
<p><a class="btn-outline btn-sm" href="<?php echo esc_url($archive_url); ?>"><?php esc_html_e('清除筛选条件', 'aksrt'); ?></a></p>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
</main>
<?php
get_footer();
+6 -6
View File
@@ -110,7 +110,7 @@ export const MODULE_LIBRARY = [
label: '排行榜',
icon: 'trophy',
desc: '按下载量 / 阅读量 / 评论数排序',
defaultConfig: { metric: 'downloads', count: 6, post_type: 'aksrt_resource', title: '' },
defaultConfig: { metric: 'downloads', count: 6, post_type: 'resource', title: '' },
fields: [
{ key: 'title', type: 'text', label: '标题' },
{
@@ -124,7 +124,7 @@ export const MODULE_LIBRARY = [
{
key: 'post_type', type: 'select', label: '内容类型',
options: [
{ label: '资源', value: 'aksrt_resource' },
{ label: '资源', value: 'resource' },
{ label: '文章', value: 'post' },
],
},
@@ -143,7 +143,7 @@ export const MODULE_LIBRARY = [
key: 'post_type', type: 'select', label: '内容类型',
options: [
{ label: '文章', value: 'post' },
{ label: '资源', value: 'aksrt_resource' },
{ label: '资源', value: 'resource' },
],
},
{ key: 'count', type: 'number', label: '数量', min: 1, max: 20 },
@@ -212,7 +212,7 @@ export const MODULE_LIBRARY = [
key: 'post_type', type: 'select', label: '内容类型',
options: [
{ label: '文章', value: 'post' },
{ label: '资源', value: 'aksrt_resource' },
{ label: '资源', value: 'resource' },
],
},
],
@@ -482,8 +482,8 @@ export const PRESET_TEMPLATES = [
modules: [
{ type: 'hero', config: {} },
{ type: 'tabs', config: { count: 8, layout: 'grid-cms' } },
{ type: 'recent', config: { post_type: 'aksrt_resource', count: 8, layout: 'grid-cms' } },
{ type: 'ranking', config: { metric: 'downloads', post_type: 'aksrt_resource', count: 6 } },
{ type: 'recent', config: { post_type: 'resource', count: 8, layout: 'grid-cms' } },
{ type: 'ranking', config: { metric: 'downloads', post_type: 'resource', count: 6 } },
],
},
{
+1 -1
View File
@@ -1,7 +1,7 @@
/**
* Resource Archive · /resources
*
* 仅在 is_post_type_archive('aksrt_resource') 时加载。
* 仅在 aksrt_is_resource_archive() 时加载。
*/
.content-shell--cms {
+2 -2
View File
@@ -225,8 +225,8 @@ class AKSRT_Home_Modules {
return [
['type' => 'hero', 'config' => []],
['type' => 'tabs', 'config' => ['count' => 8]],
['type' => 'recent', 'config' => ['post_type' => 'aksrt_resource', 'count' => 8, 'layout' => 'grid-cms']],
['type' => 'ranking', 'config' => ['metric' => 'downloads', 'count' => 6, 'post_type' => 'aksrt_resource']],
['type' => 'recent', 'config' => ['post_type' => 'resource', 'count' => 8, 'layout' => 'grid-cms']],
['type' => 'ranking', 'config' => ['metric' => 'downloads', 'count' => 6, 'post_type' => 'resource']],
];
}
}
+1 -1
View File
@@ -95,7 +95,7 @@ class AKSRT_SEO_Head {
if ($post instanceof WP_Post) {
$payload['object'] = $post;
$payload['canonical'] = (string) get_permalink($post);
$payload['og_type'] = in_array($post->post_type, ['post', 'aksrt_resource'], true) ? 'article' : 'website';
$payload['og_type'] = ($post->post_type === 'post') ? 'article' : 'website';
// 单篇 SEO 覆盖:per-post meta → 文章字段 → 站点默认。
$meta = $this->post_seo_meta($post->ID);
+2 -2
View File
@@ -108,8 +108,8 @@ class AKSRT_Setup {
/**
* 注册自定义文章类型
*
* v3.4: 移除 aksrt_resource CPT,资源元数据统一挂在 post 上,
* 通过 _aksrt_is_resource meta 标记单篇文章是否为“资源”。
* 资源不是 CPT:元数据挂在普通 post 上,
* 通过 _aksrt_is_resource meta 标记是否为资源。
*/
public function register_post_types(): void {
// Shop rewrite settings (safe fallback if settings not yet loaded)
@@ -136,11 +136,11 @@ trait AKSRT_Scripts_Part_1b {
// 4b. 路由判定
$is_home_or_archive = is_home() || is_front_page() || is_archive() || is_search();
// 资源单页(post + _aksrt_is_resource,或历史 aksrt_resource CPT)
// 资源单页(post + _aksrt_is_resource)
$is_single_resource = (function_exists('aksrt_is_resource_post') && is_singular() && aksrt_is_resource_post(get_queried_object_id()));
// 普通博客 single(排除资源型 post)
$is_single_post = is_singular('post') && !$is_single_resource;
// 资源虚拟归档 /resources(含历史 CPT 归档)
// 资源虚拟列表 /resources/
$is_resource_archive = function_exists('aksrt_is_resource_archive') && aksrt_is_resource_archive();
// /user/{nicename}/ 自定义重写路由(class-aksrt-user.php 注册)
// 此路径下 is_page() / is_page_template() 均为 false,需手动识别以正确入队 CSS
@@ -29,13 +29,7 @@ if (!function_exists('aksrt_home_widget_post_types')) {
* @return array<int, string>
*/
function aksrt_home_widget_post_types(): array {
$post_types = ['post'];
if (function_exists('aksrt_resource_post_types')) {
$post_types = array_unique(array_merge($post_types, aksrt_resource_post_types()));
}
return array_values(array_filter(array_map('sanitize_key', $post_types)));
return ['post'];
}
}
+25 -78
View File
@@ -261,16 +261,13 @@ function aksrt_category_meta_admin_js() {
add_action('admin_footer', 'aksrt_category_meta_admin_js');
// ============================================================
// 资源归档:/resources 重写规则 + 排序参数注入
// 站内搜索:包含「资源」(post + _aksrt_is_resource)
// 资源列表:/resources 虚拟归档(post + _aksrt_is_resource)
// ============================================================
// 注册 /resources 虚拟归档
add_action('init', function () {
add_rewrite_rule('^resources/?$', 'index.php?aksrt_is_resource_archive=1', 'top');
add_rewrite_rule('^resources/page/([0-9]+)/?$', 'index.php?aksrt_is_resource_archive=1&paged=$matches[1]', 'top');
// 兼容历史 /resource/{slug}:301 到 /{slug}(默认 post 永久链接)
// 旧单篇路径 /resource/{slug} → 301 到 post 永久链接
add_rewrite_rule('^resource/([^/]+)/?$', 'index.php?aksrt_legacy_resource=$matches[1]', 'top');
});
@@ -281,15 +278,12 @@ add_filter('query_vars', function ($vars) {
return $vars;
});
// 旧资源链接 301 跳转
add_action('template_redirect', function () {
$slug = get_query_var('aksrt_legacy_resource');
if (!$slug) return;
// 先按 post 查
$page = get_page_by_path((string) $slug, OBJECT, 'post');
if (!$page && post_type_exists('aksrt_resource')) {
$page = get_page_by_path((string) $slug, OBJECT, 'aksrt_resource');
if (!$slug) {
return;
}
$page = get_page_by_path((string) $slug, OBJECT, 'post');
if ($page) {
wp_redirect(get_permalink($page->ID), 301);
exit;
@@ -297,14 +291,12 @@ add_action('template_redirect', function () {
});
add_action('pre_get_posts', function ($query) {
if (is_admin() || !$query->is_main_query()) return;
if (is_admin() || !$query->is_main_query()) {
return;
}
$is_resource_archive = (bool) $query->get('aksrt_is_resource_archive')
|| (post_type_exists('aksrt_resource') && $query->is_post_type_archive('aksrt_resource'));
// 资源归档页:把查询锁定到「post + _aksrt_is_resource = 1」(含历史 CPT 兼容)
if ($is_resource_archive) {
$query->set('post_type', function_exists('aksrt_resource_post_types') ? aksrt_resource_post_types() : ['post']);
if ((bool) $query->get('aksrt_is_resource_archive')) {
$query->set('post_type', 'post');
$query->set('post_status', 'publish');
$orderby = isset($_GET['orderby']) ? sanitize_key((string) $_GET['orderby']) : 'date';
@@ -318,30 +310,19 @@ add_action('pre_get_posts', function ($query) {
$query->set('order', 'DESC');
}
// 价格筛选:?price=free | paid | all
// 价格区间:?price_min=0&price_max=99
$price_filter = isset($_GET['price']) ? sanitize_key((string) $_GET['price']) : '';
$price_min = isset($_GET['price_min']) && $_GET['price_min'] !== '' ? (float) $_GET['price_min'] : null;
$price_max = isset($_GET['price_max']) && $_GET['price_max'] !== '' ? (float) $_GET['price_max'] : null;
// 「是资源」基础约束:_aksrt_is_resource=1(兼容旧 CPT 时由 post_type 兜底)
$meta_query = [
'relation' => 'AND',
[
'relation' => 'OR',
// 新形态:post + meta=1
[
'key' => AKSRT_Domain_Model::RESOURCE_MARKER_KEY,
'value' => '1',
'compare' => '=',
],
// 历史 CPT 兼容:通过 post_type 已被包含,meta 缺失也算资源
[
'key' => AKSRT_Domain_Model::RESOURCE_MARKER_KEY,
'compare' => 'NOT EXISTS',
],
'key' => AKSRT_Domain_Model::RESOURCE_MARKER_KEY,
'value' => '1',
'compare' => '=',
],
];
if ($price_filter === 'free') {
$meta_query[] = [
'relation' => 'OR',
@@ -380,7 +361,7 @@ add_action('pre_get_posts', function ($query) {
'compare' => '>=',
'type' => 'NUMERIC',
];
} elseif ($price_max !== null) {
} else {
$meta_query[] = [
'key' => AKSRT_Domain_Model::PAY_PRICE_KEY,
'value' => $price_max,
@@ -390,29 +371,16 @@ add_action('pre_get_posts', function ($query) {
}
}
// 仅在新形态(post)下加 _aksrt_is_resource=1 强约束;
// 旧 CPT 仍在站点时,post_type 已含 aksrt_resource,配合上面 OR 兼容逻辑即可。
if (!post_type_exists('aksrt_resource')) {
// 替换 OR 兼容片段为强等于
$meta_query[0] = [
'key' => AKSRT_Domain_Model::RESOURCE_MARKER_KEY,
'value' => '1',
'compare' => '=',
];
$existing = $query->get('meta_query');
if (!is_array($existing)) {
$existing = [];
}
if (!empty($meta_query)) {
$existing = $query->get('meta_query');
if (!is_array($existing)) $existing = [];
$query->set('meta_query', array_merge($existing, $meta_query));
}
// 资源归档不要分类/标签限制
$query->set('meta_query', array_merge($existing, $meta_query));
$query->set('cat', 0);
$query->set('tag', '');
}
// 站内搜索:post 已默认覆盖(资源就是 post),仅在仍有旧 CPT 时合并
// 站内搜索:分类/标签名搜索(内容类型始终为 post)
if ($query->is_search() && empty($query->get('post_type'))) {
$search_type = sanitize_key((string) $query->get('aksrt_search_type'));
@@ -438,41 +406,20 @@ add_action('pre_get_posts', function ($query) {
]]);
}
}
if (post_type_exists('aksrt_resource')) {
// 单一数据选择策略:搜索的内容类型与索引/推送共用同一 helper。
$search_types = function_exists('aksrt_searchable_post_types')
? aksrt_searchable_post_types()
: ['post'];
if (count($search_types) > 1) {
$query->set('post_type', $search_types);
}
}
}
// 分类 / 标签归档:post 已自然包含资源(meta 标记),仅当历史 CPT 仍在时才需合并 post_type
if (($query->is_category() || $query->is_tag()) && post_type_exists('aksrt_resource')) {
$existing = $query->get('post_type');
if (empty($existing)) {
$query->set('post_type', ['post', 'aksrt_resource']);
}
}
// v4 起主题统一为 CMS / 资源站形态,所有归档都允许包含资源帖。
// 历史的 blog/cms 双流分流(按 site_profile + term flow 排除资源)已移除。
});
// 资源归档页:套 archive-aksrt_resource.php 模板(v3.4 起作为「资源虚拟归档」复用)
add_filter('template_include', function ($template) {
if (!get_query_var('aksrt_is_resource_archive')) return $template;
$candidate = locate_template(['archive-aksrt_resource.php']);
if (!get_query_var('aksrt_is_resource_archive')) {
return $template;
}
$candidate = locate_template(['archive-resources.php']);
return $candidate ?: $template;
});
// 资源归档页:标题
add_filter('document_title_parts', function ($parts) {
if (get_query_var('aksrt_is_resource_archive')) {
$parts['title'] = '资源';
$parts['title'] = __('资源', 'aksrt');
}
return $parts;
});
+1 -1
View File
@@ -221,7 +221,7 @@ add_filter('the_content', 'aksrt_paid_content_filter', 20);
add_filter('the_content', 'aksrt_paid_content_enhance', 25);
/**
* 付费文章使用统一的 single.php(不再路由到 single-aksrt_resource.php)。
* 付费/资源文章使用统一的 single.php。
*/
add_filter('single_template', function (string $template): string {
return $template;
+4 -4
View File
@@ -41,16 +41,16 @@ if (!function_exists('aksrt_get_term_flow')) {
if (!function_exists('aksrt_is_resource_post')) {
/**
* 是否为资源(post + _aksrt_is_resource meta,或历史 aksrt_resource CPT)。
* 是否为资源帖:普通 post + _aksrt_is_resource meta。
*
* @param int|WP_Post|null $post
* @return bool
*/
function aksrt_is_resource_post($post = null): bool {
$p = get_post($post);
if (!($p instanceof WP_Post)) return false;
if ($p->post_type === 'aksrt_resource') return true;
if ($p->post_type !== 'post') return false;
if (!($p instanceof WP_Post) || $p->post_type !== 'post') {
return false;
}
return (bool) get_post_meta($p->ID, '_aksrt_is_resource', true);
}
}
+3 -3
View File
@@ -594,11 +594,11 @@ if (!function_exists('aksrt_set_post_views')) :
}
if ($post_id <= 0) return;
$post_type = get_post_type($post_id);
// 只对 post / 历史 aksrt_resource CPT 计数
if (!in_array($post_type, ['post', 'aksrt_resource'], true)) return;
// 仅普通文章(资源也是 post)计数
if ($post_type !== 'post') return;
$key = 'aksrt_post_views_' . $post_id;
if (!empty($_COOKIE[$key])) return;
// 资源(post + _aksrt_is_resource,或旧 CPT)使用独立 meta key,便于排序
// 资源帖使用独立 meta key,便于排序
$is_resource = function_exists('aksrt_is_resource_post') && aksrt_is_resource_post($post_id);
if ($is_resource) {
if (function_exists('aksrt_update_post_counter')) {
+2 -13
View File
@@ -36,27 +36,16 @@ if (!function_exists('aksrt_homepage_get_current_page')) {
if (!function_exists('aksrt_homepage_get_post_types')) {
/**
* Homepage latest feed post types.
*
* Respects the homepage_content_model setting:
* - 'post': standard posts only (default)
* - 'mixed': posts + resource CPTs (explicit opt-in)
* Resources are ordinary posts with meta — always post.
*
* @return string[]
*/
function aksrt_homepage_get_post_types(): array {
// Default: plain posts only (zibll main-query feel). Mixed/resource is opt-in.
$content_model = class_exists('AKSRT_Settings')
? AKSRT_Settings::homepage_content_model()
: 'post';
if ($content_model === 'mixed' && function_exists('aksrt_resource_post_types')) {
return array_values(array_unique(aksrt_resource_post_types()));
}
return ['post'];
}
}
if (!function_exists('aksrt_homepage_get_tab_terms')) {
/**
* Homepage category tabs with full config.
+17 -29
View File
@@ -412,10 +412,7 @@ if (!function_exists('aksrt_resource_download_cost')) {
if (!function_exists('aksrt_resource_meta_query')) {
/**
* 「是资源」WP_Query meta_query 片段。
*
* v3.4: 资源 = post + _aksrt_is_resource = 1。
* 兼容历史 aksrt_resource CPT 时由调用方决定是否额外加 post_type。
* 「是资源」WP_Query meta_query 片段:post + _aksrt_is_resource = 1。
*
* @return array
*/
@@ -428,26 +425,9 @@ if (!function_exists('aksrt_resource_meta_query')) {
}
}
if (!function_exists('aksrt_resource_post_types')) {
/**
* 资源查询使用的 post_type 数组。
*
* 当历史 aksrt_resource CPT 仍在站点中存在(未迁移完成)时,会自动包含。
*
* @return array
*/
function aksrt_resource_post_types(): array {
$types = ['post'];
if (post_type_exists('aksrt_resource')) {
$types[] = 'aksrt_resource';
}
return $types;
}
}
if (!function_exists('aksrt_resource_archive_url')) {
/**
* 取资源归档页 URL(v3.4 起统一为 /resources/)。
* 资源列表页 URL(/resources/)。
*/
function aksrt_resource_archive_url(): string {
return home_url('/resources/');
@@ -456,19 +436,27 @@ if (!function_exists('aksrt_resource_archive_url')) {
if (!function_exists('aksrt_is_resource_archive')) {
/**
* 当前请求是否为资源归档(/resources)。
*
* 兼容历史 aksrt_resource CPT 归档。
* 当前请求是否为资源列表(/resources 虚拟归档)。
*/
function aksrt_is_resource_archive(): bool {
if (function_exists('post_type_exists') && post_type_exists('aksrt_resource')
&& function_exists('is_post_type_archive') && is_post_type_archive('aksrt_resource')) {
return true;
}
return (bool) get_query_var('aksrt_is_resource_archive');
}
}
/**
* Normalize homepage-module content scope.
* Values: post | resource (resource = posts marked _aksrt_is_resource).
*/
if (!function_exists('aksrt_normalize_content_scope')) {
function aksrt_normalize_content_scope($scope): string {
$scope = sanitize_key((string) $scope);
if ($scope === 'resource') {
return 'resource';
}
return 'post';
}
}
if (!function_exists('aksrt_detect_cloud_service')) {
/**
* 从下载链接自动识别云盘服务商。
+8 -537
View File
@@ -787,545 +787,16 @@ if (!function_exists('aksrt_searchable_post_types')):
* @return string[]
*/
function aksrt_searchable_post_types(): array {
$types = ['post'];
$types = ['post'];
if (post_type_exists('aksrt_resource')
&& class_exists('AKSRT_Settings')
&& AKSRT_Settings::cms_feature('search_include_resource', true)) {
$types[] = 'aksrt_resource';
}
/**
* 过滤可检索内容类型。
*
* @param string[] $types 内容类型 slug 列表。
*/
$types = apply_filters('aksrt_searchable_post_types', $types);
/**
* 过滤可检索内容类型。
*
* @param string[] $types 内容类型 slug 列表。
*/
$types = apply_filters('aksrt_searchable_post_types', $types);
return array_values(array_unique(array_filter((array) $types)));
}
endif;
if (!function_exists('aksrt_get_admin_settings_url')):
/**
* Build a quick-link URL into the admin SPA settings app.
*
* @param string $path Route path without leading #/.
* @return string
*/
function aksrt_get_admin_settings_url(string $path): string {
if (!current_user_can('manage_options')) {
return '';
}
$path = trim($path, "/# \t\n\r\0\x0B");
if ($path === '') {
return admin_url('admin.php?page=aksrt-settings');
}
return admin_url('admin.php?page=aksrt-settings#/' . $path);
}
endif;
if (!function_exists('aksrt_render_admin_quick_link')):
/**
* Render an admin-only front-end shortcut to the corresponding settings page.
*
* @param string $path Admin SPA route path.
* @param string $label Button label.
* @param string $class CSS classes.
*/
function aksrt_render_admin_quick_link(string $path, string $label = '', string $class = 'aksrt-admin-quicklink'): void {
$url = aksrt_get_admin_settings_url($path);
if ($url === '') {
return;
}
if ($label === '') {
$label = __('编辑此区域', 'aksrt');
}
?>
<a class="<?php echo esc_attr($class); ?>" href="<?php echo esc_url($url); ?>">
<i class="fas fa-sliders-h" aria-hidden="true"></i>
<span><?php echo esc_html($label); ?></span>
</a>
<?php
}
endif;
if (!function_exists('aksrt_render_search_hot_keywords')):
/**
* 渲染热门搜索关键词
*
* @param array $keywords 关键词
* @param string $class 容器类名
*/
function aksrt_render_search_hot_keywords(array $keywords = [], string $class = 'aksrt-search-hot-keywords'): void {
if (empty($keywords)) {
$keywords = aksrt_get_search_hot_keywords();
}
if (empty($keywords)) {
return;
}
$title = class_exists('AKSRT_Settings') ? AKSRT_Settings::search_popular_title() : __('热门搜索', 'aksrt');
?>
<div class="<?php echo esc_attr($class); ?>" aria-label="<?php echo esc_attr($title); ?>">
<span class="<?php echo esc_attr($class . '__label'); ?>"><?php echo esc_html($title); ?></span>
<div class="<?php echo esc_attr($class . '__list tabbar-search-overlay__tags'); ?>">
<?php foreach ($keywords as $keyword) : ?>
<a class="tabbar-search-overlay__tag" href="<?php echo aksrt_build_search_url($keyword, ['aksrt_search_type' => aksrt_get_default_search_type()]); ?>">
<?php echo esc_html($keyword); ?>
</a>
<?php endforeach; ?>
</div>
</div>
<?php
}
endif;
if (!function_exists('aksrt_get_avatar_url')):
/**
* 获取统一头像 URL
*
* @param mixed $id_or_email 用户、评论对象或邮箱
* @param array $args get_avatar_data 参数
* @return string
*/
function aksrt_get_avatar_url($id_or_email, array $args = []): string {
$data = get_avatar_data($id_or_email, array_merge(['size' => 96], $args));
return isset($data['url']) ? (string) $data['url'] : '';
}
endif;
if (!function_exists('aksrt_get_avatar_html')):
/**
* 获取统一头像 HTML
*
* @param mixed $id_or_email 用户、评论对象或邮箱
* @param int $size 头像尺寸
* @param array $args get_avatar 参数
* @return string
*/
function aksrt_get_avatar_html($id_or_email, int $size = 96, array $args = []): string {
$args = array_merge(['size' => $size], $args);
return get_avatar($id_or_email, $size, '', '', $args);
}
endif;
if (!function_exists('aksrt_get_about_info')):
/**
* 获取 About 页面信息
*
* @return array
*/
function aksrt_get_about_info() {
$public = get_option('aksrt_public_settings', array());
return array(
'display_name' => class_exists('AKSRT_Settings') ? AKSRT_Settings::about_display_name() : aksrt_get_setting('about_display_name', get_bloginfo('name')),
'bio' => class_exists('AKSRT_Settings') ? AKSRT_Settings::about_bio() : aksrt_get_setting('about_bio', ''),
'avatar_url' => class_exists('AKSRT_Settings') ? AKSRT_Settings::about_avatar() : aksrt_get_setting('about_avatar', ($public['admin_avatar_url'] ?? '')),
'contacts' => isset($public['about_contacts']) ? $public['about_contacts'] : array(),
);
}
endif;
// ============================================================
// 模板标签:社交链接图标渲染
// ============================================================
if (!function_exists('aksrt_render_social_links')):
/**
* 渲染社交链接图标
*
* @param array $links 联系方式数组 [{name:'GitHub', url:'...'}]
*/
function aksrt_render_social_links($links = array()) {
if (empty($links)) return;
echo '<div class="profile-social-links">';
foreach ($links as $contact):
$icon_key = isset($contact['icon']) ? $contact['icon'] : '';
// 旧数据兼容:从 name 猜测 icon_key
if (!$icon_key) {
$name_lower = strtolower(isset($contact['name']) ? $contact['name'] : '');
$guess = array(
'github' => 'github', 'google' => 'google', 'x' => 'x', 'twitter' => 'x',
'email' => 'email', 'wechat' => 'wechat', 'qq' => 'qq', 'weibo' => 'weibo',
'telegram' => 'telegram', 'discord' => 'discord', 'bilibili' => 'bilibili',
'linkedin' => 'linkedin', 'facebook' => 'facebook', 'youtube' => 'youtube',
'instagram' => 'instagram', 'rss' => 'web', 'site' => 'web', 'globe' => 'web',
);
foreach ($guess as $k => $v) {
if (strpos($name_lower, $k) !== false) { $icon_key = $v; break; }
}
if (!$icon_key) $icon_key = 'web';
}
$title = aksrt_icon_key_to_name($icon_key);
$svg = aksrt_inline_svg($icon_key);
if (!$svg) $svg = aksrt_inline_svg('web');
if (!$svg) continue;
printf(
'<a href="%s" target="_blank" rel="noopener" class="social-icon-link" title="%s">%s</a>',
esc_url($contact['url']),
esc_attr($title),
$svg
);
endforeach;
echo '</div>';
}
endif;
// ============================================================
// Head 输出(Favicon / Canonical / SEO / OpenGraph / Twitter / JSON-LD / robots)
// ------------------------------------------------------------
// Phase 7:上述 head 输出已统一收口到 AKSRT_SEO_Head(单一装配器,
// 见 inc/classes/class-aksrt-seo-head.php 与 seo-output-matrix.md),
// 不再在此用多个分散的 wp_head 闭包输出,避免重复与来源不可追踪。
// ============================================================
// ============================================================
// Sitemap:兼容历史 aksrt_resource CPT(v3.4 起资源已并入 post,无需特殊处理)
// ============================================================
add_filter('wp_sitemaps_post_types', function ($post_types) {
if (post_type_exists('aksrt_resource') && !isset($post_types['aksrt_resource'])) {
$post_types['aksrt_resource'] = get_post_type_object('aksrt_resource');
}
// 移除内部隐藏的 banner CPT(虽然其 public=false,但兜底)
if (isset($post_types['aksrt_banner'])) {
unset($post_types['aksrt_banner']);
}
return $post_types;
});
// ============================================================
// 模板标签函数(供 index.php / single.php / archive.php 使用)
// ============================================================
if (!function_exists('aksrt_post_categories')):
/**
* 显示文章分类标签
* 定义在 functions.php 中,确保所有模板都能调用
*/
function aksrt_post_categories() {
$categories = get_the_category_list(array(
'separator' => '',
'before' => '<span class="cat-links">',
'after' => '</span>',
));
echo $categories;
}
endif;
if (!function_exists('aksrt_estimated_reading_time')):
/**
* 估算阅读时间(分钟)
*
* @return int 阅读时间(分钟)
*/
function aksrt_estimated_reading_time() {
$content = get_post_field('post_content', get_the_ID(), 'raw');
$word_count = str_word_count(strip_tags($content));
return max(1, (int) ceil($word_count / 200)); // 假设每分钟200字
}
endif;
if (!function_exists('comments_number_label')):
/**
* 评论数量显示标签
*
* @return string 格式化的评论数
*/
function comments_number_label() {
$num = get_comments_number();
if ($num === 0) return __('暂无评论', 'aksrt');
if ($num === 1) return __('1 条评论', 'aksrt');
return sprintf(__('%d 条评论', 'aksrt'), $num);
}
endif;
// ============================================================
// 模板标签:从文章内容中提取第一张图片
// ============================================================
if (!function_exists('aksrt_get_first_content_image_url')):
/**
* 从文章内容中提取第一张图片 URL
* 支持 <img> 标签和 Gutenberg wp:image 块
*
* @param int $post_id 文章 ID,默认当前文章
* @return string 图片 URL,无图片返回空字符串
*/
function aksrt_get_first_content_image_url(int $post_id = 0): string {
$post_id = $post_id ?: get_the_ID();
$content = get_post_field('post_content', $post_id, 'raw');
if (!$content) return '';
// 匹配 <img> 标签的 src
if (preg_match('/<img[^>]+src=["\x27"]([^"\x27"]+)["\x27"]/', $content, $m)) {
return esc_url_raw($m[1]);
}
// 匹配 Gutenberg image 块的 url 属性
if (preg_match('/<!-- wp:image \{[^}]*"url"\s*:\s*"([^"]+)"/', $content, $m)) {
return esc_url_raw($m[1]);
}
return '';
}
endif;
// ============================================================
// 模板标签:首页分类推荐
// ============================================================
if (!function_exists('aksrt_get_category_recommend_settings')):
/**
* 获取分类推荐配置
*
* @return array
*/
function aksrt_get_category_recommend_settings(): array {
if (class_exists('AKSRT_Settings')) {
return AKSRT_Settings::get_category_recommend_settings();
}
return [];
}
endif;
if (!function_exists('aksrt_get_category_recommend_items')):
/**
* 获取分类推荐渲染数据
*
* @param array $settings 分类推荐配置
* @return array
*/
function aksrt_get_category_recommend_items(array $settings): array {
if (class_exists('AKSRT_Settings')) {
return AKSRT_Settings::get_category_recommend_items($settings);
}
return [];
}
endif;
if (!function_exists('aksrt_render_category_recommend_section')):
/**
* 渲染首页分类推荐模块
*
* @param array $settings 可选配置,为空则自动读取
*/
function aksrt_render_category_recommend_section(array $settings = []): void {
if (empty($settings)) {
$settings = aksrt_get_category_recommend_settings();
}
if (empty($settings['enable_category_recommend'])) return;
$items = aksrt_get_category_recommend_items($settings);
if (empty($items)) return;
$title = $settings['category_recommend_title'] ?: __('分类推荐', 'aksrt');
$columns = max(1, min(4, (int) ($settings['category_recommend_columns'] ?? 2)));
$show_desc = !empty($settings['category_recommend_show_description']);
$show_dates = !empty($settings['category_recommend_show_dates']);
$show_more = !empty($settings['category_recommend_show_more_link']);
// 懒加载配置
$lazy_cfg = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_lazyload_config() : ['enabled' => false, 'placeholder_url' => ''];
$lazy_on = !empty($lazy_cfg['enabled']);
$placeholder_url = $lazy_cfg['placeholder_url'] ?: (get_template_directory_uri() . '/assets/img/placeholder.svg');
?>
<section class="category-recommend" aria-label="<?php echo esc_attr($title); ?>">
<div class="category-recommend__header">
<h2 class="category-recommend__title"><?php echo esc_html($title); ?></h2>
</div>
<div class="category-recommend__grid" style="--cr-columns:<?php echo esc_attr((string) $columns); ?>">
<?php foreach ($items as $item) : ?>
<div class="category-recommend-card">
<div class="category-recommend-card__hero">
<a href="<?php echo esc_url($item['link']); ?>">
<?php if ($lazy_on) : ?>
<span class="aksrt-skeleton"></span>
<img class="category-recommend-card__cover aksrt-lazy" data-src="<?php echo esc_url($item['cover']); ?>" src="<?php echo esc_url($placeholder_url); ?>" alt="<?php echo esc_attr($item['name']); ?>">
<?php else : ?>
<img class="category-recommend-card__cover" src="<?php echo esc_url($item['cover']); ?>" alt="<?php echo esc_attr($item['name']); ?>">
<?php endif; ?>
</a>
<span class="category-recommend-card__badge"><?php echo esc_html(sprintf(_n('%d 篇文章', '%d 篇文章', $item['count'], 'aksrt'), $item['count'])); ?></span>
</div>
<div class="category-recommend-card__meta">
<h3 class="category-recommend-card__name">
<a href="<?php echo esc_url($item['link']); ?>"><?php echo esc_html($item['name']); ?></a>
</h3>
<?php if ($show_desc && $item['description']) : ?>
<p class="category-recommend-card__desc"><?php echo esc_html($item['description']); ?></p>
<?php endif; ?>
</div>
<?php if (!empty($item['posts'])) : ?>
<ul class="category-recommend-card__posts">
<?php foreach ($item['posts'] as $post) : ?>
<li class="category-recommend-card__post-item">
<a href="<?php echo esc_url($post['link']); ?>" class="category-recommend-card__post-title"><?php echo esc_html($post['title']); ?></a>
<?php if ($show_dates) : ?>
<span class="category-recommend-card__post-date"><?php echo esc_html($post['date']); ?></span>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<?php if ($show_more) : ?>
<div class="category-recommend-card__more">
<a href="<?php echo esc_url($item['link']); ?>"><?php esc_html_e('更多文章', 'aksrt'); ?> →</a>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
</section>
<?php
}
endif;
// ============================================================
// 模板标签:分页渲染
// ============================================================
if (!function_exists('aksrt_render_pagination')) {
/**
* 渲染文章列表分页(根据后台配置选择模式)
*
* @param WP_Query|null $query 可选,传入自定义查询对象
*/
function aksrt_render_pagination(?WP_Query $query = null, array $args = []): void {
$pagination_mode = class_exists('AKSRT_Settings')
? AKSRT_Settings::get_post_pagination_mode()
: 'numbered';
if ($pagination_mode === 'numbered') {
global $wp_query;
$q = $query ?? $wp_query;
if (!($q instanceof WP_Query) || $q->max_num_pages <= 1) {
return;
}
$current_page = max(1, (int) $q->get('paged'), (int) get_query_var('paged'), (int) get_query_var('page'));
$max_pages = (int) $q->max_num_pages;
$links = paginate_links([
'total' => $max_pages,
'current' => $current_page,
'mid_size' => 2,
'prev_text' => '<span aria-hidden="true">‹</span>',
'next_text' => '<span aria-hidden="true">›</span>',
'type' => 'plain',
]);
if (empty($links)) {
return;
}
echo '<div class="pagenav ajax-pag aksrt-pagination content-stream__footer-nav">';
echo wp_kses_post($links);
if ($max_pages > 8) {
echo '<span class="pag-jump">';
echo '<input type="number" class="jump-input" min="1" max="' . esc_attr((string) $max_pages) . '" value="' . esc_attr((string) $current_page) . '" data-max-pages="' . esc_attr((string) $max_pages) . '" />';
echo '<button type="button" class="ajax-next order-ajax-next" data-jump-archive="1">' . esc_html__('跳转', 'aksrt') . '</button>';
echo '</span>';
}
echo '</div>';
return;
}
// load_more / infinite 模式
global $wp_query;
$q = $query ?? $wp_query;
$max_pages = $q->max_num_pages;
$current_page = max(1, (int) $q->get('paged'), (int) get_query_var('paged'), (int) get_query_var('page'));
if ($current_page >= $max_pages) {
return;
}
$query_context = '';
if (!empty($args['query_context']) && is_string($args['query_context'])) {
$query_context = sanitize_key($args['query_context']);
} elseif (is_search()) {
$query_context = 'search';
} elseif (is_archive()) {
$query_context = 'archive';
}
$query_vars = $q instanceof WP_Query ? $q->query_vars : [];
if (!is_array($query_vars)) {
$query_vars = [];
} else {
unset($query_vars['paged'], $query_vars['page']);
}
$render_config = !empty($args['render_config']) && is_array($args['render_config'])
? $args['render_config']
: [];
$trigger_text = (class_exists('AKSRT_Settings') && ($t = AKSRT_Settings::get_post_list_style_settings()['ajax_trigger_text'] ?? '') !== '') ? $t : __('加载更多', 'aksrt');
?>
<div class="text-center theme-pagination ajax-pag ajax-pagination aksrt-pagination-ajax content-stream__footer-nav"
data-mode="<?php echo esc_attr($pagination_mode); ?>"
data-next-page="<?php echo esc_attr((string) ($current_page + 1)); ?>"
data-max-pages="<?php echo esc_attr((string) $max_pages); ?>"
data-query-context="<?php echo esc_attr($query_context); ?>"
data-query-vars="<?php echo esc_attr(wp_json_encode($query_vars)); ?>"
data-render-config="<?php echo esc_attr(wp_json_encode($render_config)); ?>"
data-lazyload-action="ias">
<div class="next-page ajax-next aksrt-load-more-btn" lazyload-action="ias" role="button" tabindex="0">
<?php echo esc_html($trigger_text); ?>
</div>
<div class="aksrt-loading-indicator" style="display:none;" aria-hidden="true">
<i class="fas fa-spinner fa-spin"></i>
<span><?php esc_html_e('加载中…', 'aksrt'); ?></span>
</div>
</div>
<?php
}
}
// ============================================================
// 分类推荐卡片模块:辅助函数
// ============================================================
if (!function_exists('aksrt_get_category_total_views')) :
/**
* 获取分类下所有文章的总浏览量
*
* @param int $cat_id 分类 ID
* @return int
*/
function aksrt_get_category_total_views(int $cat_id): int {
global $wpdb;
$cache_key = 'aksrt_cat_views_' . $cat_id;
$cached = wp_cache_get($cache_key, 'aksrt');
if ($cached !== false) return (int) $cached;
$sql = $wpdb->prepare(
"SELECT COALESCE(SUM(CAST(pm.meta_value AS UNSIGNED)), 0)
FROM {$wpdb->postmeta} pm
INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
INNER JOIN {$wpdb->term_relationships} tr ON tr.object_id = p.ID
INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
WHERE tt.term_id = %d
AND tt.taxonomy = 'category'
AND p.post_status = 'publish'
AND pm.meta_key IN ('aksrt_post_views', '_aksrt_res_view_count')",
$cat_id
);
$result = (int) $wpdb->get_var($sql);
wp_cache_set($cache_key, $result, 'aksrt', 300);
return $result;
return array_values(array_unique(array_filter((array) $types)));
}
endif;
@@ -20,19 +20,14 @@ if (!in_array($layout, ['card', 'list', 'masonry', 'grid-cms'], true)) {
}
$args = [
'posts_per_page' => $count,
'post_status' => 'publish',
'no_found_rows' => true,
];
if ($cat_id > 0) {
$args['cat'] = $cat_id;
// 分类页通常希望同时展示资源 post,post 已天然包含资源;旧 CPT 兼容时合并
$args['post_type'] = function_exists('aksrt_resource_post_types')
? array_unique(array_merge(['post'], aksrt_resource_post_types()))
: ['post'];
} else {
$args['post_type'] = 'post';
}
'post_type' => 'post',
'posts_per_page' => $count,
'post_status' => 'publish',
'no_found_rows' => true,
];
if ($cat_id > 0) {
$args['cat'] = $cat_id;
}
$query = new WP_Query($args);
if (!$query->have_posts()) {
+30 -21
View File
@@ -3,7 +3,7 @@
* Home Module · Hotspot 热点推荐
*
* 纯文章列表(无图片),仅桌面端显示。
* config: { count, post_type, source, title? }
* config: { count, post_type: 'post'|'resource', source, title? }
*
* source:
* 'auto' → 优先读取 aksrt_recommend meta,不足时用最新补充
@@ -13,36 +13,39 @@
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
if (!is_array($cfg)) {
$cfg = [];
}
$count = max(3, min(20, (int) ($cfg['count'] ?? 6)));
$post_type = (string) ($cfg['post_type'] ?? 'post');
$source = (string) ($cfg['source'] ?? 'auto');
$title = (string) ($cfg['title'] ?? '');
$count = max(3, min(20, (int) ($cfg['count'] ?? 6)));
$scope = function_exists('aksrt_normalize_content_scope')
? aksrt_normalize_content_scope($cfg['post_type'] ?? 'post')
: 'post';
$source = (string) ($cfg['source'] ?? 'auto');
$title = (string) ($cfg['title'] ?? '');
if (!in_array($source, ['auto', 'recent', 'views'], true)) {
$source = 'auto';
}
if (!in_array($post_type, ['post', 'aksrt_resource'], true)) {
$post_type = 'post';
}
$is_resource = $post_type === 'aksrt_resource';
$ptypes = $is_resource && function_exists('aksrt_resource_post_types')
? aksrt_resource_post_types()
: [$post_type];
$is_resource = $scope === 'resource';
// 构建查询参数
$args = [
'post_type' => $ptypes,
'post_type' => 'post',
'posts_per_page' => $count,
'post_status' => 'publish',
'no_found_rows' => true,
];
$meta_query = [];
if ($is_resource) {
$meta_query[] = function_exists('aksrt_resource_meta_query')
? aksrt_resource_meta_query()
: ['key' => '_aksrt_is_resource', 'value' => '1', 'compare' => '='];
}
if ($source === 'auto') {
// 优先推荐文章,不足时用最新补充
$args['meta_query'] = [
$meta_query[] = [
'relation' => 'OR',
['key' => 'aksrt_recommend', 'value' => '1', 'compare' => '='],
['key' => 'aksrt_recommend', 'compare' => 'NOT EXISTS'],
@@ -58,6 +61,12 @@ if ($source === 'auto') {
$args['order'] = 'DESC';
}
if (count($meta_query) === 1) {
$args['meta_query'] = $meta_query;
} elseif (count($meta_query) > 1) {
$args['meta_query'] = array_merge(['relation' => 'AND'], $meta_query);
}
$query = new WP_Query($args);
if (!$query->have_posts()) {
wp_reset_postdata();
@@ -81,10 +90,10 @@ $display_title = $title ?: $default_title;
$rank = 1;
while ($query->have_posts()) :
$query->the_post();
$cats = get_the_category();
$cats = get_the_category();
$cat_name = $cats ? $cats[0]->name : '';
$views = (int) get_post_meta(get_the_ID(), '_aksrt_res_view_count', true);
?>
$views = (int) get_post_meta(get_the_ID(), '_aksrt_res_view_count', true);
?>
<li class="hotspot__item">
<span class="hotspot__num hotspot__num--<?php echo $rank <= 3 ? 'top' : 'normal'; ?>"><?php echo (int) $rank; ?></span>
<div class="hotspot__body">
@@ -102,7 +111,7 @@ $display_title = $title ?: $default_title;
</div>
</div>
</li>
<?php
<?php
$rank++;
endwhile;
wp_reset_postdata();
+22 -22
View File
@@ -2,57 +2,57 @@
/**
* Home Module · Ranking
*
* config: { metric: 'views|downloads|comments', count, post_type, title? }
* config: { metric: 'views|downloads|comments', count, post_type: 'post'|'resource', title? }
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
if (!is_array($cfg)) {
$cfg = [];
}
$metric = (string) ($cfg['metric'] ?? 'downloads');
$count = max(1, min(20, (int) ($cfg['count'] ?? 6)));
$post_type_cfg = (string) ($cfg['post_type'] ?? 'aksrt_resource');
$title = (string) ($cfg['title'] ?? '');
$metric = (string) ($cfg['metric'] ?? 'downloads');
$count = max(1, min(20, (int) ($cfg['count'] ?? 6)));
$scope = function_exists('aksrt_normalize_content_scope')
? aksrt_normalize_content_scope($cfg['post_type'] ?? 'resource')
: 'resource';
$title = (string) ($cfg['title'] ?? '');
if (!in_array($metric, ['views', 'downloads', 'comments'], true)) {
$metric = 'downloads';
}
$is_resource_module = $post_type_cfg === 'aksrt_resource';
$is_resource_module = $scope === 'resource';
$args = [
'post_type' => 'post',
'posts_per_page' => $count,
'post_status' => 'publish',
'no_found_rows' => true,
];
if ($is_resource_module) {
$args['post_type'] = function_exists('aksrt_resource_post_types') ? aksrt_resource_post_types() : ['post'];
if (!post_type_exists('aksrt_resource')) {
$args['meta_query'] = [[
'key' => '_aksrt_is_resource',
'value' => '1',
'compare' => '=',
]];
}
} else {
$args['post_type'] = 'post';
$args['meta_query'] = [
function_exists('aksrt_resource_meta_query')
? aksrt_resource_meta_query()
: ['key' => '_aksrt_is_resource', 'value' => '1', 'compare' => '='],
];
}
switch ($metric) {
case 'views':
$args['meta_key'] = '_aksrt_res_view_count';
$args['orderby'] = 'meta_value_num';
$args['order'] = 'DESC';
$args['orderby'] = 'meta_value_num';
$args['order'] = 'DESC';
break;
case 'comments':
$args['orderby'] = 'comment_count';
$args['order'] = 'DESC';
$args['order'] = 'DESC';
break;
case 'downloads':
default:
$args['meta_key'] = '_aksrt_res_download_count';
$args['orderby'] = 'meta_value_num';
$args['order'] = 'DESC';
$args['orderby'] = 'meta_value_num';
$args['order'] = 'DESC';
break;
}
+20 -24
View File
@@ -2,30 +2,30 @@
/**
* Home Module · Recent
*
* config: { post_type, count, layout, title? }
*
* post_type 取值:
* 'post' → 最新文章
* 'aksrt_resource' → 最新资源(v3.4 起兼容 post + _aksrt_is_resource)
* config: { post_type: 'post'|'resource', count, layout, title? }
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
if (!is_array($cfg)) {
$cfg = [];
}
$post_type = (string) ($cfg['post_type'] ?? 'post');
$count = max(1, min(20, (int) ($cfg['count'] ?? 6)));
$layout = (string) ($cfg['layout'] ?? 'card');
$title = (string) ($cfg['title'] ?? '');
$scope = function_exists('aksrt_normalize_content_scope')
? aksrt_normalize_content_scope($cfg['post_type'] ?? 'post')
: ((string) ($cfg['post_type'] ?? 'post') === 'resource' ? 'resource' : 'post');
$count = max(1, min(20, (int) ($cfg['count'] ?? 6)));
$layout = (string) ($cfg['layout'] ?? 'card');
$title = (string) ($cfg['title'] ?? '');
if (!in_array($layout, ['card', 'list', 'masonry', 'grid-cms'], true)) {
$layout = 'card';
}
if (!in_array($post_type, ['post', 'aksrt_resource'], true)) $post_type = 'post';
$is_resource_module = $post_type === 'aksrt_resource';
$is_resource_module = $scope === 'resource';
$args = [
'post_type' => 'post',
'posts_per_page' => $count,
'post_status' => 'publish',
'no_found_rows' => true,
@@ -33,16 +33,11 @@ $args = [
'order' => 'DESC',
];
if ($is_resource_module) {
$args['post_type'] = function_exists('aksrt_resource_post_types') ? aksrt_resource_post_types() : ['post'];
if (!post_type_exists('aksrt_resource')) {
$args['meta_query'] = [[
'key' => '_aksrt_is_resource',
'value' => '1',
'compare' => '=',
]];
}
} else {
$args['post_type'] = 'post';
$args['meta_query'] = [
function_exists('aksrt_resource_meta_query')
? aksrt_resource_meta_query()
: ['key' => '_aksrt_is_resource', 'value' => '1', 'compare' => '='],
];
}
$query = new WP_Query($args);
@@ -53,7 +48,7 @@ if (!$query->have_posts()) {
$default_title = $is_resource_module ? __('最新资源', 'aksrt') : __('最新文章', 'aksrt');
$display_title = $title ?: $default_title;
$archive_link = $is_resource_module
$archive_link = $is_resource_module
? (function_exists('aksrt_resource_archive_url') ? aksrt_resource_archive_url() : home_url('/resources/'))
: get_permalink(get_option('page_for_posts'));
?>
@@ -78,7 +73,8 @@ $archive_link = $is_resource_module
}
set_query_var('aksrt_post_card_layout', '');
set_query_var('aksrt_post_card_scene', '');
wp_reset_postdata();
?>
</div>
</section>
<?php
wp_reset_postdata();
+6 -9
View File
@@ -71,16 +71,13 @@ $active_id = $tabs[0]['id'];
</header>
<?php foreach ($tabs as $i => $t) :
$tab_post_types = function_exists('aksrt_resource_post_types')
? array_unique(array_merge(['post'], aksrt_resource_post_types()))
: ['post'];
$query = new WP_Query([
'cat' => $t['id'],
'post_type' => $tab_post_types,
'posts_per_page' => $count,
'post_status' => 'publish',
'no_found_rows' => true,
]);
'cat' => $t['id'],
'post_type' => 'post',
'posts_per_page' => $count,
'post_status' => 'publish',
'no_found_rows' => true,
]);
?>
<div class="home-module__tab-panel tab-pane<?php echo $i === 0 ? ' is-active' : ''; ?>"
role="tabpanel"
+13 -20
View File
@@ -2,8 +2,7 @@
/**
* Resource Related
*
* 单篇资源页底部的「相关资源」区块。
* 取同分类下其它资源(post + _aksrt_is_resource = 1,兼容旧 aksrt_resource CPT),按下载量倒序。
* 单篇资源页底部「相关资源」:同分类其它资源帖(post + _aksrt_is_resource)。
*
* @package AKSRT
*/
@@ -11,13 +10,13 @@
defined('ABSPATH') || exit;
$current_id = get_the_ID();
$cat_ids = wp_get_post_categories($current_id, ['fields' => 'ids']);
if (empty($cat_ids)) return;
$post_types = function_exists('aksrt_resource_post_types') ? aksrt_resource_post_types() : ['post'];
$cat_ids = wp_get_post_categories($current_id, ['fields' => 'ids']);
if (empty($cat_ids)) {
return;
}
$args = [
'post_type' => $post_types,
'post_type' => 'post',
'posts_per_page' => 6,
'post__not_in' => [$current_id],
'category__in' => array_map('intval', $cat_ids),
@@ -25,17 +24,12 @@ $args = [
'orderby' => 'meta_value_num',
'order' => 'DESC',
'no_found_rows' => true,
'meta_query' => [
function_exists('aksrt_resource_meta_query')
? aksrt_resource_meta_query()
: ['key' => '_aksrt_is_resource', 'value' => '1', 'compare' => '='],
],
];
if (!post_type_exists('aksrt_resource')) {
$args['meta_query'] = [
'relation' => 'AND',
[
'key' => '_aksrt_is_resource',
'value' => '1',
'compare' => '=',
],
];
}
$query = new WP_Query($args);
@@ -49,10 +43,9 @@ if (!$query->have_posts()) {
<header class="resource-related__header">
<h2 class="resource-related__title"><?php esc_html_e('相关资源', 'aksrt'); ?></h2>
</header>
<div class="post-grid post-grid--grid-cms resource-related__grid">
<div class="resource-related__grid post-grid post-grid--card">
<?php
set_query_var('aksrt_post_card_layout', 'grid-cms');
set_query_var('aksrt_post_card_layout', 'card');
while ($query->have_posts()) {
$query->the_post();
get_template_part('templates/parts/content', 'post-card');