collective / collective.exportimport

Export and import content and other data from and to Plone
GNU General Public License v2.0
15 stars 15 forks source link

Robustness in content_export for CMFEditions versions, lock_info #194

Open seanupton opened 1 year ago

seanupton commented 1 year ago

These are two distinct issues — both with ContentExport (Plone 5.1, Python 2.7.18, collective.exportimport == 1.7, plone.restapi==7.8.0,) — but I'm using a common monkey patch to work around them, so I figured I would make an issue, if for nothing else, to get this discussion somewhere outside just my own head:

  1. A content item with CMFEditions history containing an item ID rename will not export, because Next/Previous serialization of plone.restapi attempts to get a position (both next, and previous) in container for an id that is no longer there, which fails with something like ValueError: 'OLD_ID_IS_HERE_BUT_CONTAINER_DOES_NOT_GET_IT' is not in list.
  2. If a content item's creator is no longer an existing Plone user, plone.restapi locking serialization fails with AttributeError: 'NoneType' object has no attribute 'getProperty'.

I ended up monkey patching in my export runscript to work around these two things:

from plone.app.dexterity.behaviors.nextprevious import NextPreviousBase

import plone.restapi

# monkey patch plone.app.dexterity.behaviors.nextprevious to handle
# CMFEditions revision objects that have had an id rename, robustly
# for collective.exportimport use case:
orig_get_next = NextPreviousBase.getNextItem
orig_get_prev = NextPreviousBase.getPreviousItem
orig_creator_name = plone.restapi.services.locking.creator_name

def forgiving_call(orig):
    def _inner(*args):
        try:
            return orig(*args)
        except (ValueError, AttributeError):
            # for next/previous serializing:
            #   the object isn't in the parent, probably because its id was
            #   renamed, and plone.restapi.serializer.nextprev.NextPrevious
            #   is trying to serialize a fetched historic CMFEditions
            #   revision of the object to serialize, and that historic
            #   version has the old ID no longer in the parent container
            # for creator_name for locking:
            #   need to be robust around a creator who is no longer in system
            #   as a plone user
            return None
    return _inner

NextPreviousBase.getNextItem = forgiving_call(orig_get_next)
NextPreviousBase.getPreviousItem = forgiving_call(orig_get_prev)
plone.restapi.services.locking.creator_name = forgiving_call(orig_creator_name)

I am unsure about how to address these in a "safe for use outside my limited-purpose runscript" because monkey patching these is a change in behavior for multiple places in plone.restapi, and my use-case is limited to making my export work without omitting content. But content will not export reliably in either condition described above for me without doing this.