{"id":11180,"date":"2025-11-26T09:41:34","date_gmt":"2025-11-26T08:41:34","guid":{"rendered":"https:\/\/bitacora.eniac2000.com\/?p=11180"},"modified":"2025-11-26T09:44:14","modified_gmt":"2025-11-26T08:44:14","slug":"actualizacion-de-este-sitio-web-reemplazo-inteligente-de-referencias-de-gallery2-a-piwigo","status":"publish","type":"post","link":"https:\/\/bitacora.eniac2000.com\/?p=11180","title":{"rendered":"Actualizaci\u00f3n de este sitio web. Reemplazo Inteligente de referencias de Gallery2 a Piwigo"},"content":{"rendered":"<div class=\"seriesmeta\">Esta entrada es la parte 5 de 5 de la serie <a href=\"https:\/\/bitacora.eniac2000.com\/?series=actualizacion-de-mi-sistema-wordpress-y-galeria-integrada\" class=\"series-1852\" title=\"Actualizaci\u00f3n de mi sistema WordPress y galer\u00eda integrada\">Actualizaci\u00f3n de mi sistema WordPress y galer\u00eda integrada<\/a><\/div>\n<p>Y llegamos as\u00ed a la etapa reina de este proceso. Hab\u00eda conseguido realizar una instalaci\u00f3n limpia de WordPress y de Piwigo, y disponer de un <em>plugin<\/em> que me permitiera integrar las fotograf\u00edas del segundo en el primero. Pero a\u00fan quedaba lo m\u00e1s importante por hacer: reemplazar las miles de referencias existentes en centenares de fotograf\u00edas, con respecto a decenas de galer\u00edas, y sin disponer de un cotejado de las referencias entre el sistema de galer\u00edas antiguo y el nuevo. Vamos, el problema que hac\u00eda a\u00f1os que estaba intentando evitar.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"687\" height=\"1024\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/migracion-sistema-obsoleto-687x1024.jpg\" alt=\"\" class=\"wp-image-11210\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/migracion-sistema-obsoleto-687x1024.jpg 687w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/migracion-sistema-obsoleto-201x300.jpg 201w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/migracion-sistema-obsoleto-768x1144.jpg 768w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/migracion-sistema-obsoleto.jpg 784w\" sizes=\"auto, (max-width: 687px) 100vw, 687px\" \/><figcaption class=\"wp-element-caption\">Migraci\u00f3n desde un sistema obsoleto, estilo <em>steampunk<\/em>. Generada con IA<\/figcaption><\/figure>\n\n\n\n<p>Por desgracia, este problema no es nuevo. La obsolescencia de <em>plugins<\/em> y herramientas <em>legacy<\/em> es un desaf\u00edo com\u00fan en la administraci\u00f3n de sitios WordPress con a\u00f1os de historia. Por ello, una vez m\u00e1s consult\u00e9 con v0 c\u00f3mo poder abordar este proceso. Y el resultado fue completamente sorprendente, algo que fue mucho m\u00e1s all\u00e1 de mis expectativas m\u00e1s optimistas. En este art\u00edculo, me centrar\u00e9 en compartir la experiencia migrando una galer\u00eda de im\u00e1genes de Gallery2 (completamente obsoleta) a Piwigo, con especial \u00e9nfasis en la soluci\u00f3n desarrollada para mapear y reemplazar autom\u00e1ticamente miles de referencias sin perder datos ni romper la experiencia del usuario.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">El Problema: Galer\u00edas <em>Legacy<\/em> en WordPress<\/h2>\n\n\n\n<p>Gallery2, que fue durante a\u00f1os la soluci\u00f3n est\u00e1ndar para gestionar galer\u00edas de im\u00e1genes en WordPress, fue  completamente abandonado a partir de 2009. Los sitios como el m\u00edo, que a\u00fan la utilizaban se enfrentaban riesgos cr\u00edticos de seguridad, incompatibilidad con versiones modernas de PHP, y vulnerabilidades sin parches disponibles.<\/p>\n\n\n\n<p>En mi caso espec\u00edfico presentaba complejidades adicionales:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Una galer\u00eda <em>legacy<\/em> en <code>https:\/\/sertorio.eniac2000.com\/gallery2\/<\/code> completamente obsoleta<\/li>\n\n\n\n<li>Centenares de posts de WordPress referenciando miles de im\u00e1genes mediante tags propietarios: <code>&lt;wpg2&gt;ID&lt;\/wpg2&gt;<\/code> y <code>&lt;wpg2id&gt;ID&lt;\/wpg2id&gt;<\/code><\/li>\n\n\n\n<li>No hab\u00eda correspondencia directa entre identificadores de Gallery2 e im\u00e1genes nuevas en Piwigo<\/li>\n\n\n\n<li>Par\u00e1metros opcionales de tama\u00f1o en los tags (<code>&lt;wpg2id&gt;69434|800&lt;\/wpg2id&gt;<\/code>) complicaban la b\u00fasqueda simple<\/li>\n\n\n\n<li>Duplicaci\u00f3n de nombres de archivo entre galer\u00edas, requiriendo comparaci\u00f3n por fecha de creaci\u00f3n<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">La Soluci\u00f3n: Arquitectura Modular de Migraci\u00f3n<\/h2>\n\n\n\n<p>Con apoyo de la IA se desarroll\u00f3 un sistema de migraci\u00f3n en cuatro fases que ejecut\u00e9 de forma secuencial, con validaci\u00f3n en cada paso y capacidad de vuelta atr\u00e1s:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Fase 1: Extracci\u00f3n y Mapeo de Identificadores<\/h3>\n\n\n\n<p>El desaf\u00edo principal era crear una tabla de correspondencia entre Gallery2 y Piwigo sin registros compartidos. Se utiliz\u00f3 una estrategia multi-capas:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>-- Extraer datos de Gallery2\nSELECT \n    g2_Item.g_id as gallery2_id,\n    g2_FileSystemEntity.g_pathComponent as filename,\n    FROM_UNIXTIME(g2_Item.g_originationTimestamp) as creation_date,\n    g2_Item.g_title as title\nFROM g2_Item\nLEFT JOIN g2_FileSystemEntity ON g2_Item.g_id = g2_FileSystemEntity.g_id\nWHERE g2_Item.g_canContainChildren = 0;\n\n-- Extraer datos de Piwigo\nSELECT \n    id as piwigo_id,\n    file as filename,\n    date_creation as creation_date,\n    name as title\nFROM piwigo_images;<\/code><\/pre>\n\n\n\n<p>El mapeo se realiz\u00f3 en el siguiente orden de precedencia:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Nivel 1 &#8211; Nombre exacto:<\/strong> B\u00fasqueda directa por nombre de archivo. \u00c9xito en el 85% de casos.<\/li>\n\n\n\n<li><strong>Nivel 2 &#8211; Nombre + Fecha:<\/strong> Cuando hab\u00eda m\u00faltiples im\u00e1genes con el mismo nombre (caso com\u00fan en galer\u00edas organizadas por fecha), se comparaba el <code>g_originationTimestamp<\/code> de Gallery2 (Unix timestamp) convertido a formato datetime con <code>date_creation<\/code> de Piwigo, con tolerancia de \u00b11 d\u00eda para manejar diferencias menores en conversi\u00f3n de <em>timezones<\/em>.<\/li>\n\n\n\n<li><strong>Nivel 3 &#8211; T\u00edtulo <em>fuzzy<\/em>:<\/strong> B\u00fasqueda aproximada en t\u00edtulos para casos donde el nombre de archivo hab\u00eda sido procesado mediante l\u00f3gica difusa.<\/li>\n\n\n\n<li><strong>Nivel 4 &#8211; Manual:<\/strong> Registro de im\u00e1genes no encontradas para revisi\u00f3n humana.<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ L\u00f3gica de conversi\u00f3n de timestamps\n$gallery2_date = new DateTime();\n$gallery2_date-&gt;setTimestamp($g2_timestamp);\n$gallery2_datetime = $gallery2_date-&gt;format('Y-m-d');\n\n$piwigo_datetime = substr($piwigo_creation_date, 0, 10);\n\n$date_diff = abs(strtotime($gallery2_datetime) - strtotime($piwigo_datetime));\n$matches_by_date = ($date_diff &lt;= 86400); \/\/ 1 d\u00eda de tolerancia\n\nif ($filename_match &amp;&amp; $matches_by_date) {\n    $match_type = 'exact_filename_date';\n    $confidence = 99;\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Fase 2 Identificaci\u00f3n de Posts Afectados<\/h3>\n\n\n\n<p>Tras establecer el cotejado de im\u00e1genes, el siguiente paso era realizar una b\u00fasqueda de base de datos simple, a fin de identificar todos los posts que conten\u00edan los tags heredados:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT ID, post_title, post_content\nFROM wp_posts\nWHERE (post_content LIKE '%&lt;wpg2&gt;%' OR post_content LIKE '%&lt;wpg2id&gt;%')\n  AND post_status IN ('publish', 'draft', 'pending')\nORDER BY post_modified DESC;<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Fase 3: Reemplazo Inteligente de Tags &#8211; La Magia del Algoritmo<\/h3>\n\n\n\n<p>Este es el coraz\u00f3n del sistema, donde se aplicaron expresiones regulares sofisticadas para detectar y reemplazar todos los formatos posibles de tags:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Detectar y procesar ambos formatos de tags con par\u00e1metros opcionales\n$patterns = &#91;\n    \/\/ Formato 1: &lt;wpg2>ID&lt;\/wpg2> o &lt;wpg2>ID|SIZE&lt;\/wpg2>\n    '&lt;wpg2>(\\d+)(?:\\|(\\d+))?&lt;\\\/wpg2>',\n    \n    \/\/ Formato 2: &lt;wpg2id>ID&lt;\/wpg2id> o &lt;wpg2id>ID|SIZE&lt;\/wpg2id>\n    '&lt;wpg2id>(\\d+)(?:\\|(\\d+))?&lt;\\\/wpg2id>'\n];\n\nforeach ($patterns as $pattern) {\n    $postContent = preg_replace_callback(\n        $pattern,\n        function($matches) use ($mappingTable) {\n            $gallery2_id = $matches&#91;1];\n            $size_param = $matches&#91;2] ?? null; \/\/ Captura el tama\u00f1o pero lo ignora\n            \n            if (isset($mappingTable&#91;$gallery2_id])) {\n                $piwigo_id = $mappingTable&#91;$gallery2_id];\n                \/\/ El par\u00e1metro de tama\u00f1o se descarta: Piwigo usa \"medium\"\n                return '&#91;piwigo imageId=' . $piwigo_id . ' size=\"medium\" link=\"true\" caption=\"true\"]';\n            }\n            \n            \/\/ Si no hay mapeo: registrar error para revisi\u00f3n manual\n            return 'MIGRATION ERROR: Gallery2 ID . $gallery2_id . no encontrado;\n        },\n        $postContent\n    );\n}<\/code><\/pre>\n\n\n\n<p>La clave de esta soluci\u00f3n es la expresi\u00f3n regular capaz de manejar:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>IDs directos:<\/strong> <code>&lt;wpg2&gt;34445&lt;\/wpg2&gt;<\/code><\/li>\n\n\n\n<li><strong>IDs con par\u00e1metro de tama\u00f1o:<\/strong> <code>&lt;wpg2id&gt;69434|800&lt;\/wpg2id&gt;<\/code> (captura solo el ID, ignora el tama\u00f1o)<\/li>\n\n\n\n<li><strong>Ambos formatos:<\/strong> El mismo regex maneja <code>&lt;wpg2&gt;<\/code> y <code>&lt;wpg2id&gt;<\/code><\/li>\n\n\n\n<li><strong>Errores elegantes:<\/strong> Si no hay mapeo, inserta un comentario HTML auditable en lugar de romper el contenido<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Fase 4: Validaci\u00f3n y Rollback<\/h3>\n\n\n\n<p>El sistema, adem\u00e1s, inclu\u00eda varias capas de validaci\u00f3n y recuperaci\u00f3n:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Modo \"dry-run\" para pruebas sin modificar la BD\nif (!$dryRun) {\n    wp_update_post(&#91;'ID' =&gt; $post-&gt;ID, 'post_content' =&gt; $newContent]);\n}\n\n\/\/ Backup autom\u00e1tico antes de migraci\u00f3n real\n$backup_query = \"INSERT INTO wp_posts_backup SELECT * FROM wp_posts \n                WHERE post_content LIKE '%&lt;wpg2%'\";\n\n\/\/ Script de rollback disponible para revertir cambios\n\/\/ Restaura autom\u00e1ticamente desde la tabla de backup<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Dashboard de Monitoreo en Tiempo Real<\/h2>\n\n\n\n<p>El sistema desarroll\u00f3, adem\u00e1s, un dashboard visual que para permitir:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Monitoreo de progreso:<\/strong> Barra visual del porcentaje de migraci\u00f3n completada<\/li>\n\n\n\n<li><strong>Estad\u00edsticas detalladas:<\/strong> Posts migrados, errores, pendientes de revisi\u00f3n manual<\/li>\n\n\n\n<li><strong>Listado de problemas:<\/strong> Identificaci\u00f3n de IDs hu\u00e9rfanos sin mapeo en Piwigo<\/li>\n\n\n\n<li><strong>Vista detallada por post:<\/strong> Ver antes\/despu\u00e9s del contenido para auditor\u00eda<\/li>\n\n\n\n<li><strong>Enlaces directos a edici\u00f3n:<\/strong> Acceso r\u00e1pido a editor de WordPress para correcciones manuales<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"712\" height=\"1024\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-migracion-712x1024.png\" alt=\"Dashboard desarrollado, con el resultado de los an\u00e1lisis\" class=\"wp-image-11208\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-migracion-712x1024.png 712w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-migracion-209x300.png 209w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-migracion-768x1105.png 768w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-migracion-1068x1536.png 1068w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-migracion-1024x1473.png 1024w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-migracion.png 1230w\" sizes=\"auto, (max-width: 712px) 100vw, 712px\" \/><figcaption class=\"wp-element-caption\">Dashboard desarrollado, con el resultado de los an\u00e1lisis<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Resultados y M\u00e9tricas de Migraci\u00f3n<\/h2>\n\n\n\n<p>El sistema proces\u00f3 exitosamente:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>M\u00e9tricas: 327 art\u00edculos de WordPress con referencias a Gallery2, y 12638 referencias encontradas<\/li>\n\n\n\n<li>85% de im\u00e1genes identificadas por nombre exacto en primer intento<\/li>\n\n\n\n<li>12% ubicadas mediante coincidencia de nombre + fecha de creaci\u00f3n<\/li>\n\n\n\n<li>3% resueltas mediante b\u00fasqueda difusa de t\u00edtulos<\/li>\n\n\n\n<li>Tasa de error: &lt;0.5% (principalmente im\u00e1genes eliminadas en Gallery2)<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Lecciones Aprendidas y Mejores Pr\u00e1cticas<\/h2>\n\n\n\n<p><strong>1. No asumir correspondencia de IDs:<\/strong> Nunca des por sentado que los identificadores coincidir\u00e1n entre sistemas. Siempre mapear usando atributos \u00fanicos como nombres de archivo y fechas.<\/p>\n\n\n\n<p><strong>2. Manejar formatos heredados complejos:<\/strong> Los tags pueden incluir par\u00e1metros opcionales. Las expresiones regulares con captura de grupos opcionales (<code>(?:\\|(\\d+))?<\/code>) son esenciales.<\/p>\n\n\n\n<p><strong>3. Conversi\u00f3n de <em>timezones<\/em> y tipos de dato:<\/strong> Al comparar fechas entre sistemas, convertir siempre a un formato com\u00fan y permitir margen de error (\u00b11 d\u00eda es prudente).<\/p>\n\n\n\n<p><strong>4. Procesamiento en lotes con <em>logging<\/em>:<\/strong> Procesar posts en lotes peque\u00f1os (50-100) permite monitorear problemas individuales sin perder todo el progreso ante un error.<\/p>\n\n\n\n<p><strong>5. Errores auditables, no silenciosos:<\/strong> Los comentarios HTML del tipo \u00abMIGRATION ERROR\u00bb dentro del contenido son visibles en el <em>frontend<\/em>, facilitando la auditor\u00eda manual.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusi\u00f3n<\/h2>\n\n\n\n<p>El resultado super\u00f3 todas mis expectativas, como comentaba. Era consciente de la magnitud del problema que ten\u00eda entre manos, cuya complejidad hubiera requerido cientos de horas de trabajo manual para poder solucionar. Sin embargo, con un adecuado manejo de las herramientas proporcionadas por la IA, junto con un conocimiento profundo del entorno, en apenas una ma\u00f1ana fui capaz de completar la actualizaci\u00f3n de las referencias y disponer, finalmente, de un sistema completamente actualizado.<\/p>\n\n\n\n<p>La principal experiencia obtenida es que la migraci\u00f3n de sistemas <em>legacy<\/em> en WordPress presenta importantes desaf\u00edos, pero es un problema manejable con las estrategias correctas. Al ser capaz de utilizar una IA generativa para desarrollar un procedimiento que permita combinar extracci\u00f3n inteligente de datos, mapeo multi-criterio, expresiones regulares sofisticadas y validaci\u00f3n cuidadosa, es posible reemplazar completamente galer\u00edas obsoletas sin perder referencias cr\u00edticas.<\/p>\n\n\n\n<p>El sistema desarrollado, adem\u00e1s, es reutilizable para migraciones similares, prueba de que la automatizaci\u00f3n inteligente supera ampliamente al reemplazo manual de miles de referencias.<\/p>\n","protected":false},"excerpt":{"rendered":"<div class=\"seriesmeta\">Esta entrada es la parte 5 de 5 de la serie <a href=\"https:\/\/bitacora.eniac2000.com\/?series=actualizacion-de-mi-sistema-wordpress-y-galeria-integrada\" class=\"series-1852\" title=\"Actualizaci\u00f3n de mi sistema WordPress y galer\u00eda integrada\">Actualizaci\u00f3n de mi sistema WordPress y galer\u00eda integrada<\/a><\/div><p>Y llegamos as\u00ed a la etapa reina de este proceso.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"advanced_seo_description":"","jetpack_seo_html_title":"","jetpack_seo_noindex":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[1845,13],"tags":[712,824,1853,1848,1846,1794],"series":[1852],"class_list":["post-11180","post","type-post","status-publish","format-standard","hentry","category-generado-con-ia","category-informatica","tag-gallery2","tag-ia","tag-mariadb","tag-piwigo","tag-v0","tag-wordpress","series-actualizacion-de-mi-sistema-wordpress-y-galeria-integrada"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/posts\/11180","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=11180"}],"version-history":[{"count":5,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/posts\/11180\/revisions"}],"predecessor-version":[{"id":11214,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/posts\/11180\/revisions\/11214"}],"wp:attachment":[{"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=11180"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=11180"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=11180"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Fseries&post=11180"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}