From 7448baf225e2511a67794dd519c21d92810b6425 Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Wed, 20 May 2026 09:11:28 -0400 Subject: [PATCH] =?UTF-8?q?Documentaci=C3=B3n=20inicial=20del=20proyecto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 20 páginas cubriendo operación, arquitectura WP, áreas funcionales, migración Joomla→WP y roadmap. Información extraída de master-feadulta.md, sesiones OpenClaw (Brevo, Autores, EFFA, Evangelios) y los .md previos del repo (AUDITORIA, PLAN, WORDPRESS_CONFIG, CARTAS_PARA_MIGRAR). --- Alta-boletin-Brevo.md | 56 +++++++++++++ Arquitectura-WordPress.md | 67 ++++++++++++++++ Auditoria-migracion.md | 145 ++++++++++++++++++++++++++++++++++ Autores.md | 84 ++++++++++++++++++++ CSS-y-bugs-Astra.md | 93 ++++++++++++++++++++++ Carta-de-la-semana.md | 55 +++++++++++++ Categorias-y-terminos.md | 92 +++++++++++++++++++++ Credenciales-y-accesos.md | 62 +++++++++++++++ Cutover-DNS.md | 62 +++++++++++++++ EFFA-Escuela.md | 82 +++++++++++++++++++ Evangelios-y-comentarios.md | 82 +++++++++++++++++++ Home.md | 48 ++++++++++- Infraestructura.md | 81 +++++++++++++++++++ Limitaciones-servidor-prod.md | 72 +++++++++++++++++ Polylang-multiidioma.md | 89 +++++++++++++++++++++ Portada-y-shortcodes.md | 77 ++++++++++++++++++ Resumen-del-proyecto.md | 50 ++++++++++++ Roadmap.md | 67 ++++++++++++++++ Scripts-de-migracion.md | 115 +++++++++++++++++++++++++++ Sincronizacion-local-prod.md | 134 +++++++++++++++++++++++++++++++ 20 files changed, 1612 insertions(+), 1 deletion(-) create mode 100644 Alta-boletin-Brevo.md create mode 100644 Arquitectura-WordPress.md create mode 100644 Auditoria-migracion.md create mode 100644 Autores.md create mode 100644 CSS-y-bugs-Astra.md create mode 100644 Carta-de-la-semana.md create mode 100644 Categorias-y-terminos.md create mode 100644 Credenciales-y-accesos.md create mode 100644 Cutover-DNS.md create mode 100644 EFFA-Escuela.md create mode 100644 Evangelios-y-comentarios.md create mode 100644 Infraestructura.md create mode 100644 Limitaciones-servidor-prod.md create mode 100644 Polylang-multiidioma.md create mode 100644 Portada-y-shortcodes.md create mode 100644 Resumen-del-proyecto.md create mode 100644 Roadmap.md create mode 100644 Scripts-de-migracion.md create mode 100644 Sincronizacion-local-prod.md diff --git a/Alta-boletin-Brevo.md b/Alta-boletin-Brevo.md new file mode 100644 index 0000000..a0c7ba0 --- /dev/null +++ b/Alta-boletin-Brevo.md @@ -0,0 +1,56 @@ +# Alta al boletín (Brevo) + +Formulario de suscripción al boletín. Embebido como iframe del formulario de Brevo (antes SendinBlue). + +## Páginas + +| Entorno | Page ID | Slug | +|---|---|---| +| Local | 43892 | `alta` | +| Producción | 43893 | `alta` | + +Título de ambas: **"Recibir la carta de novedades"**. + +## URL del formulario Brevo + +Cuenta Brevo ID `c3555982`. URL completa del iframe: + +``` +https://c3555982.sibforms.com/serve/MUIFANWlhl9iWHgEiWy2i3jSLlsBjc5BpOUzbn8JmYAR7P7A9V-3KDE3A8IhRNYE4TNG7iL2ahP-3WQlPmDLNe2zVm3bLd2BKdJF6RuF9rhobQCn4q-ryKV0XMSJLpkaLgT4h-DlCVDpt3BDNggapNhNRIT2AZoHvRpfqN91HGQ3p_38M3VAi7o2eVmDgSH4ARfqAK6bz8tlm6Lw0A== +``` + +## Contenido de la página (HTML) + +Bloque HTML único con CSS embebido + iframe: + +```html + + +
+ +
+ +``` + +## Detalle CSS importante + +El selector del título es **`.wp-block-post-title`** (FSE), NO `.entry-title` (Astra). Esto es porque las páginas usan el template FSE de Twenty Twenty-Five, no el layout clásico de Astra. + +Si el título no se centra o aparece muy grande, comprobar que estás apuntando al selector FSE correcto. Ver [Arquitectura WordPress](Arquitectura-WordPress) para entender por qué. + +## Entrada en el menú principal + +Añadida en `wp_navigation` ID=1 (el nav block FSE). + +| Entorno | URL | +|---|---| +| Local | `https://farmer.taild3aaf6.ts.net/fea/alta/` | +| Producción | `http://feadulta.org/alta/` | diff --git a/Arquitectura-WordPress.md b/Arquitectura-WordPress.md new file mode 100644 index 0000000..8f569f1 --- /dev/null +++ b/Arquitectura-WordPress.md @@ -0,0 +1,67 @@ +# Arquitectura WordPress + +## Tema y motor de plantillas + +El sitio mezcla **dos motores** de plantillas. Esto es importante porque el tema activo NO controla todo. + +| Capa | Quién la controla | +|---|---| +| Tema activo | **Astra** (clásico, opciones del Customizer) | +| Single post (post individual) | **Twenty Twenty-Five FSE** (template en BD, `wp_posts` ID 42359, `post_type=wp_template`) | +| Portada | Plantilla FSE + shortcodes inyectados desde mu-plugin | +| EFFA, Brevo, Autores, Evangelios | Páginas con HTML plano + shortcodes | + +> **Regla:** para modificar el layout del *single post* hay que editar `wp_posts` ID 42359 o ir a *Apariencia → Editor → Plantillas*. Cambiar el Customizer de Astra ahí no tiene efecto. + +Ver [CSS y bugs del tema Astra](CSS-y-bugs-Astra) para los problemas conocidos del tema. + +## Plugins activos + +| Plugin | Función | +|---|---| +| **FG Joomla to WordPress Premium + K2** | Importador de la migración. Sigue activo porque maneja redirects 301 K2 (regex automática) y conserva la BD `wp_fg_redirect` con 17.853 entradas | +| **Yoast SEO** | SEO básico, sitemaps | +| **Advanced Custom Fields (ACF)** | Campos personalizados (poco uso por ahora) | +| **Filebird** | Organización en carpetas del media library | +| **Smart Slider 3** | Slider de la portada (n2-ss-slide) | +| **UpdraftPlus** | Backups | +| **Polylang** | Multiidioma — ver [Polylang](Polylang-multiidioma) | + +## mu-plugins (must-use) + +En `wp-content/mu-plugins/`. Trackeados en git. + +| Fichero | Qué hace | +|---|---| +| `fea-homepage.php` | Shortcodes de portada, EFFA, autores, evangelios, listado de noticias, CSS condicional | +| `carta-semana-plugin.php` | Redirects de `/carta-de-la-semana/` → `/category/cartasemana/` y al post directo cuando solo hay uno | +| `fa-custom-css.php.disabled` | Inactivo | +| `stop-redirects.php` | Cortocircuita redirects en escenarios de debug | + +Ver detalles en [Portada y shortcodes](Portada-y-shortcodes), [Carta de la semana](Carta-de-la-semana), [EFFA (Escuela)](EFFA-Escuela), [Autores](Autores). + +> **Aviso de fragilidad:** los mu-plugins se han perdido del contenedor local en sesiones pasadas tras `docker recreate` o restore de UpdraftPlus. La versión canónica vive en producción: `/web/wp-content/mu-plugins/`. + +## Menús + +- El menú visible en el header usa un **navigation block** (`wp_navigation` ID=1) con URLs hardcodeadas (`kind:"custom"`). +- El menú clásico `mainmenu` está en BD pero **no se renderiza** — editarlo no tiene efecto. +- El menú multiidioma se inyecta vía JS en `wp_footer` con un mapa PHP construido con `$wpdb` directo (bypasa el filtro de Polylang). + +## Estructura de contenido + +- **post**: artículos editoriales (la mayoría del contenido, ~24.700 items) +- **page**: páginas estáticas (autores, alta, Evangelios convertidos a posts ya, etc.) +- **attachment**: 6.195 medios migrados +- **wp_navigation**: menús FSE (ID=1 es el principal) +- **wp_template / wp_template_part**: plantillas y partes FSE (ej. footer central con `[fea_noticia_centro]`) + +## Categorías importantes + +Ver [Categorías y términos](Categorias-y-terminos) para el listado completo de term_ids relevantes (EFFA, evangelios, lecturas, eucaristía, multimedia, artículos, cartas). + +## URLs y permalinks + +- Estructura: `/%postname%/` +- Idiomas como subdirectorios gestionados por Polylang (`/en/`, `/fr/`, `/it/`, `/pt/`) +- Categorías: `/category//` diff --git a/Auditoria-migracion.md b/Auditoria-migracion.md new file mode 100644 index 0000000..b25ad1e --- /dev/null +++ b/Auditoria-migracion.md @@ -0,0 +1,145 @@ +# Auditoría de migración + +Estado de la migración Joomla → WordPress en base a la auditoría del **2026-03-04** y deltas posteriores. + +## Resumen de contenido + +### Origen: Joomla + +| Origen | Total | Publicados | En papelera | +|---|---|---|---| +| `jos_content` (Joomla nativo) | 102 | 55 | — | +| `ew4r_k2_items` (K2) | 17.712 | 15.764 | 54 | +| `jos_categories` | 33 | 33 | — | +| `ew4r_k2_categories` | 7 | 7 | — | +| K2 items en borrador | 1.885 | — | — | + +> Los artículos de `jos_content` son páginas de estructura de Joomla (demo, categorías), no contenido editorial. Todo el contenido real vivía en K2. + +### Destino: WordPress + +| Tipo | Total | Publicados | Borrador | +|---|---|---|---| +| `wp_posts` (post) | 26.554 (auditoría) → 24.778 (actual tras delta de mayo) | 24.631 | 1.922 | +| `wp_posts` (attachment) | 6.195 | 6.195 | — | +| `wp_posts` (page) | 1 → varias (autores, alta, evangelios convertidos, EFFA) | — | — | +| Categorías | 1.351 | — | — | +| Tags | 12 | — | — | +| Usuarios | 1.182 | — | — | +| Menús importados | 19 → 6 útiles tras limpieza | — | — | + +## Cobertura K2 → WP + +| Métrica | Valor | +|---|---| +| K2 items publicados (no papelera) en Joomla | **15.764** | +| WP posts con `_fgj2wp_old_k2_id` (migrados de K2) | **17.649** | +| WP posts con `_fgj2wp_old_id` (migrados de `jos_content`) | **8.904** | + +✅ **No hay artículos K2 publicados sin migrar.** Diferencia ~1.885 = K2 que estaban en borrador en Joomla, también importados como drafts en WP (decisión: se mantienen como drafts). + +## Distribución por año (comparativa) + +| Año | Joomla K2 | WordPress | +|---|---|---| +| 2008 | 217 | 217 | +| 2009 | 254 | 254 | +| 2010 | 452 | 451 | +| 2011 | 630 | 613 | +| 2012 | 923 | 1.730 | +| 2013 | 986 | 1.645 | +| 2014 | 931 | 1.696 | +| 2015 | 920 | 1.641 | +| 2016 | 830 | 1.442 | +| 2017 | 739 | 1.288 | +| 2018 | 729 | 1.284 | +| 2019 | 1.002 | 1.495 | +| 2020 | 1.053 | 1.584 | +| 2021 | 1.065 | 1.594 | +| 2022 | 1.032 | 1.615 | +| 2023 | 1.276 | 1.797 | +| 2024 | 1.348 | 2.410 | +| 2025 | 1.380 | 1.814 | +| 2026 | 51 | 61 | + +> Los años 2012-2018 muestran más posts en WP que en Joomla K2 → probablemente por la importación de `jos_content` (páginas de estructura). Revisión pendiente de duplicados en ese rango. + +## Problemas detectados y estado + +### 🔴 Críticos — todos resueltos + +| Problema | Estado | Cómo se resolvió | +|---|---|---| +| 1.922 posts en borrador | ✅ Decisión 2026-03-04 | Se mantienen como borradores (eran drafts en Joomla) | +| 508 posts con enlaces internos rotos (`index.php?option=`) | ✅ 2026-03-04 | `scripts/fix_joomla_links.php` — **93.030 links reemplazados**, 42 sin resolver (K2 papelera) | +| 429 posts con URLs `/es/ayuda/` | ✅ 2026-03-04 | Mismo script | + +### 🟡 Importantes + +| Problema | Estado | Cómo se resolvió | +|---|---|---| +| 100 categorías con nombres numéricos | ✅ 2026-03-04 | Causa: FG importó el campo K2 "Autor" (extra field id=12) como nombre de categoría. `scripts/fix_numeric_categories.php` → 5 renombradas, 95 fusionadas con autores existentes | +| 333 posts con URLs absolutas a `feadulta.com` | ✅ 2026-03-04 | Son archivos externos (`/anterior/`, `/ediciones/`) — se mantienen | +| 81 posts publicados con `< 100` chars | ✅ 2026-03-04 | 13 convertidos a borrador, resto válidos | +| 1 post publicado con contenido vacío | ✅ 2026-03-04 | Revisado | + +### 🟢 Menores + +| Problema | Estado | Cómo se resolvió | +|---|---|---| +| Redirects 301 | ✅ 2026-03-04 | `scripts/generate_k2_redirects.php` — **17.853 entradas** en `wp_fg_redirect`, regex automática del plugin FG K2. Verificado: 301 funcional para `/es/.../item/NNN-alias.html` | +| Solo 1 página en WP | ✅ Solucionado | Creadas páginas de Autores, EFFA, Alta, Evangelios convertidos a posts | +| 13 menús técnicos de Joomla (`art1menu`, `comentmenu`, `eucamenu`…) | ✅ 2026-03-04 | Eliminados. Quedan 6 útiles: `mainmenu`, `secciones`, `colaboradores`, `resumeneslibros`, `idioma`, `libros` | + +## Medios (attachments) + +| Métrica | Valor | +|---|---| +| Attachments en WP | 6.195 | +| Attachments con archivo asociado | 6.195 ✅ | +| Posts con URLs absolutas a `feadulta.com` | 333 (archivos externos legítimos) | +| Imágenes con rutas legacy `/images/stories/` | 0 ✅ | +| Imágenes con rutas legacy `/media/k2/` | 0 ✅ | + +## Usuarios + +- **1.182 usuarios** migrados +- Roles verificados (2026-03-04): 1.162 subscribers, 14 authors, 6 administrators ✅ + +## Delta de mayo 2026 (post-auditoría) + +- **169 K2 items nuevos** (id > 17873) → WP IDs **43914–44082** +- **8 cartas nuevas** (catid 27/40/41, id > 9043) → WP IDs **44083–44090** +- **58 ítems `ew4r_content`** no-carta → WP IDs **44091–44151** +- **8 noticias de alcance del gap** (Joomla ids 8977–9034) → WP IDs **44152–44159** +- **`_carta_id`** asignado a los 169 K2 items nuevos por matching de fecha + +Scripts: ver [Scripts de migración](Scripts-de-migracion). + +## Pendientes documentados en su día (auditoría 2026-03-04) + +- [ ] Verificar scripts de importación sin log: `import_pensamientos.php`, `import_libros_sub.php`, `import_remaining.php`, `bulk_import.php`, `fix_attachments.php` +- [ ] (Resuelto en marzo) Estructura "Carta semanal → artículos relacionados" → implementado via `_carta_id` +- [ ] Configurar página de inicio del sitio → ✅ hecho con 5 portadas Polylang + +## Plan de acción priorizado (histórico) + +| Prioridad | Tarea | Estado | +|---|---|---| +| 1 | Arreglar 508 links `index.php?option=` | ✅ | +| 1 | Arreglar 429 links `/es/ayuda/` | ✅ | +| 1 | Renombrar 100 categorías numéricas | ✅ | +| 2 | 1.922 borradores → revisar | ✅ (decisión: mantener) | +| 2 | Revisar 81 posts cortos | ✅ | +| 2 | Revisar 333 referencias a `feadulta.com` | ✅ | +| 3 | Implementar redirects 301 | ✅ | +| 3 | Verificar scripts de importación sin log | ⏳ pendiente | +| 3 | Estructura Cartas Semanales | ✅ (`_carta_id`) | +| 3 | Revisar roles de usuarios | ✅ | +| 3 | Simplificar menús | ✅ | +| 4 | Página de inicio | ✅ | +| 4 | Plugins post-launch (AdSense, TTS, Wordfence) | ⏳ v2 | +| 4 | Test completo | ⏳ pre-cutover | +| 4 | Configurar DNS y lanzar | ⏳ pre-cutover | + +Ver [Roadmap](Roadmap) y el board de issues para el estado actual. diff --git a/Autores.md b/Autores.md new file mode 100644 index 0000000..aee709a --- /dev/null +++ b/Autores.md @@ -0,0 +1,84 @@ +# Autores + +Listado de autores del portal, en una página dedicada con dos shortcodes. + +## Página "Listado de Autores" + +- **ID:** 18636 +- **Slug:** `autores-lista` +- **Contenido:** HTML plano con dos shortcodes (sin bloques Gutenberg — ver más abajo) + +## Shortcodes (en `fea-homepage.php`) + +### `[fea_autores_habituales]` + +Autores con **≥ 30 artículos en español**, ordenados por número descendente. Muestra conteo entre paréntesis. + +Constantes relevantes: + +```php +FEA_AUTORES_EXCLUIR = [1, 890, 1049, 1540] // Fe Adulta x2, Ediciones Feadulta, José Chicharro +FEA_LANG_ES_TTID = 1404 // term_taxonomy_id del idioma ES en Polylang +``` + +Exclusiones extra para "habituales": IDs 948 y 1048 (Inma Calvo, duplicados de usuario). Por eso la llamada interna es: + +```php +fea_autores_query(30, [948, 1048]) +``` + +### `[fea_autores_completo]` + +Todos los autores con al menos un artículo en ES, orden A-Z. Layout 3 columnas (responsive: 2 en tablet, 1 en móvil). Clase wrapper `fea-autores-completo`. + +## Helpers + +```php +fea_autores_query($min_count, $extra_exclude) +fea_autores_html($rows, $show_count, $extra_class) +``` + +`fea_autores_query` hace `JOIN` con `wp_term_relationships` filtrando por el `term_taxonomy_id` del idioma ES — así solo cuenta artículos en español. + +## Avatares circulares (solución definitiva) + +`border-radius: 50%` y `clip-path` aplicados en CSS son **ignorados por Astra**. La única forma que funciona es un wrapper `` con `overflow:hidden` y los estilos inline: + +```html + + + +``` + +El `overflow:hidden` del span hace el recorte circular real. CSS externo NO puede sobreescribir inline styles porque la cascada lo posiciona después. + +## Bullets eliminados + +`list-style: none` aplicado en clase CSS es **también ignorado por Astra**. Solución: inline en el `
    `: + +```php +'
      ' +``` + +## CSS en `wp_head` (solo en página `autores-lista`) + +- `.fea-autores-summary` — cabecera colapsable con flecha ▶ azul `#046bd2` que rota al abrir +- `details[open] > .fea-autores-summary::before { transform: rotate(90deg); }` +- Secciones con `
      /` nativo HTML (sin JS), empiezan colapsadas + +## Archivo de autor — 30 posts por página + +```php +add_action('pre_get_posts', function($query) { + if ($query->is_main_query() && $query->is_author()) { + $query->set('posts_per_page', 30); + } +}); +``` + +## Aviso: contenido de páginas WP desde CLI + +El contenido de la página 18636 se guardó como **HTML plano con shortcodes**, sin bloques Gutenberg (``). Al actualizar con `wp post update --post_content` desde CLI, los bloques se guardaban como texto literal y se mostraban en la página. + +Ver [CSS y bugs del tema Astra](CSS-y-bugs-Astra) para esta y otras gotchas. diff --git a/CSS-y-bugs-Astra.md b/CSS-y-bugs-Astra.md new file mode 100644 index 0000000..04bf25b --- /dev/null +++ b/CSS-y-bugs-Astra.md @@ -0,0 +1,93 @@ +# CSS y bugs del tema Astra + +Reglas y *gotchas* aprendidas durante la implementación. Antes de pelearte con CSS, lee esto. + +## Resumen ejecutivo + +> Astra **sobreescribe** algunas propiedades visuales en su cascada con suficiente especificidad como para que un CSS externo con `!important` NO baste. La única solución probada es **inline styles** sobre el elemento. + +## Propiedades que Astra ignora desde CSS externo + +| Propiedad | Síntoma | Workaround | +|---|---|---| +| `border-radius` (en imágenes) | El radio no se aplica | Wrapper `` con `overflow:hidden` + inline styles | +| `list-style` | Bullets siguen visibles aunque pongas `none` | Inline `style="list-style:none;padding-left:0;"` en el `
        ` | +| `height: auto` en `` | El alto se fuerza por Astra | Inline `style="height:auto;"` | +| `clip-path` (en imágenes) | Ignorado | Wrapper con `overflow:hidden` | + +## Avatares circulares (caso canónico) + +Patrón que funciona en este sitio: + +```html + + + +``` + +- El `border-radius:50%` está en el span (no la imagen). +- El `overflow:hidden` del span hace el recorte real. +- La imagen es un block sólido sin radio propio. + +## `!important` NO siempre gana + +Cuando Astra aplica sus reglas con igual o mayor especificidad en cascada, `!important` en CSS externo NO basta. La cascada de inline styles está por encima del CSS de tema, así que inline siempre gana. + +## NO añadir esto a CSS global + +```css +html { font-size: 125% } +zoom: 1.25 +``` + +**Rompe Smart Slider 3.** El slider de la portada se descalibra (overflow, posicionamiento incorrecto de slides). Si necesitas escalar tipografía, hazlo selector por selector. + +## Selectores FSE vs Astra + +| Tipo de página | Selector del título | +|---|---| +| Post / page con template FSE (Twenty Twenty-Five) | `.wp-block-post-title` | +| Resto de páginas Astra clásicas | `.entry-title` | + +Si tu CSS de título no aplica, lo más probable es que estés apuntando al selector equivocado para ese tipo de página. Casi todo el sitio ya está bajo FSE. + +## CSS condicional inyectado en `wp_head` + +`fea-homepage.php` inyecta CSS solo en las páginas que lo necesitan, en lugar de cargarlo en todo el sitio: + +- Portada: `fea_is_front_page()` true +- Página `autores-lista`: ID 18636 +- Páginas EFFA: `strpos($post->post_content, 'effa_') !== false` || `post_name` contiene `effa` +- Brevo: por `page-id-43892` / `page-id-43893` en la propia página + +Esto evita conflictos con el resto del sitio. + +## `` para layout en bloques FSE + +CSS grid / flexbox **no funciona bien dentro de bloques Gutenberg FSE** en este sitio (algún bug de breakpoints). El layout multicolumna se hace con HTML `
        ` + inline styles. Ejemplos: EFFA hub, listado autores en 3 columnas (en su variante anidada), etc. + +## Contenido de páginas WP desde CLI: NO usar bloques Gutenberg + +`wp post update --post_content ' ... '` desde CLI guarda el bloque **como texto literal** y se renderiza así en la página (`` se ve a ojo en pantalla). + +**Regla:** desde CLI, escribir **HTML plano con shortcodes**. Los bloques solo desde el editor de Gutenberg (UI), nunca desde `wp-cli`. + +## `str_replace` en patches de PHP + +Antes de hacer `str_replace` para parchear `fea-homepage.php` u otro fichero PHP, **siempre verificar el string exacto con `grep`**. Espacios, comillas y saltos de línea cambian entre revisiones y el `str_replace` silenciosamente no reemplaza nada. + +```bash +grep -n 'fragmento exacto' wordpress/wp-content/mu-plugins/fea-homepage.php +``` + +Confirma que el match existe antes de aplicar el patch. + +## Resumen visual + +Si algo no se ve como esperas: + +1. ¿Es border-radius / list-style / height en imagen? → inline. +2. ¿Es bloque FSE? → `.wp-block-*`, no `.entry-*`. +3. ¿Es selector con `!important` y no aplica? → probablemente Astra te sobreescribe. Usa inline. +4. ¿Es layout multicolumna en Gutenberg FSE? → `
        ` con inline styles. diff --git a/Carta-de-la-semana.md b/Carta-de-la-semana.md new file mode 100644 index 0000000..41f86e3 --- /dev/null +++ b/Carta-de-la-semana.md @@ -0,0 +1,55 @@ +# Carta de la semana + +Sección editorial principal del sitio. Cada semana se publica una "carta" + un conjunto de artículos asociados que la desarrollan. + +## Plugin + +`carta-semana-plugin.php` (mu-plugin). + +- **Local:** `wordpress/wp-content/mu-plugins/carta-semana-plugin.php` +- **Container:** `/var/www/html/wp-content/mu-plugins/carta-semana-plugin.php` +- **Producción:** `/web/wp-content/mu-plugins/carta-semana-plugin.php` + +> **Aviso histórico:** este mu-plugin **se vació en local** en una sesión pasada (probablemente por un `docker recreate` o restauración UpdraftPlus). Si vuelve a desaparecer/quedar vacío, restaurar desde producción. Si no, las categorías `cartasemana` / `carta-semana-pasada` muestran el archivo de categoría en vez de redirigir al post de la semana. + +## Comportamiento + +1. Redirige `/carta-de-la-semana/` → `/category/cartasemana/`. +2. Si la categoría `cartasemana` tiene `count == 1`, `template_redirect` redirige a ese post directamente. +3. Lo mismo para `carta-semana-pasada`. + +## Categorías relacionadas + +| term_id | Slug | Uso | +|---|---|---| +| 6 | `cartasemana` | Categoría principal de cartas | +| 21 / 22 | `carta-semana-pasada` | (revisar slugs exactos en BD) | + +Ver [Categorías y términos](Categorias-y-terminos). + +## Relación carta ↔ artículos + +Cada artículo de una semana lleva un meta `_carta_id` con el ID del post-carta al que pertenece. Esto permite renderizar `[fea_articulos_semana]` en la portada y los listados internos. + +- Total de metas `_carta_id` en producción: **3.528** (migrados 2026-03-15). +- El meta se asigna por **matching de fecha**: artículos cuya `post_date` cae en el rango de una carta se vinculan a esa carta. Lógica implementada en los scripts de delta (`import_new_k2_items.py` + `fix_imported_k2_metas.py`). + +## Bug conocido: 404 en idiomas + +`/category/cartasemana/` en EN/FR/IT/PT devuelve **404**. + +- **Issue:** [#2 Fix carta de la semana 404 en idiomas EN/FR/IT/PT](http://localhost:3000/rafa/feadulta/issues/2) +- **Causa raíz:** combinación del bug WP_Tax_Query con Polylang. Detallado en [Polylang multiidioma](Polylang-multiidioma). +- **Síntoma SQL:** `get_posts(['lang'=>$lang, 'category__in'=>[$cat]])` genera `AND 0=1` en la query, no devuelve nada y WP responde 404. +- **Fix esperado:** no pasar `lang` explícito a `get_posts` cuando ya se está dentro del contexto de un idioma — Polylang filtra automáticamente. + +## Cartas históricas + +Histórico de cartas en `analisis-cartas/` del repo. Documentación de cartas para migrar (legacy) en el repo: [`CARTAS_PARA_MIGRAR.md`](http://localhost:3000/rafa/feadulta/src/branch/main/CARTAS_PARA_MIGRAR.md). + +## Delta de mayo 2026 + +- Importadas 8 cartas nuevas (ew4r_content id > 9043, catid 27/40/41) → WP IDs **44083–44090** (`scripts/import_new_cartas.py`). +- `_carta_id` reasignado para los 169 K2 items nuevos por matching de fecha. + +Ver [Scripts de migración](Scripts-de-migracion). diff --git a/Categorias-y-terminos.md b/Categorias-y-terminos.md new file mode 100644 index 0000000..e3bfa47 --- /dev/null +++ b/Categorias-y-terminos.md @@ -0,0 +1,92 @@ +# Categorías y términos + +Listado de `term_id`s y slugs relevantes para el código y para operaciones de mantenimiento. + +## Categorías principales + +| term_id | Slug | Nombre / uso | +|---|---|---| +| 6 | `cartasemana` | Carta de la semana (categoría principal de cartas) | +| 21 | (revisar) | Carta semana pasada | +| 22 | (revisar) | Carta semana pasada | +| 28 | `evangelios-y-comentarios` | Posts de los 4 evangelios (`Jn/Lc/Mt/Mc`) | +| 63 | `effa` | EFFA (categoría padre) | +| 64 | `proyecat` | Artículos del hub EFFA (8 items) | +| 1645 | (revisar) | Lecturas bíblicas | +| 1646 | (revisar) | Comentario editorial (primer comentario no-lectura por carta) | +| 1647 | (revisar) | Comentarios al evangelio (los que renderiza `[fea_citas_evangelio]`) | +| 1648 | `eucaristia` | Eucaristía | +| 1649 | `multimedia` | Multimedia | +| 1650 | `articulos` | Artículos | + +## Subcategorías EFFA + +| term_id | Slug | +|---|---| +| 1653 | `effa-espiritualidad` | +| 1654 | `effa-biblia-at` | +| 1655 | `effa-biblia-nt` | +| 1656 | `effa-cristologia` | +| 1657 | `effa-nuestras-creencias` | +| 1658 | `effa-fe-y-cultura` | + +## Idiomas (Polylang) + +| Idioma | term_taxonomy_id | +|---|---| +| ES | **1404** (constante `FEA_LANG_ES_TTID` en `fea-homepage.php`) | +| EN | (consultar en `wp_term_taxonomy` con `taxonomy='language'`) | +| FR | … | +| IT | … | +| PT | … | + +## Bug crítico: `wp post term set` con IDs numéricos + +```bash +wp post term set 12345 category 28 +``` + +❌ **No asigna term_id 28.** wp-cli interpreta `28` como **nombre** de término — si no existe, crea un término nuevo llamado literalmente `"28"`. (Confirmado en su día con `term_id=3299` creado por error). + +```bash +wp post term set 12345 category evangelios-y-comentarios +``` + +✅ Correcto. Usar siempre el slug. + +## Mapeo idioma EN/FR/IT/PT × `get_term_by` + +`get_term_by('slug', $slug, 'category')` **devuelve `false`** cuando el idioma actual es EN/FR/IT/PT. + +Workaround: `$wpdb` directo. Ver [Polylang multiidioma](Polylang-multiidioma). + +## Exclusiones de autores + +`FEA_AUTORES_EXCLUIR = [1, 890, 1049, 1540]` + +| ID | Usuario | +|---|---| +| 1 | Fe Adulta | +| 890 | Fe Adulta (duplicado) | +| 1049 | Ediciones Feadulta | +| 1540 | José Chicharro | + +Adicionalmente para `[fea_autores_habituales]`: + +`fea_autores_query(30, [948, 1048])` → excluye IDs 948 y 1048 (Inma Calvo duplicada). + +## Cómo verificar un term_id + +```bash +# Local +docker exec wordpress-mysql mysql -uwordpress_user -pwordpress_pass wordpress_db -e " +SELECT t.term_id, t.name, t.slug, tt.taxonomy, tt.count +FROM wp_terms t +JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id +WHERE tt.taxonomy = 'category' +ORDER BY t.term_id;" + +# Prod +ssh feadultada@feadulta.org "mysql -h127.0.0.1 -umyfeadultaa5 -pKjyGU29h \ + 278025353wordpress20260112013937 -e \"SELECT t.term_id, t.name, t.slug, tt.count FROM wp_terms t JOIN wp_term_taxonomy tt USING(term_id) WHERE tt.taxonomy='category' ORDER BY t.term_id;\"" +``` diff --git a/Credenciales-y-accesos.md b/Credenciales-y-accesos.md new file mode 100644 index 0000000..16983ec --- /dev/null +++ b/Credenciales-y-accesos.md @@ -0,0 +1,62 @@ +# Credenciales y accesos + +> **Wiki privada.** Estas credenciales solo son visibles para usuarios autenticados en Gitea local. + +## HTTP Basic Auth (protege la web hasta el cutover) + +Se quita en el cutover. Mientras está activo, cualquier petición a `feadulta.org` pide usuario/password antes de cargar WordPress. + +| | | +|---|---| +| Usuario | `feadul316` | +| Contraseña | `X5nWjWrnPg7F` | + +## WordPress Admin + +| Entorno | URL | Usuario | Contraseña | +|---|---|---|---| +| Local (Tailscale) | https://farmer.taild3aaf6.ts.net/fea/wp-admin | `eqpyk` | `NuevaFeAdulta2024!` | +| Producción | http://feadulta.org/wp-admin/ | `eqpyk` | `NuevaFeAdulta2026!` | + +## SSH producción + +| | | +|---|---| +| Host | `feadulta.org` | +| Usuario | `feadultada` | +| Contraseña | `mzdY69rn0B2N-UIX` | +| WP root | `/web/` | + +## Base de datos producción + +Solo accesible desde el server (no expuesta al exterior). + +| | | +|---|---| +| Host | `127.0.0.1` | +| DB | `278025353wordpress20260112013937` | +| Usuario | `myfeadultaa5` | +| Contraseña | `KjyGU29h` | + +Comando habitual: +```bash +mysql -h127.0.0.1 -umyfeadultaa5 -pKjyGU29h 278025353wordpress20260112013937 +``` + +## Base de datos local (Docker) + +| | | +|---|---| +| Host | `172.18.0.2` | +| DB | `wordpress_db` | +| Usuario | `wordpress_user` | +| Contraseña | `wordpress_pass` | + +## Brevo (boletín) + +- Cuenta SendinBlue/Brevo ID `c3555982` +- URL del formulario embebido: ver [Alta-boletin-Brevo](Alta-boletin-Brevo) + +## Joomla (legacy, solo lectura) + +Container local `joomla-web` (puerto 8080) sigue disponible para consulta histórica de la BD K2 original. Credenciales del container en `/home/rafa/joomla-migration/docker-compose.yml`. diff --git a/Cutover-DNS.md b/Cutover-DNS.md new file mode 100644 index 0000000..ad99102 --- /dev/null +++ b/Cutover-DNS.md @@ -0,0 +1,62 @@ +# Cutover DNS + +Operación final: pasar el dominio `feadulta.org` del Joomla viejo al WordPress nuevo, quitar el HTTP Basic Auth y dejar el sitio público. + +**Previsto: ~junio 2026.** + +## Estado actual + +- WordPress vive en `/web/` del mismo hosting Dreamhost que el Joomla original. +- El sitio está **protegido por HTTP Basic Auth** (`feadul316` / `X5nWjWrnPg7F`) → ningún visitante puede verlo accidentalmente. +- El dominio `feadulta.org` todavía resuelve al Joomla (configuración legacy). +- Los redirects 301 K2 ya están listos: 17.853 entradas en `wp_fg_redirect`. + +## Script + +`/home/rafa/joomla-migration/scripts/cutover_feadulta_com.sh` + +> El script existe pero **debe revisarse** antes de ejecutarlo. Issue: [#3 Preparar y revisar script de cutover DNS](http://localhost:3000/rafa/feadulta/issues/3). + +Acciones que típicamente debe contemplar (revisar): +- Sincronizar BD local → prod si hay deltas pendientes (issue [#4](http://localhost:3000/rafa/feadulta/issues/4)). +- Quitar HTTP Basic Auth (`.htaccess` / panel de Dreamhost). +- Cambiar config de WP para que `siteurl` / `home` apunten a `https://feadulta.org`. +- Re-generar permalinks. +- Limpiar caché. +- Verificar HTTPS / certificado. + +## Pre-cutover (lista de verificación) + +Estos son los issues abiertos del milestone `v1: Pre-cutover` que deberían cerrarse antes: + +- [#1](http://localhost:3000/rafa/feadulta/issues/1) Test completo antes del cutover DNS +- [#2](http://localhost:3000/rafa/feadulta/issues/2) Fix carta de la semana 404 en idiomas EN/FR/IT/PT +- [#3](http://localhost:3000/rafa/feadulta/issues/3) Preparar y revisar script de cutover DNS +- [#4](http://localhost:3000/rafa/feadulta/issues/4) Sincronizar base de datos local → producción antes del cutover +- Auditoría visual reciente: issues **#18-#32** (ver [Roadmap](Roadmap)) + +## Post-cutover inmediato + +1. Verificar que todas las URLs antiguas de K2 (`/es/.../item/NNN-alias.html`) redirigen 301 al post WP correspondiente. +2. Test multiidioma: portadas, navegación, categorías. +3. Test del formulario Brevo (alta boletín). +4. Revisar Yoast SEO sitemaps regenerados. +5. **Quitar `noindex`/`nofollow`** ([#19](http://localhost:3000/rafa/feadulta/issues/19)). + +## Post-cutover (v2: Post-launch) + +Estas tareas requieren el dominio activo y/o son mejoras no bloqueantes: + +- [#5](http://localhost:3000/rafa/feadulta/issues/5) Instalar y configurar AdSense (requiere dominio activo) +- [#6](http://localhost:3000/rafa/feadulta/issues/6) TTS (text-to-speech) +- [#7](http://localhost:3000/rafa/feadulta/issues/7) Wordfence +- [#8](http://localhost:3000/rafa/feadulta/issues/8) Buscador avanzado con Typesense (~24.778 posts; Relevanssi descartado por 570MB de índice) + +## Rollback + +Si algo va mal: +1. Restaurar `.htaccess` (vuelve a poner HTTP Basic Auth → sitio inaccesible). +2. Volver a apuntar DNS al Joomla viejo si Dreamhost mantiene la configuración. +3. UpdraftPlus tiene los últimos backups de WP. + +Documentar mejor el rollback en el script antes de ejecutarlo. diff --git a/EFFA-Escuela.md b/EFFA-Escuela.md new file mode 100644 index 0000000..27c73da --- /dev/null +++ b/EFFA-Escuela.md @@ -0,0 +1,82 @@ +# EFFA (Escuela de Formación de Fe Adulta) + +Sección de cursos / formación. Hub principal + 6 subpáginas temáticas. Implementada el **2026-03-06**, restaurada el **2026-03-14** tras perderse los shortcodes y CSS (probablemente por un docker recreate o un restore UpdraftPlus). + +## Estructura de páginas + +| Page ID | Slug | Permalink | Contenido | +|---|---|---|---| +| 42726 | `escuela` | `/fea/escuela/` | Hub (logo + intro + nav + `[effa_proyecto]` + CTA) | +| 42727 | `effa-espiritualidad` | `/fea/escuela/effa-espiritualidad/` | Subpágina con `[effa_seccion cat="..."]` | +| 42728 | `effa-biblia-at` | … | Antiguo Testamento | +| 42729 | `effa-biblia-nt` | … | Nuevo Testamento | +| 42730 | `effa-cristologia` | … | Cristología | +| 42731 | `effa-nuestras-creencias` | … | Nuestras creencias | +| 42732 | `effa-fe-y-cultura` | … | Fe y cultura | + +## Categorías + +| term_id | Nombre | +|---|---| +| 63 | EFFA (padre) | +| 64 | proyecat (los 8 artículos del hub) | +| 1653 | effa-espiritualidad | +| 1654 | effa-biblia-at | +| 1655 | effa-biblia-nt | +| 1656 | effa-cristologia | +| 1657 | effa-nuestras-creencias | +| 1658 | effa-fe-y-cultura | + +## Shortcodes (en `fea-homepage.php`) + +### `[effa_proyecto]` — Hub + +Renderiza los 8 artículos de `proyecat`. + +- Extrae la primera `` del `post_content` como thumbnail +- Muestra: imagen + título (bold 0.85rem) + extracto 12 palabras (gris 0.75rem) +- Ordena por `post_title` ASC +- Layout: HTML `
        ` con inline styles, 4 columnas, `array_chunk($items, 4)` + +### `[effa_seccion cat="slug"]` — Subpáginas (videos) + +- Ordena por `meta_key => '_effa_joomla_alias'` ASC (`secc1col01`, `secc1col02`…) para mantener el orden original de Joomla +- Thumbnail: prueba en orden: + 1. `get_the_post_thumbnail_url()` + 2. URL de YouTube extraída por regex + 3. Primera `` del content +- Muestra: imagen + título (bold). Sin extracto. +- Layout: HTML `
        ` 4 columnas con inline styles + +## ¿Por qué `
        ` y no flex/grid? + +> **CSS grid y flexbox NO funcionan dentro de un bloque Gutenberg FSE** en este sitio. Se intentó y se rompe el layout en breakpoints raros. La única solución probada que funciona es HTML `
        ` con inline styles. + +## CSS de EFFA + +Inyectado en `wp_head` con priority 20 condicionalmente: si el `post->post_content` contiene `effa_` **o** el `post_name` contiene `effa`. + +| Clase | Estilo | +|---|---| +| `.effa-logo` | `centered`, max-width 200px | +| `.effa-intro` | `text-align: center` | +| `.effa-nav` | Flex pills, borde inferior 2px `#e5e5e5` | +| `.effa-nav a` | Pill redondeado, hover/active: bg `#E89A1A` (dorado) | +| `.effa-cta-wrap` | `text-align: center` | +| `.effa-cta` | Botón dorado `#E89A1A`, border-radius 999px | + +## Contenido de cada subpágina + +``` +[logo] + [intro] +
          ...
        + [shortcode] + [CTA] +``` + +## Fragilidad / restauración + +Los shortcodes y el CSS se han perdido del container local **al menos una vez** (2026-03-14). Para restaurarlos sin perder tiempo, buscar el código completo en el transcript del 2026-03-06: + +``` +/home/rafa/.claude/projects/-home-rafa-joomla-migration/65e742c0-99e4-41b0-94f7-0866b38089a0.jsonl +``` + +Buscar `effa_proyecto` — última ocurrencia ~pos 15.301.049. Producción siempre tiene la versión canónica en `/web/wp-content/mu-plugins/fea-homepage.php`. diff --git a/Evangelios-y-comentarios.md b/Evangelios-y-comentarios.md new file mode 100644 index 0000000..c5ef959 --- /dev/null +++ b/Evangelios-y-comentarios.md @@ -0,0 +1,82 @@ +# Evangelios y Comentarios + +Sección con los 4 evangelios canónicos como posts; cada uno renderiza dinámicamente los comentarios asociados a sus versículos. + +Completado el **2026-03-15** (último gran hito antes del delta de mayo). + +## Posts de los evangelios + +IDs **iguales en local y producción** (sincronizados con `UPDATE wp_posts.ID` directo, ver [Sincronización local → producción](Sincronizacion-local-prod)). + +| ID | Título | Slug | Abrev | +|---|---|---|---| +| 43906 | Evangelio de Juan | `jn` | Jn | +| 43907 | Evangelio de Lucas | `lc` | Lc | +| 43908 | Evangelio de Mateo | `mt` | Mt | +| 43909 | Evangelio de Marcos | `mc` | Mc | + +> Creados originalmente como `page` el 2026-03-14 y convertidos a `post` el 2026-03-15. La conversión fue necesaria para que aparecieran dentro de la categoría 28. + +## Categoría + +`EVANGELIOS Y COMENTARIOS` (term_id=**28**, slug=`evangelios-y-comentarios`). + +- URL local: https://farmer.taild3aaf6.ts.net/fea/category/evangelios-y-comentarios/ +- URL prod: http://feadulta.org/category/evangelios-y-comentarios/ + +## Shortcode `[fea_citas_evangelio libro="Jn"]` + +Definido en `fea-homepage.php` (~línea 1406). Busca posts con meta `_cita_evangelio` que empiece por la abreviatura del libro (ej. `"Jn "`) en categoría 1647 ("Comentarios al evangelio"). + +### Mapa libro → post_id (hardcodeado en el código) + +```php +$book_post_id = [ + 'Mt' => 43908, + 'Mc' => 43909, + 'Lc' => 43907, + 'Jn' => 43906, +]; +``` + +## Metas migrados (2026-03-15) + +Estos metas estaban completos en local pero faltaban en producción. Migrados con `mysqldump` + `mysql` directo (sin `wp db query` porque `proc_open` está desactivado en prod). + +| Meta key | Registros | Descripción | +|---|---|---| +| `_cita_evangelio` | 4.290 | Referencia bíblica del post (ej. `"Jn 3, 16"`) | +| `_carta_id` | 3.528 | ID del post-carta al que pertenece el artículo | + +Comando que se ejecutó: + +```bash +# Local +docker exec wordpress-mysql mysqldump -uwordpress_user -pwordpress_pass wordpress_db wp_postmeta \ + --where="meta_key IN ('_cita_evangelio','_carta_id')" \ + --no-create-info --complete-insert --skip-extended-insert > /tmp/metas_sin_id.sql +# (+ sed para eliminar meta_id y evitar conflictos) + +# Prod (vía paramiko + scp) +mysql -h127.0.0.1 -umyfeadultaa5 -pKjyGU29h 278025353wordpress20260112013937 < /tmp/metas_evangelio.sql +``` + +## Bug encontrado: wp-cli y categorías numéricas + +```bash +wp post term set category 28 # ❌ asigna término con NOMBRE "28" (term_id=3299) +``` + +`wp post term set ID category 28` interpreta `28` como **nombre** de término, no como term_id. Crea un término nuevo llamado "28" si no existe. + +```bash +wp post term set category evangelios-y-comentarios # ✅ usa slug +``` + +**Regla:** siempre usar slug en `wp post term set`. + +## Lección aprendida: IDs sincronizados local↔prod + +- **NO** usar `get_page_by_path()` ni queries dinámicas para resolver IDs de posts conocidos — propenso a errores en runtime y en migraciones. +- **NO** usar IDs distintos en local y prod — fuerza lógica condicional en el código. +- Cuando aparece divergencia, sincronizar los IDs con UPDATE directo (offset temporal +99000 para evitar conflictos). Procedimiento completo en [Sincronización local → producción](Sincronizacion-local-prod). diff --git a/Home.md b/Home.md index 70b8881..fc03d83 100644 --- a/Home.md +++ b/Home.md @@ -1 +1,47 @@ -# feadulta - Wiki \ No newline at end of file +# feadulta.org — Wiki del proyecto + +Portal cristiano español de renovación de la fe. Documentación operativa del proyecto de migración Joomla → WordPress y de su operación. + +**Estado:** migración completa, web protegida con HTTP Basic, cutover DNS previsto ~junio 2026. + +--- + +## Índice + +### Operación +- [Credenciales y accesos](Credenciales-y-accesos) +- [Infraestructura](Infraestructura) +- [Limitaciones del servidor de producción](Limitaciones-servidor-prod) +- [Sincronización local → producción](Sincronizacion-local-prod) + +### Arquitectura WordPress +- [Arquitectura WordPress](Arquitectura-WordPress) +- [Portada y shortcodes](Portada-y-shortcodes) +- [CSS y bugs del tema Astra](CSS-y-bugs-Astra) +- [Polylang (multiidioma)](Polylang-multiidioma) +- [Categorías y términos](Categorias-y-terminos) + +### Áreas funcionales +- [Carta de la semana](Carta-de-la-semana) +- [Evangelios y comentarios](Evangelios-y-comentarios) +- [EFFA (Escuela)](EFFA-Escuela) +- [Autores](Autores) +- [Alta al boletín (Brevo)](Alta-boletin-Brevo) + +### Migración +- [Resumen del proyecto](Resumen-del-proyecto) +- [Auditoría de migración](Auditoria-migracion) +- [Scripts de migración](Scripts-de-migracion) +- [Cutover DNS](Cutover-DNS) + +### Planificación +- [Roadmap](Roadmap) + +--- + +## Repo + +- **Código:** http://localhost:3000/rafa/feadulta · https://farmer.taild3aaf6.ts.net/git/rafa/feadulta +- **Directorio local:** `/home/rafa/joomla-migration/` +- **Issues:** http://localhost:3000/rafa/feadulta/issues +- **Milestones:** `v1: Pre-cutover` · `v2: Post-launch` diff --git a/Infraestructura.md b/Infraestructura.md new file mode 100644 index 0000000..0fd934f --- /dev/null +++ b/Infraestructura.md @@ -0,0 +1,81 @@ +# Infraestructura + +## Local (desarrollo) + +Todo el entorno corre con Docker Compose en `/home/rafa/joomla-migration/`. + +### Contenedores + +| Container | Puerto | Rol | +|---|---|---| +| `wordpress-web` | 8081 | WordPress de trabajo (target de la migración) | +| `wordpress-mysql` | — (interno) | MySQL del WP local | +| `joomla-web` | 8080 | Joomla original restaurado (solo lectura, para consulta) | +| `joomla-db` | — (interno) | MySQL del Joomla legacy | + +### URL local + +WordPress se sirve detrás de Caddy + Tailscale: + +``` +https://farmer.taild3aaf6.ts.net/fea/ +``` + +Login admin: ver [Credenciales y accesos](Credenciales-y-accesos). + +### Mounts + +``` +/home/rafa/joomla-migration/wordpress/ → /var/www/html/ (container wordpress-web) +``` + +`mu-plugins/` está dentro de `wordpress/wp-content/mu-plugins/` → bind mount al container. + +> **Aviso:** En sesiones pasadas, los mu-plugins se han **perdido del contenedor** tras `docker compose recreate` o restauraciones UpdraftPlus. La versión canónica siempre está en producción (`/web/wp-content/mu-plugins/`). Si algo desaparece del local, copiar desde prod. + +### BD local + +``` +Host: 172.18.0.2 +DB: wordpress_db +User: wordpress_user / wordpress_pass +``` + +### Comandos básicos + +```bash +cd /home/rafa/joomla-migration +docker compose up -d # Levantar todo +docker compose down # Parar +docker logs wordpress-web # Ver logs WP +docker exec -it wordpress-web bash # Entrar al WP +docker exec wordpress-mysql mysql -uwordpress_user -pwordpress_pass wordpress_db +``` + +## Producción + +Hosting Dreamhost compartido. Limitaciones importantes — ver [Limitaciones del servidor de producción](Limitaciones-servidor-prod). + +| | | +|---|---| +| Host | `feadulta.org` | +| Usuario SSH | `feadultada` | +| Document root | `/web/` | +| PHP | 8.x, con `proc_open` desactivado | +| wp-cli | Instalado en `/web/`, invocar con `wp --path=/web/ ` | +| DB | MySQL local al server (127.0.0.1) | + +## Git + +- **Repo:** http://localhost:3000/rafa/feadulta (Gitea local, privado) +- **Working tree:** `/home/rafa/joomla-migration/` +- **.gitignore excluye:** WP core, plugins de terceros, temas, uploads, backups, joomla/, archivos sensibles de configuración +- **Trackeado:** mu-plugins, scripts/, docs, analisis-cartas, evangelios_html + +## Tailscale + +El PC está en la red Tailscale. Hostname: `farmer.taild3aaf6.ts.net`. Caddy lo expone: + +- `/fea/` → WordPress local +- `/git/` → Gitea +- otros servicios bajo subpaths en el mismo Caddyfile diff --git a/Limitaciones-servidor-prod.md b/Limitaciones-servidor-prod.md new file mode 100644 index 0000000..8deb81a --- /dev/null +++ b/Limitaciones-servidor-prod.md @@ -0,0 +1,72 @@ +# Limitaciones del servidor de producción + +Hosting compartido (Dreamhost). Más restrictivo de lo habitual. **Leer antes** de plantear cualquier operación contra prod. + +## Lo que NO hay instalado / disponible + +| | Implicación | +|---|---| +| ❌ `sshpass` | No se puede automatizar `ssh` con password desde el server. Las conexiones se hacen desde el local con `paramiko`. | +| ❌ `sftp` | No se pueden subir ficheros por SFTP. Hay que usar **PHP + base64 + paramiko** (ver [Sincronización local → producción](Sincronizacion-local-prod)). | +| ❌ `python3` | Nada de scripts Python en el server. Los scripts viven en local y se conectan por SSH ejecutando PHP. | +| ❌ `base64` (cmdline) | No hay binario `base64` en `PATH`. Hay que usar PHP (`base64_decode`) para decodificar payloads. | + +## PHP en prod + +- Versión PHP: 8.x (compartido) +- **`proc_open` está desactivado** → cualquier librería que lo necesite falla: + - `wp-cli db query` → ❌ FALLA. Usar `mysql` directo. + - Cualquier wrapper que use `Symfony\Process` → riesgo de falla. + +## wp-cli + +✅ Está instalado en `/web/`. Se invoca con: + +```bash +wp --path=/web/ +``` + +Funciona para la mayoría de operaciones (`post update`, `post create`, `term get`, `option get`, etc.). Solo falla lo que requiere `proc_open` (en la práctica: `wp db query`). + +## MySQL + +``` +Host: 127.0.0.1 (NO está expuesto al exterior — solo accesible desde el server) +DB: 278025353wordpress20260112013937 +User: myfeadultaa5 +Pass: KjyGU29h +``` + +Comando directo (siempre vía SSH): + +```bash +mysql -h127.0.0.1 -umyfeadultaa5 -pKjyGU29h 278025353wordpress20260112013937 -e "SELECT ..." +``` + +## Acceso SSH + +``` +ssh feadultada@feadulta.org # password: mzdY69rn0B2N-UIX +``` + +Tarda un poco en establecer la conexión (no usa key auth en este momento — sería bueno cambiarlo). + +## Estrategia de operación + +Por las limitaciones anteriores: + +1. **Todo el código y los scripts viven en local.** El server solo ejecuta comandos sueltos. +2. **Para subir ficheros:** `paramiko` desde local + `php -r "file_put_contents(...)"` en server, payload codificado en base64. +3. **Para tocar BD:** `mysqldump` local + `mysql -e` en prod, transferidos por el mismo método de payload. +4. **Para automatizar:** scripts Python en local, NO en prod. + +## Otros gotchas + +- El **caché de Astra** puede mantener CSS viejo. Hay que limpiar manualmente desde wp-admin después de cambiar shortcodes/CSS. +- **UpdraftPlus** corre en prod. Si haces un restore puede sobreescribir mu-plugins recientes — ten cuidado tras un restore con verificar `fea-homepage.php` y `carta-semana-plugin.php`. Ya pasó al menos una vez con EFFA (2026-03-14). +- **HTTP Basic Auth** se desactiva en el cutover. Si haces tests automáticos contra prod ahora, hay que pasar las credenciales (`feadul316:X5nWjWrnPg7F`) o el server contesta 401. + +## Pendientes / mejoras + +- [ ] Cambiar a SSH key auth en lugar de password (sigue pendiente). +- [ ] Documentar en este repo el comando para limpiar caché (Astra + cualquier object cache). diff --git a/Polylang-multiidioma.md b/Polylang-multiidioma.md new file mode 100644 index 0000000..d244be1 --- /dev/null +++ b/Polylang-multiidioma.md @@ -0,0 +1,89 @@ +# Polylang (multiidioma) + +5 idiomas: **ES, EN, FR, IT, PT** (heredados de Joomla). Polylang gestiona traducciones de posts, categorías, menús y páginas. + +## Idiomas + +| Idioma | term_taxonomy_id | URL prefix | +|---|---|---| +| ES | 1404 (`FEA_LANG_ES_TTID` en código) | sin prefijo (default) | +| EN | — | `/en/` | +| FR | — | `/fr/` | +| IT | — | `/it/` | +| PT | — | `/pt/` | + +## 5 portadas + +Cada idioma tiene su propia portada. Todas usan los shortcodes de `fea-homepage.php`. + +| Idioma | Page ID | +|---|---| +| ES | 26542 | +| EN | 43889 | +| FR | 42756 | +| IT | 42757 | +| PT | 42758 | + +`fea_is_front_page()` en `fea-homepage.php` detecta cualquiera de las 5 para inyectar el CSS de portada. + +## Bug WP_Tax_Query con Polylang + +Bug conocido en Polylang + WordPress core. La query: + +```php +get_posts([ + 'lang' => $lang, + 'category__in' => [$cat], +]); +``` + +genera SQL con `AND 0=1` cuando se combinan `lang` y `category__in` con `category__in` siendo numérico. La query devuelve vacío y, en contextos de archivo de categoría, **WordPress responde 404**. + +### Fix + +**NO** pasar `lang` explícito. Polylang ya filtra automáticamente por el idioma actual de la página/petición: + +```php +get_posts([ + 'category__in' => [$cat], // sin 'lang' +]); +``` + +### Dónde afecta + +- `/category/cartasemana/` en EN/FR/IT/PT → 404. Issue [#2](http://localhost:3000/rafa/feadulta/issues/2). +- Cualquier código que combine filtro por idioma y filtro por categoría numérica. + +## Bug `get_term_by('slug', ...)` en idiomas no-ES + +`get_term_by('slug', $slug, 'category')` **devuelve `false`** cuando el idioma actual no es ES, incluso si el término existe en la traducción. + +### Fix + +Saltar la API y usar `$wpdb` directo: + +```php +$tt_id = $wpdb->get_var($wpdb->prepare( + "SELECT t.term_id FROM {$wpdb->terms} t + JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id + WHERE t.slug = %s AND tt.taxonomy = %s", + $slug, 'category' +)); +``` + +## Menú multiidioma + +El selector de idioma en el header se renderiza con **JavaScript inyectado en `wp_footer`**, no con el widget de Polylang. Razones: + +- Permite construir un mapa idioma → URL traducida de la página actual con `$wpdb` directo, bypaseando el filtro de Polylang. +- Resultado robusto incluso cuando el sitio está en un idioma minoritario. + +## Comportamiento por defecto + +- Idioma por defecto: **ES**. +- URLs sin prefijo → ES. +- Cookie + browser detection para redirigir a `/lang/` la primera visita (configurado en Polylang settings). + +## Polylang admin + +`wp-admin → Idiomas → Configuración`. La gestión de traducciones de post/categorías se hace desde cada editor individual con el widget "Idioma" en la sidebar. diff --git a/Portada-y-shortcodes.md b/Portada-y-shortcodes.md new file mode 100644 index 0000000..0d4238e --- /dev/null +++ b/Portada-y-shortcodes.md @@ -0,0 +1,77 @@ +# Portada y shortcodes + +Todo el render de la portada vive en el mu-plugin `fea-homepage.php`. + +- **Local:** `wordpress/wp-content/mu-plugins/fea-homepage.php` (trackeado en git) +- **En el container:** `/var/www/html/wp-content/mu-plugins/fea-homepage.php` +- **Producción:** `/web/wp-content/mu-plugins/fea-homepage.php` + +## Shortcodes de portada + +| Shortcode | Qué muestra | +|---|---| +| `[fea_carta_semana_hero]` | Hero principal con la carta de la semana actual | +| `[fea_articulos_semana]` | Grid de artículos vinculados a la carta de la semana | +| `[fea_evangelio]` | Bloque de evangelio del domingo | +| `[fea_eucaristia]` | Bloque de eucaristía | +| `[fea_multimedia]` | Bloque de multimedia reciente | +| `[fea_noticia_centro]` | Bloque de "Noticias de alcance" del footer (template part ID 42370) | + +Todos los artículos de portada se renderizan con la helper `fea_card()` para que el card sea consistente (avatar + autor + título + extracto). + +## Helpers + +### `fea_card($post_id, $opts = [])` + +Renderiza una tarjeta con imagen, título, autor y excerpt. Es la unidad visual común de la portada. + +### `fea_title($str)` + +Convierte títulos de MAYÚSCULAS de la BD a *sentence case* (primera letra mayúscula, resto minúsculas). **Comportamiento intencional**: los títulos legacy de Joomla/K2 estaban en TODO CAPS y se normalizan al renderizar. + +### `fea_is_front_page()` + +Detecta si estamos en la portada (cualquiera de los 5 idiomas). Usado para inyectar el CSS de portada solo donde toca. + +## CSS de portada + +Inyectado en `wp_head` solo cuando `fea_is_front_page()` es true. No es CSS estático ni en el tema — vive en `fea-homepage.php`. + +### Clases relevantes + +| Clase | Uso | +|---|---| +| `.fea-section-title` | Etiqueta uppercase pequeña con borde izquierdo carmesí `#8b1a2e` (color marca) | +| `.n2-ss-slide--focus` | `visually-hidden` para evitar que los nombres de fichero del Smart Slider 3 aparezcan como texto leído por pantallas | +| `.fea-card-*` | Variantes de tarjeta | + +### Regla crítica + +El tema Astra **ignora CSS externo** para ciertas propiedades de imagen y radios. Usar **inline styles** para: +- `border-radius` +- `list-style` +- `height: auto` en imágenes + +Detalle completo en [CSS y bugs del tema Astra](CSS-y-bugs-Astra). + +## 5 portadas (multiidioma) + +Cada idioma tiene su propia página de portada en Polylang. El mismo shortcode `fea_is_front_page()` las reconoce todas. + +| Idioma | Page ID | +|---|---| +| ES | 26542 | +| EN | 43889 | +| FR | 42756 | +| IT | 42757 | +| PT | 42758 | + +## Otros shortcodes definidos en `fea-homepage.php` + +| Shortcode | Sección | Documentado en | +|---|---|---| +| `[effa_proyecto]` | Hub EFFA | [EFFA (Escuela)](EFFA-Escuela) | +| `[effa_seccion cat="..."]` | Subpáginas EFFA | [EFFA (Escuela)](EFFA-Escuela) | +| `[fea_autores_habituales]` | Listado autores | [Autores](Autores) | +| `[fea_autores_completo]` | Listado autores | [Autores](Autores) | +| `[fea_citas_evangelio libro="..."]` | Posts evangelios | [Evangelios y comentarios](Evangelios-y-comentarios) | diff --git a/Resumen-del-proyecto.md b/Resumen-del-proyecto.md new file mode 100644 index 0000000..cae692c --- /dev/null +++ b/Resumen-del-proyecto.md @@ -0,0 +1,50 @@ +# Resumen del proyecto + +## Qué es feadulta + +Portal cristiano español de renovación de la fe (*fe adulta*). Sitio editorial con casi 20 años de contenido — cartas semanales, comentarios al evangelio, multimedia y escuela de formación. + +**Volumen:** +- ~24.778 posts en WordPress +- 1.182 usuarios +- Multiidioma ES / EN / FR / IT / PT (Polylang) + +## Origen → destino + +| | Joomla (origen) | WordPress (destino) | +|---|---|---| +| Plataforma | Joomla + K2 (legacy) | WordPress 6 + Astra + FSE Twenty Twenty-Five | +| Posts editoriales | 15.764 K2 items publicados | 24.631 posts (publish) + 1.922 drafts | +| Multiidioma | Joomla nativo | Polylang | +| Hosting | Dreamhost compartido | Mismo servidor (manteniendo dominio) | + +El plugin usado para la importación fue **FG Joomla to WordPress Premium + módulo K2**. + +## Estado actual (2026-05-20) + +- Migración inicial **completada el 2026-03-09**: 15.978 K2 items → 24.778 posts WP. +- Delta de mayo importado: 169 K2 nuevos + 8 cartas + 58 misceláneas + 8 noticias de alcance (IDs 43914–44159 en WP). +- Sección EFFA, Evangelios y Comentarios, Cartas Semanales, Autores y Brevo: completados. +- Portada renovada (slider, separadores, tipografía, autor consistente). +- Web protegida por **HTTP Basic Auth** hasta el cutover. +- **Cutover DNS previsto: ~junio 2026.** + +## Cronología + +| Fecha | Hito | +|---|---| +| 2026-03-04 | Auditoría inicial: 93.030 enlaces internos corregidos, 17.853 redirects 301 creados | +| 2026-03-06 | Sección EFFA: hub + 6 subpáginas | +| 2026-03-09 | Cierre migración K2 grande (15.978 items) | +| 2026-03-13 | Alta boletín Brevo | +| 2026-03-14 | Listado de autores, restauración shortcodes EFFA | +| 2026-03-15 | Evangelios y Comentarios completados, metas migradas a prod | +| 2026-05-03/04 | Delta de cartas, K2 nuevos y misceláneas | +| 2026-06 (prev.) | Cutover DNS | + +## Pendientes principales + +- v1 Pre-cutover: fix carta de la semana 404 en idiomas, script cutover, sync BD local→prod, auditoría visual (issues #18-#32), test completo +- v2 Post-launch: AdSense, TTS, Wordfence, buscador avanzado (Typesense) + +Ver [Roadmap](Roadmap) y los issues del repo para el detalle. diff --git a/Roadmap.md b/Roadmap.md new file mode 100644 index 0000000..24c6941 --- /dev/null +++ b/Roadmap.md @@ -0,0 +1,67 @@ +# Roadmap + +Trabajo organizado en dos milestones temáticos. Ver el board completo en http://localhost:3000/rafa/feadulta/milestones. + +## v1: Pre-cutover + +Todo lo que debe estar resuelto **antes** del cutover DNS de ~junio 2026. + +### Infraestructura +- [#3](http://localhost:3000/rafa/feadulta/issues/3) Preparar y revisar script de cutover DNS · `area:infra` +- [#4](http://localhost:3000/rafa/feadulta/issues/4) Sincronizar base de datos local → producción antes del cutover · `area:infra` +- [#1](http://localhost:3000/rafa/feadulta/issues/1) Test completo antes del cutover DNS · `improvement` + +### Bugs +- [#2](http://localhost:3000/rafa/feadulta/issues/2) Fix carta de la semana 404 en idiomas EN/FR/IT/PT · `bug:important` +- [#18](http://localhost:3000/rafa/feadulta/issues/18) fix: imagen rota en bloque central pre-footer (Noticias de alcance) + +### Auditoría visual reciente (#19-#32) +Resultados de la auditoría comparativa Joomla→WP. Issues a cerrar antes del cutover: + +- [#19](http://localhost:3000/rafa/feadulta/issues/19) SEO: quitar `noindex/nofollow` antes de publicar +- [#20](http://localhost:3000/rafa/feadulta/issues/20) Idiomas: slugs y títulos cruzados en portugués y francés +- [#21](http://localhost:3000/rafa/feadulta/issues/21) Menú: enlace vacío en "Reseñas de libros" +- [#22](http://localhost:3000/rafa/feadulta/issues/22) Home: imagen `publi_2-1.gif` devuelve 404 +- [#23](http://localhost:3000/rafa/feadulta/issues/23) Archivo anterior: enlace "Otros textos web anterior" devuelve 403 +- [#24](http://localhost:3000/rafa/feadulta/issues/24) Estructura: portada y Escuela sin H1 +- [#25](http://localhost:3000/rafa/feadulta/issues/25) Accesibilidad: muchas imágenes de la portada no tienen texto alt +- [#26](http://localhost:3000/rafa/feadulta/issues/26) Navegación: categorías principales redirigen a artículos concretos +- [#27](http://localhost:3000/rafa/feadulta/issues/27) Móvil: las tarjetas de Escuela quedan demasiado estrechas +- [#28](http://localhost:3000/rafa/feadulta/issues/28) Páginas estáticas: aparecen metadatos tipo autor/categoría bajo el título +- [#29](http://localhost:3000/rafa/feadulta/issues/29) Páginas interiores: exceso de espacio vertical antes del contenido +- [#30](http://localhost:3000/rafa/feadulta/issues/30) Buscador avanzado: falta equivalente funcional al formulario facetado de Joomla +- [#31](http://localhost:3000/rafa/feadulta/issues/31) Menú Ayuda: faltan accesos estructurales presentes en Joomla +- [#32](http://localhost:3000/rafa/feadulta/issues/32) Multimedia: el enlace del footer apunta a un archivo plano, no a la landing estructural + +### Ya cerrados en v1 (9 issues) +[#9](http://localhost:3000/rafa/feadulta/issues/9) Joomla template infectado · [#10](http://localhost:3000/rafa/feadulta/issues/10) Smart Slider nombres de fichero · [#11](http://localhost:3000/rafa/feadulta/issues/11) h2 "Portada" visible · [#12](http://localhost:3000/rafa/feadulta/issues/12) Títulos en minúsculas · [#13](http://localhost:3000/rafa/feadulta/issues/13) Quitar emojis del menú · [#14](http://localhost:3000/rafa/feadulta/issues/14) Carta de la semana sin imagen ni excerpt · [#15](http://localhost:3000/rafa/feadulta/issues/15) Separadores visuales entre secciones · [#16](http://localhost:3000/rafa/feadulta/issues/16) Tipografía y jerarquía h2 · [#17](http://localhost:3000/rafa/feadulta/issues/17) Autor consistente en portada + +## v2: Post-launch + +Mejoras que requieren el dominio activo o son no-bloqueantes para el cutover. + +- [#5](http://localhost:3000/rafa/feadulta/issues/5) Instalar y configurar **AdSense** · `improvement` `area:plugin` (requiere dominio activo) +- [#6](http://localhost:3000/rafa/feadulta/issues/6) Instalar **TTS** (text-to-speech) · `improvement` `area:plugin` +- [#7](http://localhost:3000/rafa/feadulta/issues/7) Instalar **Wordfence** · `improvement` `area:plugin` +- [#8](http://localhost:3000/rafa/feadulta/issues/8) Buscador avanzado con **Typesense** · `improvement` + - ~24.778 posts. Relevanssi descartado (570MB de índice). + +## Convenciones + +### Labels usados +- `bug:critical` (#b91c1c), `bug:important` (#ea580c) +- `improvement` (#16a34a), `inconsistency` (#ca8a04) +- `security` (#db2777) +- `area:aesthetic`, `area:content`, `area:infra`, `area:joomla`, `area:plugin` + +Si añades issues nuevos, usa estos labels para que el filtrado siga funcionando. + +### Cerrar issue desde commit + +``` +Fix carta semana 404 en idiomas + +Closes #2 +``` + +Gitea cierra automáticamente el issue cuando el commit llega a `main`. diff --git a/Scripts-de-migracion.md b/Scripts-de-migracion.md new file mode 100644 index 0000000..e890a88 --- /dev/null +++ b/Scripts-de-migracion.md @@ -0,0 +1,115 @@ +# Scripts de migración + +Todos en `/home/rafa/joomla-migration/scripts/`. Trackeados en git. + +## Scripts del delta de mayo 2026 + +### `import_new_k2_items.py` + +Importa K2 items nuevos de Joomla (id > 17873) vía SSH + HEX. Resultado: 169 items → WP IDs **43914–44082**. + +### `fix_imported_k2_metas.py` + +Asigna metas, categorías y Polylang a los K2 items ya creados. Usa offset `wp_id = k2_id + 26040` para mapear. + +### `import_new_cartas.py` + +Importa cartas (catid 27/40/41, id > 9043) y asigna `_carta_id` a los K2 items por *matching de fecha*. Resultado: 8 cartas nuevas → WP IDs **44083–44090**. + +### `import_new_content.py` + +Importa ítems de `ew4r_content` que NO son cartas (multimedia, noticias, tablón). 58 items → WP IDs **44091–44151**. + +## Notas técnicas comunes a los scripts de delta + +> Estos son los problemas reales que aparecieron cuando se escribieron los scripts. No los olvides al hacer otro delta. + +### 1. `HEX()` para campos con HTML + +Las queries por SSH usan `HEX(campo)` para evitar que el HTML con saltos de línea y caracteres especiales rompa el parsing TSV cuando el resultado vuelve por stdout. + +```sql +SELECT id, HEX(content) FROM ew4r_k2_items WHERE id > ... +``` + +Luego en Python: `bytes.fromhex(row[1]).decode('utf-8')`. + +### 2. Query por stdin + +Pasar la query SQL **por stdin** (`input=query` en `subprocess.run`) para evitar problemas con backticks, comillas y caracteres de shell. + +```python +res = subprocess.run(['ssh', host, 'mysql -uX -pY db'], + input=query, capture_output=True, text=True) +``` + +### 3. `MAX(ID)` en vez de `LAST_INSERT_ID()` + +```sql +SELECT MAX(ID) FROM wp_posts; -- ✅ +SELECT LAST_INSERT_ID(); -- ❌ cada subprocess abre sesión MySQL nueva +``` + +Cada llamada SSH abre una sesión MySQL distinta, así que `LAST_INSERT_ID()` no ve el INSERT del subprocess anterior. + +### 4. MySQL strict mode + +Los `INSERT wp_posts` deben incluir todos los campos obligatorios incluso si están vacíos: + +```sql +to_ping = '' +pinged = '' +post_content_filtered = '' +``` + +Si no, MySQL rechaza el INSERT con strict mode. + +## Otros scripts en `scripts/` + +### Importación inicial (ya ejecutados) + +| Script | Estado | +|---|---| +| `assign_author_photos.php` | ✅ ejecutado | +| `assign_polylang_languages.php` | ✅ ejecutado | +| `assign_polylang_prod.php` | ✅ ejecutado en prod | +| `export_translations.py` / `export_cat_translations.py` | Exportadores de traducciones | +| `audit_translations.py` | Auditor de cobertura de traducciones | +| `retranslate_*.py` (5 ficheros) | Re-traducción por chunks/idiomas/fallos | +| `fix_joomla_links.php` | ✅ 2026-03-04: 93.030 links reemplazados en 3.400 posts | +| `fix_numeric_categories.php` | ✅ 2026-03-04: 100 categorías numéricas → autores correctos | +| `fix_remaining_titles.py`, `fix_titles.py` | Normalización de títulos | +| `generate_k2_redirects.php` | ✅ 2026-03-04: 17.853 redirects 301 en `wp_fg_redirect` | +| `translate_cartas.py` | Traducción de cartas semanales | +| `setup-wordpress.sh` | Setup inicial del WP local | +| `test_5articles.py` | Test smoke de 5 artículos | +| `cutover_feadulta_com.sh` | **Cutover DNS** — ver [Cutover DNS](Cutover-DNS) | + +### Templates / mu-plugins + +| Fichero | Notas | +|---|---| +| `fea-homepage.php` | Versión de trabajo de portada/shortcodes (también en `wp-content/mu-plugins/`) | +| `fea-homepage-template.php` | Template suelto | +| `carta-semana-plugin.php` | Versión de trabajo del plugin de carta de la semana | + +## Bug conocido `wp-cli` + categorías numéricas + +```bash +wp post term set category 28 # ❌ crea término "28" +wp post term set category evangelios-y-comentarios # ✅ +``` + +`wp post term set ID category N` interpreta `N` como **nombre**, no como term_id. + +**Regla:** siempre usar slug. Ver [Evangelios y comentarios](Evangelios-y-comentarios). + +## Bug conocido `wp-cli` en prod + +```bash +wp --path=/web/ db query "SELECT ..." # ❌ FALLA (proc_open desactivado) +``` + +`proc_open` está desactivado en el server. `wp db query` y todo lo que invoque `mysql` por dentro falla. **Usar `mysql` directo** vía SSH para queries. + +Ver [Limitaciones del servidor de producción](Limitaciones-servidor-prod). diff --git a/Sincronizacion-local-prod.md b/Sincronizacion-local-prod.md new file mode 100644 index 0000000..faba0d6 --- /dev/null +++ b/Sincronizacion-local-prod.md @@ -0,0 +1,134 @@ +# Sincronización local → producción + +Regla general: cualquier cambio en local debe replicarse en producción **del mismo modo**, porque los IDs y las versiones de código viven en paralelo. + +Antes de tocar nada, lee [Limitaciones del servidor de producción](Limitaciones-servidor-prod) — sin esas restricciones, lo que sigue no tiene sentido. + +## Tipos de cambio y método + +| Cambio | Cómo replicar a producción | +|---|---| +| **mu-plugins** (`fea-homepage.php`, `carta-semana-plugin.php`) | PHP + base64 + `paramiko` (ver más abajo) | +| **Posts / páginas nuevas** | `wp --path=/web/ post create` vía SSH (paramiko) | +| **Metas de BD masivas** | `mysqldump` local + `mysql` directo en prod | +| **Cambios puntuales en BD** | `mysql -h127.0.0.1 -u... -p... DB -e "SQL"` vía SSH | +| **Files (imágenes, JS, CSS)** | Igual que mu-plugins: PHP + base64 + paramiko | + +## Método canónico: PHP + base64 + paramiko + +Funciona porque NO requiere `sftp`, `sshpass` ni `base64` en el server — solo PHP y SSH. + +```python +import paramiko, base64 + +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect('feadulta.org', + username='feadultada', + password='mzdY69rn0B2N-UIX') + +with open('/local/path/file.php', 'rb') as f: + content = f.read() + +# Importante: vaciar el destino antes de empezar (file_put_contents con FILE_APPEND) +client.exec_command("php -r \"file_put_contents('/ruta/destino', '');\"") + +chunk_size = 50_000 # 50 KB +for i in range(0, len(content), chunk_size): + chunk = content[i:i + chunk_size] + b64 = base64.b64encode(chunk).decode() + cmd = f"php -r \"file_put_contents('/ruta/destino', base64_decode('{b64}'), FILE_APPEND);\"" + client.exec_command(cmd) +``` + +> El chunk de 50KB evita que el shell rechace la línea por longitud. PHP `base64_decode` lo reconstruye binariamente en destino. + +## Importar SQL en prod + +Después de generar `/tmp/algo.sql` (en local o subido a prod por el método anterior): + +```bash +mysql -h127.0.0.1 -umyfeadultaa5 -pKjyGU29h \ + 278025353wordpress20260112013937 < /tmp/algo.sql +``` + +Vía paramiko: + +```python +client.exec_command( + "mysql -h127.0.0.1 -umyfeadultaa5 -pKjyGU29h " + "278025353wordpress20260112013937 < /tmp/algo.sql" +) +``` + +## Export de metas/posts desde local + +```bash +docker exec wordpress-mysql mysqldump \ + -uwordpress_user -pwordpress_pass wordpress_db wp_postmeta \ + --where="meta_key IN ('_cita_evangelio','_carta_id')" \ + --no-create-info --complete-insert --skip-extended-insert \ + > /tmp/metas.sql + +# Quitar el meta_id para evitar conflictos de PK en prod +sed -i 's/INSERT INTO `wp_postmeta` (`meta_id`, /INSERT INTO `wp_postmeta` (/g; s/(NULL, /(/g' /tmp/metas.sql +``` + +## Sincronizar IDs entre local y prod + +Cuando local y prod crean el mismo post con IDs distintos (porque WP autoincrementa por separado), arreglar con UPDATEs directos en prod **usando offset temporal** para evitar conflictos de PK. + +```sql +-- 1) Mover a IDs temporales (offset +99000) +UPDATE wp_posts SET ID = ID + 99000 + WHERE ID IN (43905, 43906, 43907, 43908); +UPDATE wp_postmeta SET post_id = post_id + 99000 + WHERE post_id IN (43905, 43906, 43907, 43908); +UPDATE wp_term_relationships SET object_id = object_id + 99000 + WHERE object_id IN (43905, 43906, 43907, 43908); + +-- 2) Mover a los IDs finales (los de local) +UPDATE wp_posts SET ID = 43906 WHERE ID = 143904; +UPDATE wp_postmeta SET post_id = 43906 WHERE post_id = 143904; +UPDATE wp_term_relationships SET object_id = 43906 WHERE object_id = 143904; +-- (... repetir para cada post a sincronizar) +``` + +### Lección clave + +> **No uses IDs hardcodeados distintos por entorno** ni queries dinámicas tipo `get_page_by_path()` para resolver IDs de posts conocidos. Sincroniza los IDs entre local y prod desde el momento en que crees el post, y hardcodea con seguridad. + +Esta lección viene del trabajo con [Evangelios y comentarios](Evangelios-y-comentarios). + +## wp-cli en producción + +```bash +wp --path=/web/ +``` + +`proc_open` está desactivado → `wp db query` y similares **NO funcionan**. Usar `mysql` directo. + +## Flujo típico cuando cambias un shortcode + +1. Editas `fea-homepage.php` en `/home/rafa/joomla-migration/wordpress/wp-content/mu-plugins/`. +2. Verificas en local (https://farmer.taild3aaf6.ts.net/fea/). +3. Subes a prod con paramiko + base64. +4. Vacías cualquier caché en prod (Astra / object cache si aplica). +5. Commit + push al repo Gitea. + +## Backup antes de tocar prod + +Antes de un UPDATE masivo o de tocar un fichero PHP en prod: + +```bash +# Backup del mu-plugin +ssh feadultada@feadulta.org "cp /web/wp-content/mu-plugins/fea-homepage.php \ + /web/wp-content/mu-plugins/fea-homepage.php.bak.$(date +%Y%m%d-%H%M)" + +# Backup parcial de BD +ssh feadultada@feadulta.org "mysqldump -h127.0.0.1 -umyfeadultaa5 -pKjyGU29h \ + 278025353wordpress20260112013937 wp_postmeta \ + --where=\"meta_key='_carta_id'\" > /tmp/backup_carta_id.sql" +``` + +UpdraftPlus también está corriendo en prod, pero el backup manual es más rápido para cambios puntuales.