Clicky

Migración manual de items K2 a artículos predeterminados de Joomla! 4

Componente K2 en Joomla

El mayor problema es que la estructura de la base de datos de K2 no se corresponde con la de Joomla

K2 de Joomla! es un componente que te permite incrustar fácilmente en los contenidos objetos de media como galerias de fotos y embeber vídeos con el código <iframe> de cada clip, ya sea de Youtube, Vimeo, Facebook o incluso el código de Twitter.

Pero a mi modo de ver tiene varios problemas: No ha obtenido una gran actualización en los últimos años y los desarrolladores también llevan tiempo anunciando su compatibilidad con Joomla! 4 sin ofrecer una versión estable.

Pero el mayor problema es que la estructura de la base de datos de K2 no se corresponde en absoluto con la estructura de el contenido, categorias y etiquetas de Joomla! (tablas #_content, #_categories y #_tags en el core de Joomla!) y aunque hay al menos un plugin (de pago) que dice hacer la migración de las tablas de K2 a las tablas de Joomla! yo lo he probado con malos resultados.

Así que si queremos prescindir de K2 en nuestra instalación de Joomla! y mantener todo el contenido de los items, categorías y etiquetas de K2 y trasladarlo a los artículos de una versión de Joomla! 4, no tendremos más remedio que hacerlo manualmente (también con algunas limitaciones, como las imágenes, que explicaré más abajo).

Migración de items de K2 a artículos de Joomla!

En Internet existen bastantes entradas en inglés explicando cómo hacer un volcado de la tabla #_k2_items a la tabla #_content pero lo hacen mal porque en la consulta MySQL intentan trabajar con una estructura que no es la real. Por ejemplo en este hilo de un foro de Joomla Works, que es el se ha copiado mayormente en el resto de publicaciones, aconsejan realizar esta consulta MySQL:

INSERT INTO `yourDBname`.`prefix_content` (`id`, `title`, `alias`, `catid`, `introtext`, `fulltext`, `created`, `created_by`, `created_by_alias`, `checked_out`, `checked_out_time`, `modified`, `modified_by`, `publish_up`, `publish_down`, `access`, `featured`, `hits`, `language`)
SELECT `id`, `title`, `alias`, `catid`, `introtext`, `fulltext`, `created`, `created_by`, `created_by_alias`, `checked_out`, `checked_out_time`, `modified`, `modified_by`, `publish_up`, `publish_down`, `access`, `featured`, `hits`, `language`
FROM `yourDBname`.`prefix_k2_items`

Pero si intentamos ejecutar la consulta desde phpMyAdmin veremos que nos da error porque la estructura de ambas tablas no es la misma.

Veamos la estructura de ambas tablas:

Table structure for table `#_k2_items`

`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) CHARACTER SET utf8 NOT NULL,
`alias` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
`catid` int(11) NOT NULL,
`published` smallint(6) NOT NULL DEFAULT 0,
`introtext` mediumtext CHARACTER SET utf8 NOT NULL,
`fulltext` mediumtext CHARACTER SET utf8 NOT NULL,
`video` text CHARACTER SET utf8 DEFAULT NULL,
`gallery` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
`extra_fields` text CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
`extra_fields_search` text CHARACTER SET utf8 NOT NULL,
`created` datetime NOT NULL,
`created_by` int(11) NOT NULL DEFAULT 0,
`created_by_alias` varchar(255) CHARACTER SET utf8 NOT NULL,
`checked_out` int(10) unsigned NOT NULL,
`checked_out_time` datetime NOT NULL,
`modified` datetime NOT NULL,
`modified_by` int(11) NOT NULL DEFAULT 0,
`publish_up` datetime NOT NULL,
`publish_down` datetime NOT NULL,
`trash` smallint(6) NOT NULL DEFAULT 0,
`access` int(11) NOT NULL DEFAULT 0,
`ordering` int(11) NOT NULL DEFAULT 0,
`featured` smallint(6) NOT NULL DEFAULT 0,
`featured_ordering` int(11) NOT NULL DEFAULT 0,
`image_caption` text CHARACTER SET utf8 NOT NULL,
`image_credits` varchar(255) CHARACTER SET utf8 NOT NULL,
`video_caption` text CHARACTER SET utf8 NOT NULL,
`video_credits` varchar(255) CHARACTER SET utf8 NOT NULL,
`hits` int(10) unsigned NOT NULL,
`params` text CHARACTER SET utf8 NOT NULL,
`metadesc` text CHARACTER SET utf8 NOT NULL,
`metadata` text CHARACTER SET utf8 NOT NULL,
`metakey` text CHARACTER SET utf8 NOT NULL,
`plugins` text CHARACTER SET utf8 NOT NULL,
`language` char(7) CHARACTER SET utf8 NOT NULL,

Table structure for table `#_content`

`id` int(10) unsigned NOT NULL,
`asset_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'FK to the #__assets table.',
`title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`alias` varchar(400) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '',
`introtext` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`fulltext` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`state` tinyint(3) NOT NULL DEFAULT 0,
`catid` int(10) unsigned NOT NULL DEFAULT 0,
`created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`created_by` int(10) unsigned NOT NULL DEFAULT 0,
`created_by_alias` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`modified` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`modified_by` int(10) unsigned NOT NULL DEFAULT 0,
`checked_out` int(10) unsigned NOT NULL DEFAULT 0,
`checked_out_time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`publish_up` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`publish_down` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`images` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`urls` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`attribs` varchar(5120) COLLATE utf8mb4_unicode_ci NOT NULL,
`version` int(10) unsigned NOT NULL DEFAULT 1,
`ordering` int(11) NOT NULL DEFAULT 0,
`metakey` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`metadesc` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`access` int(10) unsigned NOT NULL DEFAULT 0,
`hits` int(10) unsigned NOT NULL DEFAULT 0,
`metadata` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`featured` tinyint(3) unsigned NOT NULL DEFAULT 0 COMMENT 'Set if article is featured.',
`language` char(7) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'The language code for the article.',
`xreference` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`note` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',

Para solventar este error debemos hacer un pequeño cambio en nuestra configuración MySQL en el archivo /etc/my.cnf o /etc/my.cnf.d/mariadb-server.cnf dependiendo de vuestro servidor. Añadiremos al archivo dentro de la sección [mysqld] la siguiente línea:

[mysqld]
sql_mode = ""

y reiniciaremos el servidor mysql, normalmente accediendo mediante SSH y ejecutando:

systemctl restart mariadb.service

Antes de seguir recomiendo encarecidamente hacer una copia de seguridad de la base/s de datos.

Hay que dejar vacía de contenido la tabla #_content por si alguna entrada pudiese tener algún ID duplicado y cause conflictos. Se puede conseguir desde phpMyAdmin marcando la tabla y eligiendo "vaciar" en el menú contextual que aparece:

vaciar tabla en phpMyAdmin

Ahora podremos lanzar la consulta MySQl en phpMyAdmin y el servidor sólo tendrá en cuenta las tablas que vayan incluídas en la consulta obviando el resto. Por ejemplo:

INSERT INTO `yourDBname`.`prefix_content` (`id`, `title`, `alias`, `catid`, `introtext`, `fulltext`, `created`, `created_by`, `created_by_alias`, `checked_out`, `checked_out_time`, `modified`, `modified_by`, `publish_up`, `publish_down`, `access`, `featured`, `hits`, `language`)
SELECT `id`, `title`, `alias`, `catid`, `introtext`, `fulltext`, `created`, `created_by`, `created_by_alias`, `checked_out`, `checked_out_time`, `modified`, `modified_by`, `publish_up`, `publish_down`, `access`, `featured`, `hits`, `language`
FROM `yourDBname`.`prefix_k2_items`

Cambiaremos yourDBname por el nombre de nuestra base de datos (se pueden recuperar los campos desde diferentes bases de datos cambiando el último FROM `yourDBname` a la base de datos que deseemos) y prefix_ con el prefijo de vuestras tablas si es que es diferente.

consulta SQL

Migración de categorías y etiquetas de K2 a categorías y etiquetas de Joomla!

Para el volcado de categorías y etiquetas realizaremos estas dos consultas SQL:

INSERT INTO `yourDBname`.`prefix_qjkn2`.`qjkn2_categories` (`id`, `title`, `alias`, `published`, `access`, `parent_id`, `language`, `extension`, `level`, `path`, `params`, `asset_id`)
SELECT `id`, `name`, `alias`, `published`, `access`, `parent_id`, `language`, `extension`, `level`, `path`, `params`, `asset_id`
FROM `yourDBname`.`prefix_qjkn2`.`qjkn2_k2_categories`

Aquí debemos tener en cuenta que en K2 el título de la categoría se llama `name` y no `title`

INSERT INTO `yourDBname`.`prefix_tags` (`parent_id`, `published`, `title`)
SELECT `id`, `published`, `name`
FROM `yourDBname`.`prefix_k2_tags`

Aquí también cambia el identificador de la etiqueta, en K2 es `id` y en Joomla! `parent_id`

¿Qué pasa con las imágenes?

K2 no tiene ninguna referencia a imágenes en la base de datos. Las imágenes se almacenan en media/k2/items/cache/. Cuando creas un nuevo item y agregas una imagen de encabezado, K2 creará 6 versiones de esa imagen. El nombre del archivo es un hash MD5 de la palabra 'Imagen' y la identificación del artículo (Observa la I mayúscula en Imagen), seguido de _Generic, _L, _M, _S, _XL y _XS. Todas las imágenes están en formato jpg, incluso si el original es png o gif.

Puedes copiar todos los archivos del directorio cache de K2 a tu carpeta de imágenes. Sugeriría crear una subcarpeta llamada K2 o algo de tu agrado, dentro de la carpeta stories de Joomla!.

Puedes elegir cuál de los 6 tamaños de archivo deseas usar con tus artículos de Joomla, e incluso puedes usar diferentes tamaños para el texto de introducción y para el texto completo. El código sql para crear una referencia al archivo correcto (extra grande) será:

CONCAT(MD5(CONCAT('Image',`id`)), '_XL.jpg')

El artículo de Joomla almacena una referencia tanto al texto de introducción como al texto completo en imágenes de un campo. El contenido de ese campo es formato JSON:

{"image_intro":"","float_intro":"","image_intro_alt":"","image_intro_caption":"","image_fulltext":"","float_fulltext":"","image_fulltext_alt":"","image_fulltext_caption":""}

Un ejemplo real:

{"image_intro":"images\/stories\/fotos-415\/hierba-marina.jpg","image_intro_alt":"Pradera de hierba marina","float_intro":"","image_intro_caption":"Pradera de hierba marina","image_fulltext":"images\/stories\/fotos-415\/hierba-marina.jpg","image_fulltext_alt":"Pradera de hierba marina","float_fulltext":"","image_fulltext_caption":"Pradera de hierba marina"}

Ten en cuenta que donde K2 tiene dos campos para el título de la imagen (título y créditos), el contenido solo tiene un título. Entonces, la consulta de las imágenes XL configuradas como introducción y texto completo se convertirá en:

CONCAT('{"image_intro":images\/stories\//k2"',MD5(CONCAT('Image',`id`)), '_XL.jpg','"float_intro":"","image_intro_alt":',`title`,'"","image_intro_caption":"',CONCAT(`image_caption`, ' Credits: ',`image_credits`),'","image_fulltext":',MD5(CONCAT('Image',`id`)), '_XL.jpg','"","float_fulltext":"","image_fulltext_alt":"',`title`,'","image_fulltext_caption":"',CONCAT(`image_caption`, ' Credits: ',`image_credits`),'"}' )

Entonces la consulta completa debería ser:

INSERT INTO `DATABASENAME_PREFIX`.`PREFIX_content` (`id`, `title`, `alias`, `catid`, `introtext`, `fulltext`, `created`, `created_by`, `created_by_alias`, `checked_out`, `checked_out_time`, `modified`, `modified_by`, `publish_up`, `publish_down`, `images`, `access`, `featured`, `hits`, `language`)
SELECT `id`, `title`, `alias`, `catid`, `introtext`, concat_ws('<br/>',`fulltext`, `gallery`) AS 'fulltext', `created`, `created_by`, `created_by_alias`, `checked_out`, `checked_out_time`, `modified`, `modified_by`, `publish_up`, `publish_down`, CONCAT('{"image_intro":images\/stories\//k2"',MD5(CONCAT('Image',`id`)), '_XL.jpg','"float_intro":"","image_intro_alt":',`title`,'"","image_intro_caption":"',CONCAT(`image_caption`, ' Credits: ',`image_credits`),'","image_fulltext":',MD5(CONCAT('Image',`id`)), '_XL.jpg','"","float_fulltext":"","image_fulltext_alt":"',`title`,'","image_fulltext_caption":"',CONCAT(`image_caption`, ' Credits: ',`image_credits`),'"}' ), `access`, `featured`, `hits`, `LANGUAGE`
FROM `DATABASENAME_PREFIX`.`PREFIX_k2_items`;

NOTA: Al realizar la consulta MySQL me arroja un error. No estoy seguro de dónde está incorrecto la consulta SQL de arriba, así que de momento he dejado pendiente el tema de las imágenes. Si encuentro una solución la publicaré aquí.

Solucionando problemas

Después de realizar estas operaciones me he encontrado con dos problemas:

Uno que el campo de variable 'state' de la tabla #_content como no lo hemos recuperado en el volcado ha quedado en 0 (cero) despublicado, así que para que los artículos aparezcan publicados debemos realizar la siguiente consulta SQL colocándonos desde phpMyAdmin en la base de datos que queremos corregir (cambiando prefix por el prefijo de nuestra tabla):

update prefix_content
set state = 1 - state;

El segundo problema es que los artículos aparecían en el frontend pero no en el backend (administrador). La solución la explico en este artículo: Artículos de Joomla visibles en la base de datos y en el frontend pero no visibles en el backend

Jesus_Caceres