Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ea5ecafc3d | |||
| 696934016f | |||
| 06d7057e76 | |||
| ef2978fc21 | |||
| 587b10c2b3 | |||
| 6b966a3b5f | |||
| 754915b91f | |||
| 75c0f07eab | |||
| f0946be1fc | |||
| d836297c89 | |||
| d9ff5e891c | |||
| 5cf588ffe3 | |||
| a734ffc933 | |||
| b3a2ae682b | |||
| 825434a0b7 | |||
| 908188ddf2 | |||
| 68b95aae89 | |||
| 82c352aa15 | |||
| 51ae723c5a | |||
| 7a623e8bb9 |
@@ -17,6 +17,7 @@ scripts/ — scripts Python/PHP de migración, traducción, TTS y mantenimi
|
||||
| `fea-homepage.php` | Tarjetas de portada |
|
||||
| `fea-share.php` | Sección "Comparte FeAdulta" (Facebook, Instagram, Imprimir) |
|
||||
| `fea-ui.php` | Estilos globales, menú, fondo cálido en artículos |
|
||||
| `fea-accesibilidad.php` | Tipografía Atkinson Hyperlegible (self-hosted) + toggle claro/oscuro persistente |
|
||||
| `fea-beta-feedback.php` | Barra de feedback beta + endpoint `/fea/v1/lang/{id}` |
|
||||
| `fea-search.php` | Buscador móvil nativo |
|
||||
| `fea-pensamientos.php` | Widget de pensamientos |
|
||||
|
||||
@@ -407,5 +407,22 @@ traducción inmediata, avisar a Rafa directamente además de pedírselo a Hermes
|
||||
| Asignar `_carta_id` a un artículo | `POST /wp-json/fea/v1/carta-id/{id}` con body `{"carta_id": N}` |
|
||||
| Borrar `_carta_id` de un artículo | `DELETE /wp-json/fea/v1/carta-id/{id}` |
|
||||
|
||||
## Evangelio y comentarios: regla y paso obligatorio para Mixbot
|
||||
|
||||
La sección empieza siempre con el enlace al post del evangelio de esa semana (por
|
||||
ejemplo, `MATEO 14, 22-33`) y después van los comentarios. Si ese post no existe,
|
||||
avisad antes de publicar la carta: no empecéis la sección por un comentario.
|
||||
|
||||
Primero guarda la carta (puede quedar como borrador) con esa sección ya completa y
|
||||
anota su ID. Después asigna ese ID a **cada comentario al evangelio** de la semana con
|
||||
el endpoint `POST /wp-json/fea/v1/carta-id/<id-del-comentario>` y el cuerpo
|
||||
`{"carta_id": <id-de-la-carta>}`.
|
||||
|
||||
No hay que escribir la cita ni el texto bíblico a mano: al asignar `_carta_id`, el
|
||||
plugin localiza la única lectura bíblica enlazada en la sección y añade
|
||||
`_cita_evangelio` automáticamente, incluso si el comentario ya estaba publicado. Si
|
||||
la carta tiene cero o varias lecturas, el plugin no adivina: corrige la sección y
|
||||
vuelve a asignar el ID de carta.
|
||||
|
||||
Campos útiles del post: `title`, `content` (HTML), `status` (`draft`/`publish`/`future`),
|
||||
`date` (ISO 8601 para programar), `categories` (array de IDs), `featured_media` (ID de adjunto).
|
||||
|
||||
@@ -0,0 +1,415 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: Fe Adulta — Accesibilidad (tipografía + contraste)
|
||||
* Description: Tipografía Atkinson Hyperlegible global (self-hosted) + toggle
|
||||
* claro/oscuro persistente, pensado para el público mayor de
|
||||
* Fe Adulta (issue rafa/feadulta#196, evidencia en comment #609).
|
||||
* Version: 1.0
|
||||
*
|
||||
* Alcance decidido en el issue (2026-08-08): SOLO dos cosas, sin más superficie
|
||||
* de UI que la necesaria —
|
||||
* 1. Atkinson Hyperlegible sustituye la tipografía del cuerpo, sin selector.
|
||||
* 2. Un único toggle claro/oscuro persistente (claro por defecto).
|
||||
* Nada de panel de accesibilidad, A+/Aa ni cambio de tamaño de letra.
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
if (!defined('FEA_A11Y_ACCENT_DARK')) {
|
||||
// Variante clara del carmesí de marca (#8b1a2e) para que enlaces/foco
|
||||
// cumplan contraste AA sobre fondo casi negro (el carmesí original da
|
||||
// ~1.8:1 sobre #0d0d0d, muy por debajo del 4.5:1 que exige texto).
|
||||
define('FEA_A11Y_ACCENT_DARK', '#e8899e');
|
||||
}
|
||||
|
||||
/* ── 1) Anti-flash: fija el tema ANTES de que el navegador pinte nada ─────
|
||||
* Se engancha con prioridad muy baja para salir de los primeros bytes de
|
||||
* <head>, igual que el resto del sitio guarda preferencias en localStorage
|
||||
* (ver fea-beta-feedback.php). Sin esto habría un parpadeo claro→oscuro. */
|
||||
add_action('wp_head', function () {
|
||||
if (is_admin()) return;
|
||||
?>
|
||||
<script>(function(){try{
|
||||
var t = localStorage.getItem('fea_theme');
|
||||
if (t === 'dark') document.documentElement.setAttribute('data-fea-theme', 'dark');
|
||||
}catch(e){}})();</script>
|
||||
<?php
|
||||
}, 1);
|
||||
|
||||
/* ── 2) Fuente self-hosted + estilos de tema oscuro ───────────────────── */
|
||||
add_action('wp_head', function () {
|
||||
if (is_admin()) return;
|
||||
$base = plugins_url('fea-accesibilidad/fonts', __FILE__);
|
||||
$accent_dark = FEA_A11Y_ACCENT_DARK;
|
||||
?>
|
||||
<style id="fea-a11y">
|
||||
/* ── Atkinson Hyperlegible (Braille Institute, OFL), subset latin ──── */
|
||||
@font-face {
|
||||
font-family: 'Atkinson Hyperlegible';
|
||||
font-style: normal;
|
||||
font-weight: 100 500;
|
||||
font-display: swap;
|
||||
src: url('<?php echo esc_url($base); ?>/atkinson-hyperlegible-regular.woff2') format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Atkinson Hyperlegible';
|
||||
font-style: normal;
|
||||
font-weight: 501 900;
|
||||
font-display: swap;
|
||||
src: url('<?php echo esc_url($base); ?>/atkinson-hyperlegible-bold.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* Sustitución global del cuerpo de texto (no opcional, issue #196).
|
||||
Especificidad baja a propósito: el menú (fea-ui.php, issue #95) usa
|
||||
una sans de UI más específica y debe seguir ganando. */
|
||||
body {
|
||||
font-family: 'Atkinson Hyperlegible', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6,
|
||||
.fea-hero-title,
|
||||
.fea-section-title {
|
||||
font-family: 'Atkinson Hyperlegible', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
||||
}
|
||||
input, textarea, select, button {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
/* ── Toggle claro/oscuro: botón inyectado junto al selector de idioma ── */
|
||||
#fea-theme-btn {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
width: 30px; height: 30px; border-radius: 50%;
|
||||
border: 1px solid rgba(0,0,0,0.25); background: none; color: inherit;
|
||||
cursor: pointer; font-size: 0.95rem; line-height: 1; padding: 0;
|
||||
}
|
||||
#fea-theme-btn:hover { background: rgba(0,0,0,0.06); }
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════
|
||||
MODO OSCURO — activado por [data-fea-theme="dark"] en <html>
|
||||
══════════════════════════════════════════════════════════════════ */
|
||||
html[data-fea-theme="dark"] {
|
||||
/* Deja que el navegador oscurezca controles nativos gratis:
|
||||
audio/video, scrollbars, checkboxes, selects sin estilo propio. */
|
||||
color-scheme: dark;
|
||||
--wp--preset--color--base: #0d0d0d;
|
||||
--wp--preset--color--contrast: #ececec;
|
||||
}
|
||||
html[data-fea-theme="dark"] body {
|
||||
background-color: #0d0d0d !important;
|
||||
color: #ececec !important;
|
||||
}
|
||||
/* En artículos, fea-ui.php fija --wp--preset--color--base en el body
|
||||
para el modo claro. El contenedor desplegable del menú hereda esa
|
||||
variable, así que hay que restaurarla aquí en oscuro. */
|
||||
html[data-fea-theme="dark"] body.single-post {
|
||||
--wp--preset--color--base: #0d0d0d;
|
||||
--wp--preset--color--contrast: #ececec;
|
||||
}
|
||||
html[data-fea-theme="dark"] a,
|
||||
html[data-fea-theme="dark"] a:visited { color: <?php echo esc_html($accent_dark); ?>; }
|
||||
html[data-fea-theme="dark"] a:focus-visible,
|
||||
html[data-fea-theme="dark"] button:focus-visible {
|
||||
outline-color: <?php echo esc_html($accent_dark); ?> !important;
|
||||
}
|
||||
|
||||
/* Cabecera/pie y bloques de grupo sin fondo explícito heredan el body;
|
||||
solo hay que corregir los pocos sitios con color fijo en claro. */
|
||||
html[data-fea-theme="dark"] input,
|
||||
html[data-fea-theme="dark"] textarea,
|
||||
html[data-fea-theme="dark"] select {
|
||||
background-color: #1c1c1c;
|
||||
color: #ececec;
|
||||
border-color: #3a3a3a;
|
||||
}
|
||||
|
||||
/* Search block uses more-specific own color rules. */
|
||||
html[data-fea-theme="dark"] .wp-block-search__inside-wrapper,
|
||||
html[data-fea-theme="dark"] .wp-block-search__input {
|
||||
background-color: #1c1c1c;
|
||||
color: #ececec;
|
||||
border-color: #3a3a3a;
|
||||
}
|
||||
html[data-fea-theme="dark"] .wp-block-search__button {
|
||||
background-color: #ececec;
|
||||
color: #0d0d0d;
|
||||
border-color: #ececec;
|
||||
}
|
||||
|
||||
html[data-fea-theme="dark"] .fea-adv-link-wrap { background: #161311; border-bottom-color: #2a2320; }
|
||||
html[data-fea-theme="dark"] .fea-adv-link { color: <?php echo esc_html($accent_dark); ?>; }
|
||||
|
||||
/* Crop the white edge embedded in slide images in dark mode only. */
|
||||
html[data-fea-theme="dark"] .fea-hero-slider .n2-ss-slide-background-image {
|
||||
overflow: hidden;
|
||||
}
|
||||
html[data-fea-theme="dark"] .fea-hero-slider .n2-ss-slide-background-image img {
|
||||
transform: scale(1.015);
|
||||
}
|
||||
|
||||
/* Portada — franja hero (fea-homepage.php) */
|
||||
html[data-fea-theme="dark"] .fea-hero-band {
|
||||
background: linear-gradient(180deg, #1a1512, #0d0d0d);
|
||||
border-bottom-color: #2a2320;
|
||||
}
|
||||
html[data-fea-theme="dark"] .fea-hero-title,
|
||||
html[data-fea-theme="dark"] .fea-section-title {
|
||||
color: #ececec;
|
||||
}
|
||||
html[data-fea-theme="dark"] .fea-section-label,
|
||||
html[data-fea-theme="dark"] .fea-section-more,
|
||||
html[data-fea-theme="dark"] .fea-card-author {
|
||||
color: <?php echo esc_html($accent_dark); ?>;
|
||||
}
|
||||
html[data-fea-theme="dark"] .fea-hero-meta { color: #b8afa8; }
|
||||
|
||||
/* Tarjetas de autor/artículo (fea-homepage.php) */
|
||||
html[data-fea-theme="dark"] .fea-card {
|
||||
background: #161311;
|
||||
border-color: #2a2320;
|
||||
}
|
||||
html[data-fea-theme="dark"] .fea-card-title a { color: #ececec; }
|
||||
|
||||
/* Archivos (categoría, búsqueda y autor) — fea-homepage.php fija aquí
|
||||
fondos y texto claros que no heredan del tema. */
|
||||
html[data-fea-theme="dark"] .wp-block-query-title {
|
||||
color: #ececec !important;
|
||||
}
|
||||
html[data-fea-theme="dark"] .fea-archive-card {
|
||||
background: #161311;
|
||||
border-color: #2a2320;
|
||||
}
|
||||
html[data-fea-theme="dark"] .fea-archive-title a {
|
||||
color: #ececec;
|
||||
}
|
||||
html[data-fea-theme="dark"] .fea-archive-excerpt {
|
||||
color: #b8afa8;
|
||||
}
|
||||
|
||||
/* Comentarios al evangelio: el panel usa colores editoriales claros fijos. */
|
||||
html[data-fea-theme="dark"] .fea-com-evangelio {
|
||||
background: #1a1711;
|
||||
border-left-color: #c9973e;
|
||||
color: #ececec;
|
||||
}
|
||||
html[data-fea-theme="dark"] .fea-com-evangelio-details summary {
|
||||
color: #f0c674;
|
||||
}
|
||||
|
||||
/* Elementos editoriales presentes en artículos y resultados. */
|
||||
html[data-fea-theme="dark"] .fea-audio {
|
||||
background: #161311;
|
||||
border-color: #3a302b;
|
||||
border-left-color: <?php echo esc_html($accent_dark); ?>;
|
||||
}
|
||||
html[data-fea-theme="dark"] .fea-byline-name { color: #ececec; }
|
||||
html[data-fea-theme="dark"] .fea-byline-cat,
|
||||
html[data-fea-theme="dark"] .fea-eed-link .fecha { color: #b8afa8; }
|
||||
html[data-fea-theme="dark"] .fea-share-rule { border-top-color: #3a302b; }
|
||||
html[data-fea-theme="dark"] .fea-share-row { color: #d5cbc3; }
|
||||
html[data-fea-theme="dark"] .fea-share-label,
|
||||
html[data-fea-theme="dark"] .fea-share-sep { color: #b8afa8; }
|
||||
|
||||
/* Formulario y filtros de búsqueda avanzada. */
|
||||
html[data-fea-theme="dark"] .fea-adv-details {
|
||||
background: #161311; border-color: #3a302b;
|
||||
}
|
||||
html[data-fea-theme="dark"] .fea-adv-summary {
|
||||
background: #24181c; border-bottom-color: #3a302b;
|
||||
color: <?php echo esc_html($accent_dark); ?>;
|
||||
}
|
||||
html[data-fea-theme="dark"] .fea-adv-label,
|
||||
html[data-fea-theme="dark"] .fea-adv-chips-label,
|
||||
html[data-fea-theme="dark"] .fea-adv-meta,
|
||||
html[data-fea-theme="dark"] .fea-archive-card .fea-adv-byline { color: #b8afa8; }
|
||||
html[data-fea-theme="dark"] .fea-adv-chip {
|
||||
background: #2a1b20; border-color: #56343d; color: #ececec;
|
||||
}
|
||||
html[data-fea-theme="dark"] .fea-adv-input,
|
||||
html[data-fea-theme="dark"] .fea-adv-select {
|
||||
background: #1c1c1c; border-color: #3a3a3a; color: #ececec;
|
||||
}
|
||||
html[data-fea-theme="dark"] .fea-adv-count { color: #b8afa8; }
|
||||
|
||||
/* Recopilatorios y galería multimedia. */
|
||||
html[data-fea-theme="dark"] .fea-recop-year { border-bottom-color: #3a302b; }
|
||||
html[data-fea-theme="dark"] .fea-recop-list li { border-bottom-color: #2a2320; }
|
||||
html[data-fea-theme="dark"] .fea-recop-list a,
|
||||
html[data-fea-theme="dark"] .fea-mm-title { color: #ececec; }
|
||||
html[data-fea-theme="dark"] .fea-recop-date,
|
||||
html[data-fea-theme="dark"] .fea-mm-date,
|
||||
html[data-fea-theme="dark"] .fea-mm-excerpt { color: #b8afa8; }
|
||||
html[data-fea-theme="dark"] .fea-mm-card { background: #161311; border-color: #2a2320; }
|
||||
html[data-fea-theme="dark"] .fea-mm-thumb { background: #241f1c; }
|
||||
|
||||
/* Galerías de pensamientos y navegación del tema base. */
|
||||
html[data-fea-theme="dark"] .fea-gallery-item {
|
||||
background: #161311; border-color: #3a302b;
|
||||
}
|
||||
html[data-fea-theme="dark"] .fea-lightbox-frame img { background: #161311; }
|
||||
html[data-fea-theme="dark"] .fea-lightbox button { background: #2a2320; color: #ececec; }
|
||||
html[data-fea-theme="dark"] .wp-block-navigation .wp-block-navigation-item__content {
|
||||
color: #ececec;
|
||||
}
|
||||
html[data-fea-theme="dark"] .wp-block-navigation__submenu-container {
|
||||
background: #161311; border-color: #3a302b;
|
||||
}
|
||||
|
||||
/* Página de apoyo: plantilla propia con colores claros explícitos. */
|
||||
html[data-fea-theme="dark"] .fea-support-page {
|
||||
--fea-support-ink: #ececec;
|
||||
--fea-support-warm: #161311;
|
||||
--fea-support-line: #3a302b;
|
||||
--fea-support-card: #161311;
|
||||
}
|
||||
html[data-fea-theme="dark"] .fea-support-hero,
|
||||
html[data-fea-theme="dark"] .fea-support-banner {
|
||||
background: #1a1711 !important;
|
||||
border-color: #3a302b !important;
|
||||
}
|
||||
html[data-fea-theme="dark"] .fea-support-card,
|
||||
html[data-fea-theme="dark"] .fea-support-button.is-secondary {
|
||||
background: #161311 !important;
|
||||
border-color: #3a302b !important;
|
||||
color: #ececec !important;
|
||||
}
|
||||
html[data-fea-theme="dark"] .fea-support-card__title,
|
||||
html[data-fea-theme="dark"] .fea-support-progress__numbers span,
|
||||
html[data-fea-theme="dark"] .fea-support-progress__note,
|
||||
html[data-fea-theme="dark"] .fea-support-sidebar__small { color: #b8afa8; }
|
||||
html[data-fea-theme="dark"] .fea-support-progress__track { background: #3a302b; }
|
||||
|
||||
/* Colaboradores: stylesheet propio del tema con tarjetas claras y !important. */
|
||||
html[data-fea-theme="dark"] .entry-content .wrapper {
|
||||
background: #161311 !important;
|
||||
border-color: #3a302b !important;
|
||||
color: #ececec !important;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,.45) !important;
|
||||
}
|
||||
html[data-fea-theme="dark"] .entry-content .wrapper strong {
|
||||
color: #ececec !important;
|
||||
}
|
||||
html[data-fea-theme="dark"] .entry-content .wrapper a,
|
||||
html[data-fea-theme="dark"] .entry-content .wrapper a:visited {
|
||||
color: #ececec !important;
|
||||
}
|
||||
html[data-fea-theme="dark"] .entry-content .wrapper a:hover {
|
||||
color: <?php echo esc_html($accent_dark); ?> !important;
|
||||
}
|
||||
|
||||
/* Selector de idioma (fea-homepage.php) */
|
||||
html[data-fea-theme="dark"] #fea-lang-btn { border-color: rgba(255,255,255,0.3); }
|
||||
html[data-fea-theme="dark"] #fea-lang-btn:hover,
|
||||
html[data-fea-theme="dark"] #fea-theme-btn:hover { background: rgba(255,255,255,0.08); }
|
||||
html[data-fea-theme="dark"] #fea-theme-btn { border-color: rgba(255,255,255,0.3); }
|
||||
html[data-fea-theme="dark"] #fea-lang-dropdown {
|
||||
background: #161311; border-color: #3a3a3a;
|
||||
box-shadow: 0 6px 16px rgba(0,0,0,.5);
|
||||
}
|
||||
html[data-fea-theme="dark"] #fea-lang-dropdown a { color: #ececec; }
|
||||
html[data-fea-theme="dark"] #fea-lang-dropdown a:hover { background: #241f1c; }
|
||||
html[data-fea-theme="dark"] #fea-lang-dropdown a[aria-current] { background: #2a2320; color: #fff; }
|
||||
|
||||
/* Buscador móvil (fea-search.php) */
|
||||
html[data-fea-theme="dark"] .fea-search-bar { background: #0d0d0d; border-bottom-color: #2a2320; }
|
||||
html[data-fea-theme="dark"] .fea-search { background: #1c1c1c; border-color: #3a3a3a; }
|
||||
html[data-fea-theme="dark"] .fea-search input[type=search] { color: #ececec; }
|
||||
|
||||
/* Reproductor de audio (fea-audio-player.php) — el <audio> nativo ya
|
||||
cambia solo vía color-scheme; solo hace falta la etiqueta de texto. */
|
||||
html[data-fea-theme="dark"] .fea-audio-label { color: #b8afa8; }
|
||||
|
||||
/* Barra y tarjeta de Beta Feedback (fea-beta-feedback.php) */
|
||||
html[data-fea-theme="dark"] #fea-beta-bar {
|
||||
background: #161311; border-top-color: #2a2320; color: #ececec;
|
||||
}
|
||||
html[data-fea-theme="dark"] #fea-beta-bar .fea-beta-dismiss { color: #b8afa8; }
|
||||
html[data-fea-theme="dark"] #fea-fb .fea-fb-card {
|
||||
background: #161311; border-color: #3a3a3a; color: #ececec;
|
||||
}
|
||||
html[data-fea-theme="dark"] #fea-fb button.fea-fb-vote {
|
||||
background: #1c1c1c; border-color: #3a3a3a; color: #ececec;
|
||||
}
|
||||
html[data-fea-theme="dark"] #fea-fb button.fea-fb-vote.sel {
|
||||
border-color: <?php echo esc_html($accent_dark); ?>; background: #2a1418;
|
||||
}
|
||||
html[data-fea-theme="dark"] #fea-fb textarea { background: #1c1c1c; border-color: #3a3a3a; color: #ececec; }
|
||||
|
||||
/* Barra de consentimiento de cookies (fea-cookie-consent.php) */
|
||||
html[data-fea-theme="dark"] #fea-cc {
|
||||
background: #161311 !important; color: #ececec !important; border-top-color: #2a2320 !important;
|
||||
}
|
||||
html[data-fea-theme="dark"] #fea-cc a { color: #b8afa8 !important; }
|
||||
html[data-fea-theme="dark"] #fea-cc .fea-cc-reject { color: #ececec !important; border-color: #3a3a3a !important; }
|
||||
</style>
|
||||
<?php
|
||||
}, 30);
|
||||
|
||||
/* ── 3) Botón de toggle, inyectado en el header junto al selector de idioma ──
|
||||
* Mismo patrón que el selector de idioma (fea-homepage.php): se crea por JS
|
||||
* y se inserta en el nav del header, para no tocar el template FSE. */
|
||||
add_action('wp_footer', function () {
|
||||
if (is_admin()) return;
|
||||
?>
|
||||
<script>
|
||||
(function() {
|
||||
// El footer puede renderizarse más de una vez en algunos templates FSE.
|
||||
// Evitamos crear un segundo toggle en ese caso.
|
||||
if (document.getElementById('fea-theme-btn')) return;
|
||||
|
||||
var btn = document.createElement('button');
|
||||
btn.id = 'fea-theme-btn';
|
||||
btn.type = 'button';
|
||||
btn.setAttribute('aria-pressed', document.documentElement.getAttribute('data-fea-theme') === 'dark' ? 'true' : 'false');
|
||||
btn.setAttribute('aria-label', 'Cambiar a modo oscuro');
|
||||
btn.textContent = document.documentElement.getAttribute('data-fea-theme') === 'dark' ? '☀️' : '🌙';
|
||||
|
||||
function setLabel(isDark) {
|
||||
btn.setAttribute('aria-pressed', isDark ? 'true' : 'false');
|
||||
btn.setAttribute('aria-label', isDark ? 'Cambiar a modo claro' : 'Cambiar a modo oscuro');
|
||||
btn.textContent = isDark ? '☀️' : '🌙';
|
||||
}
|
||||
|
||||
btn.addEventListener('click', function() {
|
||||
var isDark = document.documentElement.getAttribute('data-fea-theme') === 'dark';
|
||||
if (isDark) {
|
||||
document.documentElement.removeAttribute('data-fea-theme');
|
||||
} else {
|
||||
document.documentElement.setAttribute('data-fea-theme', 'dark');
|
||||
}
|
||||
setLabel(!isDark);
|
||||
try { localStorage.setItem('fea_theme', !isDark ? 'dark' : 'light'); } catch (e) {}
|
||||
});
|
||||
|
||||
function inject() {
|
||||
var header = document.querySelector('header.wp-block-template-part') || document.querySelector('header');
|
||||
if (!header) return;
|
||||
var navGroup = header.querySelector('.wp-block-navigation__container') || header.querySelector('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(btn);
|
||||
if (!navGroup) {
|
||||
header.style.position = 'relative';
|
||||
li.style.cssText = 'position:absolute;top:50%;right:1.5rem;transform:translateY(-50%);';
|
||||
header.appendChild(li);
|
||||
return;
|
||||
}
|
||||
// Insertar después del selector de idioma si ya se inyectó, si no, al final del nav.
|
||||
var langSwitcher = navGroup.querySelector('#fea-lang-switcher');
|
||||
if (langSwitcher) {
|
||||
var anchor = langSwitcher;
|
||||
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
|
||||
}, 21);
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: Fe Adulta — Evangelio en comentarios
|
||||
* Description: Deriva la cita de la carta semanal y muestra la lectura en los comentarios al evangelio.
|
||||
* Version: 1.0
|
||||
*/
|
||||
|
||||
function fea_evangelio_category($es_id) {
|
||||
return function_exists('fea_cat') ? (int) fea_cat($es_id) : (int) $es_id;
|
||||
}
|
||||
|
||||
function fea_evangelio_comment_categories() {
|
||||
return array_map('fea_evangelio_category', [1647, 1646]);
|
||||
}
|
||||
|
||||
function fea_evangelio_is_comment($post_id) {
|
||||
return has_category(fea_evangelio_comment_categories(), $post_id);
|
||||
}
|
||||
|
||||
/** Lee el meta del comentario; las traducciones heredan el dato editorial del original ES. */
|
||||
function fea_evangelio_comment_meta($post_id, $key) {
|
||||
$value = get_post_meta($post_id, $key, true);
|
||||
if ($value !== '' && $value !== null) return $value;
|
||||
if (!function_exists('pll_get_post')) return $value;
|
||||
|
||||
$source_id = (int) pll_get_post((int) $post_id, 'es');
|
||||
return $source_id && $source_id !== (int) $post_id
|
||||
? get_post_meta($source_id, $key, true)
|
||||
: $value;
|
||||
}
|
||||
|
||||
function fea_evangelio_reading_title($cita) {
|
||||
$cita = trim(wp_strip_all_tags((string) $cita));
|
||||
if (!preg_match('/^(Mt|Mc|Lc|Jn)\s+(.+)$/u', $cita, $m)) return '';
|
||||
$books = ['Mt' => 'MATEO', 'Mc' => 'MARCOS', 'Lc' => 'LUCAS', 'Jn' => 'JUAN'];
|
||||
return $books[$m[1]] . ' ' . preg_replace('/\s+/u', ' ', trim($m[2]));
|
||||
}
|
||||
|
||||
/** Localiza la lectura española, sin depender del slug (que puede llevar sufijos). */
|
||||
function fea_evangelio_reading_id($cita) {
|
||||
global $wpdb;
|
||||
static $cache = [];
|
||||
$title = fea_evangelio_reading_title($cita);
|
||||
if ($title === '') return 0;
|
||||
if (isset($cache[$title])) return $cache[$title];
|
||||
// La coincidencia se hace siempre contra la lectura original en español; después
|
||||
// Polylang la sustituye por la traducción del comentario cuando exista.
|
||||
$cat = 1645;
|
||||
$sql = $wpdb->prepare(
|
||||
"SELECT p.ID FROM {$wpdb->posts} p
|
||||
INNER JOIN {$wpdb->term_relationships} tr ON tr.object_id = p.ID
|
||||
INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
|
||||
WHERE p.post_type = 'post' AND p.post_status = 'publish' AND p.post_title = %s
|
||||
AND tt.taxonomy = 'category' AND tt.term_id = %d
|
||||
ORDER BY p.post_date DESC LIMIT 1",
|
||||
$title, $cat
|
||||
);
|
||||
return $cache[$title] = (int) $wpdb->get_var($sql);
|
||||
}
|
||||
|
||||
/** Devuelve los evangelios enlazados en la sección pertinente de una carta. */
|
||||
function fea_evangelio_from_carta($carta_id) {
|
||||
if (!function_exists('fea_parse_carta_sections')) return [];
|
||||
if (function_exists('pll_get_post')) $carta_id = (int) (pll_get_post((int) $carta_id, 'es') ?: $carta_id);
|
||||
$links = fea_parse_carta_sections((int) $carta_id)['evangelio'] ?? [];
|
||||
$result = [];
|
||||
foreach ($links as $id) {
|
||||
$post = get_post($id);
|
||||
if (!$post) continue;
|
||||
$cita = get_post_meta($id, '* Cita biblica', true);
|
||||
if (!$cita && preg_match('/^(MATEO|MARCOS|LUCAS|JUAN)\s+(.+)$/u', trim($post->post_title), $m)) {
|
||||
$abbr = ['MATEO' => 'Mt', 'MARCOS' => 'Mc', 'LUCAS' => 'Lc', 'JUAN' => 'Jn'][$m[1]];
|
||||
$cita = $abbr . ' ' . $m[2];
|
||||
}
|
||||
if (fea_evangelio_reading_title($cita)) $result[(string) $id] = trim($cita);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/** Busca una carta en las tres categorías históricas, dentro de cuatro días. */
|
||||
function fea_evangelio_nearest_carta_id(WP_Post $post) {
|
||||
$date = mysql2date('Y-m-d', $post->post_date, false);
|
||||
$cartas = get_posts([
|
||||
'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 10,
|
||||
'category__in' => array_map('fea_evangelio_category', [6, 21, 22]),
|
||||
'date_query' => [[
|
||||
'after' => date('Y-m-d', strtotime($date . ' -4 days')),
|
||||
'before' => date('Y-m-d', strtotime($date . ' +4 days')), 'inclusive' => true,
|
||||
]],
|
||||
'orderby' => 'date', 'order' => 'DESC',
|
||||
]);
|
||||
if (!$cartas) return 0;
|
||||
usort($cartas, function($a, $b) use ($post) {
|
||||
return abs(strtotime($a->post_date) - strtotime($post->post_date)) <=> abs(strtotime($b->post_date) - strtotime($post->post_date));
|
||||
});
|
||||
return (int) $cartas[0]->ID;
|
||||
}
|
||||
|
||||
/** Escribe solo metadatos ausentes cuando la carta identifica un único evangelio. */
|
||||
function fea_evangelio_capture_meta($post_id) {
|
||||
$post = get_post($post_id);
|
||||
if (!$post || $post->post_type !== 'post' || $post->post_status !== 'publish' || !fea_evangelio_is_comment($post_id)) return false;
|
||||
if (get_post_meta($post_id, '_cita_evangelio', true)) return false;
|
||||
$carta_id = (int) get_post_meta($post_id, '_carta_id', true);
|
||||
if (!$carta_id) $carta_id = fea_evangelio_nearest_carta_id($post);
|
||||
if (!$carta_id) return false;
|
||||
$evangelios = fea_evangelio_from_carta($carta_id);
|
||||
if (count($evangelios) !== 1) {
|
||||
error_log(sprintf('Fe Adulta: comentario %d sin cita; carta %d tiene %d evangelios.', $post_id, $carta_id, count($evangelios)));
|
||||
return false;
|
||||
}
|
||||
update_post_meta($post_id, '_cita_evangelio', reset($evangelios));
|
||||
if (!get_post_meta($post_id, '_carta_id', true)) update_post_meta($post_id, '_carta_id', $carta_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
function fea_evangelio_summary_label($lang) {
|
||||
$labels = [
|
||||
'es' => 'Leer el evangelio', 'en' => 'Read the Gospel', 'fr' => 'Lire l’Évangile',
|
||||
'it' => 'Leggi il Vangelo', 'pt' => 'Ler o Evangelho',
|
||||
];
|
||||
return $labels[$lang] ?? $labels['es'];
|
||||
}
|
||||
|
||||
add_action('transition_post_status', function($new_status, $old_status, $post) {
|
||||
if ($new_status === 'publish' && $old_status !== 'publish') fea_evangelio_capture_meta($post->ID);
|
||||
}, 20, 3);
|
||||
|
||||
/** Completa la cita cuando llega la carta, aunque el comentario ya estuviera publicado. */
|
||||
function fea_evangelio_capture_meta_on_carta_assignment($meta_id, $post_id, $meta_key, $meta_value) {
|
||||
if ($meta_key !== '_carta_id' || !(int) $meta_value) return;
|
||||
fea_evangelio_capture_meta((int) $post_id);
|
||||
}
|
||||
add_action('added_post_meta', 'fea_evangelio_capture_meta_on_carta_assignment', 20, 4);
|
||||
add_action('updated_post_meta', 'fea_evangelio_capture_meta_on_carta_assignment', 20, 4);
|
||||
|
||||
/** Añade referencia y texto plegado, cerrado por defecto, antes del comentario. */
|
||||
add_filter('the_content', function($content) {
|
||||
if (!is_single() || is_admin()) return $content;
|
||||
global $post;
|
||||
if (!$post || !fea_evangelio_is_comment($post->ID)) return $content;
|
||||
$cita = trim((string) fea_evangelio_comment_meta($post->ID, '_cita_evangelio'));
|
||||
if (!$cita) return $content;
|
||||
$reading_id = fea_evangelio_reading_id($cita);
|
||||
$lang = function_exists('pll_current_language') ? pll_current_language() : 'es';
|
||||
if ($reading_id && $lang && $lang !== 'es' && function_exists('pll_get_post')) $reading_id = (int) (pll_get_post($reading_id, $lang) ?: $reading_id);
|
||||
$reading = $reading_id ? get_post($reading_id) : null;
|
||||
$starts_with_cita = preg_match('/^\s*' . preg_quote($cita, '/') . '(?:[\s\.,:;\-–—]|$)/iu', wp_strip_all_tags($content));
|
||||
$html = '<section class="fea-com-evangelio" aria-label="' . esc_attr(fea_evangelio_summary_label($lang)) . '">';
|
||||
if (!$starts_with_cita) $html .= '<p class="fea-com-evangelio-cita">' . esc_html($cita) . '</p>';
|
||||
if ($reading && trim(wp_strip_all_tags($reading->post_content)) !== '') {
|
||||
$html .= '<details class="fea-com-evangelio-details"><summary>' . esc_html(fea_evangelio_summary_label($lang)) . '</summary>'
|
||||
. wpautop(do_shortcode($reading->post_content)) . '</details>';
|
||||
}
|
||||
return $html . '</section>' . $content;
|
||||
}, 8);
|
||||
|
||||
add_action('wp_head', function() {
|
||||
if (!is_single()) return;
|
||||
global $post;
|
||||
if (!$post || !fea_evangelio_is_comment($post->ID)) return;
|
||||
?>
|
||||
<style>
|
||||
.fea-com-evangelio { margin: 0 0 1.75rem; padding: 1rem 1.15rem; background: #f7f4ed; border-left: 4px solid #a77b2d; }
|
||||
.fea-com-evangelio-cita { margin: 0 0 .65rem; font-weight: 700; }
|
||||
.fea-com-evangelio-details summary { cursor: pointer; font-weight: 700; color: #51401e; }
|
||||
.fea-com-evangelio-details > :last-child { margin-bottom: 0; }
|
||||
.fea-com-evangelio-details[open] summary { margin-bottom: .85rem; }
|
||||
</style>
|
||||
<?php
|
||||
});
|
||||
@@ -2044,8 +2044,12 @@ add_filter('the_content', function($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);
|
||||
$carta_id = function_exists('fea_evangelio_comment_meta')
|
||||
? fea_evangelio_comment_meta($post->ID, '_carta_id')
|
||||
: get_post_meta($post->ID, '_carta_id', true);
|
||||
$cita = function_exists('fea_evangelio_comment_meta')
|
||||
? fea_evangelio_comment_meta($post->ID, '_cita_evangelio')
|
||||
: get_post_meta($post->ID, '_cita_evangelio', true);
|
||||
|
||||
if (!$carta_id && !$cita) return $content;
|
||||
|
||||
@@ -2066,23 +2070,6 @@ add_filter('the_content', function($content) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- 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([
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/** Propuesta/aplicación segura del backfill de _cita_evangelio. */
|
||||
/** Uso: wp eval-file scripts/backfill_evangelio_meta.php [--apply] --path=/var/www/html */
|
||||
|
||||
// WP-CLI no reenvía de forma consistente argumentos desconocidos a eval-file.
|
||||
// El modo escritura exige por tanto una intención explícita y auditable:
|
||||
// FEA_EVANGELIO_APPLY=1 wp eval-file scripts/backfill_evangelio_meta.php
|
||||
$apply = getenv('FEA_EVANGELIO_APPLY') === '1'
|
||||
|| in_array('--apply', $_SERVER['argv'] ?? [], true);
|
||||
$posts = get_posts([
|
||||
'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => -1,
|
||||
'category__in' => fea_evangelio_comment_categories(),
|
||||
'meta_query' => [['key' => '_cita_evangelio', 'compare' => 'NOT EXISTS']],
|
||||
'orderby' => 'date', 'order' => 'ASC',
|
||||
]);
|
||||
$out = fopen('php://output', 'w');
|
||||
fputcsv($out, ['post_id', 'fecha', 'carta_id', 'cita_propuesta', 'resultado']);
|
||||
foreach ($posts as $post) {
|
||||
$before_carta = (int) get_post_meta($post->ID, '_carta_id', true);
|
||||
// Las cartas deducidas solo se proponen: la cola histórica requiere revisión humana.
|
||||
$carta_id = $before_carta ?: fea_evangelio_nearest_carta_id($post);
|
||||
$evangelios = $carta_id ? fea_evangelio_from_carta($carta_id) : [];
|
||||
$cita = $before_carta && count($evangelios) === 1 ? reset($evangelios) : '';
|
||||
$result = $cita ? ($apply ? 'aplicado' : 'propuesta') : (!$before_carta ? 'revision_manual_carta_por_fecha' : (count($evangelios) > 1 ? 'revision_manual_multi_evangelio' : 'sin_evangelio'));
|
||||
if ($apply && $cita) {
|
||||
update_post_meta($post->ID, '_cita_evangelio', $cita);
|
||||
if (!$before_carta) update_post_meta($post->ID, '_carta_id', $carta_id);
|
||||
}
|
||||
fputcsv($out, [$post->ID, $post->post_date, $carta_id ?: '', $cita, $result]);
|
||||
}
|
||||
@@ -324,8 +324,7 @@ def main() -> int:
|
||||
ids = [int(x) for x in Path(args.ids_file).read_text().split() if x.strip().isdigit()]
|
||||
log(f"ids-file {args.ids_file}: {len(ids)} posts")
|
||||
else:
|
||||
# La carta puede llevar _carta_id (p. ej. tras un sync); no la traduzcas dos veces.
|
||||
ids = list(dict.fromkeys([args.carta] + carta_article_ids(args.carta)))
|
||||
ids = [args.carta] + carta_article_ids(args.carta)
|
||||
log(f"Carta {args.carta}: {len(ids)} posts (carta + {len(ids)-1} artículos)")
|
||||
|
||||
state = load_state()
|
||||
|
||||
@@ -20,8 +20,7 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
import minimax_tts as mm # get_post_text, add_pauses, t2a, OUT
|
||||
import translate_post as tp # carta_article_ids
|
||||
|
||||
# Voz de respaldo vigente para autores sin clonación propia (actualizada 2026-08).
|
||||
VOICE = "nico_feadulta_es_20260802"
|
||||
VOICE = "NicoFeadulta2026"
|
||||
MODEL = "speech-2.8-hd"
|
||||
CONTAINER = "wordpress-web"
|
||||
PROD = Path(__file__).resolve().parent.parent / "wordpress/wp-content/uploads/tts"
|
||||
|
||||
Reference in New Issue
Block a user