From fbb53c689064cb79193c7fc2065a38081d4a3af4 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Nov 2026 23:51:33 +0800 Subject: [PATCH] feat(categories): align archive and directory pages Co-Authored-By: Claude Opus 5 (1M context) --- assets/css/routes/categories.css | 373 +++++++++--------- category.php | 42 +- inc/helpers/jio-archive-filters.php | 10 +- inc/helpers/jio-single-components.php | 37 +- src/Core/ScriptsPart1b.php | 3 +- templates/pages/page-categories.php | 235 ++++++----- .../category-pages-contract.test.cjs | 58 +++ 7 files changed, 443 insertions(+), 315 deletions(-) create mode 100644 tests/widget-system/category-pages-contract.test.cjs diff --git a/assets/css/routes/categories.css b/assets/css/routes/categories.css index d330a786..059e62a8 100644 --- a/assets/css/routes/categories.css +++ b/assets/css/routes/categories.css @@ -1,224 +1,217 @@ -/** - * Jio - Categories Index Route Stylesheet - * - * 分类汇总页面 (templates/pages/page-categories.php) 专用样式: - * 卡片网格、封面图、文章计数徽章、子分类胶囊条、页面顶部统计与底部返回。 - * - * Tokens: 颜色 / 间距 / 圆角 / 阴影 全部来自 core/tokens.css - */ - -/* ================================================ - * 页面头:标题 + 计数 - * ================================================ */ -.jio-cats-header { - margin-bottom: var(--space-7); +/* Category directory */ +.jio-category-directory-nav { + margin-bottom: 20px; + padding: 14px 16px; } -.jio-cats-header__meta { - margin-top: var(--space-2); - color: var(--color-ink-4); - font-size: 13px; -} - -/* ================================================ - * 网格容器 - * ================================================ */ -.jio-cats-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); - gap: var(--space-5); - margin-bottom: var(--space-8); -} - -/* ================================================ - * 卡片 - * ================================================ */ -.jio-cat-card { - display: flex; - flex-direction: column; - background: var(--surface-page); - border: 1px solid var(--color-line); - border-radius: var(--radius-lg); - overflow: hidden; - box-shadow: var(--shadow-sm); - transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease; -} - -.jio-cat-card:hover { - transform: translateY(-3px); - box-shadow: var(--shadow-md); - border-color: var(--color-primary); -} - -/* 封面图 */ -.jio-cat-card__cover { - position: relative; +.jio-category-directory-nav__label { display: block; - aspect-ratio: 16 / 9; - background-color: var(--surface-3); - background-position: center; - background-size: cover; - background-repeat: no-repeat; - text-decoration: none; -} - -.jio-cat-card__cover::after { - content: ""; - position: absolute; - inset: 0; - background: linear-gradient( - 180deg, - rgba(20, 28, 48, 0) 40%, - rgba(20, 28, 48, 0.45) 100% - ); - pointer-events: none; -} - -/* 计数徽章 */ -.jio-cat-card__count-badge { - position: absolute; - top: var(--space-3); - right: var(--space-3); - z-index: 1; - display: inline-flex; - align-items: center; - padding: 4px 10px; - background: var(--color-primary); - color: #fff; - font-size: 12px; + margin-bottom: 8px; font-weight: 600; - letter-spacing: 0.02em; - border-radius: var(--radius-pill); - box-shadow: var(--shadow-sm); } -/* 卡片正文 */ -.jio-cat-card__body { - display: flex; - flex-direction: column; - gap: var(--space-3); - padding: var(--space-5); - flex: 1; -} - -.jio-cat-card__title { - margin: 0; - font-size: 18px; - font-weight: 600; - line-height: 1.4; -} - -.jio-cat-card__title a { - color: var(--color-ink); - text-decoration: none; - transition: color 0.15s ease; -} - -.jio-cat-card__title a:hover { - color: var(--color-primary); -} - -.jio-cat-card__desc { - margin: 0; - color: var(--color-ink-3); - font-family: var(--font-body); - font-size: 14px; - line-height: 1.6; - display: -webkit-box; - -webkit-line-clamp: 3; - -webkit-box-orient: vertical; - overflow: hidden; -} - -/* ================================================ - * 子分类胶囊条 - * ================================================ */ -.jio-cat-card__children { +.jio-category-directory-nav ul, +.jio-category-directory__children { margin: 0; padding: 0; list-style: none; +} + +.jio-category-directory-nav ul { display: flex; flex-wrap: wrap; - gap: 6px; - margin-top: auto; - padding-top: var(--space-3); - border-top: 1px dashed var(--color-line); + gap: 6px 14px; } -.jio-cat-card__children li { - margin: 0; - padding: 0; -} - -.jio-cat-card__children a { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 4px 10px; - background: var(--surface-2); - border: 1px solid var(--color-line); - border-radius: var(--radius-pill); - color: var(--color-ink-3); - font-size: 12px; - line-height: 1.4; +.jio-category-directory-nav a, +.jio-category-directory__child-main a { + color: var(--key-color, var(--main-color, inherit)); text-decoration: none; - transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease; } -.jio-cat-card__children a:hover { - background: var(--color-primary-soft); - border-color: var(--color-primary); - color: var(--color-ink); +.jio-category-directory-nav a:hover, +.jio-category-directory__child-main a:hover, +.jio-category-directory__intro h2 a:hover { + color: var(--focus-color, #ff5473); } -.jio-cat-card__child-count { +.jio-category-directory { + margin-bottom: 20px; +} + +.jio-category-directory__section { + margin-bottom: 20px; + padding: 15px; + border-radius: var(--main-radius, 8px); + background: var(--main-bg-color, #fff); + box-shadow: 0 0 10px var(--main-shadow, rgba(116, 116, 116, .08)); + scroll-margin-top: 90px; +} + +.jio-category-directory__summary { + display: flex; + align-items: flex-start; + gap: 15px; + margin-bottom: 14px; +} + +.jio-category-directory__cover { + flex: 0 0 180px; + width: 180px; + height: 100px; + overflow: hidden; + border-radius: var(--main-radius, 8px); + background: var(--muted-border-color, #f2f3f5); +} + +.jio-category-directory__cover img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; + transition: transform .25s ease; +} + +.jio-category-directory__cover:hover img { + transform: scale(1.04); +} + +.jio-category-directory__intro { + min-width: 0; + flex: 1; +} + +.jio-category-directory__intro h2 { + display: inline; + margin: 0; + font-size: 1.15em; +} + +.jio-category-directory__intro h2 a { + color: var(--key-color, var(--main-color, inherit)); + text-decoration: none; +} + +.jio-category-directory__count { + display: inline-block; + margin-left: 8px; + color: var(--muted-2-color, #999); + font-size: .88em; + white-space: nowrap; +} + +.jio-category-directory__intro p, +.jio-category-directory__child-desc { + margin: 6px 0 0; + color: var(--muted-color, #777); + font-size: .92em; + line-height: 1.55; +} + +.jio-category-directory__intro p { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; +} + +.jio-category-directory__more { display: inline-flex; align-items: center; - justify-content: center; - min-width: 18px; - height: 18px; - padding: 0 5px; - background: var(--surface-3); - border-radius: var(--radius-pill); - color: var(--color-ink-4); - font-size: 11px; + gap: 5px; + margin-top: 8px; + color: var(--focus-color, #ff5473); + font-size: .9em; + text-decoration: none; +} + +.jio-category-directory__children { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + padding-top: 14px; + border-top: 1px solid var(--main-border-color, rgba(127, 127, 127, .13)); +} + +.jio-category-directory__child { + min-width: 0; + padding: 10px 12px; + border-radius: var(--main-radius, 8px); + background: var(--muted-border-color, rgba(127, 127, 127, .06)); +} + +.jio-category-directory__child-main { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; font-weight: 600; } -/* ================================================ - * 空状态 - * ================================================ */ -.jio-cats-empty { - padding: var(--space-9) var(--space-5); - text-align: center; - color: var(--color-ink-3); - background: var(--surface-2); - border: 1px dashed var(--color-line); - border-radius: var(--radius-lg); +.jio-category-directory__child-main a { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } -/* ================================================ - * 底部返回 - * ================================================ */ -.jio-cats-footer { - text-align: center; - margin-top: var(--space-8); +.jio-category-directory__child .jio-category-directory__count { + flex: 0 0 auto; + margin-left: 0; + font-weight: 400; } -/* ================================================ - * 响应式 - * ================================================ */ -@media (max-width: 640px) { - .jio-cats-grid { +.jio-category-directory__child-desc { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; +} + +.jio-category-directory__child > .jio-category-directory__children { + grid-template-columns: 1fr; + gap: 6px; + margin-top: 10px; + padding-top: 10px; +} + +.jio-category-directory__child > .jio-category-directory__children > li { + padding: 7px 9px; + background: var(--main-bg-color, rgba(255, 255, 255, .6)); +} + +.jio-categories-empty { + padding: 28px 18px; + border: 1px dashed var(--main-border-color, rgba(127, 127, 127, .25)); + border-radius: var(--main-radius, 8px); + color: var(--muted-color, #8a8c98); + text-align: center; +} + +@media (max-width: 767px) { + .jio-category-directory__section { + padding: 12px; + } + + .jio-category-directory__cover { + flex-basis: 110px; + width: 110px; + height: 78px; + } + + .jio-category-directory__children { grid-template-columns: 1fr; - gap: var(--space-4); } +} - .jio-cat-card__body { - padding: var(--space-4); +@media (max-width: 420px) { + .jio-category-directory__summary { + gap: 10px; } - .jio-cat-card__title { - font-size: 17px; + .jio-category-directory__cover { + flex-basis: 88px; + width: 88px; + height: 72px; } } diff --git a/category.php b/category.php index 9985ad3b..b56b26ab 100644 --- a/category.php +++ b/category.php @@ -10,24 +10,6 @@ defined('ABSPATH') || exit; get_header(); $term = get_queried_object(); -if (class_exists('\Jio\Core\DocsPresenter')) { - $docs_presenter = \Jio\Core\DocsPresenter::get_instance(); - if ($term instanceof WP_Term && $term->taxonomy === 'category') { - $doc_candidates = get_posts([ - 'post_type' => 'page', - 'post_status' => 'publish', - 'posts_per_page' => 1, - 'meta_key' => '_jio_doc_category', - 'meta_value' => (string) $term->term_id, - 'fields' => 'ids', - ]); - if (!empty($doc_candidates)) { - get_template_part('templates/pages/page-documentnav'); - get_footer(); - return; - } - } -} if (!$term instanceof WP_Term) { get_footer(); return; @@ -35,7 +17,10 @@ if (!$term instanceof WP_Term) { $archive_settings = \Jio\Core\TermMeta::resolve_archive_settings($term); $has_sidebar = jio_archive_has_sidebar(!empty($archive_settings['show_sidebar'])); -$ajax_filter = !empty($archive_settings['filter_bar']); +$access = function_exists('jio_get_term_allow_view_data') + ? jio_get_term_allow_view_data((int) $term->term_id) + : ['allow' => true, 'not_html' => '']; +$ajax_filter = !empty($access['allow']) && !empty($archive_settings['filter_bar']); $sort_state = jio_archive_get_sort_state((array) ($archive_settings['orderby_option'] ?? [])); $filter_groups = []; foreach (['category' => __('分类', 'jio'), 'post_tag' => __('标签', 'jio')] as $taxonomy => $label) { @@ -66,7 +51,7 @@ jio_archive_render_term_header([ 'post_count' => !empty($archive_settings['post_count']), ]); ob_start(); -if ($ajax_filter) { +if (!empty($access['allow']) && $ajax_filter) { jio_archive_render_filter_bar([ 'aria_label' => __('分类筛选', 'jio'), 'groups' => $filter_groups, 'sort_enabled' => !empty($archive_settings['orderby_enabled']), @@ -76,11 +61,18 @@ if ($ajax_filter) { ]); } $filter_html = (string) ob_get_clean(); -jio_archive_render_stream(null, jio_archive_get_render_config(['context' => 'category']), [ - 'page_kind' => 'category', 'query_context' => 'archive', - 'empty_text' => __('暂无内容', 'jio'), - 'filter_html' => $filter_html, -]); +if (!empty($access['allow'])) { + jio_archive_render_stream(null, jio_archive_get_render_config(['context' => 'category']), [ + 'page_kind' => 'category', 'query_context' => 'archive', + 'empty_text' => __('暂无内容', 'jio'), + 'filter_html' => $filter_html, + ]); +} else { + echo '
'; + echo function_exists('jio_get_null') ? jio_get_null(__('抱歉!您暂无查看此内容的权限', 'jio'), 20, 'null-cap.svg', '') : ''; + echo (string) ($access['not_html'] ?? ''); + echo '
'; +} jio_archive_render_widget_zone(['cat_bottom_content']); jio_archive_close_shell(['has_sidebar' => $has_sidebar, 'bottom_fluid' => ['cat_bottom_fluid']]); get_footer(); diff --git a/inc/helpers/jio-archive-filters.php b/inc/helpers/jio-archive-filters.php index ab4eef79..e94be46b 100644 --- a/inc/helpers/jio-archive-filters.php +++ b/inc/helpers/jio-archive-filters.php @@ -31,7 +31,15 @@ if (!function_exists('jio_archive_render_term_header')) : $description = esc_html(trim(wp_strip_all_tags((string) $args['description']))); $more = ''; if ($term instanceof WP_Term) { - $more = '
'; + $edit_url = !$is_tag && current_user_can('edit_term', (int) $term->term_id) + ? get_edit_term_link((int) $term->term_id, $term->taxonomy) + : false; + $edit_label = __('编辑此分类', 'jio'); + $edit_button = $edit_url + ? '' . esc_html($edit_label) . '' + : ''; + $more = '
' . $edit_button + . '
'; } if (empty($args['show_cover'])) { echo '

' . $title . '

' . $description . '
' . $more . '
'; diff --git a/inc/helpers/jio-single-components.php b/inc/helpers/jio-single-components.php index c587d3a6..b87c81ec 100644 --- a/inc/helpers/jio-single-components.php +++ b/inc/helpers/jio-single-components.php @@ -363,12 +363,41 @@ if (!function_exists('jio_get_term_allow_view_rule')) : } endif; +if (!function_exists('jio_get_term_allow_view_data')) : + /** Resolve reading permission for a category archive. */ + function jio_get_term_allow_view_data(int $term_id): array { + $data = [ + 'allow' => true, + 'type' => '', + 'not_html' => '', + 'allow_reason' => '', + 'html' => '', + ]; + if ($term_id <= 0 || current_user_can('manage_options')) { + return $data; + } + + $rule = jio_get_term_allow_view_rule($term_id); + if (empty($rule['type']) || $rule['type'] === 'public') { + return $data; + } + $link = get_term_link($term_id, 'category'); + $redirect_url = is_wp_error($link) ? home_url('/') : (string) $link; + return jio_resolve_allow_view_rule( + (string) $rule['type'], + (array) ($rule['roles'] ?? []), + null, + $redirect_url + ); + } +endif; + if (!function_exists('jio_resolve_allow_view_rule')) : /** * @param array $roles Role constraints for type=roles. * @return array{allow:bool,type:string,not_html:string,allow_reason:string,html:string} */ - function jio_resolve_allow_view_rule(string $type, array $roles, WP_Post $post): array { + function jio_resolve_allow_view_rule(string $type, array $roles, ?WP_Post $post = null, string $redirect_url = ''): array { $type = sanitize_key($type); if (in_array($type, ['login', 'signin'], true)) { $type = 'signin'; @@ -383,7 +412,11 @@ if (!function_exists('jio_resolve_allow_view_rule')) : ]; $user_id = get_current_user_id(); - $login_url = function_exists('jio_get_login_url') ? jio_get_login_url() : wp_login_url(get_permalink($post)); + if ($redirect_url !== '') { + $login_url = function_exists('jio_get_login_url') ? jio_get_login_url($redirect_url) : wp_login_url($redirect_url); + } else { + $login_url = function_exists('jio_get_login_url') ? jio_get_login_url() : wp_login_url($post instanceof WP_Post ? get_permalink($post) : ''); + } if ($type === 'signin') { $data['html'] = '

' . esc_html__('登录后可查看', 'jio') . '

'; diff --git a/src/Core/ScriptsPart1b.php b/src/Core/ScriptsPart1b.php index b378f6cd..f96e3bd1 100644 --- a/src/Core/ScriptsPart1b.php +++ b/src/Core/ScriptsPart1b.php @@ -528,11 +528,12 @@ trait ScriptsPart1b { // 分类汇总页:路由专属 CSS if (is_page_template('templates/pages/page-categories.php')) { + $categories_css = get_template_directory() . '/assets/css/routes/categories.css'; wp_enqueue_style( 'jio-route-categories', $base_uri . '/assets/css/routes/categories.css', ['jio-public'], - $version + file_exists($categories_css) ? (string) filemtime($categories_css) : $version ); } diff --git a/templates/pages/page-categories.php b/templates/pages/page-categories.php index bc8930e9..2cbf8fa9 100644 --- a/templates/pages/page-categories.php +++ b/templates/pages/page-categories.php @@ -3,137 +3,180 @@ * Template Name: Jio - Categories * Template Post Type: page * - * 分类汇总页面:列出全站所有分类。 - * - * 用法:在后台新建一个 Page,标题任意(默认显示页面标题,否则用"分类目录"), - * 模板选择 "Jio - Categories";或将 slug 设为 "categories" 自动匹配。 + * Category directory page with recursive taxonomy navigation. * * @package Jio */ defined('ABSPATH') || exit; -get_header(); - -// ─── 数据准备 ───────────────────────────────────────────── +$post_id = (int) get_queried_object_id(); $all_terms = get_terms([ 'taxonomy' => 'category', - 'hide_empty' => false, // 全部展示,含 0 篇文章的分类 - 'orderby' => 'menu_order name', + 'hide_empty' => false, + 'orderby' => 'term_group', 'order' => 'ASC', ]); if (is_wp_error($all_terms)) { $all_terms = []; } -$top_level = []; +$top_level = []; $children_by_parent = []; foreach ($all_terms as $term) { - if ((int) $term->parent === 0) { + $parent_id = (int) $term->parent; + if ($parent_id === 0) { $top_level[] = $term; } else { - $children_by_parent[(int) $term->parent][] = $term; + $children_by_parent[$parent_id][] = $term; } } -$total_cats = count($all_terms); -$total_counts = wp_count_posts(); -$total_posts = $total_counts && isset($total_counts->publish) ? (int) $total_counts->publish : 0; - -$page_title = trim((string) get_the_title()); -if ($page_title === '') { - $page_title = __('分类目录', 'jio'); +$total_posts = wp_count_posts('post'); +$total_posts = $total_posts && isset($total_posts->publish) ? (int) $total_posts->publish : 0; +$page_title = trim((string) get_the_title($post_id)); +$page_title = $page_title !== '' ? $page_title : __('分类目录', 'jio'); +$has_sidebar = function_exists('jio_archive_has_sidebar') + ? jio_archive_has_sidebar() + : (function_exists('jio_is_show_sidebar') ? jio_is_show_sidebar() : false); +$content_style = sanitize_key((string) get_post_meta($post_id, 'page_content_style', true)); +if (!in_array($content_style, ['', 'not', 'nobox', 'full'], true)) { + $content_style = ''; } -?> +$content_style_class = $content_style !== '' ? ' page-content-' . $content_style : ' page-content-default'; + +$render_children = static function (int $parent_id, array $path) use (&$render_children, $children_by_parent): string { + $children = $children_by_parent[$parent_id] ?? []; + if ($children === []) { + return ''; + } + + $html = '
    '; + foreach ($children as $child) { + $child_id = (int) $child->term_id; + if (in_array($child_id, $path, true)) { + continue; + } + $link = get_category_link($child_id); + if (is_wp_error($link)) { + continue; + } + $description = class_exists('\\Jio\\Settings\\Settings') + ? \Jio\Settings\Settings::get_category_recommend_desc($child_id) + : (string) $child->description; + $description = trim(wp_strip_all_tags((string) $description)); + $html .= '
  • '; + $html .= '
    ' . esc_html($child->name) . ''; + $html .= '' . (int) $child->count . '
    '; + if ($description !== '') { + $html .= '

    ' . esc_html(wp_trim_words($description, 18, '…')) . '

    '; + } + $nested = $render_children($child_id, array_merge($path, [$child_id])); + if ($nested !== '') { + $html .= $nested; + } + $html .= '
  • '; + } + $html .= '
'; + return $html; +}; + +get_header(); - 'site-main container ', - 'has_sidebar' => false, + 'main_class' => 'site-main container' . $content_style_class, + 'has_sidebar' => $has_sidebar, + 'top_fluid' => ['page_top_fluid_' . $post_id, 'cat_top_fluid'], 'page_kind' => 'categories', ]); } ?>
- - esc_html($page_title), - 'desc' => $cats_meta, - ]); - ?> - - -
- term_id; - $cat_link = get_category_link($cat_id); - $cover_url = class_exists('\Jio\Settings\Settings') - ? \Jio\Settings\Settings::get_category_cover_url($cat_id) - : (get_template_directory_uri() . '/assets/img/placeholder.svg'); - $desc = class_exists('\Jio\Settings\Settings') - ? \Jio\Settings\Settings::get_category_recommend_desc($cat_id) - : (string) $cat->description; - $children = $children_by_parent[$cat_id] ?? []; - ?> -
- - - count); ?> - - - -
-

