Skip to content

Commit

Permalink
Add UID updater section.
Browse files Browse the repository at this point in the history
  • Loading branch information
optilude committed Dec 30, 2009
1 parent 9c7c07f commit 665b0f4
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 1 deletion.
3 changes: 3 additions & 0 deletions docs/HISTORY.txt
Expand Up @@ -6,6 +6,9 @@ Change History
1.1 (unreleased)
================

- Added UID updated section. See uidupdater.txt.
[optilude]

- Fixed tests for Plone 4, in the same way that they were fixed in
collective.transmogrifier.
[optilude]
Expand Down
1 change: 1 addition & 0 deletions setup.py
Expand Up @@ -11,6 +11,7 @@ def read(*rnames):
'Detailed Documentation',
'**********************', '',
read('src', 'plone', 'app', 'transmogrifier', 'atschemaupdater.txt'), '',
read('src', 'plone', 'app', 'transmogrifier', 'uidupdater.txt'), '',
read('src', 'plone', 'app', 'transmogrifier', 'workflowupdater.txt'), '',
read('src', 'plone', 'app', 'transmogrifier', 'browserdefault.txt'), '',
read('src', 'plone', 'app', 'transmogrifier', 'criteria.txt'), '',
Expand Down
6 changes: 6 additions & 0 deletions src/plone/app/transmogrifier/configure.zcml
Expand Up @@ -37,4 +37,10 @@
name="plone.app.transmogrifier.mimeencapsulator"
/>

<utility
component=".uidupdater.UIDUpdaterSection"
name="plone.app.transmogrifier.uidupdater"
/>


</configure>
63 changes: 62 additions & 1 deletion src/plone/app/transmogrifier/tests.py
Expand Up @@ -319,6 +319,64 @@ def __iter__(self):
provideUtility(OFSFilePrinter,
name=u'plone.app.transmogrifier.tests.ofsfileprinter')

def uidSetUp(test):
sectionsSetUp(test)

from Products.Archetypes.interfaces import IReferenceable

class MockReferenceableObject(object):
implements(IReferenceable)

def __init__(self, path, portal):
self.path = path
self.portal = portal

_at_uid = 'xyz'
def UID(self):
return self._at_uid

def _setUID(self, uid):
self.portal.uids_set.append((self.path, uid))
self._at_uid = uid

class MockPortal(object):
implements(IReferenceable)

_last_path = None
def unrestrictedTraverse(self, path, default):
if path[0] == '/':
return default # path is absolute
if isinstance(path, unicode):
return default
if path == 'not/existing/bar':
return default
if path.endswith('/notatcontent'):
return object()
return MockReferenceableObject(path, self)

uids_set = []

test.globs['plone'] = MockPortal()
test.globs['transmogrifier'].context = test.globs['plone']

class UIDSource(SampleSource):
classProvides(ISectionBlueprint)
implements(ISection)

def __init__(self, *args, **kw):
super(UIDSource, self).__init__(*args, **kw)
self.sample = (
dict(_path='/spam/eggs/foo', _uid='abc',), # will be set
dict(_path='/spam/eggs/bar', _uid='xyz',), # same as default
dict(_path='not/existing/bar', _uid='def',), # not found
dict( _uid='geh',), # no path
dict(_path='/spam/eggs/baz', ), # no uid
dict(_path='/spam/notatcontent', _uid='ijk',), # not referenceable
)
provideUtility(UIDSource,
name=u'plone.app.transmogrifier.tests.uidsource')


def test_suite():
return unittest.TestSuite((
doctest.DocFileSuite(
Expand All @@ -327,6 +385,9 @@ def test_suite():
doctest.DocFileSuite(
'atschemaupdater.txt',
setUp=aTSchemaUpdaterSetUp, tearDown=tearDown),
doctest.DocFileSuite(
'uidupdater.txt',
setUp=uidSetUp, tearDown=tearDown),
doctest.DocFileSuite(
'workflowupdater.txt',
setUp=workflowUpdaterSetUp, tearDown=tearDown),
Expand All @@ -340,5 +401,5 @@ def test_suite():
'criteria.txt', setUp=criteriaSetUp, tearDown=tearDown),
doctest.DocFileSuite(
'mimeencapsulator.txt',
setUp=mimeencapsulatorSetUp, tearDown=tearDown),
setUp=mimeencapsulatorSetUp, tearDown=tearDown),
))
57 changes: 57 additions & 0 deletions src/plone/app/transmogrifier/uidupdater.py
@@ -0,0 +1,57 @@
from zope.interface import classProvides, implements

