From d4a9b7fd98ea21a8a68dfe4b2c679f625aa2b151 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 19 Aug 2026 13:35:31 +0800 Subject: [PATCH] fix(page-display): connect header component visibility settings --- assets/admin/src/components/form/AksField.vue | 4 - inc/helpers/aksrt-header-menu-buttons.php | 9 +- inc/helpers/homepage/header-components.php | 34 ++++--- src/Admin/PageConfigMetaBox.php | 7 +- src/Admin/Schema/SchemaPageDisplay.php | 13 ++- src/Settings/DisplayPageGroups.php | 7 +- .../page-display-contract.test.cjs | 24 ++++- .../page-display-save-harness.php | 88 +++++++++++++++++++ 8 files changed, 152 insertions(+), 34 deletions(-) diff --git a/assets/admin/src/components/form/AksField.vue b/assets/admin/src/components/form/AksField.vue index f6871835..ffb665f5 100644 --- a/assets/admin/src/components/form/AksField.vue +++ b/assets/admin/src/components/form/AksField.vue @@ -718,10 +718,6 @@ const asyncLoading = ref(false) let asyncTimer = null async function loadAsyncOptions(query = '') { - if (props.schema.source === 'publish-types') { - asyncOptions.value = normalizedOptions.value - return - } asyncLoading.value = true try { const selected = Array.isArray(props.modelValue) ? props.modelValue : (props.modelValue ? [props.modelValue] : []) diff --git a/inc/helpers/aksrt-header-menu-buttons.php b/inc/helpers/aksrt-header-menu-buttons.php index 170df3bc..4207adb1 100644 --- a/inc/helpers/aksrt-header-menu-buttons.php +++ b/inc/helpers/aksrt-header-menu-buttons.php @@ -142,18 +142,11 @@ if (!function_exists('aksrt_render_header_menu_buttons')) : */ function aksrt_render_header_menu_buttons(): string { $settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::read_domain('page_display', 'header-nav') : []; - $types = is_array($settings['nav_newposts_btns'] ?? null) - ? array_values(array_intersect(array_map('sanitize_key', $settings['nav_newposts_btns']), ['post'])) - : ['post']; - if (!$types) { - $types = ['post']; - } $cfg = [ 'enabled' => !isset($settings['nav_newposts']) || !empty($settings['nav_newposts']), 'text' => trim((string) ($settings['nav_newposts_text'] ?? __('发布', 'aksrt'))), 'radius_pill' => !isset($settings['nav_newposts_radius']) || !empty($settings['nav_newposts_radius']), 'class' => sanitize_html_class((string) (($settings['nav_newposts_class'] ?? '') ?: 'jb-blue')), - 'types' => $types, ]; $html = ''; if (!empty($cfg['enabled'])) { @@ -169,7 +162,7 @@ if (!function_exists('aksrt_render_header_menu_buttons')) : $class .= ' radius'; } $label = (string) ($cfg['text'] ?? __('发布', 'aksrt')); - $btn = aksrt_render_new_add_btns($cfg['types'] ?? ['post'], $class, $label); + $btn = aksrt_render_new_add_btns(['post'], $class, $label); if ($btn !== '') { $html .= $btn; } diff --git a/inc/helpers/homepage/header-components.php b/inc/helpers/homepage/header-components.php index d8e9f735..3e120107 100644 --- a/inc/helpers/homepage/header-components.php +++ b/inc/helpers/homepage/header-components.php @@ -58,8 +58,11 @@ if (!function_exists('aksrt_header_slide_matches_query')) { return is_category(); case 'tag': return is_tag(); + case 'user_home': case 'author': - return is_author(); + return is_author() || (function_exists('aksrt_is_user_home_page') && aksrt_is_user_home_page()); + case 'user_center': + return function_exists('aksrt_is_user_center_page') && aksrt_is_user_center_page(); case 'single': return is_singular('post'); case 'search': @@ -68,6 +71,10 @@ if (!function_exists('aksrt_header_slide_matches_query')) { if (!is_page()) { return false; } + if ((function_exists('aksrt_is_user_home_page') && aksrt_is_user_home_page()) + || (function_exists('aksrt_is_user_center_page') && aksrt_is_user_center_page())) { + return false; + } foreach (aksrt_header_slide_excluded_page_templates() as $template) { if (is_page_template($template)) { return false; @@ -92,13 +99,6 @@ if (!function_exists('aksrt_header_slide_is_show')) { return false; } - $page_config = aksrt_get_page_header_slider_config(); - if ($page_config) { - $show_type = sanitize_key((string) ($page_config['header_slider_show_type'] ?? '')); - return !($show_type === 'only_pc' && wp_is_mobile()) - && !($show_type === 'only_sm' && !wp_is_mobile()); - } - $settings = class_exists('\\AKSRT\\Settings\\Settings') ? \AKSRT\Settings\Settings::get_header_slider_settings() : []; @@ -107,13 +107,27 @@ if (!function_exists('aksrt_header_slide_is_show')) { return false; } + $matches_global_scope = false; foreach ((array) ($settings['show_on_pages'] ?? []) as $type) { if (aksrt_header_slide_matches_query((string) $type)) { - return true; + $matches_global_scope = true; + break; } } + if (!$matches_global_scope) { + return false; + } - return false; + // Per-page data may customize an already-enabled component, but it must + // never bypass the global page scope selected in the theme settings. + $page_config = aksrt_get_page_header_slider_config(); + if ($page_config) { + $page_show_type = sanitize_key((string) ($page_config['header_slider_show_type'] ?? '')); + return !($page_show_type === 'only_pc' && wp_is_mobile()) + && !($page_show_type === 'only_sm' && !wp_is_mobile()); + } + + return true; } } diff --git a/src/Admin/PageConfigMetaBox.php b/src/Admin/PageConfigMetaBox.php index 8650defa..d818284d 100644 --- a/src/Admin/PageConfigMetaBox.php +++ b/src/Admin/PageConfigMetaBox.php @@ -50,8 +50,11 @@ final class PageConfigMetaBox { @media(max-width:782px){.aksrt-page-config-grid{grid-template-columns:1fr}.aksrt-page-config-wide{grid-column:auto}}
- -
+ +
+ +

+
diff --git a/src/Admin/Schema/SchemaPageDisplay.php b/src/Admin/Schema/SchemaPageDisplay.php index a3846c3a..7dfd088e 100644 --- a/src/Admin/Schema/SchemaPageDisplay.php +++ b/src/Admin/Schema/SchemaPageDisplay.php @@ -54,7 +54,6 @@ class SchemaPageDisplay { self::field('nav_newposts_radius', '按钮圆角', 'switcher', true, ['dependsOn' => self::enabled('nav_newposts')]), self::field('nav_newposts_class', '按钮颜色', 'palette', 'jb-blue', ['dependsOn' => self::enabled('nav_newposts'), 'options' => self::zib_palette()]), self::field('nav_newposts_text', '按钮文案', 'textarea', '发布', ['dependsOn' => self::enabled('nav_newposts'), 'sanitize_type' => 'html', 'rows' => 1]), - self::field('nav_newposts_btns', '需显示的按钮', 'async_select', ['post'], ['dependsOn' => self::enabled('nav_newposts'), 'multiple' => true, 'sortable' => true, 'source' => 'publish-types', 'options' => self::options(['post' => '文章'])]), self::field('nav_user_newposts_btn', '导航栏用户卡片发布按钮', 'select', 'post', ['options' => self::options(['post' => '文章'])]), self::field('nav_user_newposts_btn_text', '按钮文字', 'text', '发布文章'), ]); @@ -63,16 +62,16 @@ class SchemaPageDisplay { private static function header_components(): array { return self::section('header-components', '顶部多功能组件', [ self::notice('顶部多功能组件显示在导航栏下方,支持视频、图片、幻灯片、搜索和图标卡片;各组件可按电脑端、移动端单独启用。'), - self::field('header_slider_show', '启用顶部多功能组件', 'checkbox', [], ['options' => self::options(['home' => '首页', 'category' => '分类页', 'tag' => '标签页', 'author' => '用户页', 'single' => '文章页', 'search' => '搜索页', 'page' => '其它页面'])]), + self::field('header_slider_show', '启用顶部多功能组件', 'checkbox', [], ['description' => '选择允许显示组件的页面;未选择任何页面时,前端不会显示。', 'options' => self::options(['home' => '首页', 'category' => '分类页', 'tag' => '标签页', 'author' => '公开用户主页', 'user_center' => '用户中心', 'single' => '文章页', 'search' => '搜索页', 'page' => '其它页面'])]), self::field('header_slider_show_type', '顶部多功能组件的显示规则', 'radio', '', ['dependsOn' => self::not_empty('header_slider_show'), 'inline' => true, 'options' => self::options(['' => '全部显示', 'only_pc' => '仅在PC端显示', 'only_sm' => '仅在移动端显示'])]), self::field('header_slider_nav_color', '导航栏文字颜色', 'color', '#ffffff', ['dependsOn' => self::not_empty('header_slider_show')]), self::field('header_slider', '背景内容', 'group', self::header_slider_defaults(), ['dependsOn' => self::not_empty('header_slider_show'), 'min' => 1, 'button_title' => '添加背景项目', 'fields' => self::slider_item_fields()]), self::field('header_slider_option', '背景显示设置', 'fieldset', self::slider_options_defaults(), ['dependsOn' => self::not_empty('header_slider_show'), 'fields' => self::slider_option_fields()]), - self::field('header_slider_search_s', '叠加搜索组件', 'checkbox', ['pc_s', 'm_s'], ['options' => self::device_options()]), - self::field('header_slider_search_option', '搜索组件设置', 'fieldset', self::search_defaults(), ['dependsOn' => self::not_empty('header_slider_search_s'), 'fields' => self::search_fields()]), - self::field('header_slider_card_s', '叠加封面卡片组件', 'checkbox', ['pc_s', 'm_s'], ['options' => self::device_options()]), - self::field('header_slider_card_option', '图标卡片设置', 'fieldset', self::card_defaults(), ['dependsOn' => self::not_empty('header_slider_card_s'), 'fields' => self::card_fields()]), - self::field('header_slider_filter_blur', '组件高斯模糊特效', 'checkbox', ['pc_s', 'm_s'], ['options' => self::options(['pc_s' => 'PC端开启', 'm_s' => '移动端开启'])]), + self::field('header_slider_search_s', '叠加搜索组件', 'checkbox', ['pc_s', 'm_s'], ['dependsOn' => self::not_empty('header_slider_show'), 'options' => self::device_options()]), + self::field('header_slider_search_option', '搜索组件设置', 'fieldset', self::search_defaults(), ['dependsOn' => ['all' => [self::not_empty('header_slider_show'), self::not_empty('header_slider_search_s')]], 'fields' => self::search_fields()]), + self::field('header_slider_card_s', '叠加封面卡片组件', 'checkbox', ['pc_s', 'm_s'], ['dependsOn' => self::not_empty('header_slider_show'), 'options' => self::device_options()]), + self::field('header_slider_card_option', '图标卡片设置', 'fieldset', self::card_defaults(), ['dependsOn' => ['all' => [self::not_empty('header_slider_show'), self::not_empty('header_slider_card_s')]], 'fields' => self::card_fields()]), + self::field('header_slider_filter_blur', '组件高斯模糊特效', 'checkbox', ['pc_s', 'm_s'], ['dependsOn' => self::not_empty('header_slider_show'), 'options' => self::options(['pc_s' => 'PC端开启', 'm_s' => '移动端开启'])]), ]); } diff --git a/src/Settings/DisplayPageGroups.php b/src/Settings/DisplayPageGroups.php index 49e5e93c..1601de50 100644 --- a/src/Settings/DisplayPageGroups.php +++ b/src/Settings/DisplayPageGroups.php @@ -56,13 +56,18 @@ trait DisplayPageGroups { 'category', 'tag', 'author', + 'user_center', 'single', 'search', 'page', ]; $show_on = isset($s['header_slider_show']) ? (array) $s['header_slider_show'] : []; + $show_on = array_map(static function ($type): string { + $type = sanitize_key((string) $type); + return $type === 'user_home' ? 'author' : $type; + }, $show_on); $show_on = array_values(array_intersect( - array_map('sanitize_key', $show_on), + $show_on, $allowed_page_types )); $show_type = $s['header_slider_show_type'] ?? ''; diff --git a/tests/widget-system/page-display-contract.test.cjs b/tests/widget-system/page-display-contract.test.cjs index cc0ed986..f5ed833c 100644 --- a/tests/widget-system/page-display-contract.test.cjs +++ b/tests/widget-system/page-display-contract.test.cjs @@ -14,6 +14,7 @@ const provider = read('src/Admin/SchemaProvider.php') const settingsFacade = read('src/Settings/Settings.php') const scriptsPart1c = read('src/Core/ScriptsPart1c.php') const footerCss = read('assets/css/frontend/footer/zibll-shell.css') +const headerMenuButtons = read('inc/helpers/aksrt-header-menu-buttons.php') function activeRepositorySource() { const roots = ['assets/admin/src', 'assets/js', 'inc', 'src', 'templates'] @@ -54,7 +55,7 @@ test('top-level field IDs match the Zibll V9.0 eight-page contract', () => { assert.deepEqual(topLevelFieldIds('header_nav'), [ 'header_theme_custom', 'header_theme_bg_custom', 'header_theme_bg', 'header_theme_color_custom', 'header_layout', 'mobile_header_layout', 'mobile_navbar_align', 'nav_fixed', 'nav_scroll_down_hide', - 'nav_newposts', 'nav_newposts_radius', 'nav_newposts_class', 'nav_newposts_text', 'nav_newposts_btns', + 'nav_newposts', 'nav_newposts_radius', 'nav_newposts_class', 'nav_newposts_text', 'nav_user_newposts_btn', 'nav_user_newposts_btn_text', ]) assert.deepEqual(topLevelFieldIds('header_components'), [ @@ -90,10 +91,17 @@ test('defaults, nested structures, dependencies, and excluded product families a assert.match(schema, /'author_main_tab_opt'.*?'post'.*?'favorite'.*?'comment'.*?'follow'/s) assert.match(schema, /'home_posts_list_s', '首页文章', 'switcher', true/) assert.match(schema, /'page_header_style', '页面标题', 'radio', ''/) + assert.match(schema, /'author' => '公开用户主页'/) + assert.match(schema, /'user_center' => '用户中心'/) + assert.match(schema, /'header_slider_search_s'.*?'dependsOn' => self::not_empty\('header_slider_show'\)/) + assert.match(schema, /'header_slider_card_s'.*?'dependsOn' => self::not_empty\('header_slider_show'\)/) + assert.match(schema, /'header_slider_filter_blur'.*?'dependsOn' => self::not_empty\('header_slider_show'\)/) assert.match(schema, /'all' => \[self::enabled\('home_posts_list_s'\), self::enabled\('home_list1_orderby_s'\)\]/) - for (const dead of ['nav_pay_vip', 'nav_user_pay_vip', 'forum_home', 'forum_plate', 'forum_post', 'ajax_list_cat_topics', 'ajax_list_tag_topics']) { + for (const dead of ['nav_newposts_btns', 'nav_pay_vip', 'nav_user_pay_vip', 'forum_home', 'forum_plate', 'forum_post', 'ajax_list_cat_topics', 'ajax_list_tag_topics']) { assert.equal(schema.includes(dead), false, `legacy/excluded field remains: ${dead}`) } + assert.equal(headerMenuButtons.includes('nav_newposts_btns'), false) + assert.match(headerMenuButtons, /aksrt_render_new_add_btns\(\['post'\], \$class, \$label\)/) }) test('schema REST saves are privileged, section-whitelisted, and deeply sanitized', () => { @@ -206,4 +214,16 @@ test('save contract rejects invalid input and round-trips canonical nested value }) assert.equal(data.footer_readback.fcode_t1_code, 'Allowed') assert.deepEqual(data.footer_readback.footer_mini_img, data.footer_clean.footer_mini_img) + assert.deepEqual(data.visibility, { + disabled: false, + disabled_with_page_override: false, + page_enabled: true, + public_user_home: true, + user_center: true, + legacy_author_public_home: true, + generic_page_excludes_home: false, + generic_page_excludes_center: false, + templated_public_user_home: true, + templated_user_center: true, + }) }) diff --git a/tests/widget-system/page-display-save-harness.php b/tests/widget-system/page-display-save-harness.php index 8e1a0f18..0d88bb2d 100644 --- a/tests/widget-system/page-display-save-harness.php +++ b/tests/widget-system/page-display-save-harness.php @@ -7,6 +7,25 @@ namespace { define('ABSPATH', __DIR__); $GLOBALS['aksrt_test_options'] = []; $GLOBALS['aksrt_test_manage_options'] = false; + $GLOBALS['aksrt_visibility_query'] = ''; + $GLOBALS['aksrt_visibility_page_config'] = []; + + final class WP_Query { + /** @var array */ + private array $vars; + + /** @param array $vars */ + public function __construct(array $vars = []) { + $this->vars = $vars; + } + + /** @return mixed */ + public function get(string $key) { + return $this->vars[$key] ?? null; + } + } + + $GLOBALS['wp_query'] = new WP_Query(); function sanitize_key($value): string { return preg_replace('/[^a-z0-9_\-]/', '', strtolower((string) $value)); @@ -66,11 +85,47 @@ namespace { function current_user_can($capability): bool { return $capability === 'manage_options' && !empty($GLOBALS['aksrt_test_manage_options']); } + + function is_admin(): bool { return false; } + function wp_is_mobile(): bool { return false; } + function is_home(): bool { return $GLOBALS['aksrt_visibility_query'] === 'home'; } + function is_front_page(): bool { return $GLOBALS['aksrt_visibility_query'] === 'home'; } + function is_category(): bool { return $GLOBALS['aksrt_visibility_query'] === 'category'; } + function is_tag(): bool { return $GLOBALS['aksrt_visibility_query'] === 'tag'; } + function is_author(): bool { return $GLOBALS['aksrt_visibility_query'] === 'author'; } + function is_singular(string $type = ''): bool { return $type === 'post' && $GLOBALS['aksrt_visibility_query'] === 'single'; } + function is_search(): bool { return $GLOBALS['aksrt_visibility_query'] === 'search'; } + function is_page(): bool { + return in_array($GLOBALS['aksrt_visibility_query'], ['page', 'user_home_page', 'user_center_page'], true); + } + function is_page_template(string $template): bool { return false; } + function get_queried_object_id(): int { return 42; } + + /** @return mixed */ + function get_post_meta(int $post_id, string $key, bool $single = false) { + return $GLOBALS['aksrt_visibility_page_config']; + } + + function aksrt_is_user_home_page(): bool { + return in_array($GLOBALS['aksrt_visibility_query'], ['user_home', 'user_home_page'], true); + } + + function aksrt_is_user_center_page(): bool { + return in_array($GLOBALS['aksrt_visibility_query'], ['user_center', 'user_center_page'], true); + } } namespace AKSRT\Settings { class Settings { + /** @var array */ + public static array $headerSlider = []; + public static function flush_settings_cache(): void {} + + /** @return array */ + public static function get_header_slider_settings(): array { + return self::$headerSlider; + } } } @@ -78,6 +133,7 @@ namespace { require_once dirname(__DIR__, 2) . '/src/Admin/Schema/SchemaPageDisplay.php'; require_once dirname(__DIR__, 2) . '/src/Admin/SchemaProvider.php'; require_once dirname(__DIR__, 2) . '/src/Admin/ControllerBase.php'; + require_once dirname(__DIR__, 2) . '/inc/helpers/homepage/header-components.php'; $controller = new class extends \AKSRT\Admin\ControllerBase { public function register_routes(): void {} @@ -129,6 +185,37 @@ namespace { $category_clean = $provider->sanitize_payload('page_display', $category_payload, 'category'); $category_validation = $provider->validate('page_display', $category_clean, 'category'); + /** + * @param list $show_on + * @param array $page_config + */ + function aksrt_visibility_case(string $query, array $show_on, array $page_config = []): bool { + $GLOBALS['aksrt_visibility_query'] = $query; + $GLOBALS['aksrt_visibility_page_config'] = $page_config; + \AKSRT\Settings\Settings::$headerSlider = [ + 'show_on_pages' => $show_on, + 'show_type' => '', + ]; + return aksrt_header_slide_is_show(); + } + + $enabled_page_override = [ + 'header_slider_show' => true, + 'header_slider_show_type' => '', + ]; + $visibility = [ + 'disabled' => aksrt_visibility_case('home', []), + 'disabled_with_page_override' => aksrt_visibility_case('page', [], $enabled_page_override), + 'page_enabled' => aksrt_visibility_case('page', ['page'], $enabled_page_override), + 'public_user_home' => aksrt_visibility_case('user_home', ['user_home']), + 'user_center' => aksrt_visibility_case('user_center', ['user_center']), + 'legacy_author_public_home' => aksrt_visibility_case('user_home', ['author']), + 'generic_page_excludes_home' => aksrt_visibility_case('user_home_page', ['page']), + 'generic_page_excludes_center' => aksrt_visibility_case('user_center_page', ['page']), + 'templated_public_user_home' => aksrt_visibility_case('user_home_page', ['user_home']), + 'templated_user_center' => aksrt_visibility_case('user_center_page', ['user_center']), + ]; + echo json_encode([ 'permission_denied' => $permission_denied, 'permission_allowed' => $permission_allowed, @@ -141,5 +228,6 @@ namespace { 'footer_valid' => $footer_validation['valid'], 'footer_clean' => $footer_clean, 'footer_readback' => $footer_readback, + 'visibility' => $visibility, ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); }