true]); } $ctx = [ 'source' => isset($_POST['source']) ? sanitize_text_field(wp_unslash($_POST['source'])) : 'frontend', 'event' => isset($_POST['event']) ? sanitize_text_field(wp_unslash($_POST['event'])) : 'unknown', 'page' => isset($_POST['page']) ? esc_url_raw(wp_unslash($_POST['page'])) : '', 'text' => isset($_POST['text']) ? sanitize_text_field(wp_unslash($_POST['text'])) : '', 'size' => isset($_POST['size']) ? (int) wp_unslash($_POST['size']) : 0, 'error' => isset($_POST['error']) ? sanitize_text_field(wp_unslash($_POST['error'])) : '', ]; $this->debug_log('frontend debug event', $ctx); wp_send_json_success(['logged' => true]); } /** * Passive event listener polyfill — 全局加载,消除 Chrome Violation 警告 */ public function enqueue_passive_polyfill(): void { wp_enqueue_script( 'aksrt-passive-events', get_template_directory_uri() . '/assets/js/passive-events.js', [], AKSRT_VERSION, false ); } /** * 加载后台资源 — 仅在 AKSRT 设置页面加载(与前端样式完全隔离) */ public function enqueue_admin($hook): void { // 只在 AKSRT 设置页面 (SPA 容器) 加载 $screen = get_current_screen(); if (!$screen || $screen->base !== 'toplevel_page_aksrt-settings') { return; } // Admin 专用面板样式 — 独立于前端,不受前端 CSS 影响 wp_enqueue_style( 'aksrt-admin-panel', AKSRT_URI . '/assets/css/admin/admin-panel.css', [], // 无依赖,不加载任何前端样式 AKSRT_VERSION ); // 注意: SPA 主资源 (Vue3 build) 由 AKSRT_Admin::enqueue_admin_assets() 负责加载 } /** * 输出运行时 CSS 变量覆盖 * * 仅注入用户可在后台配置的覆盖值(logo / favicon / primary), * 主体宽度统一使用固定 token:1280 / 1080 / 300。 */ public function inline_css_vars(): void { (new AKSRT_Inline_Style_Builder())->output(); } /** * 输出自定义头部/底部代码 */ public function output_custom_codes(): void { (new AKSRT_Custom_Code_Renderer())->output_header(); } public function output_loading_overlay(): void { (new AKSRT_Loading_Overlay_Renderer())->output(); } /** * 输出自定义底部代码 */ public function output_custom_footer_codes(): void { (new AKSRT_Custom_Code_Renderer())->output_footer(); } /** * Body 类增强 */ public function body_classes(array $classes): array { if (is_front_page()) { $classes[] = 'front-page'; } if (function_exists('aksrt_is_fixed_homepage_request') && aksrt_is_fixed_homepage_request()) { $classes[] = 'aksrt-homepage-layout'; $classes[] = 'aksrt-homepage-active'; } if (is_singular()) { $classes[] = 'single-post-' . get_the_ID(); } if (is_archive()) { $classes[] = 'archive-page'; } if ((function_exists('aksrt_is_user_center_page') && aksrt_is_user_center_page()) || (function_exists('aksrt_is_user_home_page') && aksrt_is_user_home_page()) || get_query_var('user_nicename')) { $classes[] = 'aksrt-profile'; } // 区分本人中心与公开主页的 body class if (function_exists('aksrt_is_user_center_page') && aksrt_is_user_center_page()) { $classes[] = 'aksrt-user-center'; } if (function_exists('aksrt_is_user_home_page') && aksrt_is_user_home_page()) { $classes[] = 'aksrt-user-home'; } if (function_exists('aksrt_is_auth_page_request') && aksrt_is_auth_page_request()) { $classes[] = 'auth-page'; } return $classes; } /** * 搜索历史同步(登录用户) */ public function handle_search_history(): void { check_ajax_referer('aksrt_public_nonce', 'nonce'); $user_id = get_current_user_id(); if (!$user_id) { wp_send_json_error(['message' => '未登录'], 401); } $mode = isset($_POST['mode']) ? sanitize_key(wp_unslash($_POST['mode'])) : 'get'; $meta_key = 'aksrt_search_history'; if ($mode === 'save') { $raw = isset($_POST['items']) ? wp_unslash($_POST['items']) : '[]'; $items = json_decode($raw, true); if (!is_array($items)) { $items = []; } $clean = []; foreach (array_slice($items, 0, 20) as $item) { if (!is_array($item) || empty($item['keyword'])) { continue; } $clean[] = [ 'keyword' => sanitize_text_field($item['keyword']), 'type' => sanitize_key($item['type'] ?? 'post'), ]; } update_user_meta($user_id, $meta_key, $clean); wp_send_json_success(['items' => $clean]); } $saved = get_user_meta($user_id, $meta_key, true); wp_send_json_success(['items' => is_array($saved) ? $saved : []]); } /** * 搜索建议(实时搜索提示) */ public function handle_search_suggestions(): void { check_ajax_referer('aksrt_public_nonce', 'nonce'); $q = isset($_POST['q']) ? sanitize_text_field(wp_unslash($_POST['q'])) : ''; $type = isset($_POST['type']) ? sanitize_key(wp_unslash($_POST['type'])) : 'post'; if (mb_strlen($q) < 2) { wp_send_json_success(['items' => []]); } $items = []; if ($type === 'post' || $type === '') { $posts = get_posts([ 'post_type' => 'post', 'post_status' => 'publish', 's' => $q, 'posts_per_page' => 6, 'no_found_rows' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, ]); foreach ($posts as $p) { $items[] = [ 'title' => $p->post_title, 'url' => get_permalink($p), 'meta' => get_the_date('', $p), ]; } } elseif ($type === 'category') { $terms = get_terms([ 'taxonomy' => 'category', 'search' => $q, 'number' => 6, 'hide_empty' => true, ]); if (!is_wp_error($terms)) { foreach ($terms as $t) { $items[] = [ 'title' => $t->name, 'url' => get_term_link($t), 'meta' => sprintf(__('%d 篇文章', 'aksrt'), $t->count), ]; } } } elseif ($type === 'tag') { $terms = get_terms([ 'taxonomy' => 'post_tag', 'search' => $q, 'number' => 6, 'hide_empty' => true, ]); if (!is_wp_error($terms)) { foreach ($terms as $t) { $items[] = [ 'title' => $t->name, 'url' => get_term_link($t), 'meta' => sprintf(__('%d 篇文章', 'aksrt'), $t->count), ]; } } } wp_send_json_success(['items' => $items]); } private function debug_log(string $message, array $context = []): void { if (!defined('WP_DEBUG') || !WP_DEBUG) { return; } $payload = '[AKSRT Scripts] ' . $message; if (!empty($context)) { $json = wp_json_encode($context, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); if (false !== $json) { $payload .= ' ' . $json; } } error_log($payload); } /** * Resolve public route URLs from the current canonical page-template ownership. * * @return array */ private function resolve_frontend_routes(): array { return [ 'messages' => $this->resolve_template_page_url('templates/pages/page-messages.php', '/messages/'), 'cart' => $this->resolve_template_page_url('templates/pages/page-cart.php', '/cart/'), 'orders' => $this->resolve_template_page_url('templates/pages/page-orders.php', '/orders/'), 'download' => $this->resolve_template_page_url('templates/pages/page-download.php', '/download/'), 'resources' => home_url('/resources/'), ]; } /** * Resolve the permalink for a template-owned page with a stable fallback. */ private function resolve_template_page_url(string $template_file, string $fallback): string { if (function_exists('aksrt_find_template_page')) { $page = aksrt_find_template_page($template_file); if ($page instanceof WP_Post) { return get_permalink($page); } } return home_url($fallback); } }