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; ?> '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; ?> '🇪🇸', '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 .= '
  • ' . $flag . ' ' . $code . '
  • '; } ?> .wp-block-post-title { display:none !important; }'; }, 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 = '' . esc_html($cats[0]->name) . ''; } echo '
    ' . '' . '' . '' . '
    ' . '' . $cat_str . '
    ' . '
    '; }); add_action('wp_head', function() { if (!is_single()) return; ?> 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 '
    ' . '
    ' . '' . '' . esc_html($author_name) . '' . '
    ' . '

    ' . esc_html($title) . '

    ' . '
    '; } /** 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 '
    ' . '' . '' . '

    ' . esc_html(fea_title($c->post_title)) . '

    ' . '
    ' . '' . '' . esc_html($author_name) . ' · ' . $fecha . '' . '
    ' . '
    '; }); // ── 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 = '
    ' . '

    ' . esc_html($atts['titulo']) . '

    ' . '
    '; foreach ($posts as $post) $html .= fea_card($post); return $html . '
    '; }); // ── 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 = '
    ' . '

    ' . esc_html($atts['titulo']) . '

    ' . '
    '; foreach ($posts as $post) $html .= fea_card($post); return $html . '
    '; }); // ── 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 = '
    ' . '

    ' . esc_html($atts['titulo']) . '

    ' . '
    '; foreach ($posts as $post) $html .= fea_card($post); return $html . '
    '; }); // ── 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 = '
    ' . '

    ' . esc_html($atts['titulo']) . '

    ' . '
    '; foreach ($posts as $post) $html .= fea_card($post); return $html . '
    '; }); // ── Shortcode: [fea_noticia_centro] — bloque central del footer (Noticias) ─── add_shortcode('fea_noticia_centro', function() { $uploads = wp_upload_dir()['baseurl']; $cat_url = get_category_link(fea_cat(41)); $latest = get_posts([ 'posts_per_page' => 1, 'category__in' => [fea_cat(41)], 'post_status' => 'publish', 'orderby' => 'date', 'order' => 'DESC', ]); $title = !empty($latest) ? fea_title($latest[0]->post_title) : ''; $url = !empty($latest) ? get_permalink($latest[0]->ID) : $cat_url; $html = ''; $html .= ''; $html .= ''; if ($title) { $html .= '

    ' . '' . esc_html($title) . '

    '; } return $html; }); // ── 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( '/]*\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; ?> ]*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; ?> 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 = ''; return $out; } add_shortcode('fea_autores_habituales', function() { $rows = fea_autores_query(30, [948, 1048]); if (empty($rows)) return '

    No hay datos disponibles.

    '; $n = count($rows); $out = '
    Autores habituales (' . $n . ' autores)'; $out .= fea_autores_html($rows, true); $out .= '
    '; return $out; }); add_shortcode('fea_autores_completo', function() { $rows = fea_autores_query(0); if (empty($rows)) return '

    No hay datos disponibles.

    '; $n = count($rows); $out = '
    Lista completa por orden alfabético (' . $n . ' autores)'; $out .= fea_autores_html($rows, false, 'fea-autores-completo'); $out .= '
    '; return $out; }); add_action('wp_head', function() { if (!is_page('autores-lista')) return; ?> 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 '

    No hay contenido en esta sección todavía.

    '; $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('~]+src="([^"]+)"~', $p->post_content, $mi)) { $thumb = $mi[1]; } $cell = ''; $cell .= ''; if ($thumb) $cell .= ''; $cell .= '' . esc_html(fea_title($p->post_title)) . ''; $cell .= ''; $cell .= ''; $items[] = $cell; } $html = ''; foreach (array_chunk($items, 4) as $row) { while (count($row) < 4) $row[] = ''; $html .= '' . implode('', $row) . ''; } $html .= '
    '; 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('~]+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 = ''; $cell .= ''; if ($thumb) $cell .= ''; $cell .= '' . esc_html(fea_title($p->post_title)) . ''; $cell .= ''; $cell .= '' . esc_html($excerpt) . ''; $cell .= ''; $items[] = $cell; } $html = ''; foreach (array_chunk($items, 4) as $row) { while (count($row) < 4) $row[] = ''; $html .= '' . implode('', $row) . ''; } $html .= '
    '; 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; ?> '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 '

    No hay comentarios para este evangelio.

    '; // 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 = '
    '; foreach ($groups as $cita => $posts) { $n = count($posts); $anchor = fea_cita_to_anchor($cita); $html .= '
    '; $html .= '' . '' . esc_html($cita) . '' . ' ' . $n . ' comentario' . ($n !== 1 ? 's' : '') . '' . ''; $html .= '
      '; foreach ($posts as $p) { $av = $avatar_url($p->autor_email, $p->post_author); $html .= '
    • ' . '' . '' . '' . '' . '' . esc_html(fea_title($p->post_title)) . '' . '' . esc_html($p->autor_name) . '' . '' . '
    • '; } $html .= '
    '; } $html .= '
    '; return $html; }); add_action('wp_head', function() { global $post; if (!$post || !has_shortcode($post->post_content, 'fea_citas_evangelio')) return; ?> ID) || !in_array($post->ID, $index_ids)) return $content; $bookmap = ['JUAN'=>'Jn','LUCAS'=>'Lc','MARCOS'=>'Mc','MATEO'=>'Mt']; return preg_replace_callback( '~]*)>\s*(]*>)\s*((JUAN|LUCAS|MARCOS|MATEO)\s+(\d+),\s*([\d\-–\s]+))\s*()\s*

    ~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 '' . $m[2] . $m[3] . $m[7] . '

    '; }, $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 = ''; // .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; ?>