Files
wordpress-theme-aksrt/inc/helpers/search/jio-search-box.php
T
kate522andClaude Opus 5 f80ca1e27d fix(widgets): restore single-card reference UI for search and quote widgets
The 搜索框 and 一言 sidebar widgets fell into the generic renderer branch, so
they rendered a .jio-panel card wrapping a second bespoke card
(.jio-widget-search / .jio-widget-quote). In-article 一言 also used
divergent modifier classes and an extra hover rule.

- search: render through the shared jio_get_search_box() (legacy keyword,
  history and category pickers) inside one reference card, mirroring the
  reference widget's markup and title default 「搜索」
- quote: render the reference .yiyan-box card itself as the click root; the
  shell now emits only the reference widget-container/title wrapper
- article 一言: adopt the reference class lists for header, footer and the
  standalone after-article box; drop the invented yiyan--* modifiers and
  the duplicate hover/opacity rules
- client: fill .cn/.en in the reference order (first line .cn, second .en)
  and drop the stale window.JioYiyan bridge plus the dead qv-yiyan.php AJAX
  path in runtime main.js
- schema: replace the ad-hoc search_style radio with the reference switches
  (keywords, history, input category, more_cats) and default title
- assets: enqueue assets/js/yiyan.js through the quote widget's asset list

Tests: widget-system suite 820/821 (the one failure is the pre-existing
frontend-lifecycle theme-init stub); new search/quote card contracts pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-11-03 15:56:52 +08:00

