Añadir mu-plugins y scripts de feadulta

This commit is contained in:
2026-06-28 15:10:46 -04:00
parent bce7e42f44
commit b6116b066d
106 changed files with 17600 additions and 2 deletions
+632
View File
@@ -0,0 +1,632 @@
<?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);