from collective.transmogrifier.interfaces import ISectionBlueprint
from collective.transmogrifier.interfaces import ISection
from collective.transmogrifier.utils import Matcher
from collective.transmogrifier.utils import defaultKeys

from Products.Archetypes.interfaces import IReferenceable
from Products.Archetypes.config import UUID_ATTR

class UIDUpdaterSection(object):
classProvides(ISectionBlueprint)
implements(ISection)

def __init__(self, transmogrifier, name, options, previous):
self.previous = previous
self.context = transmogrifier.context

if 'path-key' in options:
pathkeys = options['path-key'].splitlines()
else:
pathkeys = defaultKeys(options['blueprint'], name, 'path')
self.pathkey = Matcher(*pathkeys)

if 'uid-key' in options:
uidkeys = options['uid-key'].splitlines()
else:
uidkeys = defaultKeys(options['blueprint'], name, 'uid')
self.uidkey = Matcher(*uidkeys)


def __iter__(self):

for item in self.previous:

pathkey = self.pathkey(*item.keys())[0]
uidkey = self.uidkey(*item.keys())[0]

if not pathkey or not uidkey: # not enough info
yield item; continue

path = item[pathkey]
uid = item[uidkey]

obj = self.context.unrestrictedTraverse(path.lstrip('/'), None)
if obj is None: # path doesn't exist
yield item; continue

if IReferenceable.providedBy(obj):
oldUID = obj.UID()
if oldUID != uid:
if not oldUID:
setattr(obj, UUID_ATTR, uid)
else:
obj._setUID(uid)

yield item
79 changes: 79 additions & 0 deletions src/plone/app/transmogrifier/uidupdater.txt
@@ -0,0 +1,79 @@
UID updater section
===================

If an Archetypes content object is created in a pipeline, e.g. by the standard
content constructor section, it will get a new UID. If you are importing
content from another Plone site, and you have references (or links embedded
in content using Plone's link-by-UID feature) to existing content, you may
want to retain UIDs. The UID updater section allows you to set the UID on an
existing object for this purpose.

The UID updater blueprint name is ``plone.app.transmogrifier.uidupdater``.

UID updating requires two pieces of information: the path to the object
to update, and the new UID to set.

To determine the path, the UID updater section inspects each item and looks
for a path key, as described below. Any item missing this key will be skipped.
Similarly, items with a path that doesn't exist or are not referenceable
(Archetypes) objects will be skipped.

The object path will be found under the first key found among the following:

* ``_plone.app.transmogrifier.atschemaupdater_[sectionname]_path``
* ``_plone.app.transmogrifier.atschemaupdater_path``
* ``_[sectionname]_path``
* ``_path``

where ``[sectionname]`` is replaced with the name given to the current
section. This allows you to target the right section precisely if
needed.

Alternatively, you can specify what key to use for the path by specifying the
``path-key`` option, which should be a list of keys to try (one key per line;
use a ``re:`` or ``regexp:`` prefix to specify regular expressions).

Paths to objects are always interpreted as relative to the context.

Similarly, the UID to set must be a string under a given key. You can set the
key with the ``uid-key`` option, which behaves much like ``path-key``. The
default is to look under:

* ``_plone.app.transmogrifier.atschemaupdater_[sectionname]_uid``
* ``_plone.app.transmogrifier.atschemaupdater_uid``
* ``_[sectionname]_uid``
* ``_uid``

If the UID key is missing, the item will be skipped.

Below is an example of a standard updater. The test uid source produces
items with two keys: a path under ``_path`` and a UID string under ``_uid``.

>>> import pprint
>>> atschema = """
... [transmogrifier]
... pipeline =
... schemasource
... schemaupdater
... printer
...
... [schemasource]
... blueprint = plone.app.transmogrifier.tests.uidsource
...
... [schemaupdater]
... blueprint = plone.app.transmogrifier.uidupdater
...
... [printer]
... blueprint = collective.transmogrifier.sections.tests.pprinter
... """
>>> registerConfig(u'plone.app.transmogrifier.tests.uid', atschema)
>>> transmogrifier(u'plone.app.transmogrifier.tests.uid')
[('_path', '/spam/eggs/foo'), ('_uid', 'abc')]
[('_path', '/spam/eggs/bar'), ('_uid', 'xyz')]
[('_path', 'not/existing/bar'), ('_uid', 'def')]
[('_uid', 'geh')]
[('_path', '/spam/eggs/baz')]
[('_path', '/spam/notatcontent'), ('_uid', 'ijk')]

>>> pprint.pprint(plone.uids_set)
[('spam/eggs/foo', 'abc')]

0 comments on commit 665b0f4

Please sign in to comment.