MySQL импорт в MODX Revo сайта на Joomla (или любого другого)

Сейчас веду работы по переносу сайта с древней Joomla 1.5 (уже несколько раз пахабно взломанной) на MODX Revolution с соблюдением всех мер безопасности.

Провёл эксперимент по импортированию содержимого старого сайта не путём написания php скрипта, работающего через процессоры, а напрямую манипулируя БД MySQL в phpMyAdmin.

Эксперимент удался: новые записи в БД MODX Revo принял как родные объекты.

Дано: бекап сайта на Joomla (дамп базы данных и архив с файлами)

Задача: переносим весь сайт на MODX последней версии

Так как исходный сайт на Joomla был взломан многократно (факт), и хакерские шеллы могут лежать абсолютно где угодно, то из файлового архива я буду брать только картинки, причём только те, в которых записаны реально картинки.

Текстовые документы в Joomla хранятся в базе данных MySQL, так же как и в MODX Revo, поэтому я подумал, что можно попробовать произвести импорт нативными (родными) средствами СУБД. О чём собственно и повествует данная статья.

Последовательность действий

  1. На сервере с новым создаваемым сайтом создаём вторую пустую базу данных и импортируем в неё дамп старого Joomla-сайта.
  2. В БД нового сайта создаём на время таблицу идентичную по структуре таблице modx_site_content, но без ключей.
  3. Путём сопоставления записей одной CMS и другой копируем содержимое из БД старого сайта в БД нового сайта, в нашу временную таблицу.
  4. Просматриваем всю временную таблицу, чистим по возможности от мусора.
  5. Делаем бекап и полностью очищаем таблицу modx_site_content. Сбрасываем счётчик auto_increment в 1.
  6. Копируем содержимое из временной таблицы в девственно чистую таблицу modx_site_content. (дабы не было конфликтов с дублированием записей с уникальными ключами, а ключей в modx_site_content очень много...)

На этом собственно всё. Можно на всякий пожарный почистить кэш сайта. Из всех перечисленных действий самый сложные - шаги №№ 2,3. Поэтому разберём некоторые из шагов более детально.

По шагам

шаг 2)

Создаём временную таблицу идентичную modx_site_content. "Временная" у меня чисто условно, я не стал использовать ключ TEMPORARY, чтобы иметь возможность просматривать результирующее содержимое.

CREATE TABLE `content` LIKE `modx_site_content`;

Создастся пустая таблица полностью повторяющая структуру образца.

Далее во вкладке Структура новой таблицы, нужно домотать страницу до раздела Индексы и удалить все ключи в этой таблице (с помощью красных крестиков). Ключ PRIMARY не будет удаляться, пока вы не отмените свойство auto_increment у столбца id.

Должно получится так:

шаг 3)

Затем начинаем копировать содержимое из старой базы - в новую, прописывая соответствие полей.

Следующий код копирует секции (родительские каталоги). Соответствие следующее: "значение" as "имя столбца в таблице modx_site_content", причём значение мы либо берём из старой таблицы, либо указываем своё статичное значение в одинарных кавычках.

INSERT INTO `db_new_site`.`content`
(
SELECT
`id` as id,
'document' as type,
'text/html' as contentType,
title as pagetitle,
'' as longtitle,
'' as description,
alias as alias,
'' as link_attributes,
'1' as published,
'0' as pub_date,
'0' as unpub_date,
'0' as parent,
'0' as isfolder,
'' as introtext,
'' as content,
'1' as richtext,
'1' as template,
`id` as menuindex,
'1' as searchable,
'1' as cacheable,
'1' as createdby,
'1396331126' as createdon,
'1' as editedby,
'1396331304' as editedon,
'0' as deleted,
'0' as deletedon,
'0' as deletedby,
'0' as publishedon,
'0' as publishedby,
'' as menutitle,
'0' as donthit,
'0' as privateweb,
'0' as privatemgr,
'0' as content_dispo,
'0' as hidemenu,
'modDocument' as class_key,
'web' as context_key,
'1' as content_type,
'' as uri,
'0' as uri_override,
'0' as hide_children_in_tree,
'1' as show_in_tree,
NULL as properties
FROM `db_old_site`.`jos_sections` as j
)

Следующий код копирует сами текстовые материалы. Соответствие следующее: "значение" as "имя столбца в таблице modx_site_content". Причём id я решил присвоить в своём порядке, для этого добавил переменную @a. А так как разделы мы уже внесли и часть id уже занята, то нужно сделать отступ, указав в @a = id последней внесённой записи предыдущим запросом. В этом запросе нужно обратить внимание на соответствие id родителей (parent) идентификаторам секций, которые мы добавляли во временную таблицу предыдущим запросом.

set @a = 20;

INSERT INTO `db_new_site`.`content`
(SELECT
@a:=@a+1 as id,
'document' as type,
'text/html' as contentType,
title as pagetitle,
'' as longtitle,
metadesc as description,
alias as alias,
'' as link_attributes,
'1' as published,
'0' as pub_date,
'0' as unpub_date,
sectionid as parent,
'0' as isfolder,
`introtext` as introtext,
`fulltext` as content,
'1' as richtext,
'1' as template,
@a as menuindex,
'1' as searchable,
'1' as cacheable,
'1' as createdby,
'1396331126' as createdon,
'1' as editedby,
'1396331304' as editedon,
'0' as deleted,
'0' as deletedon,
'0' as deletedby,
'0' as publishedon,
'0' as publishedby,
'' as menutitle,
'0' as donthit,
'0' as privateweb,
'0' as privatemgr,
'0' as content_dispo,
'0' as hidemenu,
'modDocument' as class_key,
'web' as context_key,
'1' as content_type,
'' as uri,
'0' as uri_override,
'0' as hide_children_in_tree,
'1' as show_in_tree,
NULL as properties
FROM `db_old_site`.`jos_content` as j
)

шаг 4)

Самостоятельно проверьте получившуюся таблицу, чтобы всё было нормально

шаг 5)

Сделайте бекап таблицы modx_site_content (в phpMyAdmin вкладка экспорт) и опустошите её и сбросьте счётчик auto_index:

TRUNCATE TABLE `modx_site_content`;
ALTER TABLE  `modx_site_content` AUTO_INCREMENT = 0;

шаг 6)

Последний шаг: из временной таблицы копируем всё содержимое в рабочую таблицу.

INSERT INTO `modx_site_content`
(SELECT * FROM `content`);

Этих действий достаточно, для того чтобы скопировать содержимое из Joomla в MODX. Это возможно благодаря тому, что по сути строка в БД - это сериализованый объект API MODX. Прямое добавление строки в базу данных, приводит к появлению самого обычного объекта в системе. За это - спасибо моделям и схемам.