- name); ?> -

- - -

+ + - - + + + esc_html($page_title), + 'desc' => sprintf( + /* translators: 1: total category count, 2: total published post count */ + esc_html__('共 %1$d 个分类 · 共 %2$d 篇文章', 'jio'), + count($all_terms), + $total_posts + ), + ]); + if ($content_style !== 'not' && trim((string) get_the_content()) !== '') { + echo '
'; + the_content(); + wp_link_pages([ + 'before' => '

', + 'after' => '

', + ]); + echo '
'; + } + ?> + + + + +
+ term_id; + $link = get_category_link($term_id); + if (is_wp_error($link)) { + continue; + } + $cover = function_exists('jio_widget_term_cover_url') + ? jio_widget_term_cover_url($term_id, true) + : get_template_directory_uri() . '/assets/img/placeholder.svg'; + $description = class_exists('\Jio\Settings\Settings') + ? \Jio\Settings\Settings::get_category_recommend_desc($term_id) + : (string) $term->description; + $description = trim(wp_strip_all_tags((string) $description)); + ?> + + +
+ +

+ + + + +
-
- -
- -
-

-
- - - - -
false]); + jio_archive_close_shell([ + 'has_sidebar' => $has_sidebar, + 'bottom_fluid' => ['cat_bottom_fluid', 'page_bottom_fluid_' . $post_id], + ]); } - get_footer(); diff --git a/tests/widget-system/category-pages-contract.test.cjs b/tests/widget-system/category-pages-contract.test.cjs new file mode 100644 index 00000000..54762afc --- /dev/null +++ b/tests/widget-system/category-pages-contract.test.cjs @@ -0,0 +1,58 @@ +const test = require('node:test') +const assert = require('node:assert/strict') +const fs = require('node:fs') +const path = require('node:path') + +const root = path.resolve(__dirname, '..', '..') +const read = (file) => fs.readFileSync(path.join(root, file), 'utf8') + +test('category archives use the canonical stream and enforce category view rules', () => { + const archive = read('category.php') + const permissions = read('inc/helpers/jio-single-components.php') + const header = read('inc/helpers/jio-archive-filters.php') + + assert.doesNotMatch(archive, /_jio_doc_category|page-documentnav|DocsPresenter/) + assert.match(archive, /TermMeta::resolve_archive_settings\(\$term\)/) + assert.match(archive, /jio_get_term_allow_view_data\(\(int\) \$term->term_id\)/) + assert.match(archive, /if \(!empty\(\$access\['allow'\]\)\) \{[\s\S]*?jio_archive_render_stream/) + assert.match(archive, /content-stream--category posts-row ajaxpager/) + assert.match(archive, /jio_archive_render_term_header\(/) + assert.match(archive, /cat_top_content/) + assert.match(archive, /cat_bottom_content/) + + assert.match(permissions, /function jio_get_term_allow_view_data\(int \$term_id\)/) + assert.match(permissions, /jio_resolve_allow_view_rule\([\s\S]*?\$redirect_url/) + assert.match(permissions, /function jio_resolve_allow_view_rule\(string \$type, array \$roles, \?WP_Post \$post = null, string \$redirect_url = ''\)/) + assert.match(header, /current_user_can\('edit_term', \(int\) \$term->term_id\)/) + assert.match(header, /class="main-search-btn" search-type="post" search-trem=/) +}) + +test('category directory lists all terms recursively with archive links and page widget zones', () => { + const template = read('templates/pages/page-categories.php') + const css = read('assets/css/routes/categories.css') + const scripts = read('src/Core/ScriptsPart1b.php') + + assert.match(template, /'taxonomy'\s*=>\s*'category'[\s\S]*?'hide_empty'\s*=>\s*false/) + assert.match(template, /'orderby'\s*=>\s*'term_group'/) + assert.match(template, /\$children_by_parent\[\$parent_id\]\[\]/) + assert.match(template, /\$render_children\(\$child_id, array_merge\(\$path, \[\$child_id\]\)\)/) + assert.match(template, /in_array\(\$child_id, \$path, true\)/) + assert.match(template, /get_category_link\(\$child_id\)/) + assert.match(template, /jio_widget_term_cover_url\(\$term_id, true\)/) + assert.match(template, /jio_get_lazy_attr\('lazy_other'/) + assert.doesNotMatch(template, /loading=["'](?:lazy|eager)["']/) + assert.match(template, /get_category_recommend_desc\(\$term_id\)/) + assert.match(template, /page_top_content_/) + assert.match(template, /page_bottom_content_/) + assert.match(template, /page_top_fluid_/) + assert.match(template, /page_bottom_fluid_/) + assert.match(template, /cat_top_fluid/) + assert.match(template, /cat_bottom_fluid/) + assert.match(scripts, /is_page_template\('templates\/pages\/page-categories\.php'\)/) + + assert.match(css, /\.jio-category-directory-nav/) + assert.match(css, /\.jio-category-directory__section/) + assert.match(css, /\.jio-category-directory__children/) + assert.match(css, /@media \(max-width: 767px\)/) + assert.doesNotMatch(template + css, /jio-cats-grid|jio-cat-card/) +})