390 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Search box markup (legacy parity).
*
* Used by layout-2 menu_search AJAX panel and layout 1/3 .navbar-search.
*
* @package Jio
*/
defined('ABSPATH') || exit;
if (!function_exists('jio_save_history_search')) :
/** Store the legacy history_search cookie (newest first, maximum thirty). */
function jio_save_history_search(string $search): void {
$search = trim(wp_strip_all_tags($search));
$search_text = jio_get_search_keywords_text($search);
if ($search_text === '' || mb_strlen($search_text) >= 30 || headers_sent()) {
return;
}
$history = jio_get_search_history_keywords();
$history = array_values(array_filter($history, static function (string $item) use ($search_text): bool {
return jio_get_search_keywords_text($item) !== $search_text;
}));
array_unshift($history, $search);
$history = array_slice($history, 0, 30);
setcookie('history_search', wp_json_encode($history), [
'expires' => time() + MONTH_IN_SECONDS,
'path' => COOKIEPATH ?: '/',
'domain' => COOKIE_DOMAIN,
'secure' => is_ssl(),
'httponly' => false,
'samesite' => 'Lax',
]);
$_COOKIE['history_search'] = wp_json_encode($history);
}
endif;
if (!function_exists('jio_get_main_search')) :
/** legacy main-search defaults backed only by the reference option IDs. */
function jio_get_main_search(array $args = [], bool $echo = false) {
$settings = class_exists('\Jio\Settings\Settings') ? \Jio\Settings\Settings::get_all() : [];
$search_cat = !array_key_exists('search_cat', $settings) || !empty($settings['search_cat']);
$search_type = !array_key_exists('search_type', $settings) || !empty($settings['search_type']);
$defaults = [
'class' => '',
'show_keywords' => !array_key_exists('search_popular_key', $settings) || !empty($settings['search_popular_key']),
'show_history' => !array_key_exists('search_history', $settings) || !empty($settings['search_history']),
'show_posts' => !array_key_exists('search_posts', $settings) || !empty($settings['search_posts']),
'show_input_cat' => $search_cat,
'more_cats' => $search_cat ? (array) ($settings['search_more_cat_obj'] ?? []) : [],
'in_cat' => $search_cat ? ($settings['search_cat_in'] ?? '') : '',
'show_type' => $search_type,
'in_type' => $search_type ? sanitize_key((string) ($settings['search_type_in'] ?? 'post')) : '',
'in_user' => '',
];
return jio_get_search_box(wp_parse_args($args, $defaults), $echo);
}
endif;
if (!function_exists('jio_get_search_history_keywords')) :
/**
* @return list<string>
*/
function jio_get_search_history_keywords(): array {
$raw = isset($_COOKIE['history_search'])
? wp_unslash((string) $_COOKIE['history_search'])
: '';
$decoded = $raw !== '' ? json_decode(stripslashes($raw), true) : [];
if (!is_array($decoded)) {
return [];
}
$hist = array_map(
static fn($item): string => sanitize_text_field((string) $item),
array_slice($decoded, 0, 30)
);
return array_values(array_unique(array_filter(array_map('trim', $hist))));
}
endif;
if (!function_exists('jio_get_search_keywords_text')) :
function jio_get_search_keywords_text(string $search): string {
$search = str_replace('&amp;type', '&type', $search);
$index = strpos($search, '&type=');
return trim($index === false ? $search : substr($search, 0, $index));
}
endif;
if (!function_exists('jio_update_search_keywords')) :
function jio_update_search_keywords(string $search): void {
$search = trim(wp_strip_all_tags($search));
$text = jio_get_search_keywords_text($search);
if (mb_strlen($text) < 2 || mb_strlen($text) > 8) {
return;
}
if (class_exists('\Jio\Settings\Settings') && !\Jio\Settings\Settings::hot_search_enabled()) {
return;
}
$keywords = get_option('search_keywords', []);
$keywords = is_array($keywords) ? $keywords : [];
$limit = class_exists('\Jio\Settings\Settings')
? max(10, min(50, (int) \Jio\Settings\Settings::get_value('search_popular_key_num', 20)))
: 20;
$keywords = array_slice($keywords, 0, $limit + 10, true);
$keywords[$search] = isset($keywords[$search]) ? (int) $keywords[$search] + 1 : 1;
arsort($keywords, SORT_NUMERIC);
update_option('search_keywords', $keywords, false);
}
endif;
if (!function_exists('jio_get_search_keywords_but')) :
/**
* Render keyword chip links.
*
* @param list<string> $keywords
* @param string $type popular|history
*/
function jio_get_search_keywords_but(array $keywords, string $type = 'popular', int $limit = 0): string {
if ($limit > 0) {
$keywords = array_slice($keywords, 0, $limit);
}
$out = '';
foreach ($keywords as $kw) {
$kw = trim((string) $kw);
if ($kw === '') {
continue;
}
$text = jio_get_search_keywords_text($kw);
$query = ['s' => $text];
if (preg_match('/&type=([a-z0-9_-]+)/i', $kw, $match)) {
$query['type'] = sanitize_key($match[1]);
}
$out .= '<a class="search_keywords muted-2-color but em09 mr6 mb6" data-search-keyword="' . esc_attr($kw) . '" href="'
. esc_url(add_query_arg($query, home_url('/'))) . '">'
. esc_html($text) . '</a> ';
}
return $out;
}
endif;
if (!function_exists('jio_get_search_box')) :
/**
* legacy-compatible search box.
*
* @param array<string,mixed> $args
* @param bool $echo
* @return string|void
*/
function jio_get_search_box(array $args = [], bool $echo = false) {
$defaults = [
's' => '',
'class' => '',
'show_form' => true,
'show_keywords' => true,
'popular_limit' => 0,
'show_history' => true,
'keywords_title' => '',
'placeholder' => '',
'show_input_cat' => true,
'show_posts' => false,
'show_type' => false,
'in_cat' => '',
'in_type' => 'post',
'in_user' => '',
'more_cats' => [],
];
$args = wp_parse_args($args, $defaults);
if ($args['keywords_title'] === '' && class_exists('\Jio\Settings\Settings')
&& method_exists('\Jio\Settings\Settings', 'search_popular_title')) {
$args['keywords_title'] = (string) \Jio\Settings\Settings::search_popular_title();
}
if ($args['keywords_title'] === '') {
$args['keywords_title'] = __('热门搜索', 'jio');
}
if ($args['placeholder'] === '' && function_exists('jio_get_search_placeholder')) {
$args['placeholder'] = jio_get_search_placeholder();
}
if ($args['placeholder'] === '') {
$args['placeholder'] = __('开启精彩搜索', 'jio');
}
$args['placeholder'] = esc_attr((string) $args['placeholder']);
$form_html = '';
if (!empty($args['show_form'])) {
$state = function_exists('jio_get_search_form_state')
? jio_get_search_form_state()
: [
'type' => 'post',
'types' => ['post' => __('文章', 'jio')],
'category_id' => 0,
'category_enabled' => false,
'categories' => [],
];
$cat_html = '';
$category_terms = [];
if (!empty($args['show_input_cat'])) {
$category_ids = is_array($args['more_cats'])
? $args['more_cats']
: (preg_split('/[,,\s]+/', (string) $args['more_cats']) ?: []);
if ($category_ids && function_exists('jio_get_search_categories')) {
$category_terms = jio_get_search_categories($category_ids);
}
if ((int) $args['in_cat'] > 0) {
$selected_term = get_term((int) $args['in_cat']);
if ($selected_term && !is_wp_error($selected_term)) {
$selected_term_name = (string) $selected_term->name;
if (!$category_terms) {
$category_terms = [$selected_term];
}
}
}
}
if ($category_terms) {
$in_cat_id = (int) $args['in_cat'];
$in_cat_name = $in_cat_id > 0 ? (string) ($selected_term_name ?? '') : '';
$all_cat = '<li data-for="trem" data-value="null"><a href="javascript:;" class="text-ellipsis">'
. esc_html__('全部', 'jio') . '</a></li>';
foreach ($category_terms as $cat) {
if (!$cat instanceof WP_Term) {
continue;
}
$cid = (int) $cat->term_id;
$cname = (string) $cat->name;
if ($cid <= 0 || $cname === '') {
continue;
}
if ($cid === $in_cat_id) {
$in_cat_name = $cname;
}
$all_cat .= '<li data-for="trem" data-value="' . esc_attr((string) $cid) . '"><a href="javascript:;" class="text-ellipsis">'
. esc_html(mb_strlen($cname) > 8 ? mb_substr($cname, 0, 8) . '…' : $cname)
. '</a></li>';
}
$all_cat = '<ul class="dropdown-menu cat-drop">' . $all_cat . '</ul>';
$label = $in_cat_name !== ''
? (mb_strlen($in_cat_name) > 6 ? mb_substr($in_cat_name, 0, 6) . '…' : $in_cat_name)
: __('请选择', 'jio');
$input_cat = '<a href="javascript:;" class="padding-h10" data-toggle="dropdown"><span name="trem">'
. esc_html($label) . '</span><i class="fa ml6 fa-sort opacity5" aria-hidden="true"></i></a>';
$input_cat .= $all_cat;
$cat_html = '<div class="option-dropdown splitters-this-r search-drop"><div class="dropdown">'
. $input_cat . '</div></div>';
}
$type_html = '';
$type_dropdown_html = '';
$types = is_array($state['types'] ?? null) ? $state['types'] : [];
$in_type = (string) ($args['in_type'] ?: ($state['type'] ?? 'post'));
if (!empty($args['show_type']) && count($types) > 1) {
$type_name = (string) ($types[$in_type] ?? reset($types));
if ($cat_html !== '') {
$type_items = '';
foreach ($types as $key => $label) {
$cls = $in_type === (string) $key ? ' active' : '';
$type_items .= '<a class="but' . esc_attr($cls) . '" data-for="type" data-value="'
. esc_attr((string) $key) . '" href="javascript:;">' . esc_html((string) $label) . '</a>';
}
$type_html = '<div class="flex jc mb10 search-type-but-average"><div class="but-average radius">'
. $type_items . '</div></div>';
} else {
$type_items = '';
foreach ($types as $key => $label) {
$type_items .= '<li><a href="javascript:;" class="text-ellipsis" data-for="type" data-value="'
. esc_attr((string) $key) . '">' . esc_html((string) $label) . '</a></li>';
}
$type_dropdown_html = '<div class="option-dropdown splitters-this-r search-drop"><div class="dropdown">'
. '<a href="javascript:;" class="padding-h10" data-toggle="dropdown"><span name="type">'
. esc_html($type_name) . '</span><i class="fa ml6 fa-sort opacity5" aria-hidden="true"></i></a>'
. '<ul class="dropdown-menu">' . $type_items . '</ul></div></div>';
}
}
$s = esc_attr(wp_strip_all_tags((string) $args['s']));
$input_html = '<div class="search-input-text">';
$input_html .= '<input type="text" name="s" class="line-form-input" tabindex="1" value="' . $s . '">';
$input_html .= '<i class="line-form-line"></i>';
$input_html .= '<div class="scale-placeholder' . ($s !== '' ? ' is-focus' : '') . '" default="'
. $args['placeholder'] . '">' . $args['placeholder'] . '</div>';
$input_html .= '<div class="abs-right muted-color"><button type="submit" tabindex="2" class="null" aria-label="'
. esc_attr__('搜索', 'jio') . '"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-search"></use></svg></button></div>';
$input_html .= '</div>';
$input_html .= '<input type="hidden" name="type" value="' . esc_attr($in_type) . '">';
if ($cat_html !== '' || (string) $args['in_cat'] !== '') {
$input_html .= '<input type="hidden" name="trem" value="'
. esc_attr((string) $args['in_cat']) . '">';
}
if ((string) $args['in_user'] !== '') {
$input_html .= '<input type="hidden" name="user" value="' . esc_attr((string) $args['in_user']) . '">';
}
$form_html = '<form method="get" class="padding-10 search-form" action="'
. esc_url(home_url('/')) . '">'
. $type_html
. '<div class="line-form">' . ($cat_html !== '' ? $cat_html : $type_dropdown_html) . $input_html . '</div>'
. '</form>';
}
// Popular keywords.
$keywords_html = '';
if (!empty($args['show_keywords'])) {
$keywords = function_exists('jio_get_search_hot_keywords') ? jio_get_search_hot_keywords() : [];
$limit = (int) $args['popular_limit'];
$keyword_link = jio_get_search_keywords_but(is_array($keywords) ? $keywords : [], 'popular', $limit);
if ($keyword_link !== '') {
$keywords_html = '<div class="search-keywords">'
. '<p class="muted-color"><span>' . esc_html((string) $args['keywords_title']) . '</span></p>'
. '<div>' . $keyword_link . '</div>'
. '</div>';
}
}
// History.
$history_html = '';
if (!empty($args['show_history'])) {
$hist = jio_get_search_history_keywords();
$keyword_link = jio_get_search_keywords_but($hist, 'history');
if ($keyword_link !== '') {
$history_html = '<div class="search-keywords history-search">';
$history_html .= '<p class="muted-color"><span>' . esc_html__('历史搜索', 'jio') . '</span>';
$history_html .= '<a class="pull-right trash-history-search muted-3-color" href="javascript:;" data-cookie-path="'
. esc_attr(COOKIEPATH ?: '/') . '" data-cookie-domain="' . esc_attr(COOKIE_DOMAIN) . '" aria-label="'
. esc_attr__('清空历史', 'jio') . '"><i class="fa fas fa-trash-alt em12" aria-hidden="true"></i></a></p>';
$history_html .= '<div>' . $keyword_link . '</div></div>';
}
}
// Hot posts (thumb swiper).
$posts_html = '';
if (!empty($args['show_posts']) && function_exists('jio_get_search_posts')) {
$slides = jio_get_search_posts(6, 'views', true);
if ($slides !== '') {
$posts_html = '<div class="padding-10 relates relates-thumb">';
$posts_html .= '<p class="muted-color">' . esc_html__('热门文章', 'jio') . '</p>';
$posts_html .= '<div class="swiper-container swiper-scroll">';
$posts_html .= '<div class="swiper-wrapper">' . $slides . '</div>';
$posts_html .= '<div class="swiper-button-prev"></div><div class="swiper-button-next"></div>';
$posts_html .= '</div></div>';
}
}
$class_tokens = preg_split('/\s+/', trim((string) $args['class'])) ?: [];
$class_tokens = array_values(array_filter(array_map('sanitize_html_class', $class_tokens)));
$class = $class_tokens ? ' ' . implode(' ', $class_tokens) : '';
$html = '<div class="search-input' . esc_attr($class) . '">'
. $form_html . $keywords_html . $history_html . $posts_html
. '</div>';
if ($echo) {
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return;
}
return $html;
}
endif;
if (!function_exists('jio_get_remote_box')) :
/**
* Lazy remote HTML mount (legacy).
*
* @param array<string,mixed> $args
*/
function jio_get_remote_box(array $args = []): string {
$defaults = [
'type' => 'ias',
'class' => '',
'loader' => '<i class="loading-spot"><i></i></i>',
'url' => admin_url('admin-ajax.php'),
'query' => false,
];
$args = wp_parse_args($args, $defaults);
$url = (string) $args['url'];
if (!empty($args['query']) && is_array($args['query'])) {
$url = add_query_arg($args['query'], $url);
}
$attr = ' remote-box="' . esc_url($url) . '"';
$class = (string) $args['class'];
if (($args['type'] ?? '') === 'ias') {
$attr .= ' lazyload-action="ias"';
$class .= ' lazyload';
}
if (($args['type'] ?? '') === 'load') {
$attr .= ' load-click';
}
$class_attr = $class !== '' ? ' class="' . esc_attr(trim($class)) . '"' : '';
return '<div' . $class_attr . $attr . '>' . $args['loader'] . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
endif;