An add-on for Plone to Export and import content, members, relations, translations and localroles.
Export and import content, members, relations, translations, localroles and much more.
Export and import all kinds of data from and to Plone sites using a intermediate json-format. The main use-case is migrations since it enables you to for example migrate from Plone 4 with Archetypes and Python 2 to Plone 6 with Dexterity and Python 3 in one step. Most features use plone.restapi to serialize and deserialize data.
See also the training on migrating with exportimport: https://training.plone.org/migrations/exportimport.html
Contents
Export supports:
Import supports:
Use the form with the URL /@@export_content, and select what you want to export:
You can export one or more types and a whole site or only a specific path in a site. Since items are exported ordered by path importing them will create the same structure as you had originally.
The downloaded json-file will have the name of the path you exported from, e.g. Plone.json.
The exports for members, relations, localroles and relations are linked to in this form but can also be called individually: /@@export_members, /@@export_relations, /@@export_localroles, /@@export_translations, /@@export_ordering, /@@export_discussion.
Use the form with the URL /@@import_content, and upload a json-file that you want to import:
The imports for members, relations, localroles and relations are linked to in this form but can also be called individually: /@@import_members, /@@import_relations, /@@import_localroles, /@@import_translations, /@@import_ordering, /@@import_discussion.
As a last step in a migration there is another view @@reset_dates that resets the modified date on imported content to the date initially contained in the imported json-file. This is necessary since varous changes during a migration will likely result in a updated modified-date. During import the original is stored as obj.modification_date_migrated on each new object and this view sets this date.
If you select 'Save to file on server', the Export view will save json files in the <var> directory of your Plone instanc in /var/instance. The import view will look for files under /var/instance/import. These directories will normally be different, under different Plone instances and possibly on different servers.
You can set the environment variable 'COLLECTIVE_EXPORTIMPORT_CENTRAL_DIRECTORY' to add a 'shared' directory on one server or maybe network share. With this variable set, collective.exportimport will both save to and load .json files from the same server directory. This saves time not having to move .json files around from the export- to the import location. You should be aware that the Export views will overwrite any existing previous .json file export that have the same name.
When a in-place-migration is not required you can choose this addon to migrate the most important parts of your site to json and then import it into a new Plone instance of your targeted version:
How to migrate additional features like Annotations or Marker Interfaces is discussed in the FAQ section.
You can use this addon to
Exporting content is basically a wrapper for the serializers of plone.restapi:
Importing content is a elaborate wrapper for the deserializers of plone.restapi:
A main use-case of this package is migration from one Plone-Version to another.
Exporting Archetypes content and importing that as Dexterity content works fine but due to changes in field-names some settings would get lost. For example the setting to exclude content from the navigation was renamed from excludeFromNav to exclude_from_nav.
To fix this you can check the checkbox "Modify exported data for migrations". This will modify the data during export:
You can choose between four options how to deal with content that already exists:
- Skip: Don't import at all
- Replace: Delete item and create new
- Update: Reuse and only overwrite imported data
- Ignore: Create with a new id
Imported content is initially created with invokeFactory using portal_type and id of the exported item before deserializing the rest of the data. You can set additional values by specifying a dict factory_kwargs that will be passed to the factory. Like this you can set values on the imported object that are expected to be there by subscribers to IObjectAddedEvent.
Exporting versions of Archetypes content will not work because of a bug in plone.restapi (https://github.com/plone/plone.restapi/issues/1335). For export to work you need to use a version between 7.7.0 and 8.0.0 (if released) or a source-checkout of the branch 7.x.x.
Exporting and importing large amounts of content can take a while. Export is pretty fast but import is constrained by some features of Plone, most importantly versioning:
During import you can commit every x number of items which will free up memory and disk-space in your TMPDIR (where blobs are added before each commit).
When exporting large numbers of blobs (binary files and images) you will get huge json-files and may run out of memory. You have various options to deal with this. The best way depends on how you are going to import the blobs:
This addon is designed to be adapted to your requirements and has multiple hooks to make that easy.
To make that easier here are packages you can reuse to override and extend the export and import. Use these templates and adapt them to your own projects:
Many examples for customizing the export and import are collected in the chapter "FAQ, Tips and Tricks" below.
Note
As a rule of thumb you should make changes to the data during import unless you need access to the original object for the required changes. One reason is that this way the serialized content in the json-file more closely represents the original data. Another reason is that it allows you to fix issues during the process you are currently developing (i.e. without having to redo the export).
Register it with your own browserlayer to override the default:
Register it:
Run all exports and save all data in var/instance/:
Run all imports using the data exported in the example above:
Note
The views @@export_all and @@import_all are also contained in the helper-packages https://github.com/starzel/contentexport and https://github.com/starzel/contentimport
This section covers frequent use-cases and examples for features that are not required for all migrations.
Using global_obj_hook during export to inspect content and decide to skip it.
Use global_dict_hook during export to inspect content and modify the serialized json. You can also use dict_hook_<somecontenttype> to better structure your code for readability.
Sometimes you need to handle data that you add in global_dict_hook during export in corresponding code in global_object_hook during import.
The following example about placeful workflow policy is a perfect example for that pattern:
A lot of fixes can be done during import using the global_dict_hook or dict_hook_<contenttype>.
Here we prevent the expire-date to be before the effective date since that would lead to validation-errors during deserializing:
Here we drop empty lines from the creators:
This example migrates a PloneHelpCenter to a simple folder/document structure during import. There are a couple more types to handle (as folder or document) but you get the idea, don't you?
If you change types during import you need to take care of other cases where types are referenced.Examples are collection-queries (see "Fixing invalid collection queries" below) or constrains (see here):
Some core-features of Plone (e.g. comments) use annotations to store data. The core features are already covered but your custom code or community addons may use annotations as well. Here is how you can migrate them.
Export: Only export those Annotations that your really need.
Import:
Some features also store data in annotations on the portal, e.g. plone.contentrules.localassignments, plone.portlets.categoryblackliststatus, plone.portlets.contextassignments, syndication_settings. Depending on your requirements you may want to export and import those as well.
Export: You may only want to export the marker-interfaces you need. It is a good idea to inspect a list of all used marker interfaces in a portal before deciding what to migrate.
Import:
The event-handlers of versioning can seriously slow down your imports. It is a good idea to skip it before the import:
Re-enable versioning and create initial versions after all imports and fixes are done, e.g in the view @@import_all.
Sometimes you get validation-errors during import because the data cannot be validated. That can happen when options in a field are generated from content in the site. In these cases you cannot be sure that all options already exist in the portal while importing the content.
It may also happen, when you have validators that rely on content or configuration that does not exist on import.
Note
For relation fields this is not necessary since relations are imported after content anyway!
There are two ways to handle these issues:
You need to specify which content-types and fields you want to handle that way.
It is put in a key, that the normal import will ignore and set using setattr() before deserializing the rest of the data.
Note
Using global_obj_hook_before_deserializing makes sure that data is there when the event-handlers are run after import.
You can also wait until all content is imported before setting the values on these fields. Again you need to find out which fields for which types you want to handle that way.
Here the data is stored in an annotation on the imported object from which it is later read. This example also supports setting some data with setattr without validating it:
You then need a new step in the migration to move the deferred values from the annotation to the field:
This additional view obviously needs to be registered:
Often it is better to export and log items for which no container could be found instead of re-creating the original structure.
By default only users and groups stores in Plone are exported/imported. You can export/import Zope user like this.
Export
Import:
When you migrate multiple similar sites that are configured manually it can be useful to export and import configuration that was set by hand.
This custom export exports and imports some selected settings and addons from a Plone 4.3 site.
Export:
Import:
The import installs the addons and load the settings in the registry. Since Plone 5 portal_properties is no longer used.
The pull-request https://github.com/collective/collective.exportimport/pull/130 has views @@export_registry and @@import_registry. These views export and import registry records that do not use the default-setting specified in the schema for that registry record. The export alone could also be usefull to figure out which settings were modified for a site.
That code will probably not be merged but you can use it in your own projects.
To be able to export PFG as easyform you should use the branch migration_features_1.x of collective.easyform in your old site. Easyform does not need to be installed, we only need the methods fields_model and actions_model.
Export:
Import exported PloneFormGen data into Easyform:
Some queries changes between Plone 4 and 5. This fixes the issues.
The actual migration of topics to collections in collective.exportimport.serializer.SerializeTopicToJson does not (yet) take care of that.
You can reuse the migration-code provided by @@migrate_to_volto in plone.volto in a migration. The following example (used for migrating https://plone.org to Volto) can be used to migrate a site from any older version to Plone 6 with Volto.
You need to have the Blocks Conversion Tool (https://github.com/plone/blocks-conversion-tool) running that takes care of migrating richtext-values to Volto-blocks.
See https://6.docs.plone.org/backend/upgrading/version-specific-migration/migrate-to-volto.html for more details on the changes the migration to Volto does.
Versions older than Plone 4 do not support plone.restapi which is required to serialize the content used by collective.exportimport.
To migrate Plone 1, 2 and 3 to Plone 6 you can use collective.jsonify for the export and collective.exportimport for the import.
Use https://github.com/collective/collective.jsonify to export content.
You include the methods of collective.jsonify using External Methods. See https://github.com/collective/collective.jsonify/blob/master/docs/install.rst for more info.
To work better with collective.exportimport you could extend the exported data using the feature additional_wrappers. Add info on the parent of an item to make it easier for collective.exportimport to import the data.
Here is a full example for json_methods.py which should be in BUILDOUT_ROOT/parts/instance/Extensions/
Here is a full example for json_methods.py which should be in <BUILDOUT_ROOT>/parts/instance/Extensions/
To use these create three "External Method" in the ZMI root at the Zope root to use that:
Then you can pass the extender to the export using a query-string: http://localhost:8080/Plone/export_content?additional_wrappers=extend_item
Two issues need to be dealt with to allow collective.exportimport to import the data generated by collective.jsonify.
Starting with version 1.8 you can pass an iterator to the import.
You need to create a directory-walker that sorts the json-files the right way. By default it would import them in the order 1.json, 10.json, 100.json, 101.json and so on.
The walker takes the path to be the root with one or more directories holding the json-files. The sorting of the files is done using the number in the filename.
The method prepare_data modifies the data before passing it to the import. A very similar task is done by collective.exportimport during export.
You can pass the generator filesystem_walker to the import:
collective.jsonify puts the info on relations, translations and default-pages in the export-file. You can use the approach to defer imports to deal with that data after all items were imported. The example ImportDeferred above uses that approach to set the default pages.
This global_obj_hook below stores that data in a annotation:
Install collective.exportimport by adding it to your buildout:
[buildout] ... eggs = collective.exportimport
and then running bin/buildout
You don't need to activate the add-on in the Site Setup Add-ons control panel to be able to use the forms @@export_content and @@import_content in your site.
You do need to add it to your buildout configuration and run buildout to make these features available at all. See https://docs.plone.org/manage/installing/installing_addons.html for details.
collective.exportimport depends on plone.restapi. For Plone 4, you need to pin plone.restapi to 7.x . When installing plone.restapi version 7.x.x in Plone 4 you may need to add the following version pins to your buildout:
[versions] PyJWT = 1.7.1 six = 1.11.0 attrs = 21.2.0 plone.rest = 1.6.2 plone.schema = 1.3.0 # Last pyrsistent version that is python 2 compatible: pyrsistent = 0.15.7 # Required by: # jsonschema==3.2.0 functools32 = 3.2.3.post2 # Required by: # plone.schema==1.3.0 jsonschema = 3.2.0 # Required by: # importlib-metadata==1.3.0 pathlib2 = 2.3.5 # Required by: # pathlib2==2.3.5 scandir = 1.10.0 # plone.app.contenttypes > 1.0 plone.app.contenttypes = 1.1.9 importlib-metadata = 2.1.3 zipp = 1.2.0 configparser = 4.0.2 contextlib2 = 0.6.0.post1
These versions are taken from the plone.restapi 7.x README: https://pypi.org/project/plone.restapi/7.8.1/
If you are having issues, please let us know.
The project is licensed under the GPLv2.