Files
feadulta/mu-plugins/fea-search-advanced.php
T

633 lines
33 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
/**
* Plugin Name: Fe Adulta — Buscador avanzado (#8)
* Description: Replica el «Buscador avanzado» K2 de Joomla con WordPress nativo.
* Filtros: palabra (FULLTEXT vía fea-search-fulltext.php), autor, tema
* (categoría), cita bíblica (_cita_evangelio), fecha.
* Formulario visible en la página de resultados (search template) y en
* la página dedicada /buscar. Multiidioma (Polylang).
* Version: 1.0
*/
if (!defined('ABSPATH')) exit;
// ─────────────────────────────────────────────────────────────────
// Constantes de configuración
// ─────────────────────────────────────────────────────────────────
/**
* IDs de usuarios a excluir del selector de autores.
* 1,890,1049,1540 = cuentas técnicas/admin.
* 408,409,1563..1570 = pseudo-autores "Nuevo/Antiguo Testamento" (y sus traducciones)
* que NO son personas. Además, abajo se excluye cualquier display_name que contenga
* "Testament" de forma robusta (por si aparecen nuevos IDs).
*/
defined('FEA_AUTORES_EXCLUIR') or define('FEA_AUTORES_EXCLUIR', [
1, 890, 1049, 1540,
408, 409, 1563, 1564, 1565, 1566, 1567, 1568, 1569, 1570,
]);
/**
* Categorías de ESTADO DE CARTA a excluir del selector de categorías:
* 6 (cartasemana), 21 (cartas-de-otras-semanas), 22 (carta-semana-pasada).
*/
defined('FEA_CATS_CARTA_EXCLUIR') or define('FEA_CATS_CARTA_EXCLUIR', [6, 21, 22]);
// ─────────────────────────────────────────────────────────────────
// i18n mínimo (es / en / fr / it / pt)
// ─────────────────────────────────────────────────────────────────
function fea_adv_t(string $key): string {
$lang = function_exists('pll_current_language') ? pll_current_language() : 'es';
$strings = [
'search_advanced' => ['es' => 'Búsqueda avanzada', 'en' => 'Advanced search',
'fr' => 'Recherche avancée', 'it' => 'Ricerca avanzata', 'pt' => 'Pesquisa avançada'],
'word' => ['es' => 'Palabra o frase', 'en' => 'Word or phrase',
'fr' => 'Mot ou phrase', 'it' => 'Parola o frase', 'pt' => 'Palavra ou frase'],
'author' => ['es' => 'Autor', 'en' => 'Author',
'fr' => 'Auteur', 'it' => 'Autore', 'pt' => 'Autor'],
'all_authors' => ['es' => '— Cualquier autor —', 'en' => '— Any author —',
'fr' => '— Tout auteur —', 'it' => '— Qualsiasi autore —', 'pt' => '— Qualquer autor —'],
'topic' => ['es' => 'Categoría', 'en' => 'Category',
'fr' => 'Catégorie', 'it' => 'Categoria', 'pt' => 'Categoria'],
'all_topics' => ['es' => '— Cualquier categoría —', 'en' => '— Any category —',
'fr' => '— Toute catégorie —', 'it' => '— Qualsiasi categoria —', 'pt' => '— Qualquer categoria —'],
'biblical_ref' => ['es' => 'Cita bíblica', 'en' => 'Biblical reference',
'fr' => 'Référence biblique', 'it' => 'Citazione biblica', 'pt' => 'Referência bíblica'],
'biblical_ph' => ['es' => 'Ej: Jn 3', 'en' => 'E.g. Jn 3',
'fr' => 'Ex: Jn 3', 'it' => 'Es: Gv 3', 'pt' => 'Ex: Jo 3'],
'date_from' => ['es' => 'Desde', 'en' => 'From',
'fr' => 'Du', 'it' => 'Dal', 'pt' => 'De'],
'date_to' => ['es' => 'Hasta', 'en' => 'To',
'fr' => "Jusqu'au", 'it' => 'Al', 'pt' => 'Até'],
'search_btn' => ['es' => 'Buscar', 'en' => 'Search',
'fr' => 'Rechercher', 'it' => 'Cerca', 'pt' => 'Pesquisar'],
'reset_btn' => ['es' => 'Limpiar', 'en' => 'Clear',
'fr' => 'Effacer', 'it' => 'Cancella', 'pt' => 'Limpar'],
'results' => ['es' => 'resultado(s)', 'en' => 'result(s)',
'fr' => 'résultat(s)', 'it' => 'risultato/i', 'pt' => 'resultado(s)'],
'no_results' => ['es' => 'Sin resultados. Prueba con otros términos.',
'en' => 'No results. Try other terms.',
'fr' => 'Aucun résultat. Essayez d\'autres termes.',
'it' => 'Nessun risultato. Prova con altri termini.',
'pt' => 'Sem resultados. Tente outros termos.'],
'active_filters' => ['es' => 'Filtros activos:', 'en' => 'Active filters:',
'fr' => 'Filtres actifs:', 'it' => 'Filtri attivi:', 'pt' => 'Filtros ativos:'],
'filter_author' => ['es' => 'Autor', 'en' => 'Author',
'fr' => 'Auteur', 'it' => 'Autore', 'pt' => 'Autor'],
'filter_topic' => ['es' => 'Categoría', 'en' => 'Category',
'fr' => 'Catégorie', 'it' => 'Categoria', 'pt' => 'Categoria'],
'filter_cita' => ['es' => 'Cita', 'en' => 'Ref.',
'fr' => 'Réf.', 'it' => 'Cit.', 'pt' => 'Ref.'],
'filter_date' => ['es' => 'Fecha', 'en' => 'Date',
'fr' => 'Date', 'it' => 'Data', 'pt' => 'Data'],
'by' => ['es' => 'por', 'en' => 'by',
'fr' => 'par', 'it' => 'di', 'pt' => 'por'],
];
$row = $strings[$key] ?? [];
return $row[$lang] ?? $row['es'] ?? $key;
}
// ─────────────────────────────────────────────────────────────────
// Query vars
// ─────────────────────────────────────────────────────────────────
add_filter('query_vars', function (array $vars): array {
$vars[] = 'fea_author';
$vars[] = 'fea_cat';
$vars[] = 'fea_cita';
$vars[] = 'fea_date_from';
$vars[] = 'fea_date_to';
return $vars;
});
// ─────────────────────────────────────────────────────────────────
// pre_get_posts — aplica los filtros avanzados
// ─────────────────────────────────────────────────────────────────
add_action('pre_get_posts', function (WP_Query $q): void {
if (is_admin() || !$q->is_main_query()) return;
// Leer los parámetros avanzados desde $_GET directamente (más fiable en pre_get_posts)
$fea_author = isset($_GET['fea_author']) ? (int)$_GET['fea_author'] : 0;
$fea_cat = isset($_GET['fea_cat']) ? (int)$_GET['fea_cat'] : 0;
$fea_cita = isset($_GET['fea_cita']) ? sanitize_text_field($_GET['fea_cita']) : '';
$fea_dfr = isset($_GET['fea_date_from']) ? sanitize_text_field($_GET['fea_date_from']) : '';
$fea_dto = isset($_GET['fea_date_to']) ? sanitize_text_field($_GET['fea_date_to']) : '';
$has_adv = ($fea_author > 0 || $fea_cat > 0 || $fea_cita !== '' || $fea_dfr !== '' || $fea_dto !== '');
$is_search = $q->is_search();
// Activar si: es búsqueda, o si hay vars avanzadas (con o sin ?s=)
if (!$is_search && !$has_adv) return;
// Si hay filtros avanzados pero no ?s=, convertimos el query en listado de posts
// (evitamos que WP muestre la home o una 404)
if ($has_adv && !$is_search) {
$q->set('post_type', 'post');
$q->set('post_status', 'publish');
// Forzamos is_search para que el template search se active
$q->is_home = false;
$q->is_front_page = false;
$q->is_archive = false;
$q->is_search = true;
}
// Autor
if ($fea_author > 0) $q->set('author', $fea_author);
// Categoría (tema)
if ($fea_cat > 0) $q->set('cat', $fea_cat);
// Cita bíblica: coincidencia por PREFIJO (el valor empieza por el término, ej. "Jn").
// Usamos REGEXP '^<term>' con el término escapado para evitar metacaracteres.
if ($fea_cita !== '') {
$regex = '^' . preg_quote($fea_cita, '/');
$q->set('meta_query', [
[
'key' => '_cita_evangelio',
'value' => $regex,
'compare' => 'REGEXP',
],
]);
}
// Fechas: la UI usa <input type="date"> → formato YYYY-MM-DD (fecha completa).
// Tratamos los límites como fechas exactas, inclusive.
if ($fea_dfr !== '' || $fea_dto !== '') {
$date_query = ['inclusive' => true];
if ($fea_dfr !== '' && preg_match('/^\d{4}-\d{2}-\d{2}$/', $fea_dfr)) {
[$y, $m, $d] = array_map('intval', explode('-', $fea_dfr));
$date_query['after'] = ['year' => $y, 'month' => $m, 'day' => $d];
}
if ($fea_dto !== '' && preg_match('/^\d{4}-\d{2}-\d{2}$/', $fea_dto)) {
[$y, $m, $d] = array_map('intval', explode('-', $fea_dto));
$date_query['before'] = ['year' => $y, 'month' => $m, 'day' => $d];
}
// Sólo aplicamos si quedó al menos un límite válido
if (isset($date_query['after']) || isset($date_query['before'])) {
$q->set('date_query', [$date_query]);
}
}
});
// ─────────────────────────────────────────────────────────────────
// Helpers: obtener datos del formulario
// ─────────────────────────────────────────────────────────────────
/** Devuelve los autores elegibles (≥30 posts, sin excluidos), cacheado por request. */
function fea_adv_get_authors(): array {
static $cache = null;
if ($cache !== null) return $cache;
global $wpdb;
$excl = implode(',', array_map('intval', FEA_AUTORES_EXCLUIR));
// Excluimos por ID y, de forma robusta, cualquier display_name que contenga "Testament"
// (Nuevo/Antiguo Testamento, New/Old Testament, Nouveau/Ancien Testament, etc.).
$cache = $wpdb->get_results("
SELECT u.ID, u.display_name, COUNT(p.ID) as cnt
FROM {$wpdb->users} u
JOIN {$wpdb->posts} p ON p.post_author = u.ID
WHERE p.post_status = 'publish'
AND p.post_type = 'post'
AND u.ID NOT IN ({$excl})
AND u.display_name NOT LIKE '%Testament%'
GROUP BY u.ID
HAVING cnt >= 30
ORDER BY u.display_name
");
return $cache;
}
/**
* Lista CURADA de categorías-sección reales (term_ids ES, idioma canónico), en orden
* editorial. Las 153 categorías reales de la BD incluyen categorías-autor y residuales
* de la migración K2 → desplegable inmanejable; por eso se cura a las secciones del sitio.
* En idiomas ≠ ES se traduce cada term con Polylang.
*/
defined('FEA_CATS_CURADA') or define('FEA_CATS_CURADA', [
1650, // Artículos
1647, // Comentarios al evangelio
1645, // Lecturas bíblicas
1648, // Eucaristía
1646, // Comentario editorial
1649, // Multimedia
63, // EFFA
14, // A la fuente cada día
23, // Cartas que nos llegan
41, // Noticias de alcance
24, // Tablón de anuncios
54, // Canciones religiosas
45, // Canciones-plegarias
]);
/**
* Etiqueta bonita en el desplegable (solo ES) para categorías cuyo nombre en BD quedó
* sin formatear en la migración K2 (no toca el dato del término).
*/
defined('FEA_CATS_LABEL') or define('FEA_CATS_LABEL', [
14 => 'A la fuente cada día', // en BD es "Alafuentecadadia"
]);
/**
* Devuelve las categorías del selector (lista curada FEA_CATS_CURADA), traducidas al
* idioma activo (Polylang) y conservando el orden editorial.
* Devuelve array de objetos {term_id, name} con el term_id DEL IDIOMA ACTUAL.
*/
function fea_adv_get_categories(): array {
static $cache = null;
if ($cache !== null) return $cache;
$lang = function_exists('pll_current_language') ? pll_current_language() : '';
$default = function_exists('pll_default_language') ? pll_default_language() : 'es';
$out = [];
foreach (FEA_CATS_CURADA as $es_id) {
$tid = (int) $es_id;
if ($lang && $lang !== $default && function_exists('pll_get_term')) {
$tr = pll_get_term($es_id, $lang);
if ($tr) $tid = (int) $tr;
}
$term = get_term($tid, 'category');
if (!$term || is_wp_error($term)) continue;
$name = $term->name;
if ((!$lang || $lang === $default) && isset(FEA_CATS_LABEL[$es_id])) {
$name = FEA_CATS_LABEL[$es_id];
}
$out[] = (object) ['term_id' => (int) $term->term_id, 'name' => $name];
}
$cache = $out;
return $cache;
}
/** URL base del idioma actual (para el action del form). */
function fea_adv_lang_base(): string {
$base = home_url('/');
if (function_exists('pll_current_language')) {
$lang = pll_current_language();
$default = function_exists('pll_default_language') ? pll_default_language() : 'es';
if ($lang && $lang !== $default) $base = home_url('/' . $lang . '/');
}
return $base;
}
/** Nombre traducido de una categoría (vía Polylang si está disponible). */
function fea_adv_cat_name(int $cat_id, string $fallback): string {
if (function_exists('pll_current_language')) {
$lang = pll_current_language();
$default = function_exists('pll_default_language') ? pll_default_language() : 'es';
if ($lang !== $default) {
$translated_id = function_exists('pll_get_term') ? pll_get_term($cat_id, $lang) : 0;
if ($translated_id) {
$term = get_term($translated_id);
if ($term && !is_wp_error($term)) return $term->name;
}
}
}
$term = get_term($cat_id, 'category');
return ($term && !is_wp_error($term)) ? $term->name : $fallback;
}
// ─────────────────────────────────────────────────────────────────
// Renderiza el formulario avanzado
// ─────────────────────────────────────────────────────────────────
function fea_adv_form_html(): string {
$action = esc_url(fea_adv_lang_base());
$s = esc_attr(get_search_query());
// Leer de $_GET para fiabilidad (get_query_var puede llegar vacío si la var no estaba registrada aún)
$sel_aut = isset($_GET['fea_author']) ? (int)$_GET['fea_author'] : 0;
$sel_cat = isset($_GET['fea_cat']) ? (int)$_GET['fea_cat'] : 0;
$sel_cit = isset($_GET['fea_cita']) ? esc_attr(sanitize_text_field($_GET['fea_cita'])) : '';
$sel_dfr = isset($_GET['fea_date_from'])? esc_attr(sanitize_text_field($_GET['fea_date_from'])) : '';
$sel_dto = isset($_GET['fea_date_to']) ? esc_attr(sanitize_text_field($_GET['fea_date_to'])) : '';
$authors = fea_adv_get_authors();
$cats = fea_adv_get_categories();
$t_title = esc_html(fea_adv_t('search_advanced'));
$t_word = esc_html(fea_adv_t('word'));
$t_author = esc_html(fea_adv_t('author'));
$t_allaut = esc_html(fea_adv_t('all_authors'));
$t_topic = esc_html(fea_adv_t('topic'));
$t_alltop = esc_html(fea_adv_t('all_topics'));
$t_cita = esc_html(fea_adv_t('biblical_ref'));
$t_citaph = esc_attr(fea_adv_t('biblical_ph'));
$t_dfr = esc_html(fea_adv_t('date_from'));
$t_dto = esc_html(fea_adv_t('date_to'));
$t_btn = esc_html(fea_adv_t('search_btn'));
$t_reset = esc_html(fea_adv_t('reset_btn'));
$html = '<div class="fea-adv-wrap" id="fea-adv-search">';
$html .= '<details class="fea-adv-details" open>';
$html .= '<summary class="fea-adv-summary">' . $t_title . '</summary>';
$html .= '<form class="fea-adv-form" method="get" action="' . $action . '">';
// Fila 1: Palabra
$html .= '<div class="fea-adv-row">';
$html .= '<label class="fea-adv-label" for="fea-s">' . $t_word . '</label>';
$html .= '<input class="fea-adv-input" id="fea-s" type="search" name="s" value="' . $s . '" autocomplete="off">';
$html .= '</div>';
// Fila 2: Autor
$html .= '<div class="fea-adv-row">';
$html .= '<label class="fea-adv-label" for="fea-author">' . $t_author . '</label>';
$html .= '<select class="fea-adv-select" id="fea-author" name="fea_author">';
$html .= '<option value="">' . $t_allaut . '</option>';
foreach ($authors as $a) {
$sel = selected($sel_aut, (int)$a->ID, false);
$name = esc_html($a->display_name);
$html .= "<option value=\"{$a->ID}\"{$sel}>{$name}</option>";
}
$html .= '</select></div>';
// Fila 3: Categoría (real, dinámica)
$html .= '<div class="fea-adv-row">';
$html .= '<label class="fea-adv-label" for="fea-cat">' . $t_topic . '</label>';
$html .= '<select class="fea-adv-select" id="fea-cat" name="fea_cat">';
$html .= '<option value="">' . $t_alltop . '</option>';
foreach ($cats as $c) {
$cat_name = esc_html($c->name);
$sel = selected($sel_cat, $c->term_id, false);
$html .= "<option value=\"{$c->term_id}\"{$sel}>{$cat_name}</option>";
}
$html .= '</select></div>';
// Fila 4: Cita bíblica
$html .= '<div class="fea-adv-row">';
$html .= '<label class="fea-adv-label" for="fea-cita">' . $t_cita . '</label>';
$html .= '<input class="fea-adv-input" id="fea-cita" type="text" name="fea_cita" value="' . $sel_cit . '" placeholder="' . $t_citaph . '">';
$html .= '</div>';
// Fila 5: Fechas
$html .= '<div class="fea-adv-row fea-adv-dates">';
$html .= '<span class="fea-adv-label">' . $t_dfr . '</span>';
$html .= '<input class="fea-adv-input fea-adv-date" type="date" name="fea_date_from" value="' . $sel_dfr . '">';
$html .= '<span class="fea-adv-label fea-adv-to">' . $t_dto . '</span>';
$html .= '<input class="fea-adv-input fea-adv-date" type="date" name="fea_date_to" value="' . $sel_dto . '">';
$html .= '</div>';
// Botones
$html .= '<div class="fea-adv-actions">';
$html .= '<button class="fea-adv-btn fea-adv-btn-primary" type="submit">' . $t_btn . '</button>';
$html .= '<a class="fea-adv-btn fea-adv-btn-secondary" href="' . $action . '">' . $t_reset . '</a>';
$html .= '</div>';
$html .= '</form></details></div>';
return $html;
}
// ─────────────────────────────────────────────────────────────────
// Chips de filtros activos + contador de resultados
// ─────────────────────────────────────────────────────────────────
function fea_adv_chips_html(): string {
$chips = [];
$aut_id = isset($_GET['fea_author']) ? (int)$_GET['fea_author'] : 0;
if ($aut_id > 0) {
$udata = get_userdata($aut_id);
$name = $udata ? esc_html($udata->display_name) : $aut_id;
$chips[] = fea_adv_chip(fea_adv_t('filter_author') . ': ' . $name, 'fea_author');
}
$cat_id = isset($_GET['fea_cat']) ? (int)$_GET['fea_cat'] : 0;
if ($cat_id > 0) {
$term = get_term($cat_id, 'category');
$name = ($term && !is_wp_error($term)) ? esc_html($term->name) : $cat_id;
$chips[] = fea_adv_chip(fea_adv_t('filter_topic') . ': ' . $name, 'fea_cat');
}
$cita = isset($_GET['fea_cita']) ? sanitize_text_field($_GET['fea_cita']) : '';
if ($cita !== '') {
$chips[] = fea_adv_chip(fea_adv_t('filter_cita') . ': ' . esc_html($cita), 'fea_cita');
}
$dfr = isset($_GET['fea_date_from']) ? sanitize_text_field($_GET['fea_date_from']) : '';
$dto = isset($_GET['fea_date_to']) ? sanitize_text_field($_GET['fea_date_to']) : '';
if ($dfr !== '' || $dto !== '') {
$label = fea_adv_t('filter_date') . ': ' . ($dfr ?: '?') . ' ' . ($dto ?: '?');
$chips[] = fea_adv_chip(esc_html($label), 'fea_date_from', 'fea_date_to');
}
if (empty($chips)) return '';
return '<div class="fea-adv-chips"><span class="fea-adv-chips-label">' .
esc_html(fea_adv_t('active_filters')) . '</span>' . implode('', $chips) . '</div>';
}
/** Genera un chip con botón ✕ que elimina el filtro de la URL. */
function fea_adv_chip(string $label, string ...$remove_params): string {
$url = remove_query_arg($remove_params);
return '<span class="fea-adv-chip">' . $label .
' <a href="' . esc_url($url) . '" class="fea-adv-chip-x" aria-label="Eliminar filtro">×</a></span>';
}
// ─────────────────────────────────────────────────────────────────
// Inyección en la página de resultados (template search)
// ─────────────────────────────────────────────────────────────────
/**
* Inyecta el formulario avanzado + chips antes del primer bloque wp:query-title
* del template de búsqueda, modificando el HTML renderizado del bloque principal.
*/
add_filter('render_block', function (string $html, array $block): string {
if (is_admin()) return $html;
// Inyectar en búsquedas y cuando hay filtros avanzados activos
$has_adv_get = !empty($_GET['fea_author']) || !empty($_GET['fea_cat']) ||
!empty($_GET['fea_cita']) || !empty($_GET['fea_date_from']) || !empty($_GET['fea_date_to']);
if (!is_search() && !is_page('buscar') && !$has_adv_get) return $html;
if (($block['blockName'] ?? '') !== 'core/query-title') return $html;
$form = fea_adv_form_html();
$chips = fea_adv_chips_html();
// Contador de resultados (sólo cuando hay consulta activa)
$counter = '';
if (is_search() || $has_adv_get) {
global $wp_query;
if ($wp_query && $wp_query->found_posts !== null) {
$n = (int) $wp_query->found_posts;
$counter = '<div class="fea-adv-count">' . $n . ' ' . esc_html(fea_adv_t('results')) . '</div>';
}
}
return $form . $chips . $counter . $html;
}, 10, 2);
/**
* Añade byline (autor) en cada tarjeta fea-archive-card dentro del template search.
* Lo hacemos inyectando tras wp:post-date en el contexto correcto.
*/
add_filter('render_block', function (string $html, array $block): string {
if (is_admin()) return $html;
$has_adv_get2 = !empty($_GET['fea_author']) || !empty($_GET['fea_cat']) ||
!empty($_GET['fea_cita']) || !empty($_GET['fea_date_from']) || !empty($_GET['fea_date_to']);
if (!is_search() && !is_page('buscar') && !$has_adv_get2) return $html;
if (($block['blockName'] ?? '') !== 'core/post-date') return $html;
$post_id = $block['attrs']['postId'] ?? (in_the_loop() ? get_the_ID() : 0);
if (!$post_id) $post_id = get_the_ID();
if (!$post_id) return $html;
$author_id = (int) get_post_field('post_author', $post_id);
$author_name = get_the_author_meta('display_name', $author_id);
if (!$author_name) return $html;
$author_url = get_author_posts_url($author_id);
$byline = '<div class="fea-adv-byline">' . esc_html(fea_adv_t('by')) . ' ' .
'<a href="' . esc_url($author_url) . '">' . esc_html($author_name) . '</a></div>';
return $html . $byline;
}, 10, 2);
// ─────────────────────────────────────────────────────────────────
// Enlace «Búsqueda avanzada» desde la barra fea-search
// ─────────────────────────────────────────────────────────────────
add_filter('render_block', function (string $html, array $block): string {
if (is_admin()) return $html;
if (($block['blockName'] ?? '') !== 'core/template-part') return $html;
$slug = $block['attrs']['slug'] ?? '';
if (!in_array($slug, ['header', 'cabecera-portada'], true)) return $html;
// Sólo inyectamos el enlace si ya hay una barra de búsqueda (.fea-search-bar)
// Buscamos la barra y le añadimos el enlace de búsqueda avanzada.
if (strpos($html, 'fea-search-bar') === false) return $html;
$adv_url = home_url('/buscar/');
if (function_exists('pll_current_language')) {
$lang = pll_current_language();
$default = function_exists('pll_default_language') ? pll_default_language() : 'es';
if ($lang && $lang !== $default) {
// Intenta obtener la página /buscar traducida
$page = get_page_by_path('buscar');
if ($page) {
$tl = function_exists('pll_get_post') ? pll_get_post($page->ID, $lang) : 0;
if ($tl) $adv_url = get_permalink($tl);
}
}
}
$label = esc_html(fea_adv_t('search_advanced'));
$link = '<div class="fea-adv-link-wrap"><a class="fea-adv-link" href="' . esc_url($adv_url) . '">' . $label . '</a></div>';
// Insertamos el enlace justo al final del bloque .fea-search-bar (cerrando el div externo)
// fea-search.php genera: <div class="fea-search-bar"><form ...>...</form></div>
// Reemplazamos la ÚLTIMA ocurrencia del cierre del div de .fea-search-bar
$marker = '</div>';
$pos = strpos($html, 'fea-search-bar');
if ($pos !== false) {
// Buscamos el </div> que cierra .fea-search-bar (el wrapper externo)
// La estructura es: <div class="fea-search-bar"><form>...</form></div>
// Hay dos </div>: uno cierra el form y otro cierra .fea-search-bar
// Usamos una sustitución segura: buscamos el patrón exacto del cierre
$html = preg_replace('#(</form></div>)#', '$1' . $link, $html, 1);
}
return $html;
}, 25, 2);
// ─────────────────────────────────────────────────────────────────
// Página /buscar — inyectar formulario vía the_content
// ─────────────────────────────────────────────────────────────────
add_filter('the_content', function (string $content): string {
if (!is_page('buscar')) return $content;
// Envoltura: título + formulario + contenido original de la página
$form = fea_adv_form_html();
return $form . '<div class="fea-buscar-intro">' . $content . '</div>';
});
// Nota: la página /buscar se crea con scripts/create_buscar_page.php (ya ejecutado).
// El formulario se inyecta en the_content (hook arriba) y vía render_block en search.
// ─────────────────────────────────────────────────────────────────
// CSS
// ─────────────────────────────────────────────────────────────────
add_action('wp_head', function (): void {
if (!is_search() && !is_page('buscar') &&
empty($_GET['fea_author']) && empty($_GET['fea_cat']) &&
empty($_GET['fea_cita']) && empty($_GET['fea_date_from']) && empty($_GET['fea_date_to'])) return;
?>
<style>
/* ── Formulario buscador avanzado ─────────────────────────────── */
.fea-adv-wrap{max-width:860px;margin:0 auto 1.5rem;padding:0 1rem}
.fea-adv-details{background:#faf6f7;border:1px solid #e8d5da;border-radius:8px;overflow:hidden}
.fea-adv-summary{
padding:.75rem 1.2rem;font-weight:600;font-size:1rem;color:#8b1a2e;
cursor:pointer;list-style:none;display:flex;align-items:center;gap:.5rem;
background:#fff0f2;border-bottom:1px solid #e8d5da;
}
.fea-adv-summary::-webkit-details-marker{display:none}
.fea-adv-summary::before{content:"▸";transition:transform .2s}
details[open] .fea-adv-summary::before{transform:rotate(90deg)}
.fea-adv-form{display:grid;grid-template-columns:1fr 1fr;gap:.75rem 1.2rem;padding:1rem 1.2rem}
@media(max-width:600px){.fea-adv-form{grid-template-columns:1fr}}
/* min-width:0 evita que el contenido fuerce a la celda del grid a desbordar */
.fea-adv-row{display:flex;flex-direction:column;gap:.25rem;min-width:0}
.fea-adv-dates{grid-column:1/-1;flex-direction:row;align-items:center;flex-wrap:wrap;gap:.5rem}
.fea-adv-dates .fea-adv-label{white-space:nowrap}
.fea-adv-label{font-size:.8rem;font-weight:600;color:#5a3a40;text-transform:uppercase;letter-spacing:.04em}
.fea-adv-to{margin-left:.5rem}
.fea-adv-input,.fea-adv-select{
box-sizing:border-box;border:1px solid #d9c4c9;border-radius:6px;padding:.5rem .75rem;
font-size:.95rem;background:#fff;color:#222;width:100%;max-width:100%;
transition:border-color .15s;
}
.fea-adv-input:focus,.fea-adv-select:focus{border-color:#8b1a2e;outline:0;box-shadow:0 0 0 2px #8b1a2e33}
.fea-adv-date{box-sizing:border-box;width:auto;min-width:150px;flex:0 0 auto}
.fea-adv-actions{grid-column:1/-1;display:flex;gap:.75rem;align-items:center;margin-top:.25rem}
.fea-adv-btn{padding:.55rem 1.4rem;border-radius:999px;font-size:.95rem;font-weight:600;cursor:pointer;text-decoration:none;border:2px solid transparent}
.fea-adv-btn-primary{background:#8b1a2e;color:#fff;border-color:#8b1a2e}
.fea-adv-btn-primary:hover{background:#6f1525;border-color:#6f1525}
.fea-adv-btn-secondary{background:transparent;color:#8b1a2e;border-color:#8b1a2e}
.fea-adv-btn-secondary:hover{background:#8b1a2e;color:#fff}
/* ── Contador y chips ─────────────────────────────────────────── */
.fea-adv-count{max-width:860px;margin:.5rem auto .25rem;padding:0 1rem;
font-size:.9rem;color:#666}
.fea-adv-chips{max-width:860px;margin:.5rem auto;padding:0 1rem;
display:flex;flex-wrap:wrap;gap:.4rem;align-items:center}
.fea-adv-chips-label{font-size:.8rem;color:#5a3a40;font-weight:600;margin-right:.25rem}
.fea-adv-chip{display:inline-flex;align-items:center;gap:.35rem;background:#f0e2e5;
border:1px solid #d9c4c9;border-radius:999px;padding:.2rem .75rem;font-size:.82rem;color:#5a3a40}
.fea-adv-chip-x{color:#8b1a2e;text-decoration:none;font-weight:700;font-size:1rem;line-height:1}
.fea-adv-chip-x:hover{color:#6f1525}
/* ── Byline en tarjetas de resultado ──────────────────────────── */
.fea-archive-card .fea-adv-byline{font-size:.78rem;color:#7a5a62;margin-top:.1rem}
.fea-archive-card .fea-adv-byline a{color:#8b1a2e;text-decoration:none}
.fea-archive-card .fea-adv-byline a:hover{text-decoration:underline}
/* ── Enlace «Búsqueda avanzada» en barra header ───────────────── */
.fea-adv-link-wrap{display:none;justify-content:center;padding:.3rem 1rem .4rem;
background:#faf6f7;border-bottom:1px solid #efe2e5}
@media(max-width:600px){.fea-adv-link-wrap{display:flex}}
.fea-adv-link{font-size:.8rem;color:#8b1a2e;text-decoration:none;font-weight:500}
.fea-adv-link:hover{text-decoration:underline}
</style>
<?php
}, 10);
// CSS del enlace «Búsqueda avanzada» en header (se muestra en todas las páginas)
add_action('wp_head', function (): void {
?>
<style>
.fea-adv-link-wrap{display:none;justify-content:center;padding:.3rem 1rem .4rem;
background:#faf6f7;border-bottom:1px solid #efe2e5}
@media(max-width:600px){.fea-adv-link-wrap{display:flex}}
.fea-adv-link{font-size:.8rem;color:#8b1a2e;text-decoration:none;font-weight:500}
.fea-adv-link:hover{text-decoration:underline}
</style>
<?php
}, 15);