diff --git a/mu-plugins/fea-homepage.php b/mu-plugins/fea-homepage.php index b056c8d..0baef27 100755 --- a/mu-plugins/fea-homepage.php +++ b/mu-plugins/fea-homepage.php @@ -1204,8 +1204,9 @@ add_shortcode('fea_evangelio', function($atts) { // "El Evangelio de cada día": dos devocionales diarios indexados por día del año. // · A la fuente cada día (texto, Fray Marcos) → categoría term_id 14 // · Otro evangelio es posible (vídeo YouTube) → categoría term_id 15 -// Los posts están titulados "D mes" (ej. "21 junio"). Se muestra el de HOY -// (zona horaria del sitio) o el día indicado por ?fed=M-D, con pestañas para +// El texto de Fray Marcos se resuelve contra el calendario litúrgico versionado +// (fecha ISO -> book_index), no por el título civil heredado de Joomla. Se muestra +// el de HOY (zona horaria del sitio) o el día indicado por ?fed=M-D, con pestañas para // que el usuario elija formato y navegación día anterior / siguiente. // Contenido solo en ES (devocional sin traducción) → categorías 14/15 fijas. add_shortcode('fea_evangelio_diario', function($atts) { @@ -1220,23 +1221,44 @@ add_shortcode('fea_evangelio_diario', function($atts) { $gm = (int) $mm[1]; $gd = (int) $mm[2]; if ($gm >= 1 && $gm <= 12 && $gd >= 1 && $gd <= 31) { $m = $gm; $d = $gd; } } - $titulo_dia = $d . ' ' . $MESES[$m]; // "21 junio" + $date = $now->setDate((int) $now->format('Y'), $m, $d); + $calendar_path = defined('FEA_EVANGELIO_CALENDAR_PATH') + ? FEA_EVANGELIO_CALENDAR_PATH + : WP_CONTENT_DIR . '/fea-data/calendario-liturgico-2026-2040.json'; + $calendar = is_readable($calendar_path) + ? json_decode((string) file_get_contents($calendar_path), true) + : null; + $book_index = (int) ($calendar['dates'][$date->format('Y-m-d')]['book_index'] ?? 0); - $find = function($cat) use ($titulo_dia) { + $find_text = function($index) { + global $wpdb; + if ($index < 1) return null; + $id = $wpdb->get_var($wpdb->prepare( + "SELECT p.ID FROM {$wpdb->posts} p + JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID + WHERE pm.meta_key = '_fea_book_index' AND pm.meta_value = %d + AND p.post_type = 'post' AND p.post_status = 'publish' + ORDER BY p.ID ASC LIMIT 1", + $index)); + return $id ? get_post((int) $id) : null; + }; + $texto = $find_text($book_index); + // El vídeo queda deliberadamente con el índice civil heredado: su catálogo + // no forma parte de la migración litúrgica de #20. + $titulo_dia = $d . ' ' . $MESES[$m]; + $find_video = function() use ($titulo_dia) { global $wpdb; $id = $wpdb->get_var($wpdb->prepare( "SELECT p.ID FROM {$wpdb->posts} p 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 - WHERE tt.taxonomy = 'category' AND tt.term_id = %d + WHERE tt.taxonomy = 'category' AND tt.term_id = 15 AND p.post_type = 'post' AND p.post_status = 'publish' AND LOWER(TRIM(p.post_title)) = %s - ORDER BY p.ID ASC LIMIT 1", - $cat, $titulo_dia)); + ORDER BY p.ID ASC LIMIT 1", $titulo_dia)); return $id ? get_post((int) $id) : null; }; - $texto = $find(14); - $video = $find(15); + $video = $find_video(); // navegación por día del calendario (año irrelevante; usamos un año bisiesto fijo) $base = get_permalink(); diff --git a/scripts/import_evangelio_diario.php b/scripts/import_evangelio_diario.php new file mode 100644 index 0000000..ede2788 --- /dev/null +++ b/scripts/import_evangelio_diario.php @@ -0,0 +1,82 @@ + $entry) { + $key = $norm($entry['motto']); + if ($key !== '') $by_motto[$key][] = $i + 1; +} +$existing = get_posts([ + 'post_type' => 'post', 'post_status' => 'any', 'posts_per_page' => -1, + 'category' => 14, 'orderby' => 'ID', 'order' => 'ASC', +]); +$used = []; +$updated = $ambiguous = $unmatched = 0; +$to_update = []; +foreach ($existing as $post) { + $matches = []; + foreach ($by_motto as $motto => $indices) { + if (str_contains($norm($post->post_content), $motto)) $matches = array_merge($matches, $indices); + } + $matches = array_values(array_unique($matches)); + if (count($matches) !== 1 || isset($used[$matches[0]])) { + $ambiguous += count($matches) > 1; $unmatched += count($matches) === 0; + continue; + } + $idx = $matches[0]; $used[$idx] = true; + $entry = $book[$idx - 1]; + $to_update[] = [$post->ID, $idx, $entry]; + $updated++; +} +$planned_new = count($book) - count($used); +if ($ambiguous || $unmatched) { + printf("DRY-RUN: existentes indexados=%d; nuevos=%d; ambiguos=%d; sin coincidencia=%d; total=%d\n", + $updated, $planned_new, $ambiguous, $unmatched, count($book)); + fwrite(STDERR, "ABORTAR APLICACIÓN: hay posts existentes sin correspondencia única.\n"); + exit(3); +} +if ($apply) foreach ($to_update as [$post_id, $idx, $entry]) { + update_post_meta($post_id, '_fea_book_index', (string) $idx); + update_post_meta($post_id, '_fea_cita', $entry['citation']); + update_post_meta($post_id, '_fea_fuente', 'A la fuente cada día — Fray Marcos, entrada #' . $idx); +} +$created = 0; +foreach ($book as $i => $entry) { + $idx = $i + 1; + if (isset($used[$idx])) continue; + $body = '
' . esc_html($entry['citation']) . '
' + . '' . esc_html($entry['gospel']) . '
' + . '' . esc_html($entry['motto']) . '
'; + foreach ($entry['paragraphs'] as $paragraph) $body .= '' . esc_html($paragraph) . '
'; + if ($apply) { + $id = wp_insert_post(['post_title' => $entry['title'], 'post_content' => $body, + 'post_status' => 'publish', 'post_category' => [14]], true); + if (is_wp_error($id)) { fwrite(STDERR, $id->get_error_message() . "\n"); exit(1); } + update_post_meta($id, '_fea_book_index', (string) $idx); + update_post_meta($id, '_fea_cita', $entry['citation']); + update_post_meta($id, '_fea_fuente', 'A la fuente cada día — Fray Marcos, entrada #' . $idx); + } + $created++; +} +printf("%s: existentes indexados=%d; nuevos=%d; ambiguos=%d; sin coincidencia=%d; total=%d\n", + $apply ? 'APLICADO' : 'DRY-RUN', $updated, $created, $ambiguous, $unmatched, count($book));