From 1af0e9b753cfd64589a084704d91d4b90d329af2 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 13 Jul 2026 23:47:04 +0800 Subject: [PATCH] refactor(resource): drop aksrt_resource CPT residue, post+meta only --- archive-aksrt_resource.php | 191 ------ archive-resources.php | 188 ++++++ assets/admin/src/utils/home-module-schema.js | 12 +- assets/css/frontend/resource-archive.css | 2 +- inc/classes/class-aksrt-home-modules.php | 4 +- inc/classes/class-aksrt-seo-head.php | 2 +- inc/classes/class-aksrt-setup.php | 4 +- .../trait-aksrt-scripts-part-1b.php | 4 +- .../widgets/class-aksrt-home-widgets.php | 8 +- inc/core-hooks/part-2.php | 103 +--- inc/core-hooks/part-3.php | 2 +- inc/helpers/aksrt-flow.php | 8 +- inc/helpers/aksrt-frontend-tags.php | 6 +- inc/helpers/aksrt-homepage.php | 15 +- inc/helpers/aksrt-resource.php | 46 +- inc/helpers/aksrt-template-tags.php | 545 +----------------- .../parts/home-modules/module-category.php | 21 +- .../parts/home-modules/module-hotspot.php | 51 +- .../parts/home-modules/module-ranking.php | 44 +- .../parts/home-modules/module-recent.php | 44 +- templates/parts/home-modules/module-tabs.php | 15 +- templates/parts/resource-related.php | 33 +- 22 files changed, 362 insertions(+), 986 deletions(-) delete mode 100644 archive-aksrt_resource.php create mode 100644 archive-resources.php diff --git a/archive-aksrt_resource.php b/archive-aksrt_resource.php deleted file mode 100644 index 04abc9ce..00000000 --- a/archive-aksrt_resource.php +++ /dev/null @@ -1,191 +0,0 @@ - __('最新', '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 !== ''; -?> - -
-
-
- - - - -

-

- found_posts))); - ?> -

-
-
- -
-
-
- - - -
- -
- - - -
-

- -

- -
- -
-
-
-
- - __('最新', '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 !== ''; +?> + +
+
+
+ + + + +

+

+ found_posts))); + ?> +

+
+
+ +
+
+
+ + + +
+ +
+ + + +
+

+ +

+ +
+ +
+
+
+
+ + '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']], ]; } } diff --git a/inc/classes/class-aksrt-seo-head.php b/inc/classes/class-aksrt-seo-head.php index fa5c5c85..19bcdee8 100644 --- a/inc/classes/class-aksrt-seo-head.php +++ b/inc/classes/class-aksrt-seo-head.php @@ -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); diff --git a/inc/classes/class-aksrt-setup.php b/inc/classes/class-aksrt-setup.php index bfd10fa9..d56cf07d 100644 --- a/inc/classes/class-aksrt-setup.php +++ b/inc/classes/class-aksrt-setup.php @@ -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) diff --git a/inc/classes/scripts-traits/trait-aksrt-scripts-part-1b.php b/inc/classes/scripts-traits/trait-aksrt-scripts-part-1b.php index f5286d9d..1335afaf 100644 --- a/inc/classes/scripts-traits/trait-aksrt-scripts-part-1b.php +++ b/inc/classes/scripts-traits/trait-aksrt-scripts-part-1b.php @@ -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 diff --git a/inc/classes/widgets/class-aksrt-home-widgets.php b/inc/classes/widgets/class-aksrt-home-widgets.php index e8dcf6df..aaf55950 100644 --- a/inc/classes/widgets/class-aksrt-home-widgets.php +++ b/inc/classes/widgets/class-aksrt-home-widgets.php @@ -29,13 +29,7 @@ if (!function_exists('aksrt_home_widget_post_types')) { * @return array */ 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']; } } diff --git a/inc/core-hooks/part-2.php b/inc/core-hooks/part-2.php index c85eba67..070f9966 100644 --- a/inc/core-hooks/part-2.php +++ b/inc/core-hooks/part-2.php @@ -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; }); diff --git a/inc/core-hooks/part-3.php b/inc/core-hooks/part-3.php index a8105eb9..37b02c46 100644 --- a/inc/core-hooks/part-3.php +++ b/inc/core-hooks/part-3.php @@ -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; diff --git a/inc/helpers/aksrt-flow.php b/inc/helpers/aksrt-flow.php index e2ca0088..6d48d754 100644 --- a/inc/helpers/aksrt-flow.php +++ b/inc/helpers/aksrt-flow.php @@ -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); } } diff --git a/inc/helpers/aksrt-frontend-tags.php b/inc/helpers/aksrt-frontend-tags.php index 0623f5d3..6ca0e03b 100644 --- a/inc/helpers/aksrt-frontend-tags.php +++ b/inc/helpers/aksrt-frontend-tags.php @@ -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')) { diff --git a/inc/helpers/aksrt-homepage.php b/inc/helpers/aksrt-homepage.php index abf606bb..311b93db 100644 --- a/inc/helpers/aksrt-homepage.php +++ b/inc/helpers/aksrt-homepage.php @@ -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. diff --git a/inc/helpers/aksrt-resource.php b/inc/helpers/aksrt-resource.php index 58fdfaba..6311607d 100644 --- a/inc/helpers/aksrt-resource.php +++ b/inc/helpers/aksrt-resource.php @@ -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')) { /** * 从下载链接自动识别云盘服务商。 diff --git a/inc/helpers/aksrt-template-tags.php b/inc/helpers/aksrt-template-tags.php index 16be33d7..48ca5b26 100644 --- a/inc/helpers/aksrt-template-tags.php +++ b/inc/helpers/aksrt-template-tags.php @@ -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'); - } - ?> - - - - - -
- -
- - - - - -
-
- 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 ''; -} -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' => '', - 'after' => '', - )); - 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 - * 支持 标签和 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 ''; - - // 匹配 标签的 src - if (preg_match('/]+src=["\x27"]([^"\x27"]+)["\x27"]/', $content, $m)) { - return esc_url_raw($m[1]); - } - - // 匹配 Gutenberg image 块的 url 属性 - if (preg_match('/