Transmogrifier is a migration framework that can help you easily migrate from one platform to another. It has been written in a way that allows re-use of migration code through blueprints. In this talk we will walk through the steps necessary to migrate from Drupal, a popular CMS written in PHP, into Plone. We will see how to use the various blueprints available to build a pipeline that prepares and imports the content into Plone
9. my_items.csv PLONE SYMPOSIUM EAST 2011
_path , _type , title , description
/folder1 , Folder , First Folder , This is folder One
/folder2 , Folder , Second Folder , This is folder Two
/folder1/foo , Document , One Foo , A document named foo
/folder2/foo , Document , Two Foo , Another doc named foo
18. Registering Configs PLONE SYMPOSIUM EAST 2011
<transmogrifier:registerConfig
name="my.migration.base"
title="My migration base config"
description="Base settings for all transmogrifier imports"
configuration="config/base.cfg"
/>
19. Registering Configs PLONE SYMPOSIUM EAST 2011
<transmogrifier:registerConfig
name="my.migration.pages"
title="Drupal pages"
description="Import pages from Drupal into Plone"
configuration="config/pages.cfg"
/>
20. Registering Configs PLONE SYMPOSIUM EAST 2011
<transmogrifier:registerConfig
name="my.migration.articles"
title="Drupal articles"
description="Import articles from Drupal into Plone"
configuration="config/articles.cfg"
/>
21. Registering Configs PLONE SYMPOSIUM EAST 2011
<transmogrifier:registerConfig
name="my.migration.blogs"
title="Drupal blog entries"
description="Import blog entries from Drupal into Plone"
configuration="config/blogs.cfg"
/>
22. Registering Configs PLONE SYMPOSIUM EAST 2011
<transmogrifier:registerConfig
name="my.migration.comments"
title="Drupal comments"
description="Import comments from Drupal into Plone"
configuration="config/comments.cfg"
/>
23. transmogrifier.txt PLONE SYMPOSIUM EAST 2011
my.migration.pages
my.migration.articles
my.migration.blogs
my.migration.comments
25. base.cfg pipeline PLONE SYMPOSIUM EAST 2011
[transmogrifier]
pipeline =
drupal
portal_type
url_normalizer
path
publication_state
text_mimetype
mimetype_encapsulator
folders
constructor
commenting
comments
schema_update
workflow
reindex_object
[settings]
# Path to use if there isn’t one given
base_path = other-content
# Have to escape python string formatting for when
# this gets passed into sqlalchemy
date_format = %%Y/%%m/%%d %%k:%%i:%%s
27. articles.cfg PLONE SYMPOSIUM EAST 2011
[transmogrifier]
include = my.migration.base
[drupal]
query =
SELECT
node.title,
node.status AS status,
GROUP_CONCAT(tag_data.name SEPARATOR 'n') AS subject,
FROM_UNIXTIME(node.created, "${settings:date_format}") AS creation_date,
FROM_UNIXTIME(node.created, "${settings:date_format}") AS effectiveDate,
FROM_UNIXTIME(node.changed, "${settings:date_format}") AS modification_date,
body_data.body_value AS text
url_alias.alias AS _path
FROM node
INNER JOIN field_data_field_tags AS tag_field ON tag_field.entity_id = node.nid
INNER JOIN taxonomy_term_data AS tag_data ON tag_data.tid = tag_field.field_tags_tid
INNER JOIN field_data_body AS body_data ON body_data.entity_id = node.nid
INNER JOIN url_alias ON url_alias.source = CONCAT("node/", node.nid)
GROUP BY
node.title,
node.status,
node.created,
node.changed,
body_data.body_value,
url_alias.alias;
[portal_type]
value = string:Document
28. pages.cfg PLONE SYMPOSIUM EAST 2011
[transmogrifier]
include = my.migration.base
[drupal]
query =
my.migration.base
SELECT
node.title,
node.status AS _status,
FROM_UNIXTIME(node.created, "${settings:date_format}") AS creation_date,
FROM_UNIXTIME(node.created, "${settings:date_format}") AS effectiveDate,
FROM_UNIXTIME(node.changed, "${settings:date_format}") AS modification_date,
body_data.body_value AS text,
url_alias.alias AS _path
FROM node
INNER JOIN field_data_body AS body_data ON body_data.entity_id = node.nid
INNER JOIN url_alias ON url_alias.source = CONCAT("node/", node.nid)
WHERE node.type = "page"
GROUP BY
node.title,
node.status,
node.created,
node.changed,
body_data.body_value,
url_alias.alias;
[portal_type]
value = string:Document
29. blogs.cfg PLONE SYMPOSIUM EAST 2011
[transmogrifier]
include = my.migration.base
[drupal]
query =
SELECT
node.title,
node.satus AS _status,
FROM_UNIXTIME(node.created, "${settings:date_format}") AS creation_date,
FROM_UNIXTIME(node.created, "${settings:date_format}") AS effectiveDate,
FROM_UNIXTIME(node.created, "${settings:date_format}") AS modification_date,
body_data.body_value AS text,
url_alias.alias AS _path
FROM node
INNER JOIN field_data_body AS body_data ON body_data.entity_id = node.nid
INNER JOIN url_alias ON url_alias.source = CONCAT("node/", node.nid)
WHERE node.type = "blog"
GROUP BY
node.title,
node.status,
node.created,
node.changed,
body_data.body_value,
url_alias.alias;
[portal_type]
value = string:BlogEntry
[commenting]
value = python:True
30. comments.cfg PLONE SYMPOSIUM EAST 2011
[transmogrifier]
include = my.migration.base
[drupal]
query =
my.migration.base
SELECT
comment.subject AS title,
FROM_UNIXTIME(comment.created, "${settings:date_format}") AS published,
FROM_UNIXTIME(comment.changed, "${settings:date_format}") AS updated,
comment.name AS author_name,
body_data.comment_body_value AS text,
url_alias.alias AS _parent_path
FROM comment
INNER JOIN field_data_comment_body AS body_data ON body_data.entity_id =
comment.cid
INNER JOIN node ON node.nid = comment.nid
INNER JOIN url_alias ON url_alias.source = CONCAT("node/", node.nid)
GROUP BY
comment.subject,
comment.created,
comment.changed,
comment.name,
body_data.comment_body_value,
url_alias.alias;
[portal_type]
# Override the portal type to use the "comment_type"
key = string:_comment_type
value = string:plone.app.discussion
31. base.cfg overrides PLONE SYMPOSIUM EAST 2011
[path]
blueprint = collective.transmogrifier.sections.inserter
# only add a path if one does not exist
condition = python:'_path' not in item and not '_parent_path' in item
key = string:_path
# Add the value in the extended configuration
value = string:${settings:base_path}/${item/_id}
[portal_type]
blueprint = collective.transmogrifier.sections.inserter
key = string:_type
# We will add the value in the extended config, but we need a
# default set here
value = string:
[commenting]
blueprint = collective.transmogrifier.sections.inserter
key = string:allowDiscussion
# default to false
value = python:False
33. Item Modification PLONE SYMPOSIUM EAST 2011
[publication_state]
blueprint = collective.transmogrifier.sections.inserter
condition = python:'_status' in item and item['_status'] == 1
key = string:_transitions
value = string:publish
[text_mimetype]
# This could probably be taken from the database as well
blueprint = collective.transmogrifier.sections.inserter
key = string:_text_mimetype
value = string:text/html
[mimetype_encapsulator]
blueprint = plone.app.transmogrifier.mimeencapsulator
key = text
mimetype = python:item.get('_%s_mimetype', key)
field = key
condition = mimetype