Files
feadulta/wordpress/wp-content/mu-plugins/fea-homepage.php
T
rafa c6f16ba739 fix: sección pre-footer portada + scripts importación delta marzo-mayo 2026
- fea-homepage.php: nuevo shortcode [fea_prefooter] — 3 columnas (Ediciones |
  Noticias de alcance | Publicidad) sobre el footer. Imagen noticias_2025.jpg
  apunta a wp-content/uploads/recursos/ (fix ruta Joomla rota). Columna central
  y vídeo de la semana son dinámicos (último post de las categorías respectivas).
- scripts/import_new_k2_items.py: importa 169 K2 items nuevos (id > 17873)
- scripts/fix_imported_k2_metas.py: asigna metas/cats/Polylang a esos 169 posts
- scripts/import_new_cartas.py: importa 8 cartas nuevas (ew4r_content id > 9043,
  catid 27/40/41) y asigna _carta_id a los artículos K2 por fecha
- scripts/import_new_content.py: importa 58 ítems ew4r_content no-carta
  (multimedia, noticias, tablón, etc.) con mapping catid→WP term

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 22:27:33 -04:00

1569 lines
63 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 — Homepage
* Description: Portada con selección editorial via ACF.
* Version: 1.4
*/
// ── Flush rewrite rules una sola vez tras cambios de configuración ────────
add_action('init', function() {
if (get_option('fea_flush_needed')) {
flush_rewrite_rules(true);
delete_option('fea_flush_needed');
}
});
// ── Portada multilingüe: sirve la página traducida en el home de cada idioma ──
// Polylang free no traduce page_on_front automáticamente.
// Este hook intercepta el query del home de idioma y carga la página correcta.
add_action('pre_get_posts', function(WP_Query $query) {
if (!$query->is_main_query()) return;
if (!function_exists('pll_current_language') || !function_exists('pll_get_post')) return;
// Solo actuar cuando WP cree que está en el home/front page
if (!$query->is_home() && !$query->is_front_page()) return;
$lang = pll_current_language();
if (!$lang || $lang === 'es') return;
$translated_id = pll_get_post(26542, $lang);
if (!$translated_id) return;
// Forzar carga de la página traducida como si fuera la portada estática
$query->set('page_id', $translated_id);
$query->set('post_type', 'page');
$query->is_home = false;
$query->is_front_page = true;
$query->is_singular = true;
$query->is_page = true;
}, 1);
// Asegura que option_page_on_front devuelve la página traducida al idioma actual
// (necesario para is_front_page() y para que el template FSE lo reconozca)
add_filter('option_page_on_front', function($value) {
if (!function_exists('pll_current_language') || !function_exists('pll_get_post')) return $value;
static $running = false;
if ($running) return $value; // evitar recursión
$running = true;
$lang = pll_current_language();
$running = false;
if (!$lang || $lang === 'es') return $value;
$translated = pll_get_post((int) $value, $lang);
return $translated ?: $value;
});
// ── Traducir links de categoría "Esta semana" / "Semana pasada" al idioma actual ─
// Las categorías traducidas EN/FR/IT/PT dan 404 (bug WP_Tax_Query AND 0=1 con Polylang).
// Solución: redirigir ES cat URL → post traducido directamente (no a la categoría).
// Para "Otras semanas" (cartas-de-otras-semanas, count=729): se deja como está.
add_action('wp_footer', function() {
if (!function_exists('pll_current_language') || !function_exists('pll_get_post')) return;
$lang = pll_current_language();
if (!$lang || $lang === 'es') return;
global $wpdb;
$map = [];
// Categorías de carta única (count=1) → redirigir al post traducido
$single_carta_cats = [
'cartasemana' => 6,
'carta-semana-pasada' => 22,
];
foreach ($single_carta_cats as $slug => $es_cat_id) {
// term_taxonomy_id de la categoría ES (bypassa Polylang)
$tt_id = (int) $wpdb->get_var($wpdb->prepare(
"SELECT term_taxonomy_id FROM {$wpdb->term_taxonomy}
WHERE term_id=%d AND taxonomy='category'", $es_cat_id
));
if (!$tt_id) continue;
// Post ES más reciente en esa categoría
$es_post_id = (int) $wpdb->get_var($wpdb->prepare(
"SELECT p.ID FROM {$wpdb->posts} p
JOIN {$wpdb->term_relationships} tr ON p.ID=tr.object_id
WHERE tr.term_taxonomy_id=%d AND p.post_status='publish'
ORDER BY p.post_date DESC LIMIT 1", $tt_id
));
if (!$es_post_id) continue;
// Post traducido
$trans_id = pll_get_post($es_post_id, $lang);
if (!$trans_id) continue;
$trans_url = get_permalink($trans_id);
if ($trans_url) {
$map[home_url('/category/' . $slug . '/')] = $trans_url;
}
}
if (empty($map)) return;
?>
<script>
(function(){
var map = <?php echo json_encode($map); ?>;
document.querySelectorAll('a[href]').forEach(function(a){
var clean = a.href.split('?')[0];
if (map[clean]) a.href = map[clean];
});
})();
</script>
<?php
}, 25);
// ── Foto de perfil de autor via ACF (campo en perfil de usuario) ──────────
add_action('acf/init', function() {
if (!function_exists('acf_add_local_field_group')) return;
acf_add_local_field_group([
'key' => 'group_user_foto_perfil',
'title' => 'Foto de perfil',
'fields' => [[
'key' => 'field_user_foto_perfil',
'label' => 'Foto',
'name' => 'foto_perfil',
'type' => 'image',
'instructions' => 'Sube una foto cuadrada del autor (mínimo 100×100px).',
'return_format' => 'url',
'preview_size' => 'thumbnail',
'upload_folder' => 'autores',
]],
'location' => [[
['param' => 'user_form', 'operator' => '==', 'value' => 'all'],
]],
]);
});
// Usar la foto del autor en lugar del Gravatar cuando existe
// Lee el attachment ID guardado en el meta 'foto_perfil' (campo ACF)
add_filter('get_avatar_url', function($url, $id_or_email, $args) {
$user_id = null;
if (is_numeric($id_or_email)) $user_id = (int) $id_or_email;
elseif ($id_or_email instanceof WP_User) $user_id = $id_or_email->ID;
elseif (is_string($id_or_email)) {
$user = get_user_by('email', $id_or_email);
if ($user) $user_id = $user->ID;
}
if (!$user_id) return $url;
$attach_id = get_user_meta($user_id, 'foto_perfil', true);
if ($attach_id) {
$foto = wp_get_attachment_image_url((int) $attach_id, 'full');
if ($foto) return $foto;
}
return $url;
}, 10, 3);
// ── Ordenar por fecha los resultados del buscador ACF en campos de portada ─
add_filter('acf/fields/relationship/query/key=field_portada_articulos', function($args) {
$args['orderby'] = 'date';
$args['order'] = 'DESC';
return $args;
});
add_filter('acf/fields/relationship/query/key=field_portada_multimedia', function($args) {
$args['orderby'] = 'date';
$args['order'] = 'DESC';
return $args;
});
// ── Campos ACF para la portada ────────────────────────────────────────────
add_action('acf/init', function() {
if (!function_exists('acf_add_local_field_group')) return;
$front_page_id = (int) get_option('page_on_front');
acf_add_local_field_group([
'key' => 'group_portada_fea',
'title' => 'Contenido de la portada',
'fields' => [
[
'key' => 'field_portada_articulos',
'label' => 'Artículos seleccionados',
'name' => 'portada_articulos',
'type' => 'relationship',
'instructions' => 'Elige los artículos que aparecerán en la portada esta semana (máx. 9). Puedes buscar por título.',
'post_type' => ['post'],
'post_status' => ['publish', 'draft'],
'filters' => ['search', 'taxonomy'],
'elements' => ['featured_image'],
'min' => 0,
'max' => 9,
'return_format' => 'object',
'query_args' => ['orderby' => 'date', 'order' => 'DESC'],
],
[
'key' => 'field_portada_multimedia',
'label' => 'Multimedia seleccionado',
'name' => 'portada_multimedia',
'type' => 'relationship',
'instructions' => 'Elige los vídeos o audios para la portada (máx. 4).',
'post_type' => ['post'],
'post_status' => ['publish', 'draft'],
'filters' => ['search'],
'elements' => ['featured_image'],
'min' => 0,
'max' => 4,
'return_format' => 'object',
'query_args' => ['orderby' => 'date', 'order' => 'DESC'],
],
],
'location' => [[
['param' => 'page', 'operator' => '==', 'value' => (string) $front_page_id],
]],
'position' => 'normal',
'style' => 'default',
'label_placement' => 'top',
]);
});
// ── Selector de idioma (dropdown con bandera + 2 letras) ─────────────────
add_action('wp_head', function() {
if (!function_exists('pll_the_languages')) return;
?>
<style>
#fea-lang-switcher {
position: relative;
display: inline-flex;
align-items: center;
font-family: inherit;
z-index: 9999;
}
#fea-lang-btn {
display: inline-flex;
align-items: center;
gap: 5px;
background: none;
border: 1px solid rgba(0,0,0,0.25);
border-radius: 5px;
padding: 4px 9px;
cursor: pointer;
font-size: 0.82rem;
font-weight: 700;
color: inherit;
line-height: 1.4;
white-space: nowrap;
}
#fea-lang-btn:hover { background: rgba(0,0,0,0.06); }
#fea-lang-btn .arrow { font-size: 0.6rem; opacity: 0.6; }
#fea-lang-dropdown {
display: none;
position: absolute;
top: calc(100% + 4px);
right: 0;
background: #fff;
border: 1px solid #ddd;
border-radius: 7px;
box-shadow: 0 6px 16px rgba(0,0,0,0.13);
min-width: 90px;
padding: 4px 0;
list-style: none;
margin: 0;
}
#fea-lang-switcher.open #fea-lang-dropdown { display: block; }
#fea-lang-dropdown li { margin: 0; padding: 0; }
#fea-lang-dropdown a {
display: flex;
align-items: center;
gap: 7px;
padding: 7px 14px;
font-size: 0.82rem;
font-weight: 600;
text-decoration: none;
color: #222;
white-space: nowrap;
}
#fea-lang-dropdown a:hover { background: #f5f5f5; }
#fea-lang-dropdown a[aria-current] { font-weight: 700; color: #000; background: #efefef; }
</style>
<?php
});
add_action('wp_footer', function() {
if (!function_exists('pll_the_languages')) return;
$flags = ['es' => '🇪🇸', 'en' => '🇬🇧', 'fr' => '🇫🇷', 'it' => '🇮🇹', 'pt' => '🇵🇹'];
$langs = pll_the_languages(['raw' => 1, 'hide_if_no_translation' => 0]);
if (!$langs) return;
$current = array_filter($langs, fn($l) => $l['current_lang']);
$current = $current ? array_values($current)[0] : null;
$cur_slug = $current ? $current['slug'] : 'es';
$cur_flag = $flags[$cur_slug] ?? '🌐';
$cur_code = strtoupper($cur_slug);
$items = '';
foreach ($langs as $l) {
$flag = $flags[$l['slug']] ?? '🌐';
$code = strtoupper($l['slug']);
$cur = $l['current_lang'] ? ' aria-current="true"' : '';
$items .= '<li><a href="' . esc_url($l['url']) . '"' . $cur . '>' . $flag . ' ' . $code . '</a></li>';
}
?>
<div id="fea-lang-switcher" role="navigation" aria-label="Idioma / Language">
<button id="fea-lang-btn" aria-haspopup="listbox" aria-expanded="false"
onclick="var s=this.closest('#fea-lang-switcher'),o=s.classList.toggle('open');this.setAttribute('aria-expanded',o)">
<?= $cur_flag ?> <?= $cur_code ?> <span class="arrow">▼</span>
</button>
<ul id="fea-lang-dropdown" role="listbox"><?= $items ?></ul>
</div>
<script>
(function() {
var sw = document.getElementById('fea-lang-switcher');
if (!sw) return;
// Cerrar al clicar fuera
document.addEventListener('click', function(e) {
if (!sw.contains(e.target)) {
sw.classList.remove('open');
document.getElementById('fea-lang-btn').setAttribute('aria-expanded', 'false');
}
});
// Inyectar en el nav principal del header
function inject() {
// Buscar la barra de navegación principal (primer nivel dentro del header)
var header = document.querySelector('header.wp-block-template-part');
if (!header) header = document.querySelector('header');
if (!header) return;
// Buscar el div/group que contiene el menú de navegación
var navGroup = header.querySelector('.wp-block-navigation__container');
if (!navGroup) navGroup = header.querySelector('nav');
if (!navGroup) {
// Fallback: poner en el header con posición absoluta
header.style.position = 'relative';
sw.style.cssText = 'position:absolute;top:50%;right:1.5rem;transform:translateY(-50%);';
header.appendChild(sw);
return;
}
// Crear li wrapper para el nav
var li = document.createElement('li');
li.className = 'wp-block-navigation-item';
li.style.cssText = 'display:flex;align-items:center;margin-left:0.5rem;';
li.appendChild(sw);
// Insertar DESPUÉS del buscador (si existe)
var searchItem = navGroup.querySelector('.wp-block-search');
if (searchItem) {
// Subir hasta el li/div hermano directo del navGroup
var anchor = searchItem;
while (anchor.parentNode && anchor.parentNode !== navGroup) {
anchor = anchor.parentNode;
}
anchor.parentNode.insertBefore(li, anchor.nextSibling);
} else {
navGroup.appendChild(li);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', inject);
} else {
inject();
}
})();
</script>
<?php
}, 20);
// ── Centrar bloque slider+librería (header template, todas las páginas) ───
add_action('wp_head', function() {
?>
<style>
/* El bloque de columnas con el slider usa márgenes negativos del FSE
que lo desplazan. Lo forzamos a centrar con max-width explícito. */
.wp-block-columns:has(.wp-block-nextend-smartslider3) {
max-width: min(var(--wp--style--global--wide-size, 1340px), 100%);
margin-left: auto !important;
margin-right: auto !important;
box-sizing: border-box;
padding-left: var(--wp--preset--spacing--30);
padding-right: var(--wp--preset--spacing--30);
}
/* Ocultar columna librería en tablet */
@media (max-width: 900px) {
.fea-slider-block .wp-block-column:last-child {
display: none !important;
}
}
/* Ocultar slider en móvil (banner superior sigue visible) */
@media (max-width: 600px) {
.fea-slider-block {
display: none !important;
}
}
/* Buscador del header: reducir altura */
.wp-block-search__input {
padding-top: 0.25rem !important;
padding-bottom: 0.25rem !important;
line-height: 1.3 !important;
}
.wp-block-search__button {
padding-top: 0.25rem !important;
padding-bottom: 0.25rem !important;
}
</style>
<?php
});
// ── Estilos ───────────────────────────────────────────────────────────────
add_action('wp_head', function() {
if (!fea_is_front_page()) return;
?>
<style>
.fea-hero { border-bottom: 2px solid #111; padding-bottom: 2rem; margin-bottom: 2.5rem; }
.fea-hero-link { display: block; text-decoration: none; color: inherit; }
.fea-hero-link:hover .fea-hero-title { text-decoration: underline; text-underline-offset: 4px; }
.fea-section-label { display: inline-block; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: #888; margin-bottom: 0.6rem; }
.fea-hero-title { font-size: clamp(1.5rem, 4vw, 2.2rem); font-weight: 700; line-height: 1.2; margin: 0 0 0.75rem; color: #111; }
.fea-hero-meta { display: flex; align-items: center; gap: 0.5rem; font-size: 0.875rem; color: #666; }
.fea-section { margin-bottom: 4.5rem; padding-top: 0.5rem; }
.fea-section-title { font-size: 0.85rem; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: #333; margin: 0 0 1.5rem; padding: 0.4rem 0 0.55rem 0.8rem; border-left: 3px solid #8b1a2e; border-bottom: 1px solid #e0e0e0; }
.fea-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; }
@media (max-width: 720px) { .fea-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 480px) { .fea-grid { grid-template-columns: 1fr; } }
.fea-card { border-bottom: 1px solid #e5e5e5; padding-bottom: 1.1rem; }
/* Ocultar visualmente el texto de accesibilidad del Smart Slider (nombres de fichero con guiones bajos) */
.n2-ss-slide--focus { position: absolute !important; width: 1px !important; height: 1px !important; padding: 0 !important; margin: -1px !important; overflow: hidden !important; clip: rect(0,0,0,0) !important; white-space: nowrap !important; border: 0 !important; }
.fea-card-meta { display: flex; align-items: center; gap: 0.4rem; margin-bottom: 0.4rem; }
.fea-avatar { border-radius: 50%; width: 28px !important; height: 28px !important; flex-shrink: 0; display: inline-block !important; }
.fea-card-author { font-size: 0.78rem; font-weight: 600; color: #555; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fea-card-title { font-size: 0.93rem; font-weight: 600; line-height: 1.35; margin: 0; }
.fea-card-title a { text-decoration: none; color: #111; }
.fea-card-title a:hover { text-decoration: underline; text-underline-offset: 3px; }
</style>
<?php
});
// ── Ocultar título de página en todas las portadas ────────────────────────
add_action('wp_head', function() {
if (!fea_is_front_page()) return;
echo '<style>.wp-block-post-title { display:none !important; }</style>';
}, 20);
// ── Byline personalizado en artículos individuales ────────────────────────
// ── Byline personalizado: se gestiona desde el template FSE (ID 42359) ────
// El template wp_template 'single' ya contiene wp:avatar + wp:post-author-name
// + wp:post-terms. Este hook solo añade los estilos necesarios.
add_action('astra_single_header_bottom', function() {
if (!is_single()) return;
$author_id = (int) get_the_author_meta('ID');
$author_name = get_the_author_meta('display_name');
$avatar_url = get_avatar_url($author_id, ['size' => 48]);
$author_url = get_author_posts_url($author_id);
$cat_str = '';
$cats = get_the_category();
if ($cats) {
$cat_url = get_category_link($cats[0]->term_id);
$cat_str = '<a href="' . esc_url($cat_url) . '" class="fea-byline-cat">'
. esc_html($cats[0]->name) . '</a>';
}
echo '<div class="fea-byline">'
. '<a href="' . esc_url($author_url) . '" class="fea-byline-avatar-link">'
. '<img src="' . esc_url($avatar_url) . '" alt="" width="48" height="48" class="fea-byline-avatar">'
. '</a>'
. '<div class="fea-byline-info">'
. '<a href="' . esc_url($author_url) . '" class="fea-byline-name">' . esc_html($author_name) . '</a>'
. $cat_str
. '</div>'
. '</div>';
});
add_action('wp_head', function() {
if (!is_single()) return;
?>
<style>
.fea-byline { display: flex; align-items: center; gap: 0.75rem; margin-top: 0.75rem; }
.fea-byline-avatar-link { flex-shrink: 0; }
.fea-byline-avatar { border-radius: 50%; display: block; }
.fea-byline-info { display: flex; flex-direction: column; gap: 0.2rem; }
.fea-byline-name { font-size: 0.9rem; font-weight: 600; color: #222; text-decoration: none; }
.fea-byline-name:hover { text-decoration: underline; }
.fea-byline-cat { font-size: 0.78rem; color: #888; text-decoration: none; }
.fea-byline-cat:hover { text-decoration: underline; color: #555; }
</style>
<?php
});
// ── Helpers ───────────────────────────────────────────────────────────────
function fea_title(string $title): string {
$lower = mb_strtolower($title, 'UTF-8');
return mb_strtoupper(mb_substr($lower, 0, 1, 'UTF-8'), 'UTF-8') . mb_substr($lower, 1, null, 'UTF-8');
}
function fea_card(object $post): string {
$author_id = $post->post_author;
$author_name = get_the_author_meta('display_name', $author_id);
$avatar_url = get_avatar_url($author_id, ['size' => 40, 'default' => 'identicon']);
$url = get_permalink($post->ID);
$title = fea_title($post->post_title);
return '<article class="fea-card">'
. '<div class="fea-card-meta">'
. '<img src="' . esc_url($avatar_url) . '" alt="" width="28" height="28" class="fea-avatar" loading="lazy">'
. '<span class="fea-card-author">' . esc_html($author_name) . '</span>'
. '</div>'
. '<h3 class="fea-card-title"><a href="' . esc_url($url) . '">' . esc_html($title) . '</a></h3>'
. '</article>';
}
/** IDs de las páginas de portada en todos los idiomas. */
function fea_front_page_ids(): array {
return [26542, 42756, 42757, 42758, 43889]; // ES, FR, IT, PT, EN
}
/** True si la página actual es alguna de las portadas (multilingüe). */
function fea_is_front_page(): bool {
if (is_front_page()) return true;
$id = get_queried_object_id();
return $id && in_array($id, fea_front_page_ids(), true);
}
/** Devuelve el idioma actual de Polylang, o 'es' si no está activo. */
function fea_current_lang(): string {
return (function_exists('pll_current_language') ? pll_current_language() : null) ?: 'es';
}
/** Etiquetas de sección traducidas por idioma. */
function fea_labels(): array {
$all = [
'es' => [
'carta' => 'Carta de la semana',
'evangelio' => 'Comentarios al evangelio',
'articulos' => 'Artículos de esta semana',
'eucaristia' => 'Para una eucaristía más participativa',
'multimedia' => 'Multimedia',
],
'en' => [
'carta' => 'Letter of the week',
'evangelio' => 'Gospel commentary',
'articulos' => 'Articles of the week',
'eucaristia' => 'For a more participatory Eucharist',
'multimedia' => 'Multimedia',
],
'fr' => [
'carta' => 'Lettre de la semaine',
'evangelio' => 'Commentaires de l\'évangile',
'articulos' => 'Articles de la semaine',
'eucaristia' => 'Pour une eucharistie plus participative',
'multimedia' => 'Multimédia',
],
'it' => [
'carta' => 'Lettera della settimana',
'evangelio' => 'Commenti al vangelo',
'articulos' => 'Articoli della settimana',
'eucaristia' => 'Per una eucaristia più partecipativa',
'multimedia' => 'Multimedia',
],
'pt' => [
'carta' => 'Carta da semana',
'evangelio' => 'Comentários ao evangelho',
'articulos' => 'Artigos da semana',
'eucaristia' => 'Para uma eucaristia mais participativa',
'multimedia' => 'Multimédia',
],
];
return $all[fea_current_lang()] ?? $all['es'];
}
/**
* ID de la página de portada para el idioma actual.
*/
function fea_front_page_id(): int {
$es_front = 26542;
$lang = fea_current_lang();
if ($lang === 'es') return $es_front;
if (function_exists('pll_get_post')) {
$translated = pll_get_post($es_front, $lang);
if ($translated) return (int) $translated;
}
return $es_front;
}
/**
* Traduce un term_id de categoría ES al idioma actual.
* Si no hay traducción disponible, devuelve el ID original.
*/
function fea_cat(int $es_cat_id): int {
$lang = fea_current_lang();
if ($lang === 'es') return $es_cat_id;
if (function_exists('pll_get_term')) {
$translated = pll_get_term($es_cat_id, $lang);
if ($translated) return (int) $translated;
}
return $es_cat_id;
}
// ── Shortcode: [fea_carta_semana_hero] ────────────────────────────────────
add_shortcode('fea_carta_semana_hero', function() {
$lang = fea_current_lang();
$labels = fea_labels();
$cartas = get_posts([
'posts_per_page' => 1,
'category__in' => [fea_cat(6)],
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
]);
if (!$cartas) return '';
$c = $cartas[0];
$url = get_permalink($c->ID);
$fecha = date_i18n('j F Y', strtotime($c->post_date));
$author_name = get_the_author_meta('display_name', $c->post_author);
$avatar_url = get_avatar_url($c->post_author, ['size' => 32, 'default' => 'identicon']);
return '<section class="fea-hero">'
. '<a href="' . esc_url($url) . '" class="fea-hero-link">'
. '<span class="fea-section-label">' . esc_html($labels['carta']) . '</span>'
. '<h2 class="fea-hero-title">' . esc_html(fea_title($c->post_title)) . '</h2>'
. '<div class="fea-hero-meta">'
. '<img src="' . esc_url($avatar_url) . '" alt="" width="32" height="32" class="fea-avatar">'
. '<span>' . esc_html($author_name) . ' · ' . $fecha . '</span>'
. '</div>'
. '</a></section>';
});
// ── Shortcode: [fea_articulos_semana] ─────────────────────────────────────
add_shortcode('fea_articulos_semana', function($atts) {
$lang = fea_current_lang();
$labels = fea_labels();
$atts = shortcode_atts(['titulo' => $labels['articulos']], $atts);
$page = fea_front_page_id();
// Selección editorial ACF (solo para ES; otros idiomas usan fallback)
$posts = [];
if ($lang === 'es' && function_exists('get_field')) {
$seleccion = get_field('portada_articulos', $page) ?: [];
foreach ($seleccion as $p) {
if ($p->post_status === 'publish') $posts[] = $p;
}
}
// Fallback: últimos artículos en el idioma actual
if (empty($posts)) {
$posts = get_posts([
'posts_per_page' => 9,
'category__in' => [fea_cat(1650)],
'category__not_in' => array_map('fea_cat', [6, 21, 22, 23, 26, 58, 40, 1645, 1646, 1647, 1648, 1649, 1651, 1652]),
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
]);
}
if (!$posts) return '';
$html = '<section class="fea-section">'
. '<h2 class="fea-section-title">' . esc_html($atts['titulo']) . '</h2>'
. '<div class="fea-grid">';
foreach ($posts as $post) $html .= fea_card($post);
return $html . '</div></section>';
});
// ── Shortcode: [fea_evangelio] ────────────────────────────────────────────
// Editorial (cat 1646) primero, luego comentarios (cat 1647). Máx 7 en total.
add_shortcode('fea_evangelio', function($atts) {
$lang = fea_current_lang();
$labels = fea_labels();
$atts = shortcode_atts(['titulo' => $labels['evangelio']], $atts);
$editorial = get_posts([
'posts_per_page' => 1,
'category__in' => [fea_cat(1646)],
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
]);
$comentarios = get_posts([
'posts_per_page' => 6,
'category__in' => [fea_cat(1647)],
'category__not_in' => [fea_cat(1646)],
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
]);
$posts = array_merge($editorial, $comentarios);
if (!$posts) return '';
$html = '<section class="fea-section">'
. '<h2 class="fea-section-title">' . esc_html($atts['titulo']) . '</h2>'
. '<div class="fea-grid">';
foreach ($posts as $post) $html .= fea_card($post);
return $html . '</div></section>';
});
// ── Shortcode: [fea_eucaristia] ───────────────────────────────────────────
add_shortcode('fea_eucaristia', function($atts) {
$lang = fea_current_lang();
$labels = fea_labels();
$atts = shortcode_atts(['titulo' => $labels['eucaristia']], $atts);
$posts = get_posts([
'posts_per_page' => 6,
'category__in' => [fea_cat(1648)],
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
]);
if (!$posts) return '';
$html = '<section class="fea-section">'
. '<h2 class="fea-section-title">' . esc_html($atts['titulo']) . '</h2>'
. '<div class="fea-grid">';
foreach ($posts as $post) $html .= fea_card($post);
return $html . '</div></section>';
});
// ── Shortcode: [fea_multimedia] ───────────────────────────────────────────
add_shortcode('fea_multimedia', function($atts) {
$lang = fea_current_lang();
$labels = fea_labels();
$atts = shortcode_atts(['titulo' => $labels['multimedia']], $atts);
$page = fea_front_page_id();
$posts = [];
if ($lang === 'es' && function_exists('get_field')) {
$seleccion = get_field('portada_multimedia', $page) ?: [];
foreach ($seleccion as $p) {
if ($p->post_status === 'publish') $posts[] = $p;
}
}
if (empty($posts)) {
$posts = get_posts([
'posts_per_page' => 4,
'category__in' => array_map('fea_cat', [1649, 26, 58]),
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
]);
}
if (!$posts) return '';
$html = '<section class="fea-section">'
. '<h2 class="fea-section-title">' . esc_html($atts['titulo']) . '</h2>'
. '<div class="fea-grid">';
foreach ($posts as $post) $html .= fea_card($post);
return $html . '</div></section>';
});
// ── Shortcode: [fea_prefooter] — 3 columnas sobre el footer ──────────────────
add_shortcode('fea_prefooter', function() {
$uploads = wp_upload_dir()['baseurl'];
// Columna centro: última noticia de alcance (cat term_id=41)
$latest_noticias = get_posts([
'posts_per_page' => 1,
'category__in' => [fea_cat(41)],
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
]);
$cat_noticias_url = get_category_link(fea_cat(41));
$noticia_title = !empty($latest_noticias) ? fea_title($latest_noticias[0]->post_title) : '';
$noticia_url = !empty($latest_noticias) ? get_permalink($latest_noticias[0]->ID) : $cat_noticias_url;
// Columna derecha: último vídeo (cat term_id=58)
$latest_video = get_posts([
'posts_per_page' => 1,
'category__in' => [fea_cat(58)],
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
]);
$video_url = !empty($latest_video) ? get_permalink($latest_video[0]->ID) : get_category_link(fea_cat(58));
$effa_url = get_permalink(42726);
$suma_url = get_permalink(18036);
$libros_url = get_category_link(1616);
$videos_escuela_url = get_permalink(21921);
ob_start(); ?>
<div class="fea-prefooter">
<div class="fea-prefooter-col">
<a href="<?php echo esc_url($libros_url); ?>">
<img src="<?php echo esc_url($uploads . '/banners/como_adquirir_nuestros_libros.gif'); ?>" alt="Cómo adquirir nuestros libros" style="display:block;margin:0 auto 8px;" />
</a>
<a href="<?php echo esc_url($videos_escuela_url); ?>">
<img src="<?php echo esc_url($uploads . '/quienes_somos/ultimos_videos.jpg'); ?>" alt="Últimos vídeos de la escuela" width="282" height="97" style="display:block;margin:0 auto;" />
</a>
</div>
<div class="fea-prefooter-col fea-prefooter-center">
<a href="<?php echo esc_url($cat_noticias_url); ?>">
<img src="<?php echo esc_url($uploads . '/recursos/noticias_2025.jpg'); ?>" alt="Noticias de alcance" width="300" height="340" style="display:block;margin:0 auto;" />
</a>
<?php if ($noticia_title): ?>
<p class="fea-prefooter-noticia">
<a href="<?php echo esc_url($noticia_url); ?>"><?php echo esc_html($noticia_title); ?></a>
</p>
<?php endif; ?>
</div>
<div class="fea-prefooter-col">
<a href="<?php echo esc_url($suma_url); ?>">
<img src="<?php echo esc_url($uploads . '/banners/este_portal.gif'); ?>" alt="Este portal" style="display:block;margin:0 auto 5px;" />
</a>
<a href="<?php echo esc_url($effa_url); ?>">
<img src="<?php echo esc_url($uploads . '/banners/acceso_a_EFFA.jpg'); ?>" alt="Acceso a EFFA" style="display:block;margin:0 auto 5px;" />
</a>
<a href="<?php echo esc_url($video_url); ?>">
<img src="<?php echo esc_url($uploads . '/banners/video_de_la_semana1.jpg'); ?>" alt="Vídeo de la semana" style="display:block;margin:0 auto;" />
</a>
</div>
</div>
<?php
$css = '<style>
.fea-prefooter{display:flex;gap:1.5rem;justify-content:center;align-items:flex-start;padding:2rem 1rem;border-top:2px solid #e5e5e5;margin-top:3rem;}
.fea-prefooter-col{flex:1;max-width:320px;text-align:center;}
.fea-prefooter-noticia{margin-top:0.6rem;font-size:0.85rem;font-weight:600;color:#0000cc;line-height:1.3;}
.fea-prefooter-noticia a{color:#0000cc;}
@media(max-width:640px){.fea-prefooter{flex-direction:column;align-items:center;}.fea-prefooter-col{max-width:100%;}}
</style>';
return $css . ob_get_clean();
});
// ── Reescribir links internos al idioma activo (Polylang) ─────────────────
add_filter('the_content', function($content) {
if (!function_exists('pll_current_language') || !function_exists('pll_get_post')) return $content;
$lang = pll_current_language();
if (!$lang || $lang === 'es') return $content;
return preg_replace_callback(
'/<a\s([^>]*\s)?href=["\']([^"\']+)["\']([^>]*)>/i',
function($m) use ($lang) {
$href = $m[2];
$home = home_url();
if (strpos($href, $home) === false) return $m[0];
$post_id = url_to_postid($href);
if (!$post_id) return $m[0];
$translated_id = pll_get_post($post_id, $lang);
if (!$translated_id || $translated_id === $post_id) return $m[0];
$new_url = get_permalink($translated_id);
if (!$new_url) return $m[0];
return str_replace($href, $new_url, $m[0]);
},
$content
);
}, 20);
// ── Ordenar categoría Evangelios y Comentarios por título ASC ─────────────
add_action('pre_get_posts', function($query) {
if ($query->is_main_query() && $query->is_category('evangelios-y-comentarios')) {
$query->set('orderby', 'title');
$query->set('order', 'ASC');
}
});
// ── Acordeón de versículos en posts de Evangelios y Comentarios ───────────
add_action('wp_footer', function() {
if (!is_single()) return;
global $post;
if (!has_category('evangelios-y-comentarios', $post)) return;
?>
<style>
.versiculo-group { margin: 0.5em 0; }
.versiculo-toggle {
display: flex; align-items: center; gap: 10px;
cursor: default; margin: 0.8em 0 0;
}
.versiculo-toggle .v-icon {
display: inline-flex; align-items: center; justify-content: center;
width: 22px; height: 22px; min-width: 22px; border-radius: 50%;
background: #c8860a; color: white; font-size: 18px; line-height: 1;
cursor: pointer; user-select: none; font-weight: bold;
transition: background 0.15s;
}
.versiculo-group.open .v-icon { background: #6b3080; }
.v-icon::after { content: '+'; }
.versiculo-group.open .v-icon::after { content: '\2212'; }
.versiculo-body { display: none; padding-left: 4px; }
.versiculo-group.open .versiculo-body { display: block; }
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
var content = document.querySelector('.wp-block-post-content, .entry-content');
if (!content) return;
var verseRe = /^(JUAN|LUCAS|MARCOS|MATEO)\s+\d/i;
function isVerse(el) {
if (el.tagName !== 'P') return false;
var links = el.querySelectorAll('a');
if (links.length !== 1) return false;
return verseRe.test(el.textContent.trim());
}
var children = Array.from(content.children);
var i = 0;
while (i < children.length) {
var child = children[i];
if (isVerse(child)) {
var group = document.createElement('div');
group.className = 'versiculo-group';
var header = document.createElement('div');
header.className = 'versiculo-toggle';
var icon = document.createElement('span');
icon.className = 'v-icon';
var link = child.querySelector('a').cloneNode(true);
header.appendChild(icon);
header.appendChild(link);
group.appendChild(header);
var body = document.createElement('div');
body.className = 'versiculo-body';
child.parentNode.insertBefore(group, child);
child.remove();
i++;
while (i < children.length && !isVerse(children[i])) {
body.appendChild(children[i]);
i++;
}
group.appendChild(body);
icon.addEventListener('click', function() {
this.closest('.versiculo-group').classList.toggle('open');
});
} else {
i++;
}
}
});
</script>
<?php
}, 30);
// ── Avatares en artículos de versículos (Evangelios y Comentarios) ─────────
add_action('wp_footer', function() {
if (!is_single()) return;
global $post, $wpdb;
if (!has_category('evangelios-y-comentarios', $post)) return;
// Extraer todos los slugs de links internos del contenido
preg_match_all('/<a\s[^>]*href=["\']([^"\']+)["\']/', $post->post_content, $m);
$slugs = [];
foreach ($m[1] as $url) {
$slug = basename(rtrim(parse_url($url, PHP_URL_PATH), '/'));
if ($slug) {
$slugs[] = $slug;
}
}
$slugs = array_values(array_unique(array_filter($slugs)));
if (empty($slugs)) return;
// Una sola query: slug → author_id
$ph = implode(',', array_fill(0, count($slugs), '%s'));
$rows = $wpdb->get_results(
$wpdb->prepare(
"SELECT post_name, post_author FROM {$wpdb->posts}
WHERE post_name IN ($ph) AND post_status IN ('publish','draft') AND post_type='post'",
$slugs
)
);
// Construir mapa slug → avatar_url (reutilizando cache de autor)
$author_cache = [];
$avatar_map = [];
foreach ($rows as $row) {
$aid = (int) $row->post_author;
if (!isset($author_cache[$aid])) {
$user = get_userdata($aid);
$author_cache[$aid] = ['src' => get_avatar_url($aid, ['size' => 40, 'default' => 'identicon']), 'name' => $user ? $user->display_name : ''];
}
if ($author_cache[$aid]) {
$avatar_map[$row->post_name] = $author_cache[$aid];
}
}
if (empty($avatar_map)) return;
?>
<style>
.versiculo-body ul { padding-left: 0; list-style: none; }
.versiculo-body li { display: flex; align-items: center; gap: 7px; margin-bottom: 4px; }
.fea-li-avatar { width: 24px; height: 24px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.transl-toggle { font-size: 11px; color: #888; cursor: pointer; margin-left: 6px; user-select: none; white-space: nowrap; flex-shrink: 0; }
.transl-toggle:hover { color: #777; }
.transl-block { display: none; margin: 2px 0 6px 32px; font-size: 11px; line-height: 1.8; }
.transl-block.open { display: block; }
.transl-block a { color: #aaa; display: block; }
.transl-block a:hover { color: #555; }
</style>
<script>
(function() {
var map = <?php echo json_encode($avatar_map); ?>;
function slug(url) {
return url.replace(/\/$/, '').split('/').pop().split('?')[0].split('#')[0];
}
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.versiculo-body li').forEach(function(li) {
var a = li.querySelector('a');
if (!a) return;
var entry = map[slug(a.href)];
if (!entry) return;
var img = document.createElement('img');
img.src = entry.src;
img.title = entry.name;
img.alt = '';
img.className = 'fea-li-avatar';
li.insertBefore(img, li.firstChild);
});
// Colapsar traducciones (p margin-left:90px) bajo "(…)"
document.querySelectorAll('.versiculo-body').forEach(function(body) {
Array.from(body.children).forEach(function(el) {
if (el.tagName !== 'P') return;
var ml = parseInt(el.style.marginLeft || '0');
if (ml < 60) return;
var prev = el.previousElementSibling;
while (prev && prev.tagName !== 'UL') prev = prev.previousElementSibling;
if (!prev) return;
var lis = prev.querySelectorAll('li');
if (!lis.length) return;
var lastLi = lis[lis.length - 1];
el.classList.add('transl-block');
var btn = document.createElement('span');
btn.className = 'transl-toggle';
btn.textContent = 'Otros idiomas (\u2026)';
lastLi.appendChild(btn);
btn.addEventListener('click', function(e) {
e.stopPropagation();
el.classList.toggle('open');
});
});
});
});
})();
</script>
<?php
}, 31);
// ── Shortcodes lista de autores ────────────────────────────────────────────
// IDs excluidos: Fe Adulta (1,890), Ediciones Feadulta (1540), José Chicharro (1049)
if (!defined('FEA_AUTORES_EXCLUIR')) define('FEA_AUTORES_EXCLUIR', [1, 890, 1049, 1540]);
if (!defined('FEA_LANG_ES_TTID')) define('FEA_LANG_ES_TTID', 1404);
function fea_autores_query($min_count = 0, $extra_exclude = []) {
global $wpdb;
$excl = implode(',', array_merge(FEA_AUTORES_EXCLUIR, $extra_exclude));
$ttid = FEA_LANG_ES_TTID;
$having = $min_count > 0 ? "HAVING cnt >= $min_count" : '';
$order = $min_count > 0 ? 'cnt DESC' : 'u.display_name ASC';
return $wpdb->get_results("
SELECT u.ID, u.display_name, COUNT(*) as cnt
FROM {$wpdb->posts} p
JOIN {$wpdb->term_relationships} tr ON tr.object_id = p.ID
JOIN {$wpdb->users} u ON u.ID = p.post_author
WHERE p.post_type = 'post'
AND p.post_status = 'publish'
AND tr.term_taxonomy_id = $ttid
AND u.ID NOT IN ($excl)
GROUP BY p.post_author
$having
ORDER BY $order
");
}
function fea_autores_html($rows, $show_count = false, $extra_class = "") {
$extra = $extra_class ? ' ' . $extra_class : '';
$out = '<ul class="fea-autores-lista' . $extra . '" style="list-style:none;padding-left:0;">';
foreach ($rows as $r) {
$url = esc_url(get_author_posts_url($r->ID));
$name = esc_html($r->display_name);
$avatar = esc_url(get_avatar_url($r->ID, ['size' => 40, 'default' => 'identicon']));
$cnt = $show_count ? ' <span class="fea-autor-cnt">(' . $r->cnt . ')</span>' : '';
$out .= '<li><span style="display:inline-block;width:40px;height:40px;min-width:40px;border-radius:50%;overflow:hidden;flex-shrink:0;"><img src="' . $avatar . '" width="40" height="40" alt="" loading="lazy" style="width:40px;height:40px;object-fit:cover;display:block;"></span>&nbsp;&nbsp;<a href="' . $url . '">' . $name . '</a>' . $cnt . '</li>';
}
$out .= '</ul>';
return $out;
}
add_shortcode('fea_autores_habituales', function() {
$rows = fea_autores_query(30, [948, 1048]);
if (empty($rows)) return '<p>No hay datos disponibles.</p>';
$n = count($rows);
$out = '<details class="fea-autores-section"><summary class="fea-autores-summary">Autores habituales <span class="fea-autor-cnt">(' . $n . ' autores)</span></summary>';
$out .= fea_autores_html($rows, true);
$out .= '</details>';
return $out;
});
add_shortcode('fea_autores_completo', function() {
$rows = fea_autores_query(0);
if (empty($rows)) return '<p>No hay datos disponibles.</p>';
$n = count($rows);
$out = '<details class="fea-autores-section"><summary class="fea-autores-summary">Lista completa por orden alfabético <span class="fea-autor-cnt">(' . $n . ' autores)</span></summary>';
$out .= fea_autores_html($rows, false, 'fea-autores-completo');
$out .= '</details>';
return $out;
});
add_action('wp_head', function() {
if (!is_page('autores-lista')) return;
?>
<style>
.fea-autores-section { margin-bottom: 1.5em; }
.fea-autores-summary {
font-size: 1.3rem; font-weight: 600; cursor: pointer;
padding: 0.5em 0; list-style: none;
display: flex; align-items: center; gap: 0.5em;
border-bottom: 2px solid #046bd2; margin-bottom: 0.8em;
user-select: none;
}
.fea-autores-summary::-webkit-details-marker { display: none; }
.fea-autores-summary::before {
content: "\25B6"; font-size: 0.75em; color: #046bd2;
transition: transform 0.2s; display: inline-block;
}
details[open] > .fea-autores-summary::before { transform: rotate(90deg); }
.fea-autores-lista { list-style: none; padding: 0; margin: 0.5em 0 1em; }
.fea-autores-lista li {
display: flex; align-items: center; gap: 12px;
padding: 3px 0; border-bottom: 1px solid #f5f5f5;
}
.fea-autores-lista li img.fea-autor-avatar {
width: 40px; height: 40px; min-width: 40px;
border-radius: 50%; clip-path: circle(50%);
object-fit: cover; display: block; flex-shrink: 0;
}
.fea-autor-cnt { color: #888; font-size: 0.85em; }
.fea-autores-completo { column-count: 3; column-gap: 2em; }
.fea-autores-completo li { break-inside: avoid; }
@media (max-width: 700px) { .fea-autores-completo { column-count: 2; } }
@media (max-width: 480px) { .fea-autores-completo { column-count: 1; } }
</style>
<?php
}, 20);
// ── Aumentar posts por página en archivos de autor ─────────────────────────
add_action('pre_get_posts', function($query) {
if ($query->is_main_query() && $query->is_author()) {
$query->set('posts_per_page', 30);
}
});
// ── Shortcodes EFFA (Escuela de Formación en Fe Adulta) ────────────────────
// Sección de vídeos (subpáginas): thumbnail YT o primera imagen del contenido
add_shortcode('effa_seccion', function($atts) {
$atts = shortcode_atts(['cat' => '', 'num' => -1], $atts);
if (!$atts['cat']) return '';
$posts = get_posts([
'numberposts' => (int) $atts['num'],
'category_name' => $atts['cat'],
'post_status' => 'publish',
'orderby' => 'meta_value',
'meta_key' => '_effa_joomla_alias',
'order' => 'ASC',
]);
if (!$posts) return '<p>No hay contenido en esta sección todavía.</p>';
$items = [];
foreach ($posts as $p) {
$url = get_permalink($p->ID);
$thumb = get_the_post_thumbnail_url($p->ID, 'medium');
if (!$thumb && preg_match('~youtube\.com/watch\?v=([a-zA-Z0-9_-]+)~', $p->post_content, $m)) {
$thumb = 'https://img.youtube.com/vi/' . $m[1] . '/mqdefault.jpg';
}
if (!$thumb && preg_match('~<img[^>]+src="([^"]+)"~', $p->post_content, $mi)) {
$thumb = $mi[1];
}
$cell = '<td style="width:25%;padding:6px;vertical-align:top;">';
$cell .= '<a href="' . esc_url($url) . '" style="text-decoration:none;color:inherit;display:block;">';
if ($thumb) $cell .= '<img src="' . esc_url($thumb) . '" style="width:100%;height:auto;display:block;border-radius:4px;">';
$cell .= '<strong style="display:block;font-size:0.85rem;margin-top:5px;color:#222;">' . esc_html(fea_title($p->post_title)) . '</strong>';
$cell .= '</a>';
$cell .= '</td>';
$items[] = $cell;
}
$html = '<table style="width:100%;border-collapse:collapse;table-layout:fixed;">';
foreach (array_chunk($items, 4) as $row) {
while (count($row) < 4) $row[] = '<td></td>';
$html .= '<tr>' . implode('', $row) . '</tr>';
}
$html .= '</table>';
return $html;
});
// Hub del proyecto (8 artículos de presentación): primera imagen + extracto
add_shortcode('effa_proyecto', function() {
$posts = get_posts([
'numberposts' => -1,
'category_name' => 'proyecat',
'post_status' => 'publish',
'orderby' => 'name',
'order' => 'ASC',
]);
if (!$posts) return '';
$items = [];
foreach ($posts as $p) {
$url = get_permalink($p->ID);
preg_match('~<img[^>]+src="([^"]+)"~', $p->post_content, $mi);
$thumb = $mi[1] ?? '';
$text = trim(preg_replace('/\s+/', ' ', wp_strip_all_tags($p->post_content)));
$excerpt = wp_trim_words($text, 12, '…');
$cell = '<td style="width:25%;padding:6px;vertical-align:top;">';
$cell .= '<a href="' . esc_url($url) . '" style="text-decoration:none;color:inherit;display:block;">';
if ($thumb) $cell .= '<img src="' . esc_url($thumb) . '" style="width:100%;height:auto;display:block;border-radius:4px;">';
$cell .= '<strong style="display:block;font-size:0.85rem;margin-top:5px;color:#222;">' . esc_html(fea_title($p->post_title)) . '</strong>';
$cell .= '</a>';
$cell .= '<span style="display:block;font-size:0.75rem;color:#666;margin-top:3px;line-height:1.3;">' . esc_html($excerpt) . '</span>';
$cell .= '</td>';
$items[] = $cell;
}
$html = '<table style="width:100%;border-collapse:collapse;table-layout:fixed;">';
foreach (array_chunk($items, 4) as $row) {
while (count($row) < 4) $row[] = '<td></td>';
$html .= '<tr>' . implode('', $row) . '</tr>';
}
$html .= '</table>';
return $html;
});
// CSS EFFA (tabs nav, logo, CTA, cards)
add_action('wp_head', function() {
global $post;
if (!$post || (strpos($post->post_content, 'effa_') === false && strpos($post->post_name ?? '', 'effa') === false)) return;
?>
<style>
.effa-logo { display: block; margin: 0 auto 1rem; max-width: 200px; }
.effa-intro { text-align: center; margin-bottom: 1.5rem; }
.effa-nav { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 1.5rem 0 2rem; padding: 0 0 1rem; list-style: none; border-bottom: 2px solid #e5e5e5; }
.effa-nav li { list-style: none; }
.effa-nav a { display: inline-block; padding: 0.35em 1em; border: 1px solid #ccc; border-radius: 999px; font-size: 0.85rem; text-decoration: none; color: #444; transition: background 0.15s, color 0.15s, border-color 0.15s; }
.effa-nav a:hover, .effa-nav a.active { background: #E89A1A; color: #fff; border-color: #E89A1A; }
.effa-cta-wrap { text-align: center; margin: 2rem 0 1rem; }
.effa-cta { display: inline-block; padding: 0.6em 1.8em; background: #E89A1A; color: #fff; border-radius: 999px; text-decoration: none; font-weight: 700; font-size: 1rem; }
.effa-cta:hover { background: #c97d10; color: #fff; }
</style>
<?php
}, 20);
// ── Índice dinámico de evangelio por libro ─────────────────────────────────
// "Jn 4, 5-42" → "jn-4-5-42" (anchor ID format)
function fea_cita_to_anchor(string $cita): string {
return strtolower(preg_replace('/[,\.\s]+/', '-', trim($cita)));
}
// Parse "Jn 4, 5-42" → [4, 5] for numeric sorting
function fea_cita_sort_key(string $cita): array {
preg_match('/(\d+),\s*(\d+)/', $cita, $m);
return [(int)($m[1] ?? 0), (int)($m[2] ?? 0)];
}
add_shortcode('fea_citas_evangelio', function($atts) {
$atts = shortcode_atts(['libro' => 'Jn'], $atts);
$libro = preg_replace('/[^A-Za-z]/', '', $atts['libro']); // sanitize
global $wpdb;
$cat_com = 1647;
// One query: all posts + author + avatar email in this book
$rows = $wpdb->get_results($wpdb->prepare(
"SELECT p.ID, p.post_title, p.post_author,
pm.meta_value AS cita,
u.display_name AS autor_name,
u.user_email AS autor_email
FROM {$wpdb->posts} p
JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID AND pm.meta_key = '_cita_evangelio'
JOIN {$wpdb->term_relationships} tr ON tr.object_id = p.ID
JOIN {$wpdb->term_taxonomy} tt ON tt.term_taxonomy_id = tr.term_taxonomy_id AND tt.term_id = %d
JOIN {$wpdb->users} u ON u.ID = p.post_author
WHERE p.post_status = 'publish'
AND pm.meta_value LIKE %s
ORDER BY pm.meta_value, p.post_date ASC",
$cat_com, $libro . ' %'
));
if (empty($rows)) return '<p>No hay comentarios para este evangelio.</p>';
// Group by cita
$groups = [];
foreach ($rows as $r) $groups[$r->cita][] = $r;
// Sort groups numerically by chapter, then verse
uksort($groups, function($a, $b) {
[$ca, $va] = fea_cita_sort_key($a);
[$cb, $vb] = fea_cita_sort_key($b);
return $ca !== $cb ? $ca - $cb : $va - $vb;
});
// Avatar URL by email (Gravatar, with fallback)
$avatar_cache = [];
$avatar_url = function(string $email, int $id) use (&$avatar_cache): string {
if (!isset($avatar_cache[$id])) {
$url = get_avatar_url($id, ['size' => 40, 'default' => 'mystery']);
$avatar_cache[$id] = $url ?: 'https://www.gravatar.com/avatar/' . md5(strtolower(trim($email))) . '?s=40&d=mystery';
}
return $avatar_cache[$id];
};
$html = '<div class="fea-indice-evangelio">';
foreach ($groups as $cita => $posts) {
$n = count($posts);
$anchor = fea_cita_to_anchor($cita);
$html .= '<details class="fea-cita-group" id="' . esc_attr($anchor) . '">';
$html .= '<summary class="fea-cita-summary">'
. '<span class="fea-cita-ref">' . esc_html($cita) . '</span>'
. ' <span class="fea-cita-count">' . $n . ' comentario' . ($n !== 1 ? 's' : '') . '</span>'
. '</summary>';
$html .= '<ul class="fea-cita-lista">';
foreach ($posts as $p) {
$av = $avatar_url($p->autor_email, $p->post_author);
$html .= '<li class="fea-cita-item">'
. '<span class="fea-cita-avatar">'
. '<img src="' . esc_url($av) . '" width="40" height="40" alt="" loading="lazy">'
. '</span>'
. '<span class="fea-cita-meta">'
. '<a href="' . esc_url(get_permalink($p->ID)) . '">' . esc_html(fea_title($p->post_title)) . '</a>'
. '<span class="fea-cita-autor">' . esc_html($p->autor_name) . '</span>'
. '</span>'
. '</li>';
}
$html .= '</ul></details>';
}
$html .= '</div>';
return $html;
});
add_action('wp_head', function() {
global $post;
if (!$post || !has_shortcode($post->post_content, 'fea_citas_evangelio')) return;
?>
<style>
.fea-indice-evangelio { margin: 1.5rem 0; }
.fea-cita-group {
border-bottom: 1px solid #e5e5e5;
padding: 0;
}
.fea-cita-summary {
display: flex;
align-items: baseline;
gap: 0.6rem;
padding: 0.75rem 0.5rem;
cursor: pointer;
list-style: none;
user-select: none;
}
.fea-cita-summary::-webkit-details-marker { display: none; }
.fea-cita-summary::before {
content: '';
font-size: 1.1rem;
color: #046bd2;
transition: transform 0.15s;
display: inline-block;
flex-shrink: 0;
}
.fea-cita-group[open] > .fea-cita-summary::before { transform: rotate(90deg); }
.fea-cita-ref {
font-weight: 700;
font-size: 0.95rem;
}
.fea-cita-count {
font-size: 0.8rem;
color: #888;
}
.fea-cita-lista {
list-style: none;
padding: 0 0 0.75rem 1.5rem;
margin: 0;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.fea-cita-item {
display: flex;
align-items: center;
gap: 0.75rem;
}
.fea-cita-avatar {
display: inline-block;
width: 40px; height: 40px; min-width: 40px;
border-radius: 50%;
overflow: hidden;
flex-shrink: 0;
}
.fea-cita-avatar img {
width: 40px; height: 40px;
object-fit: cover; display: block;
}
.fea-cita-meta {
display: flex;
flex-direction: column;
gap: 0.1rem;
line-height: 1.3;
}
.fea-cita-meta a { font-size: 0.9rem; }
.fea-cita-autor {
font-size: 0.78rem;
color: #888;
}
</style>
<?php
}, 20);
// ── Pie de comentario: carta, cita evangelio, otros comentarios ────────────
// Add id anchors to verse headings in the 4 gospel index pages
add_filter('the_content', function($content) {
global $post;
$index_ids = [17874, 17881, 17882, 17883];
if (!isset($post->ID) || !in_array($post->ID, $index_ids)) return $content;
$bookmap = ['JUAN'=>'Jn','LUCAS'=>'Lc','MARCOS'=>'Mc','MATEO'=>'Mt'];
return preg_replace_callback(
'~<p([^>]*)>\s*(<a[^>]*>)\s*((JUAN|LUCAS|MARCOS|MATEO)\s+(\d+),\s*([\d\-\s]+))\s*(</a>)\s*</p>~i',
function($m) use ($bookmap) {
$abbr = $bookmap[strtoupper($m[4])] ?? '';
if (!$abbr) return $m[0];
$anchor = strtolower($abbr . '-' . $m[5] . '-' . preg_replace('/[\s\-]+/', '-', trim($m[6])));
$anchor = rtrim($anchor, '-');
return '<p' . $m[1] . ' id="' . $anchor . '">' . $m[2] . $m[3] . $m[7] . '</p>';
},
$content
);
}, 9); // priority 9 so it runs before the_content shortcode processing
add_filter('the_content', function($content) {
if (!is_single()) return $content;
global $post;
if (!in_category('comentarios-al-evangelio', $post)) return $content;
$carta_id = get_post_meta($post->ID, '_carta_id', true);
$cita = get_post_meta($post->ID, '_cita_evangelio', true);
if (!$carta_id && !$cita) return $content;
$html = '<div class="fea-com-footer">';
// --- Carta de la semana ---
if ($carta_id) {
$carta = get_post($carta_id);
if ($carta && $carta->post_status === 'publish') {
$fecha = date_i18n('j F Y', strtotime($carta->post_date));
$html .= '<div class="fea-com-row fea-com-carta">'
. '<span class="fea-com-label">Carta de la semana</span>'
. '<a href="' . esc_url(get_permalink($carta_id)) . '">'
. esc_html(fea_title($carta->post_title))
. ' <span class="fea-com-fecha">(' . $fecha . ')</span>'
. '</a>'
. '</div>';
}
}
// --- Cita del evangelio — link directo al anchor en el índice ---
if ($cita) {
$book_post_id = ['Mt' => 43908, 'Mc' => 43909, 'Lc' => 43907, 'Jn' => 43906];
$abbr = substr($cita, 0, 2);
$anchor = fea_cita_to_anchor($cita);
$index_url = isset($book_post_id[$abbr])
? esc_url(get_permalink($book_post_id[$abbr]) . '#' . $anchor)
: '';
$html .= '<div class="fea-com-row fea-com-cita">'
. '<span class="fea-com-label">Evangelio</span>'
. ($index_url
? '<a href="' . $index_url . '">' . esc_html($cita) . '</a>'
: '<span>' . esc_html($cita) . '</span>')
. '</div>';
}
// --- Otros comentarios de esta misma cita ---
if ($cita) {
$others = get_posts([
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => 30,
'post__not_in' => [$post->ID],
'category_name' => 'comentarios-al-evangelio',
'meta_key' => '_cita_evangelio',
'meta_value' => $cita,
'orderby' => 'date',
'order' => 'ASC',
]);
if (!empty($others)) {
$html .= '<div class="fea-com-row fea-com-otros">'
. '<span class="fea-com-label">Otros comentarios sobre ' . esc_html($cita) . '</span>'
. '<ul class="fea-com-lista">';
foreach ($others as $o) {
$html .= '<li><a href="' . esc_url(get_permalink($o->ID)) . '">'
. esc_html(fea_title($o->post_title)) . '</a></li>';
}
$html .= '</ul></div>';
}
}
$html .= '</div>'; // .fea-com-footer
return $content . $html;
});
add_action('wp_head', function() {
if (!is_single()) return;
global $post;
if (!$post || !in_category('comentarios-al-evangelio', $post)) return;
?>
<style>
.fea-com-footer {
margin-top: 2.5rem;
padding-top: 1.5rem;
border-top: 2px solid #e5e5e5;
font-size: 0.9rem;
}
.fea-com-row {
display: flex;
flex-wrap: wrap;
gap: 0.4rem 0.8rem;
align-items: baseline;
margin-bottom: 1rem;
}
.fea-com-label {
font-weight: 700;
color: #555;
white-space: nowrap;
flex-shrink: 0;
}
.fea-com-label::after { content: ':'; margin-right: 0.2em; }
.fea-com-fecha { color: #888; font-size: 0.85em; }
.fea-com-lista {
list-style: none;
padding: 0;
margin: 0.3rem 0 0;
display: flex;
flex-wrap: wrap;
gap: 0.3rem 0;
flex-direction: column;
}
.fea-com-lista li::before {
content: '';
margin-right: 0.4em;
color: #046bd2;
}
.fea-com-otros .fea-com-label { display: block; margin-bottom: 0.4rem; }
.fea-com-otros { flex-direction: column; align-items: flex-start; }
</style>
<?php
}, 20);