plone.app.transmogrifier


Nameplone.app.transmogrifier JSON
Version 3.0.1 PyPI version JSON
download
home_pagehttps://github.com/collective/plone.app.transmogrifier
SummaryPlone blueprints for collective.transmogrifier pipelines
upload_time2023-06-02 17:48:48
maintainer
docs_urlNone
authorJarn
requires_python>=3.7
licenseGPL
keywords content import filtering plone
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ===============================
Plone transmogrifier blueprints
===============================

.. contents::

This package contains several blueprints for `collective.transmogrifier`_
pipelines, commonly used to import content into a Plone site.

Installation
============

See docs/INSTALL.rst for installation instructions.

Credits
=======

Development sponsored by
    Elkjøp Nordic AS

Design and development
    `Martijn Pieters`_ at Jarn_
    `Florian Schulze`_ at Jarn_

.. _Martijn Pieters: mailto:mj@jarn.com
.. _Florian Schulze: mailto:fschulze@jarn.com
.. _Jarn: http://www.jarn.com/
.. _collective.transmogrifier: https://https://github.com/collective/collective.transmogrifier

Detailed Documentation
======================

Browser default section
-----------------------

A browser default pipeline section sets the default-page on a folder, and the
layout template on content objects. They are the Transmogrifier equivalent of
the ``display`` menu in Plone. The browser default section blueprint name is
``plone.app.transmogrifier.browserdefault``. Browser default sections operate
on objects already present in the ZODB, be they created by a constructor or
pre-existing objects.

Setting the browser default needs at least 1 piece of information: the path to
the object to modify. To determine the path, the browser default section
inspects each item and looks for one key, as described below. Any item missing
this piece of information will be skipped. Similarly, items with a path that
doesn't exist or do not support the Plone ISelectableBrowserDefault interface
will be skipped as well.

For the object path, it'll look (in order) for
``_plone.app.transmogrifier.browserdefault_[sectionname]_path``,
``_plone.app.transmogrifier.browserdefault_path``, ``_[sectionname]_path``
and ``_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).

Once an object has been located, the section will looks for defaultpage
and layout keys. Like the path key, these can be specified in the source
configuration, named by the ``default-page-key`` and ``layout-key`` options,
respectively, and like the path key, the default keys the section looks for
are the usual list of specific-to-generic keys based on blueprint and section
names, from
``_plone.app.transmogrifier.browserdefault_[sectionname]_defaultpage`` and
``_plone.app.transmogrifier.browserdefault_[sectionname]_layout`` down to
``_defaultpage`` and ``_layout``.

The defaultpage key will set the id of the default page that should be
presented when the content object is loaded, and the layout key will set the
id of the layout to use for the content item.

::

    >>> import pprint
    >>> browserdefault = """
    ... [transmogrifier]
    ... pipeline =
    ...     browserdefaultsource
    ...     browserdefault
    ...     printer
    ...
    ... [browserdefaultsource]
    ... blueprint = plone.app.transmogrifier.tests.browserdefaultsource
    ...
    ... [browserdefault]
    ... blueprint = plone.app.transmogrifier.browserdefault
    ...
    ... [printer]
    ... blueprint = collective.transmogrifier.sections.logger
    ... name = logger
    ... """
    >>> registerConfig(u'plone.app.transmogrifier.tests.browserdefault',
    ...                browserdefault)
    >>> transmogrifier(u'plone.app.transmogrifier.tests.browserdefault')
    >>> print(handler)
    logger INFO
      {'_layout': 'spam', '_path': '/spam/eggs/foo'}
    logger INFO
      {'_defaultpage': 'eggs', '_path': '/spam/eggs/bar'}
    logger INFO
      {'_defaultpage': 'eggs', '_layout': 'spam', '_path': '/spam/eggs/baz'}
    logger INFO
        {'_layout': 'spam',
       '_path': 'not/existing/bar',
       'title': 'Should not be updated, not an existing path'}
    logger INFO
        {'_path': 'spam/eggs/incomplete',
       'title': 'Should not be updated, no layout or defaultpage'}
    logger INFO
        {'_layout': '',
       '_path': 'spam/eggs/emptylayout',
       'title': 'Should not be updated, no layout or defaultpage'}
    logger INFO
        {'_defaultpage': '',
       '_path': 'spam/eggs/emptydefaultpage',
       'title': 'Should not be updated, no layout or defaultpage'}
    >>> pprint.pprint(plone.updated)
    [('spam/eggs/foo', 'layout', 'spam'),
     ('spam/eggs/bar', 'defaultpage', 'eggs'),
     ('spam/eggs/baz', 'layout', 'spam'),
     ('spam/eggs/baz', 'defaultpage', 'eggs')]


DatesUpdater section
--------------------

This blueprint sets creation, modification and effective dates on objects.

Blueprint name: ``plone.app.transmogrifier.datesupdater``

Option path-key: The key for the path to the object.

Option creation-key: The key for the creation date.

Option modification-key: The key for the modification date.

Option effective-key: The key for the effective date.

Option expiration-key: The key for the expiration date.

::

    >>> import pprint
    >>> pipeline = """
    ... [transmogrifier]
    ... pipeline =
    ...     schemasource
    ...     datesupdater
    ...     logger
    ...
    ... [schemasource]
    ... blueprint = plone.app.transmogrifier.tests.schemasource
    ...
    ... [datesupdater]
    ... blueprint = plone.app.transmogrifier.datesupdater
    ... path-key = _path
    ... creation-key = creation_date
    ... modification-key = modification_date
    ... effective-key = effective_date
    ... expiration-key = expiration_date
    ...
    ... [logger]
    ... blueprint = collective.transmogrifier.sections.logger
    ... name = logger
    ... """
    >>> registerConfig(u'plone.app.transmogrifier.tests.datesupdater', pipeline)

    >>> transmogrifier(u'plone.app.transmogrifier.tests.datesupdater')


Print out the source structure::

    >>> print(handler)
    logger INFO
        {'_path': '/spam/eggs/foo',
       'creation_date': DateTime('2010/10/10 00:00:00 UTC'),
       'effective_date': DateTime('2010/10/10 00:00:00 UTC'),
       'expiration_date': DateTime('2012/12/12 00:00:00 UTC'),
       'modification_date': DateTime('2011/11/11 00:00:00 UTC')}
    logger INFO
        {'_path': '/spam/eggs/bar',
       'creation_date': DateTime('2010/10/10 00:00:00 UTC')}
    logger INFO
        {'_path': '/spam/eggs/baz',
       'modification_date': DateTime('2011/11/11 00:00:00 UTC')}
    logger INFO
        {'_path': '/spam/eggs/qux',
       'effective_date': DateTime('2010/10/10 00:00:00 UTC')}
    logger INFO
        {'_path': '/spam/eggs/norf',
       'expiration_date': DateTime('2012/12/12 00:00:00 UTC')}
    logger INFO
        {'_path': 'not/existing/bar',
       'creation_date': DateTime('2010/10/10 00:00:00 UTC'),
       'effective_date': DateTime('2010/10/10 00:00:00 UTC'),
       'expiration_date': DateTime('2012/12/12 00:00:00 UTC'),
       'modification_date': DateTime('2011/11/11 00:00:00 UTC')}
    logger INFO
        {'creation_date': DateTime('2010/10/10 00:00:00 UTC'),
       'effective_date': DateTime('2010/10/10 00:00:00 UTC'),
       'expiration_date': DateTime('2012/12/12 00:00:00 UTC'),
       'modification_date': DateTime('2011/11/11 00:00:00 UTC')}


That was changed on the object::

    >>> pprint.pprint(plone.updated)
    [('spam/eggs/foo', 'creation_date', DateTime('2010/10/10 00:00:00 UTC')),
     ('spam/eggs/foo', 'modification_date', DateTime('2011/11/11 00:00:00 UTC')),
     ('spam/eggs/foo', 'effective_date', DateTime('2010/10/10 00:00:00 UTC')),
     ('spam/eggs/foo', 'expiration_date', DateTime('2012/12/12 00:00:00 UTC')),
     ('spam/eggs/bar', 'creation_date', DateTime('2010/10/10 00:00:00 UTC')),
     ('spam/eggs/baz', 'modification_date', DateTime('2011/11/11 00:00:00 UTC')),
     ('spam/eggs/qux', 'effective_date', DateTime('2010/10/10 00:00:00 UTC')),
     ('spam/eggs/norf', 'expiration_date', DateTime('2012/12/12 00:00:00 UTC'))]


Mime encapsulator section
-------------------------

A mime encapsulator section wraps arbitrary data in ``OFS.Image.File``
objects, together with a MIME type. This wrapping is a pre-requisite for
image, file or text fields, which can only take such File objects.
The mime encapsulator blueprint name is
``plone.app.transmogrifier.mimeencapsulator``.

An encapsulator section needs 3 pieces of information: the key at which to
find the data to encapsulate, the MIME type of this data, and the name of the
field where the encapsulated data will be stored. The idea is that the data
is copied from a "data key" (defaulting to ``_data`` and settable with the
``data-key`` option), wrapped into a ``File`` object with a MIME type (read
from the ``mimetype`` option, which contains a TALES expression), and then
saved into the pipeline item dictionary under a new key, most likely
corresponding to a field name (read from the ``field`` option,
which is also a TALES expression).

The data key defaults to the series ``_[blueprintname]_[sectionname]_data``,
``_[blueprintname]_data``, ``_[sectionname]_data`` and ``_data``, where
``[blueprintname]`` is ``plone.app.transmogrifier.mimeencapsulator`` and
``[sectionname]`` is replaced with the name of the current section. You can
override this by specifying the ``data-key`` option.

You specify the mimetype with the ``mimetype`` option, which takes a TALES
expression.

The ``field`` option, also a TALES expression, sets the output field name.

Optionally, you can specify a ``condition`` option, again a TALES expression,
that when evaluating to ``False``, causes the section to skip encapsulation
for  that item.

::

    >>> encapsulator = """
    ... [transmogrifier]
    ... pipeline =
    ...     source
    ...     encapsulator
    ...     conditionalencapsulator
    ...     printer
    ...
    ... [source]
    ... blueprint = plone.app.transmogrifier.tests.encapsulatorsource
    ...
    ... [encapsulator]
    ... blueprint = plone.app.transmogrifier.mimeencapsulator
    ... # Read the mimetype from the item
    ... mimetype = item/_mimetype
    ... field = string:datafield
    ...
    ... [conditionalencapsulator]
    ... blueprint = plone.app.transmogrifier.mimeencapsulator
    ... data-key = portrait
    ... mimetype = python:item.get('_%s_mimetype' % key)
    ... # replace the data in-place
    ... field = key
    ... condition = mimetype
    ...
    ... [printer]
    ... blueprint = plone.app.transmogrifier.tests.ofsfileprinter
    ... """
    >>> registerConfig(u'plone.app.transmogrifier.tests.encapsulator',
    ...                encapsulator)
    >>> transmogrifier(u'plone.app.transmogrifier.tests.encapsulator')
    datafield: (application/x-test-data) foobarbaz
    portrait: (image/jpeg) someportraitdata


The ``field`` expression has access to the following:

``item``
    The current pipeline item

``key``
    The name of the matched data key

``match``
    If the key was matched by a regular expression, the match object, otherwise boolean True

``transmogrifier``
    The transmogrifier

``name``
    The name of the splitter section

``options``
    The splitter options

``modules``
    ``sys.modules``


The ``mimetype`` expression has access to the same information as the ``field``
expression, plus:

``field``
    The name of the field in which the encapsulated data will be stored.

The ``condition`` expression has access to the same information as the
``mimetype`` expression, plus:

``mimetype``
    The mimetype used to encapsulate the data.


PathFixer section
-----------------

When importing contents from a old site into a new, the path to the Plone site
root may have changed. This blueprint updates the old paths to match the new
structrue by removing or appending strings from the right side of the path
value.

It also converts the path to ``str``. Also raise exception if path have any invalid characters from it.

Blueprint name: ``plone.app.transmogrifier.pathfixer``

Option path-key: The key of the item under which the path to be manipulated can
                 be found. E.g. ``_path``.

Option stripstring: A string to strip from the path value.

Option prependstring: A string to append to the path value.


Look, here. Original path structure from
plone.app.transmogrifier.tests.schemasource is::

    /spam/eggs/foo
    relative/path
    /spam/eggs/another


Now lets manipulate it::

    >>> import pprint
    >>> pipeline = """
    ... [transmogrifier]
    ... pipeline =
    ...     schemasource
    ...     pathfixer
    ...     logger
    ...
    ... [schemasource]
    ... blueprint = plone.app.transmogrifier.tests.schemasource
    ...
    ... [pathfixer]
    ... blueprint = plone.app.transmogrifier.pathfixer
    ... path-key = _path
    ... stripstring = /spam/eggs/
    ... prependstring = subfolder/
    ...
    ... [logger]
    ... blueprint = collective.transmogrifier.sections.logger
    ... name = logger
    ... key = _path
    ... """
    >>> registerConfig(u'plone.app.transmogrifier.tests.pathfixer', pipeline)

    >>> transmogrifier(u'plone.app.transmogrifier.tests.pathfixer')
    >>> print(handler)
    logger INFO
      subfolder/foo
    logger INFO
      subfolder/relative/path
    logger INFO
      subfolder/another



Portal Transforms section
-------------------------

A portal transforms pipeline section lets you use Portal Transforms to
transform item values. The portal transforms section blueprint name is
``plone.app.transmogrifier.portaltransforms``.

What values to transform is determined by the ``keys`` option, which takes a
set of newline-separated key names. If a key name starts with ``re:`` or
``regexp:`` it is treated as a regular expression instead.

You can specify what transformation to apply in two ways. Firstly, you can
directly specify a transformation by naming it with the ``transform`` option;
the named transformation is run directly. Alternatively you can let the portal
transforms tool figure out what transform to use by specifying ``target`` and
an optional ``from`` mimetype. The portal transforms tool will select one or
more transforms based on these mimetypes, and if no ``from`` option is given
the original item value is used to determine one.

Also optional is the ``condition`` option, which lets you specify a TALES
expression that when evaluating to False will prevent any transformations from
happening. The condition is evaluated for every matched key.

::

    >>> ptransforms = """
    ... [transmogrifier]
    ... pipeline =
    ...     source
    ...     transform-id
    ...     transform-title
    ...     transform-status
    ...     printer
    ...
    ... [source]
    ... blueprint = collective.transmogrifier.sections.tests.samplesource
    ... encoding = utf8
    ...
    ... [transform-id]
    ... blueprint = plone.app.transmogrifier.portaltransforms
    ... transform = identity
    ... keys = id
    ...
    ... [transform-title]
    ... blueprint = plone.app.transmogrifier.portaltransforms
    ... target = text/plain
    ... keys = title
    ...
    ... [transform-status]
    ... blueprint = plone.app.transmogrifier.portaltransforms
    ... from = text/plain
    ... target = text/plain
    ... keys = status
    ...
    ... [printer]
    ... blueprint = collective.transmogrifier.sections.logger
    ... name = logger
    ... """
    >>> registerConfig(u'plone.app.transmogrifier.tests.ptransforms',
    ...                ptransforms)

    >>> transmogrifier(u'plone.app.transmogrifier.tests.ptransforms')
    >>> print(handler)
    logger INFO
        {'id': "Transformed 'foo' using the identity transform",
       'status': "Transformed b'\\xe2\\x84\\x97' from text/plain to text/plain",
       'title': "Transformed b'The Foo Fighters \\xe2\\x84\\x97' to text/plain"}
    logger INFO
        {'id': "Transformed 'bar' using the identity transform",
       'status': "Transformed b'\\xe2\\x84\\xa2' from text/plain to text/plain",
       'title': "Transformed b'Brand Chocolate Bar \\xe2\\x84\\xa2' to text/plain"}
    logger INFO
        {'id': "Transformed 'monty-python' using the identity transform",
       'status': "Transformed b'\\xc2\\xa9' from text/plain to text/plain",
       'title': 'Transformed b"Monty Python\'s Flying Circus \\xc2\\xa9" to '
                'text/plain'}

The ``condition`` expression has access to the following:

``item``
    The current pipeline item

``key``
    The name of the matched key

``match``
    If the key was matched by a regular expression, the match object, otherwise boolean True

``transmogrifier``
    The transmogrifier

``name``
    The name of the splitter section

``options``
    The splitter options

``modules``
    ``sys.modules``


Redirector section
------------------

A redirector section uses `plone.app.redirector` to manage redirects and update
paths in keys.

::

    >>> import pprint
    >>> redirector = """
    ... [transmogrifier]
    ... pipeline =
    ...     source
    ...     clean-old-paths
    ...     old-paths
    ...     content-element
    ...     redirect
    ...     href
    ...     logger
    ...
    ... [source]
    ... blueprint = collective.transmogrifier.sections.csvsource
    ... filename = plone.app.transmogrifier:redirector.csv
    ...
    ... [clean-old-paths]
    ... blueprint = collective.transmogrifier.sections.manipulator
    ... condition = not:item/_old_paths|nothing
    ... delete = _old_paths
    ...
    ... [old-paths]
    ... blueprint = collective.transmogrifier.sections.inserter
    ... key = string:_old_paths
    ... condition = exists:item/_old_paths
    ... value = python:item['_old_paths'].split('|')
    ...
    ... [content-element]
    ... blueprint = collective.transmogrifier.sections.inserter
    ... key = string:_content_element
    ... condition = item/remoteUrl
    ... value = python:modules['xml.etree.ElementTree'].Element(\
    ...     'a', dict(href=item['remoteUrl']))
    ...
    ... [redirect]
    ... blueprint = plone.app.transmogrifier.redirector
    ...
    ... [href]
    ... blueprint = collective.transmogrifier.sections.inserter
    ... key = string:_content_element
    ... condition = exists:item/_content_element
    ... value = python:item['_content_element'].attrib['href']
    ...
    ... [logger]
    ... blueprint = collective.transmogrifier.sections.logger
    ... name = logger
    ... level = INFO
    ... """
    >>> registerConfig(
    ...     u'plone.app.transmogrifier.tests.redirector', redirector)

    >>> transmogrifier(u'plone.app.transmogrifier.tests.redirector')
    >>> print(handler)
    logger INFO
      {'_old_paths': ['corge', 'waldo'], '_redirect_path': 'foo', 'remoteUrl': ''}
    logger INFO
      {'_redirect_path': 'foo', 'remoteUrl': ''}
    logger INFO
        {'_old_paths': ['corge/item-00', 'waldo/item-00'],
       '_redirect_path': 'foo/item-00',
       'remoteUrl': ''}
    logger INFO
        {'_content_element': 'foo/item-00',
       '_old_paths': ['corge/grault', 'waldo/fred'],
       '_redirect_path': 'foo/bar',
       'remoteUrl': 'foo/item-00'}
    logger INFO
        {'_content_element': '/foo/item-00#fragment',
       '_old_paths': ['corge/grault/item-01', 'waldo/fred/item-01'],
       '_redirect_path': 'http://nohost/foo/bar/item-01',
       'remoteUrl': '/foo/item-00#fragment'}
    logger INFO
      {'_redirect_path': '/foo/bar/qux', 'remoteUrl': ''}
    logger INFO
        {'_content_element': 'http://nohost/foo/bar/item-01',
       '_redirect_path': '/foo/bar/qux/item-02',
       'remoteUrl': 'http://nohost/foo/bar/item-01'}

    >>> import pprint
    >>> from zope.component import getUtility
    >>> from plone.app.redirector.interfaces import IRedirectionStorage
    >>> storage = getUtility(IRedirectionStorage)
    >>> pprint.pprint(dict((path, storage.get(path)) for path in storage))
    {'/plone/corge': '/plone/foo',
     '/plone/corge/grault': '/plone/foo/bar',
     '/plone/corge/grault/item-01': 'http://nohost/foo/bar/item-01',
     '/plone/corge/item-00': '/plone/foo/item-00',
     '/plone/waldo': '/plone/foo',
     '/plone/waldo/fred': '/plone/foo/bar',
     '/plone/waldo/fred/item-01': 'http://nohost/foo/bar/item-01',
     '/plone/waldo/item-00': '/plone/foo/item-00'}


Indexing section
----------------

A ReindexObject section allows you to reindex an existing object in the
portal_catalog. ReindexObject sections operate on objects already present in the
ZODB, be they created by a constructor or pre-existing objects.

The ReindexObject blueprint name is ``plone.app.transmogrifier.reindexobject``.

To determine the path, the ReindexObject 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
or do not inherit from CMFCatalogAware will be skipped as well.

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

* ``_plone.app.transmogrifier.reindexobject_[sectionname]_path``
* ``_plone.app.transmogrifier.reindexobject_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.

::

    >>> import pprint
    >>> reindexobject_1 = """
    ... [transmogrifier]
    ... pipeline =
    ...     reindexobjectsource
    ...     reindexobject
    ...     printer
    ...
    ... [reindexobjectsource]
    ... blueprint = plone.app.transmogrifier.tests.reindexobjectsource
    ...
    ... [reindexobject]
    ... blueprint = plone.app.transmogrifier.reindexobject
    ...
    ... [printer]
    ... blueprint = collective.transmogrifier.sections.logger
    ... name = logger
    ... """
    >>> registerConfig(u'plone.app.transmogrifier.tests.reindexobject_1', reindexobject_1)

    >>> transmogrifier(u'plone.app.transmogrifier.tests.reindexobject_1')
    >>> print(handler)
    logger INFO
      {'_path': '/spam/eggs/foo'}
    logger INFO
      {'_path': '/spam/eggs/bar'}
    logger INFO
      {'_path': '/spam/eggs/baz'}
    logger INFO
        {'_path': 'not/a/catalog/aware/content',
       'title': 'Should not be reindexed, not a CMFCatalogAware content'}
    logger INFO
        {'_path': 'not/existing/bar',
       'title': 'Should not be reindexed, not an existing path'}

    >>> pprint.pprint(plone.reindexed)
    [('spam/eggs/foo', 'reindexed', 'indexes: all'),
     ('spam/eggs/bar', 'reindexed', 'indexes: all'),
     ('spam/eggs/baz', 'reindexed', 'indexes: all')]

    Reset:
    >>> plone.reindexed = []



Index only the ``foo`` index::

    >>> import pprint
    >>> reindexobject_2 = """
    ... [transmogrifier]
    ... pipeline =
    ...     reindexobjectsource
    ...     reindexobject
    ...     printer
    ...
    ... [reindexobjectsource]
    ... blueprint = plone.app.transmogrifier.tests.reindexobjectsource
    ...
    ... [reindexobject]
    ... blueprint = plone.app.transmogrifier.reindexobject
    ... indexes = foo
    ...
    ... [printer]
    ... blueprint = collective.transmogrifier.sections.logger
    ... name = logger
    ... """
    >>> registerConfig(u'plone.app.transmogrifier.tests.reindexobject_2', reindexobject_2)

    >>> transmogrifier(u'plone.app.transmogrifier.tests.reindexobject_2')

    >>> pprint.pprint(plone.reindexed)
    [('spam/eggs/foo', 'reindexed', 'indexes: foo'),
     ('spam/eggs/bar', 'reindexed', 'indexes: foo'),
     ('spam/eggs/baz', 'reindexed', 'indexes: foo')]

    Reset:
    >>> plone.reindexed = []


Index only the ``foo``, ``bar`` and ``baz`` indexes::

    >>> import pprint
    >>> reindexobject_3 = """
    ... [transmogrifier]
    ... pipeline =
    ...     reindexobjectsource
    ...     reindexobject
    ...     printer
    ...
    ... [reindexobjectsource]
    ... blueprint = plone.app.transmogrifier.tests.reindexobjectsource
    ...
    ... [reindexobject]
    ... blueprint = plone.app.transmogrifier.reindexobject
    ... indexes =
    ...     foo
    ...     bar
    ...     baz
    ...
    ... [printer]
    ... blueprint = collective.transmogrifier.sections.logger
    ... name = logger
    ... """
    >>> registerConfig(u'plone.app.transmogrifier.tests.reindexobject_3', reindexobject_3)

    >>> transmogrifier(u'plone.app.transmogrifier.tests.reindexobject_3')

    >>> pprint.pprint(plone.reindexed)
    [('spam/eggs/foo', 'reindexed', 'indexes: foo, bar, baz'),
     ('spam/eggs/bar', 'reindexed', 'indexes: foo, bar, baz'),
     ('spam/eggs/baz', 'reindexed', 'indexes: foo, bar, baz')]

    Reset:
    >>> plone.reindexed = []


UID updater section
-------------------

If an 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
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
    >>> schema = """
    ... [transmogrifier]
    ... pipeline =
    ...     schemasource
    ...     schemaupdater
    ...     printer
    ...
    ... [schemasource]
    ... blueprint = plone.app.transmogrifier.tests.uidsource
    ...
    ... [schemaupdater]
    ... blueprint = plone.app.transmogrifier.uidupdater
    ...
    ... [printer]
    ... blueprint = collective.transmogrifier.sections.logger
    ... name = logger
    ... """
    >>> registerConfig('plone.app.transmogrifier.tests.uid', schema)
    >>> transmogrifier('plone.app.transmogrifier.tests.uid')
    >>> print(handler)
    logger INFO
      {'_path': '/spam/eggs/foo', '_uid': 'abc'}
    logger INFO
      {'_path': '/spam/eggs/bar', '_uid': 'xyz'}
    logger INFO
      {'_path': 'not/existing/bar', '_uid': 'def'}
    logger INFO
      {'_uid': 'geh'}
    logger INFO
      {'_path': '/spam/eggs/baz'}
    logger INFO
      {'_path': '/spam/notatcontent', '_uid': 'ijk'}

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


URL Normalizer section
----------------------

A URLNormalizer section allows you to parse any piece of text into a url-safe
string which is then assigned to a specified key. It uses plone.i18n.normalizer
to perform the normalization. The url normalizer section blueprint name is
``plone.app.transmogrifier.urlnormalizer``.

The URL normalizer accepts the following optional keys -
``source-key``: The name of the object key that you wish to normalize,
``destination-key``: Where you want the normalized string to be stored,
``locale``: if you want the normalizer to be aware of locale, use this.

::

    >>> import pprint
    >>> urlnormalizer = """
    ... [transmogrifier]
    ... pipeline =
    ...     urlnormalizersource
    ...     urlnormalizer
    ...     printer
    ...
    ... [urlnormalizersource]
    ... blueprint = plone.app.transmogrifier.tests.urlnormalizersource
    ...
    ... [urlnormalizer]
    ... blueprint = plone.app.transmogrifier.urlnormalizer
    ... source-key = title
    ... destination-key = string:id
    ... locale = string:en
    ...
    ... [printer]
    ... blueprint = collective.transmogrifier.sections.logger
    ... name = logger
    ... """
    >>> registerConfig(u'plone.app.transmogrifier.tests.urlnormalizer',
    ...                urlnormalizer)
    >>> transmogrifier(u'plone.app.transmogrifier.tests.urlnormalizer')
    >>> print(handler)
    logger INFO
      {'id': 'mytitle', 'title': 'mytitle'}
    logger INFO
      {'id': 'is-this-a-title-of-any-sort', 'title': 'Is this a title of any sort?'}
    logger INFO
        {'id': 'put-some-br-1lly-v4lues-here-there',
       'title': 'Put some <br /> $1llY V4LUES -- here&there'}
    logger INFO
        {'id': 'what-about-line-breaks-system',
       'title': 'What about \r\n line breaks (system)'}
    logger INFO
      {'id': 'try-one-of-these-oh', 'title': 'Try one of these --------- oh'}
    logger INFO
      {'language': 'My language is de'}
    logger INFO
      {'language': 'my language is en'}

As you can see, only items containing the specified source-key have been
processed, the others have been ignored and yielded without change.

Destination-key and locale accept TALES expressions, so for example you could
set your destination-key based on your locale element, which is in turn derived
from your source-key:

::

    >>> import pprint
    >>> urlnormalizer = """
    ... [transmogrifier]
    ... pipeline =
    ...     urlnormalizersource
    ...     urlnormalizer
    ...     printer
    ...
    ... [urlnormalizersource]
    ... blueprint = plone.app.transmogrifier.tests.urlnormalizersource
    ...
    ... [urlnormalizer]
    ... blueprint = plone.app.transmogrifier.urlnormalizer
    ... source-key = language
    ... locale = python:str(item.get('${urlnormalizer:source-key}', 'na')[-2:])
    ... destination-key = ${urlnormalizer:locale}
    ...
    ... [printer]
    ... blueprint = collective.transmogrifier.sections.logger
    ... name = logger
    ... """
    >>> registerConfig(u'plone.app.transmogrifier.tests.urlnormalizer2',
    ...                urlnormalizer)

    >>> handler.clear()
    >>> transmogrifier(u'plone.app.transmogrifier.tests.urlnormalizer2')
    >>> print(handler)
    logger INFO
      {'title': 'mytitle'}
    logger INFO
      {'title': 'Is this a title of any sort?'}
    logger INFO
      {'title': 'Put some <br /> $1llY V4LUES -- here&there'}
    logger INFO
      {'title': 'What about \r\n line breaks (system)'}
    logger INFO
      {'title': 'Try one of these --------- oh'}
    logger INFO
      {'de': 'my-language-is-de', 'language': 'My language is de'}
    logger INFO
      {'en': 'my-language-is-en', 'language': 'my language is en'}

In this case only items containing the 'language' key have been processed, and
the destination-key has been set to the same value as the locale was. This is
more to illuminate the fact that the locale was set, rather than providing a
sensible use-case for destination-key.

If ZERO options are specified, the normalizer falls back to a set of default
values as follows:
``source-key``: title,
``locale``: en,
``destination-key``: _id

::

    >>> import pprint
    >>> urlnormalizer = """
    ... [transmogrifier]
    ... pipeline =
    ...     urlnormalizersource
    ...     urlnormalizer
    ...     printer
    ...
    ... [urlnormalizersource]
    ... blueprint = plone.app.transmogrifier.tests.urlnormalizersource
    ...
    ... [urlnormalizer]
    ... blueprint = plone.app.transmogrifier.urlnormalizer
    ...
    ... [printer]
    ... blueprint = collective.transmogrifier.sections.logger
    ... name = logger
    ... """
    >>> registerConfig(u'plone.app.transmogrifier.tests.urlnormalizer3',
    ...                urlnormalizer)

    >>> handler.clear()
    >>> transmogrifier(u'plone.app.transmogrifier.tests.urlnormalizer3')
    >>> print(handler)
    logger INFO
      {'_id': 'mytitle', 'title': 'mytitle'}
    logger INFO
      {'_id': 'is-this-a-title-of-any-sort', 'title': 'Is this a title of any sort?'}
    logger INFO
        {'_id': 'put-some-br-1lly-v4lues-here-there',
       'title': 'Put some <br /> $1llY V4LUES -- here&there'}
    logger INFO
        {'_id': 'what-about-line-breaks-system',
       'title': 'What about \r\n line breaks (system)'}
    logger INFO
      {'_id': 'try-one-of-these-oh', 'title': 'Try one of these --------- oh'}
    logger INFO
      {'language': 'My language is de'}
    logger INFO
      {'language': 'my language is en'}

In this case, the destination-key is set to a controller variable, like _path,
as it is expected that the newly formed Id will in most cases be used further
down the pipeline in constructing the full, final path to the new Plone object.

It should be noted that this section can effectively transform *any* section of
text and turn it into a normalized, web safe string (max 255 chars) This string
does not necessarily need to be used for a URL.


Disable / enable versioning sections
------------------------------------

It can be helpful to disable versioning during content construction to avoid
storing incomplete versions in the content item's revision history.

For example::

    [transmogrifier]
    pipeline =
        schemasource
        disable_versioning
        constructor
        enable_versioning
        schemaupdater

    [disable_versioning]
    blueprint = plone.app.transmogrifier.versioning.disable

    [constructor]
    blueprint = collective.transmogrifier.sections.constructor

    [enable_versioning]
    blueprint = plone.app.transmogrifier.versioning.enable



Workflow updater section
------------------------

A workflow updater pipeline section is another important transmogrifier content
import pipeline element. It executes workflow transitions on Plone content
based on the items it processes. The workflow updater section blueprint name is
``plone.app.transmogrifier.workflowupdater``. Workflow updater sections operate
on objects already present in the ZODB, be they created by a constructor or
pre-existing objects.

Workflow updating needs 2 pieces of information: the path to the object, and
what transitions to execute. To determine these, the workflow updater section
inspects each item and looks for two keys, as described below. Any item missing
any of these two pieces will be skipped. Similarly, items with a path that
doesn't exist will be skipped as well.

For the object path, it'll look (in order) for
``_plone.app.transmogrifier.atschemaupdater_[sectionname]_path``,
``_plone.app.transmogrifier.atschemaupdater_path``, ``_[sectionname]_path`` and
``_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).

For the transitions, use the ``transitions-key`` option (same interpretation
as ``path-key``), defaulting to
``_plone.app.transmogrifier.atschemaupdater_[sectionname]_transitions``,
``_plone.app.transmogrifier.atschemaupdater_transitions``,
``_[sectionname]_transitions`` and ``_transitions``.

Unicode paths are encoded to ASCII. Paths to objects are always interpreted as
relative to the context object. Transitions are specified as a sequence of
transition names, or as a string specifying one transition, or a list of
dictionaries containing 'action' as transition id, 'review_state' as state id
and 'time' as a DateTime representing the transition time (if so, the worflow
history will be updated with the provided date). Transitions are executed in
order, failing transitions are silently ignored.

::

    >>> import pprint
    >>> workflow = """
    ... [transmogrifier]
    ... pipeline =
    ...     workflowsource
    ...     workflowupdater
    ...     printer
    ...
    ... [workflowsource]
    ... blueprint = plone.app.transmogrifier.tests.workflowsource
    ...
    ... [workflowupdater]
    ... blueprint = plone.app.transmogrifier.workflowupdater
    ...
    ... [printer]
    ... blueprint = collective.transmogrifier.sections.logger
    ... name = logger
    ... """
    >>> registerConfig(u'plone.app.transmogrifier.tests.workflow',
    ...                workflow)
    >>> transmogrifier(u'plone.app.transmogrifier.tests.workflow')
    >>> print(handler)
    logger INFO
      {'_path': '/spam/eggs/foo', '_transitions': 'spam'}
    logger INFO
      {'_path': '/spam/eggs/baz', '_transitions': ('spam', 'eggs')}
    logger INFO
        {'_path': 'not/existing/bar',
       '_transitions': ('spam', 'eggs'),
       'title': 'Should not be updated, not an existing path'}
    logger INFO
        {'_path': 'spam/eggs/incomplete',
       'title': 'Should not be updated, no transitions'}
    logger INFO
        {'_path': '/spam/eggs/nosuchtransition',
       '_transitions': ('nonsuch',),
       'title': 'Should not be updated, no such transition'}
    logger INFO
        {'_path': '/spam/eggs/bla',
       '_transitions': ({'action': 'spam',
                         'review_state': 'spammed',
                         'time': DateTime('2014/06/20 00:00:00 GMT+0')},)}

    >>> pprint.pprint(plone.updated)
    [('spam/eggs/foo', 'spam'),
     ('spam/eggs/baz', 'spam'),
     ('spam/eggs/baz', 'eggs'),
     ('spam/eggs/bla', 'spam')]


Changelog
=========

3.0.1 (2023-06-02)
------------------

- Remove Python 2.4 compatibility code.
  [wesleybl]

- Add Python 3.10 and 3.11 support.
  [wesleybl]

- Remove ``z3c.autoinclude`` of entry_points.
  [wesleybl]

- Remove dependency on ``zest.releaser`` in extra test.
  [wesleybl]

- Fix ``ModuleNotFoundError: No module named 'Products.CMFDynamicViewFTI.interface'`` in Plone 6.
  [wesleybl]


3.0.0 (2022-06-29)
------------------

- Implement plone/code-analysis-action
  [ericof]

- Drop support to Plone versions 4.3, 5.0 and 5.1
  [ericof]

- Drop support to Python 2.7, Python 3.6 and Products.Archetypes
  [ericof]


2.0.0 (2021-09-17)
------------------

- Raise exception in pathfixer if path is not ascii.
  [wesleybl]

- Add support for Python 3.6, 3.7 and 3.8.
  [wesleybl]

- Add support for Plone 5.0, 5.1 and 5.2.
  [wesleybl]

- Remove supports to Plone 4.0, 4.1 and 4.2.
  [wesleybl]

- Remove Python 2.6 support.
  [wesleybl]


1.4.2 (2019-09-24)
------------------

- ``plone.app.transmogrifier.atschemaupdater`` updates fields in fixed order
  (field names) for bette debuggability.
  [gotcha]

- ``plone.app.transmogrifier.pathfixer`` now also converts a path into ``str`` and removes any invalid characters from it;
  this avoids ``UnicodeEncodeError`` in many blueprint sections.
  [hvelarde]


1.4.1 (2018-02-27)
------------------

- Avoid failures on redirector section when there is no object in referenced path.
  [hvelarde]

- Fix ``plone.app.transmogrifier.browserdefault`` blueprint section:
  ``default_page`` and ``layout`` properties should be string, not unicode.
  [sunew]


1.4 (2015-10-23)
----------------

- Support updating effective and expiration dates on ``plone.app.transmogrifier.datesupdater`` blueprint.
  Fix field discovering logic to avoid skipping the ones set as ``None``.
  Fix documentation.
  [hvelarde]

- Support indexing of individual indexes for the
  ``plone.app.transmogrifier.reindexobject`` blueprint.
  [thet]


1.3 (2015-01-22)
----------------

- Ignore if workflow_history is not available on objects when running the
  workflowupdater blueprint.
  [thet]

- Add datesupdater section to set creation_date and modification_date on
  objects.
  [thet]

- Add pathfixer section to remove/prepend parts of the path.
  [thet]

- PEP 8.
  [thet]

- Fix uidsection for dexterity.
  [shylux]

- Allow to import transition date in the worflow history
  [ebrehault]

- Fix field accessor and mutator for updating schemaextended field values
  with schemaupdater.
  In some cases when using fields extended by schemaextender it defines
  an accessor attribute which is not accessable. To cover all fields, its
  better to access and mutate over the getAccessor and getMutator methods on
  archetype fields.
  [elioschmutz]

- Add a section to manage `plone.app.redirector` and to use it to
  update paths.
  [rpatterson]

- Support field accessor and mutator for updating field values with
  schemaupdater.
  [phgross]


1.2 (2011-05-23)
----------------

- Sections to disable and enable versioning within the pipeline.
  [elro]

- Convert paths to strings.
  [elro]

- Add a 'verbose' option to reindexobject blueprint
  that logs the object currently reindexed and number of objects reindexed.
  [thomasdesvenain]

- Check for CatalogAware base class when reindexing an object instead of
  CMFCatalogAware because in Plone 4 folders do not inherit from
  CMFCatalogAware.
  [buchi]


1.1 (2010-03-30)
----------------

- Added Indexing section. See reindexobject.rst.
  [sylvainb]

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

- Fixed tests for Plone 4, in the same way that they were fixed in
  collective.transmogrifier.
  [optilude]


1.0 (2009-08-09)
----------------

- Initial package.
  [mj]


            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/collective/plone.app.transmogrifier",
    "name": "plone.app.transmogrifier",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "",
    "keywords": "content import filtering plone",
    "author": "Jarn",
    "author_email": "info@jarn.com",
    "download_url": "https://files.pythonhosted.org/packages/64/9e/2d062e3c4b14853259d2c9e3da80a6da87dc1b3e8a59caaf013b8ca6e5cd/plone.app.transmogrifier-3.0.1.tar.gz",
    "platform": null,
    "description": "===============================\nPlone transmogrifier blueprints\n===============================\n\n.. contents::\n\nThis package contains several blueprints for `collective.transmogrifier`_\npipelines, commonly used to import content into a Plone site.\n\nInstallation\n============\n\nSee docs/INSTALL.rst for installation instructions.\n\nCredits\n=======\n\nDevelopment sponsored by\n    Elkj\u00f8p Nordic AS\n\nDesign and development\n    `Martijn Pieters`_ at Jarn_\n    `Florian Schulze`_ at Jarn_\n\n.. _Martijn Pieters: mailto:mj@jarn.com\n.. _Florian Schulze: mailto:fschulze@jarn.com\n.. _Jarn: http://www.jarn.com/\n.. _collective.transmogrifier: https://https://github.com/collective/collective.transmogrifier\n\nDetailed Documentation\n======================\n\nBrowser default section\n-----------------------\n\nA browser default pipeline section sets the default-page on a folder, and the\nlayout template on content objects. They are the Transmogrifier equivalent of\nthe ``display`` menu in Plone. The browser default section blueprint name is\n``plone.app.transmogrifier.browserdefault``. Browser default sections operate\non objects already present in the ZODB, be they created by a constructor or\npre-existing objects.\n\nSetting the browser default needs at least 1 piece of information: the path to\nthe object to modify. To determine the path, the browser default section\ninspects each item and looks for one key, as described below. Any item missing\nthis piece of information will be skipped. Similarly, items with a path that\ndoesn't exist or do not support the Plone ISelectableBrowserDefault interface\nwill be skipped as well.\n\nFor the object path, it'll look (in order) for\n``_plone.app.transmogrifier.browserdefault_[sectionname]_path``,\n``_plone.app.transmogrifier.browserdefault_path``, ``_[sectionname]_path``\nand ``_path``, where ``[sectionname]`` is replaced with the name given to the\ncurrent section. This allows you to target the right section precisely if\nneeded. Alternatively, you can specify what key to use for the path by\nspecifying the ``path-key`` option, which should be a list of keys to try (one\nkey per line, use a ``re:`` or ``regexp:`` prefix to specify regular\nexpressions).\n\nOnce an object has been located, the section will looks for defaultpage\nand layout keys. Like the path key, these can be specified in the source\nconfiguration, named by the ``default-page-key`` and ``layout-key`` options,\nrespectively, and like the path key, the default keys the section looks for\nare the usual list of specific-to-generic keys based on blueprint and section\nnames, from\n``_plone.app.transmogrifier.browserdefault_[sectionname]_defaultpage`` and\n``_plone.app.transmogrifier.browserdefault_[sectionname]_layout`` down to\n``_defaultpage`` and ``_layout``.\n\nThe defaultpage key will set the id of the default page that should be\npresented when the content object is loaded, and the layout key will set the\nid of the layout to use for the content item.\n\n::\n\n    >>> import pprint\n    >>> browserdefault = \"\"\"\n    ... [transmogrifier]\n    ... pipeline =\n    ...     browserdefaultsource\n    ...     browserdefault\n    ...     printer\n    ...\n    ... [browserdefaultsource]\n    ... blueprint = plone.app.transmogrifier.tests.browserdefaultsource\n    ...\n    ... [browserdefault]\n    ... blueprint = plone.app.transmogrifier.browserdefault\n    ...\n    ... [printer]\n    ... blueprint = collective.transmogrifier.sections.logger\n    ... name = logger\n    ... \"\"\"\n    >>> registerConfig(u'plone.app.transmogrifier.tests.browserdefault',\n    ...                browserdefault)\n    >>> transmogrifier(u'plone.app.transmogrifier.tests.browserdefault')\n    >>> print(handler)\n    logger INFO\n      {'_layout': 'spam', '_path': '/spam/eggs/foo'}\n    logger INFO\n      {'_defaultpage': 'eggs', '_path': '/spam/eggs/bar'}\n    logger INFO\n      {'_defaultpage': 'eggs', '_layout': 'spam', '_path': '/spam/eggs/baz'}\n    logger INFO\n        {'_layout': 'spam',\n       '_path': 'not/existing/bar',\n       'title': 'Should not be updated, not an existing path'}\n    logger INFO\n        {'_path': 'spam/eggs/incomplete',\n       'title': 'Should not be updated, no layout or defaultpage'}\n    logger INFO\n        {'_layout': '',\n       '_path': 'spam/eggs/emptylayout',\n       'title': 'Should not be updated, no layout or defaultpage'}\n    logger INFO\n        {'_defaultpage': '',\n       '_path': 'spam/eggs/emptydefaultpage',\n       'title': 'Should not be updated, no layout or defaultpage'}\n    >>> pprint.pprint(plone.updated)\n    [('spam/eggs/foo', 'layout', 'spam'),\n     ('spam/eggs/bar', 'defaultpage', 'eggs'),\n     ('spam/eggs/baz', 'layout', 'spam'),\n     ('spam/eggs/baz', 'defaultpage', 'eggs')]\n\n\nDatesUpdater section\n--------------------\n\nThis blueprint sets creation, modification and effective dates on objects.\n\nBlueprint name: ``plone.app.transmogrifier.datesupdater``\n\nOption path-key: The key for the path to the object.\n\nOption creation-key: The key for the creation date.\n\nOption modification-key: The key for the modification date.\n\nOption effective-key: The key for the effective date.\n\nOption expiration-key: The key for the expiration date.\n\n::\n\n    >>> import pprint\n    >>> pipeline = \"\"\"\n    ... [transmogrifier]\n    ... pipeline =\n    ...     schemasource\n    ...     datesupdater\n    ...     logger\n    ...\n    ... [schemasource]\n    ... blueprint = plone.app.transmogrifier.tests.schemasource\n    ...\n    ... [datesupdater]\n    ... blueprint = plone.app.transmogrifier.datesupdater\n    ... path-key = _path\n    ... creation-key = creation_date\n    ... modification-key = modification_date\n    ... effective-key = effective_date\n    ... expiration-key = expiration_date\n    ...\n    ... [logger]\n    ... blueprint = collective.transmogrifier.sections.logger\n    ... name = logger\n    ... \"\"\"\n    >>> registerConfig(u'plone.app.transmogrifier.tests.datesupdater', pipeline)\n\n    >>> transmogrifier(u'plone.app.transmogrifier.tests.datesupdater')\n\n\nPrint out the source structure::\n\n    >>> print(handler)\n    logger INFO\n        {'_path': '/spam/eggs/foo',\n       'creation_date': DateTime('2010/10/10 00:00:00 UTC'),\n       'effective_date': DateTime('2010/10/10 00:00:00 UTC'),\n       'expiration_date': DateTime('2012/12/12 00:00:00 UTC'),\n       'modification_date': DateTime('2011/11/11 00:00:00 UTC')}\n    logger INFO\n        {'_path': '/spam/eggs/bar',\n       'creation_date': DateTime('2010/10/10 00:00:00 UTC')}\n    logger INFO\n        {'_path': '/spam/eggs/baz',\n       'modification_date': DateTime('2011/11/11 00:00:00 UTC')}\n    logger INFO\n        {'_path': '/spam/eggs/qux',\n       'effective_date': DateTime('2010/10/10 00:00:00 UTC')}\n    logger INFO\n        {'_path': '/spam/eggs/norf',\n       'expiration_date': DateTime('2012/12/12 00:00:00 UTC')}\n    logger INFO\n        {'_path': 'not/existing/bar',\n       'creation_date': DateTime('2010/10/10 00:00:00 UTC'),\n       'effective_date': DateTime('2010/10/10 00:00:00 UTC'),\n       'expiration_date': DateTime('2012/12/12 00:00:00 UTC'),\n       'modification_date': DateTime('2011/11/11 00:00:00 UTC')}\n    logger INFO\n        {'creation_date': DateTime('2010/10/10 00:00:00 UTC'),\n       'effective_date': DateTime('2010/10/10 00:00:00 UTC'),\n       'expiration_date': DateTime('2012/12/12 00:00:00 UTC'),\n       'modification_date': DateTime('2011/11/11 00:00:00 UTC')}\n\n\nThat was changed on the object::\n\n    >>> pprint.pprint(plone.updated)\n    [('spam/eggs/foo', 'creation_date', DateTime('2010/10/10 00:00:00 UTC')),\n     ('spam/eggs/foo', 'modification_date', DateTime('2011/11/11 00:00:00 UTC')),\n     ('spam/eggs/foo', 'effective_date', DateTime('2010/10/10 00:00:00 UTC')),\n     ('spam/eggs/foo', 'expiration_date', DateTime('2012/12/12 00:00:00 UTC')),\n     ('spam/eggs/bar', 'creation_date', DateTime('2010/10/10 00:00:00 UTC')),\n     ('spam/eggs/baz', 'modification_date', DateTime('2011/11/11 00:00:00 UTC')),\n     ('spam/eggs/qux', 'effective_date', DateTime('2010/10/10 00:00:00 UTC')),\n     ('spam/eggs/norf', 'expiration_date', DateTime('2012/12/12 00:00:00 UTC'))]\n\n\nMime encapsulator section\n-------------------------\n\nA mime encapsulator section wraps arbitrary data in ``OFS.Image.File``\nobjects, together with a MIME type. This wrapping is a pre-requisite for\nimage, file or text fields, which can only take such File objects.\nThe mime encapsulator blueprint name is\n``plone.app.transmogrifier.mimeencapsulator``.\n\nAn encapsulator section needs 3 pieces of information: the key at which to\nfind the data to encapsulate, the MIME type of this data, and the name of the\nfield where the encapsulated data will be stored. The idea is that the data\nis copied from a \"data key\" (defaulting to ``_data`` and settable with the\n``data-key`` option), wrapped into a ``File`` object with a MIME type (read\nfrom the ``mimetype`` option, which contains a TALES expression), and then\nsaved into the pipeline item dictionary under a new key, most likely\ncorresponding to a field name (read from the ``field`` option,\nwhich is also a TALES expression).\n\nThe data key defaults to the series ``_[blueprintname]_[sectionname]_data``,\n``_[blueprintname]_data``, ``_[sectionname]_data`` and ``_data``, where\n``[blueprintname]`` is ``plone.app.transmogrifier.mimeencapsulator`` and\n``[sectionname]`` is replaced with the name of the current section. You can\noverride this by specifying the ``data-key`` option.\n\nYou specify the mimetype with the ``mimetype`` option, which takes a TALES\nexpression.\n\nThe ``field`` option, also a TALES expression, sets the output field name.\n\nOptionally, you can specify a ``condition`` option, again a TALES expression,\nthat when evaluating to ``False``, causes the section to skip encapsulation\nfor  that item.\n\n::\n\n    >>> encapsulator = \"\"\"\n    ... [transmogrifier]\n    ... pipeline =\n    ...     source\n    ...     encapsulator\n    ...     conditionalencapsulator\n    ...     printer\n    ...\n    ... [source]\n    ... blueprint = plone.app.transmogrifier.tests.encapsulatorsource\n    ...\n    ... [encapsulator]\n    ... blueprint = plone.app.transmogrifier.mimeencapsulator\n    ... # Read the mimetype from the item\n    ... mimetype = item/_mimetype\n    ... field = string:datafield\n    ...\n    ... [conditionalencapsulator]\n    ... blueprint = plone.app.transmogrifier.mimeencapsulator\n    ... data-key = portrait\n    ... mimetype = python:item.get('_%s_mimetype' % key)\n    ... # replace the data in-place\n    ... field = key\n    ... condition = mimetype\n    ...\n    ... [printer]\n    ... blueprint = plone.app.transmogrifier.tests.ofsfileprinter\n    ... \"\"\"\n    >>> registerConfig(u'plone.app.transmogrifier.tests.encapsulator',\n    ...                encapsulator)\n    >>> transmogrifier(u'plone.app.transmogrifier.tests.encapsulator')\n    datafield: (application/x-test-data) foobarbaz\n    portrait: (image/jpeg) someportraitdata\n\n\nThe ``field`` expression has access to the following:\n\n``item``\n    The current pipeline item\n\n``key``\n    The name of the matched data key\n\n``match``\n    If the key was matched by a regular expression, the match object, otherwise boolean True\n\n``transmogrifier``\n    The transmogrifier\n\n``name``\n    The name of the splitter section\n\n``options``\n    The splitter options\n\n``modules``\n    ``sys.modules``\n\n\nThe ``mimetype`` expression has access to the same information as the ``field``\nexpression, plus:\n\n``field``\n    The name of the field in which the encapsulated data will be stored.\n\nThe ``condition`` expression has access to the same information as the\n``mimetype`` expression, plus:\n\n``mimetype``\n    The mimetype used to encapsulate the data.\n\n\nPathFixer section\n-----------------\n\nWhen importing contents from a old site into a new, the path to the Plone site\nroot may have changed. This blueprint updates the old paths to match the new\nstructrue by removing or appending strings from the right side of the path\nvalue.\n\nIt also converts the path to ``str``. Also raise exception if path have any invalid characters from it.\n\nBlueprint name: ``plone.app.transmogrifier.pathfixer``\n\nOption path-key: The key of the item under which the path to be manipulated can\n                 be found. E.g. ``_path``.\n\nOption stripstring: A string to strip from the path value.\n\nOption prependstring: A string to append to the path value.\n\n\nLook, here. Original path structure from\nplone.app.transmogrifier.tests.schemasource is::\n\n    /spam/eggs/foo\n    relative/path\n    /spam/eggs/another\n\n\nNow lets manipulate it::\n\n    >>> import pprint\n    >>> pipeline = \"\"\"\n    ... [transmogrifier]\n    ... pipeline =\n    ...     schemasource\n    ...     pathfixer\n    ...     logger\n    ...\n    ... [schemasource]\n    ... blueprint = plone.app.transmogrifier.tests.schemasource\n    ...\n    ... [pathfixer]\n    ... blueprint = plone.app.transmogrifier.pathfixer\n    ... path-key = _path\n    ... stripstring = /spam/eggs/\n    ... prependstring = subfolder/\n    ...\n    ... [logger]\n    ... blueprint = collective.transmogrifier.sections.logger\n    ... name = logger\n    ... key = _path\n    ... \"\"\"\n    >>> registerConfig(u'plone.app.transmogrifier.tests.pathfixer', pipeline)\n\n    >>> transmogrifier(u'plone.app.transmogrifier.tests.pathfixer')\n    >>> print(handler)\n    logger INFO\n      subfolder/foo\n    logger INFO\n      subfolder/relative/path\n    logger INFO\n      subfolder/another\n\n\n\nPortal Transforms section\n-------------------------\n\nA portal transforms pipeline section lets you use Portal Transforms to\ntransform item values. The portal transforms section blueprint name is\n``plone.app.transmogrifier.portaltransforms``.\n\nWhat values to transform is determined by the ``keys`` option, which takes a\nset of newline-separated key names. If a key name starts with ``re:`` or\n``regexp:`` it is treated as a regular expression instead.\n\nYou can specify what transformation to apply in two ways. Firstly, you can\ndirectly specify a transformation by naming it with the ``transform`` option;\nthe named transformation is run directly. Alternatively you can let the portal\ntransforms tool figure out what transform to use by specifying ``target`` and\nan optional ``from`` mimetype. The portal transforms tool will select one or\nmore transforms based on these mimetypes, and if no ``from`` option is given\nthe original item value is used to determine one.\n\nAlso optional is the ``condition`` option, which lets you specify a TALES\nexpression that when evaluating to False will prevent any transformations from\nhappening. The condition is evaluated for every matched key.\n\n::\n\n    >>> ptransforms = \"\"\"\n    ... [transmogrifier]\n    ... pipeline =\n    ...     source\n    ...     transform-id\n    ...     transform-title\n    ...     transform-status\n    ...     printer\n    ...\n    ... [source]\n    ... blueprint = collective.transmogrifier.sections.tests.samplesource\n    ... encoding = utf8\n    ...\n    ... [transform-id]\n    ... blueprint = plone.app.transmogrifier.portaltransforms\n    ... transform = identity\n    ... keys = id\n    ...\n    ... [transform-title]\n    ... blueprint = plone.app.transmogrifier.portaltransforms\n    ... target = text/plain\n    ... keys = title\n    ...\n    ... [transform-status]\n    ... blueprint = plone.app.transmogrifier.portaltransforms\n    ... from = text/plain\n    ... target = text/plain\n    ... keys = status\n    ...\n    ... [printer]\n    ... blueprint = collective.transmogrifier.sections.logger\n    ... name = logger\n    ... \"\"\"\n    >>> registerConfig(u'plone.app.transmogrifier.tests.ptransforms',\n    ...                ptransforms)\n\n    >>> transmogrifier(u'plone.app.transmogrifier.tests.ptransforms')\n    >>> print(handler)\n    logger INFO\n        {'id': \"Transformed 'foo' using the identity transform\",\n       'status': \"Transformed b'\\\\xe2\\\\x84\\\\x97' from text/plain to text/plain\",\n       'title': \"Transformed b'The Foo Fighters \\\\xe2\\\\x84\\\\x97' to text/plain\"}\n    logger INFO\n        {'id': \"Transformed 'bar' using the identity transform\",\n       'status': \"Transformed b'\\\\xe2\\\\x84\\\\xa2' from text/plain to text/plain\",\n       'title': \"Transformed b'Brand Chocolate Bar \\\\xe2\\\\x84\\\\xa2' to text/plain\"}\n    logger INFO\n        {'id': \"Transformed 'monty-python' using the identity transform\",\n       'status': \"Transformed b'\\\\xc2\\\\xa9' from text/plain to text/plain\",\n       'title': 'Transformed b\"Monty Python\\'s Flying Circus \\\\xc2\\\\xa9\" to '\n                'text/plain'}\n\nThe ``condition`` expression has access to the following:\n\n``item``\n    The current pipeline item\n\n``key``\n    The name of the matched key\n\n``match``\n    If the key was matched by a regular expression, the match object, otherwise boolean True\n\n``transmogrifier``\n    The transmogrifier\n\n``name``\n    The name of the splitter section\n\n``options``\n    The splitter options\n\n``modules``\n    ``sys.modules``\n\n\nRedirector section\n------------------\n\nA redirector section uses `plone.app.redirector` to manage redirects and update\npaths in keys.\n\n::\n\n    >>> import pprint\n    >>> redirector = \"\"\"\n    ... [transmogrifier]\n    ... pipeline =\n    ...     source\n    ...     clean-old-paths\n    ...     old-paths\n    ...     content-element\n    ...     redirect\n    ...     href\n    ...     logger\n    ...\n    ... [source]\n    ... blueprint = collective.transmogrifier.sections.csvsource\n    ... filename = plone.app.transmogrifier:redirector.csv\n    ...\n    ... [clean-old-paths]\n    ... blueprint = collective.transmogrifier.sections.manipulator\n    ... condition = not:item/_old_paths|nothing\n    ... delete = _old_paths\n    ...\n    ... [old-paths]\n    ... blueprint = collective.transmogrifier.sections.inserter\n    ... key = string:_old_paths\n    ... condition = exists:item/_old_paths\n    ... value = python:item['_old_paths'].split('|')\n    ...\n    ... [content-element]\n    ... blueprint = collective.transmogrifier.sections.inserter\n    ... key = string:_content_element\n    ... condition = item/remoteUrl\n    ... value = python:modules['xml.etree.ElementTree'].Element(\\\n    ...     'a', dict(href=item['remoteUrl']))\n    ...\n    ... [redirect]\n    ... blueprint = plone.app.transmogrifier.redirector\n    ...\n    ... [href]\n    ... blueprint = collective.transmogrifier.sections.inserter\n    ... key = string:_content_element\n    ... condition = exists:item/_content_element\n    ... value = python:item['_content_element'].attrib['href']\n    ...\n    ... [logger]\n    ... blueprint = collective.transmogrifier.sections.logger\n    ... name = logger\n    ... level = INFO\n    ... \"\"\"\n    >>> registerConfig(\n    ...     u'plone.app.transmogrifier.tests.redirector', redirector)\n\n    >>> transmogrifier(u'plone.app.transmogrifier.tests.redirector')\n    >>> print(handler)\n    logger INFO\n      {'_old_paths': ['corge', 'waldo'], '_redirect_path': 'foo', 'remoteUrl': ''}\n    logger INFO\n      {'_redirect_path': 'foo', 'remoteUrl': ''}\n    logger INFO\n        {'_old_paths': ['corge/item-00', 'waldo/item-00'],\n       '_redirect_path': 'foo/item-00',\n       'remoteUrl': ''}\n    logger INFO\n        {'_content_element': 'foo/item-00',\n       '_old_paths': ['corge/grault', 'waldo/fred'],\n       '_redirect_path': 'foo/bar',\n       'remoteUrl': 'foo/item-00'}\n    logger INFO\n        {'_content_element': '/foo/item-00#fragment',\n       '_old_paths': ['corge/grault/item-01', 'waldo/fred/item-01'],\n       '_redirect_path': 'http://nohost/foo/bar/item-01',\n       'remoteUrl': '/foo/item-00#fragment'}\n    logger INFO\n      {'_redirect_path': '/foo/bar/qux', 'remoteUrl': ''}\n    logger INFO\n        {'_content_element': 'http://nohost/foo/bar/item-01',\n       '_redirect_path': '/foo/bar/qux/item-02',\n       'remoteUrl': 'http://nohost/foo/bar/item-01'}\n\n    >>> import pprint\n    >>> from zope.component import getUtility\n    >>> from plone.app.redirector.interfaces import IRedirectionStorage\n    >>> storage = getUtility(IRedirectionStorage)\n    >>> pprint.pprint(dict((path, storage.get(path)) for path in storage))\n    {'/plone/corge': '/plone/foo',\n     '/plone/corge/grault': '/plone/foo/bar',\n     '/plone/corge/grault/item-01': 'http://nohost/foo/bar/item-01',\n     '/plone/corge/item-00': '/plone/foo/item-00',\n     '/plone/waldo': '/plone/foo',\n     '/plone/waldo/fred': '/plone/foo/bar',\n     '/plone/waldo/fred/item-01': 'http://nohost/foo/bar/item-01',\n     '/plone/waldo/item-00': '/plone/foo/item-00'}\n\n\nIndexing section\n----------------\n\nA ReindexObject section allows you to reindex an existing object in the\nportal_catalog. ReindexObject sections operate on objects already present in the\nZODB, be they created by a constructor or pre-existing objects.\n\nThe ReindexObject blueprint name is ``plone.app.transmogrifier.reindexobject``.\n\nTo determine the path, the ReindexObject section inspects each item and looks\nfor a path key, as described below. Any item missing this key will be skipped.\nSimilarly, items with a path that doesn't exist or are not referenceable\nor do not inherit from CMFCatalogAware will be skipped as well.\n\nThe object path will be found under the first key found among the following:\n\n* ``_plone.app.transmogrifier.reindexobject_[sectionname]_path``\n* ``_plone.app.transmogrifier.reindexobject_path``\n* ``_[sectionname]_path``\n* ``_path``\n\nwhere ``[sectionname]`` is replaced with the name given to the current section.\nThis allows you to target the right section precisely if needed.\n\nAlternatively, you can specify what key to use for the path by specifying the\n``path-key`` option, which should be a list of keys to try (one key per line;\nuse a ``re:`` or ``regexp:`` prefix to specify regular expressions).\n\nPaths to objects are always interpreted as relative to the context.\n\n::\n\n    >>> import pprint\n    >>> reindexobject_1 = \"\"\"\n    ... [transmogrifier]\n    ... pipeline =\n    ...     reindexobjectsource\n    ...     reindexobject\n    ...     printer\n    ...\n    ... [reindexobjectsource]\n    ... blueprint = plone.app.transmogrifier.tests.reindexobjectsource\n    ...\n    ... [reindexobject]\n    ... blueprint = plone.app.transmogrifier.reindexobject\n    ...\n    ... [printer]\n    ... blueprint = collective.transmogrifier.sections.logger\n    ... name = logger\n    ... \"\"\"\n    >>> registerConfig(u'plone.app.transmogrifier.tests.reindexobject_1', reindexobject_1)\n\n    >>> transmogrifier(u'plone.app.transmogrifier.tests.reindexobject_1')\n    >>> print(handler)\n    logger INFO\n      {'_path': '/spam/eggs/foo'}\n    logger INFO\n      {'_path': '/spam/eggs/bar'}\n    logger INFO\n      {'_path': '/spam/eggs/baz'}\n    logger INFO\n        {'_path': 'not/a/catalog/aware/content',\n       'title': 'Should not be reindexed, not a CMFCatalogAware content'}\n    logger INFO\n        {'_path': 'not/existing/bar',\n       'title': 'Should not be reindexed, not an existing path'}\n\n    >>> pprint.pprint(plone.reindexed)\n    [('spam/eggs/foo', 'reindexed', 'indexes: all'),\n     ('spam/eggs/bar', 'reindexed', 'indexes: all'),\n     ('spam/eggs/baz', 'reindexed', 'indexes: all')]\n\n    Reset:\n    >>> plone.reindexed = []\n\n\n\nIndex only the ``foo`` index::\n\n    >>> import pprint\n    >>> reindexobject_2 = \"\"\"\n    ... [transmogrifier]\n    ... pipeline =\n    ...     reindexobjectsource\n    ...     reindexobject\n    ...     printer\n    ...\n    ... [reindexobjectsource]\n    ... blueprint = plone.app.transmogrifier.tests.reindexobjectsource\n    ...\n    ... [reindexobject]\n    ... blueprint = plone.app.transmogrifier.reindexobject\n    ... indexes = foo\n    ...\n    ... [printer]\n    ... blueprint = collective.transmogrifier.sections.logger\n    ... name = logger\n    ... \"\"\"\n    >>> registerConfig(u'plone.app.transmogrifier.tests.reindexobject_2', reindexobject_2)\n\n    >>> transmogrifier(u'plone.app.transmogrifier.tests.reindexobject_2')\n\n    >>> pprint.pprint(plone.reindexed)\n    [('spam/eggs/foo', 'reindexed', 'indexes: foo'),\n     ('spam/eggs/bar', 'reindexed', 'indexes: foo'),\n     ('spam/eggs/baz', 'reindexed', 'indexes: foo')]\n\n    Reset:\n    >>> plone.reindexed = []\n\n\nIndex only the ``foo``, ``bar`` and ``baz`` indexes::\n\n    >>> import pprint\n    >>> reindexobject_3 = \"\"\"\n    ... [transmogrifier]\n    ... pipeline =\n    ...     reindexobjectsource\n    ...     reindexobject\n    ...     printer\n    ...\n    ... [reindexobjectsource]\n    ... blueprint = plone.app.transmogrifier.tests.reindexobjectsource\n    ...\n    ... [reindexobject]\n    ... blueprint = plone.app.transmogrifier.reindexobject\n    ... indexes =\n    ...     foo\n    ...     bar\n    ...     baz\n    ...\n    ... [printer]\n    ... blueprint = collective.transmogrifier.sections.logger\n    ... name = logger\n    ... \"\"\"\n    >>> registerConfig(u'plone.app.transmogrifier.tests.reindexobject_3', reindexobject_3)\n\n    >>> transmogrifier(u'plone.app.transmogrifier.tests.reindexobject_3')\n\n    >>> pprint.pprint(plone.reindexed)\n    [('spam/eggs/foo', 'reindexed', 'indexes: foo, bar, baz'),\n     ('spam/eggs/bar', 'reindexed', 'indexes: foo, bar, baz'),\n     ('spam/eggs/baz', 'reindexed', 'indexes: foo, bar, baz')]\n\n    Reset:\n    >>> plone.reindexed = []\n\n\nUID updater section\n-------------------\n\nIf an content object is created in a pipeline, e.g. by the standard\ncontent constructor section, it will get a new UID. If you are importing\ncontent from another Plone site, and you have references (or links embedded\nin content using Plone's link-by-UID feature) to existing content, you may\nwant to retain UIDs. The UID updater section allows you to set the UID on an\nexisting object for this purpose.\n\nThe UID updater blueprint name is ``plone.app.transmogrifier.uidupdater``.\n\nUID updating requires two pieces of information: the path to the object\nto update, and the new UID to set.\n\nTo determine the path, the UID updater section inspects each item and looks\nfor a path key, as described below. Any item missing this key will be skipped.\nSimilarly, items with a path that doesn't exist or are not referenceable\nobjects will be skipped.\n\nThe object path will be found under the first key found among the following:\n\n* ``_plone.app.transmogrifier.atschemaupdater_[sectionname]_path``\n* ``_plone.app.transmogrifier.atschemaupdater_path``\n* ``_[sectionname]_path``\n* ``_path``\n\nwhere ``[sectionname]`` is replaced with the name given to the current\nsection. This allows you to target the right section precisely if\nneeded.\n\nAlternatively, you can specify what key to use for the path by specifying the\n``path-key`` option, which should be a list of keys to try (one key per line;\nuse a ``re:`` or ``regexp:`` prefix to specify regular expressions).\n\nPaths to objects are always interpreted as relative to the context.\n\nSimilarly, the UID to set must be a string under a given key. You can set the\nkey with the ``uid-key`` option, which behaves much like ``path-key``. The\ndefault is to look under:\n\n* ``_plone.app.transmogrifier.atschemaupdater_[sectionname]_uid``\n* ``_plone.app.transmogrifier.atschemaupdater_uid``\n* ``_[sectionname]_uid``\n* ``_uid``\n\nIf the UID key is missing, the item will be skipped.\n\nBelow is an example of a standard updater. The test uid source produces\nitems with two keys: a path under ``_path`` and a UID string under ``_uid``.\n\n::\n\n    >>> import pprint\n    >>> schema = \"\"\"\n    ... [transmogrifier]\n    ... pipeline =\n    ...     schemasource\n    ...     schemaupdater\n    ...     printer\n    ...\n    ... [schemasource]\n    ... blueprint = plone.app.transmogrifier.tests.uidsource\n    ...\n    ... [schemaupdater]\n    ... blueprint = plone.app.transmogrifier.uidupdater\n    ...\n    ... [printer]\n    ... blueprint = collective.transmogrifier.sections.logger\n    ... name = logger\n    ... \"\"\"\n    >>> registerConfig('plone.app.transmogrifier.tests.uid', schema)\n    >>> transmogrifier('plone.app.transmogrifier.tests.uid')\n    >>> print(handler)\n    logger INFO\n      {'_path': '/spam/eggs/foo', '_uid': 'abc'}\n    logger INFO\n      {'_path': '/spam/eggs/bar', '_uid': 'xyz'}\n    logger INFO\n      {'_path': 'not/existing/bar', '_uid': 'def'}\n    logger INFO\n      {'_uid': 'geh'}\n    logger INFO\n      {'_path': '/spam/eggs/baz'}\n    logger INFO\n      {'_path': '/spam/notatcontent', '_uid': 'ijk'}\n\n    >>> pprint.pprint(plone.uids_set)\n    [('spam/eggs/foo', 'abc')]\n\n\nURL Normalizer section\n----------------------\n\nA URLNormalizer section allows you to parse any piece of text into a url-safe\nstring which is then assigned to a specified key. It uses plone.i18n.normalizer\nto perform the normalization. The url normalizer section blueprint name is\n``plone.app.transmogrifier.urlnormalizer``.\n\nThe URL normalizer accepts the following optional keys -\n``source-key``: The name of the object key that you wish to normalize,\n``destination-key``: Where you want the normalized string to be stored,\n``locale``: if you want the normalizer to be aware of locale, use this.\n\n::\n\n    >>> import pprint\n    >>> urlnormalizer = \"\"\"\n    ... [transmogrifier]\n    ... pipeline =\n    ...     urlnormalizersource\n    ...     urlnormalizer\n    ...     printer\n    ...\n    ... [urlnormalizersource]\n    ... blueprint = plone.app.transmogrifier.tests.urlnormalizersource\n    ...\n    ... [urlnormalizer]\n    ... blueprint = plone.app.transmogrifier.urlnormalizer\n    ... source-key = title\n    ... destination-key = string:id\n    ... locale = string:en\n    ...\n    ... [printer]\n    ... blueprint = collective.transmogrifier.sections.logger\n    ... name = logger\n    ... \"\"\"\n    >>> registerConfig(u'plone.app.transmogrifier.tests.urlnormalizer',\n    ...                urlnormalizer)\n    >>> transmogrifier(u'plone.app.transmogrifier.tests.urlnormalizer')\n    >>> print(handler)\n    logger INFO\n      {'id': 'mytitle', 'title': 'mytitle'}\n    logger INFO\n      {'id': 'is-this-a-title-of-any-sort', 'title': 'Is this a title of any sort?'}\n    logger INFO\n        {'id': 'put-some-br-1lly-v4lues-here-there',\n       'title': 'Put some <br /> $1llY V4LUES -- here&there'}\n    logger INFO\n        {'id': 'what-about-line-breaks-system',\n       'title': 'What about \\r\\n line breaks (system)'}\n    logger INFO\n      {'id': 'try-one-of-these-oh', 'title': 'Try one of these --------- oh'}\n    logger INFO\n      {'language': 'My language is de'}\n    logger INFO\n      {'language': 'my language is en'}\n\nAs you can see, only items containing the specified source-key have been\nprocessed, the others have been ignored and yielded without change.\n\nDestination-key and locale accept TALES expressions, so for example you could\nset your destination-key based on your locale element, which is in turn derived\nfrom your source-key:\n\n::\n\n    >>> import pprint\n    >>> urlnormalizer = \"\"\"\n    ... [transmogrifier]\n    ... pipeline =\n    ...     urlnormalizersource\n    ...     urlnormalizer\n    ...     printer\n    ...\n    ... [urlnormalizersource]\n    ... blueprint = plone.app.transmogrifier.tests.urlnormalizersource\n    ...\n    ... [urlnormalizer]\n    ... blueprint = plone.app.transmogrifier.urlnormalizer\n    ... source-key = language\n    ... locale = python:str(item.get('${urlnormalizer:source-key}', 'na')[-2:])\n    ... destination-key = ${urlnormalizer:locale}\n    ...\n    ... [printer]\n    ... blueprint = collective.transmogrifier.sections.logger\n    ... name = logger\n    ... \"\"\"\n    >>> registerConfig(u'plone.app.transmogrifier.tests.urlnormalizer2',\n    ...                urlnormalizer)\n\n    >>> handler.clear()\n    >>> transmogrifier(u'plone.app.transmogrifier.tests.urlnormalizer2')\n    >>> print(handler)\n    logger INFO\n      {'title': 'mytitle'}\n    logger INFO\n      {'title': 'Is this a title of any sort?'}\n    logger INFO\n      {'title': 'Put some <br /> $1llY V4LUES -- here&there'}\n    logger INFO\n      {'title': 'What about \\r\\n line breaks (system)'}\n    logger INFO\n      {'title': 'Try one of these --------- oh'}\n    logger INFO\n      {'de': 'my-language-is-de', 'language': 'My language is de'}\n    logger INFO\n      {'en': 'my-language-is-en', 'language': 'my language is en'}\n\nIn this case only items containing the 'language' key have been processed, and\nthe destination-key has been set to the same value as the locale was. This is\nmore to illuminate the fact that the locale was set, rather than providing a\nsensible use-case for destination-key.\n\nIf ZERO options are specified, the normalizer falls back to a set of default\nvalues as follows:\n``source-key``: title,\n``locale``: en,\n``destination-key``: _id\n\n::\n\n    >>> import pprint\n    >>> urlnormalizer = \"\"\"\n    ... [transmogrifier]\n    ... pipeline =\n    ...     urlnormalizersource\n    ...     urlnormalizer\n    ...     printer\n    ...\n    ... [urlnormalizersource]\n    ... blueprint = plone.app.transmogrifier.tests.urlnormalizersource\n    ...\n    ... [urlnormalizer]\n    ... blueprint = plone.app.transmogrifier.urlnormalizer\n    ...\n    ... [printer]\n    ... blueprint = collective.transmogrifier.sections.logger\n    ... name = logger\n    ... \"\"\"\n    >>> registerConfig(u'plone.app.transmogrifier.tests.urlnormalizer3',\n    ...                urlnormalizer)\n\n    >>> handler.clear()\n    >>> transmogrifier(u'plone.app.transmogrifier.tests.urlnormalizer3')\n    >>> print(handler)\n    logger INFO\n      {'_id': 'mytitle', 'title': 'mytitle'}\n    logger INFO\n      {'_id': 'is-this-a-title-of-any-sort', 'title': 'Is this a title of any sort?'}\n    logger INFO\n        {'_id': 'put-some-br-1lly-v4lues-here-there',\n       'title': 'Put some <br /> $1llY V4LUES -- here&there'}\n    logger INFO\n        {'_id': 'what-about-line-breaks-system',\n       'title': 'What about \\r\\n line breaks (system)'}\n    logger INFO\n      {'_id': 'try-one-of-these-oh', 'title': 'Try one of these --------- oh'}\n    logger INFO\n      {'language': 'My language is de'}\n    logger INFO\n      {'language': 'my language is en'}\n\nIn this case, the destination-key is set to a controller variable, like _path,\nas it is expected that the newly formed Id will in most cases be used further\ndown the pipeline in constructing the full, final path to the new Plone object.\n\nIt should be noted that this section can effectively transform *any* section of\ntext and turn it into a normalized, web safe string (max 255 chars) This string\ndoes not necessarily need to be used for a URL.\n\n\nDisable / enable versioning sections\n------------------------------------\n\nIt can be helpful to disable versioning during content construction to avoid\nstoring incomplete versions in the content item's revision history.\n\nFor example::\n\n    [transmogrifier]\n    pipeline =\n        schemasource\n        disable_versioning\n        constructor\n        enable_versioning\n        schemaupdater\n\n    [disable_versioning]\n    blueprint = plone.app.transmogrifier.versioning.disable\n\n    [constructor]\n    blueprint = collective.transmogrifier.sections.constructor\n\n    [enable_versioning]\n    blueprint = plone.app.transmogrifier.versioning.enable\n\n\n\nWorkflow updater section\n------------------------\n\nA workflow updater pipeline section is another important transmogrifier content\nimport pipeline element. It executes workflow transitions on Plone content\nbased on the items it processes. The workflow updater section blueprint name is\n``plone.app.transmogrifier.workflowupdater``. Workflow updater sections operate\non objects already present in the ZODB, be they created by a constructor or\npre-existing objects.\n\nWorkflow updating needs 2 pieces of information: the path to the object, and\nwhat transitions to execute. To determine these, the workflow updater section\ninspects each item and looks for two keys, as described below. Any item missing\nany of these two pieces will be skipped. Similarly, items with a path that\ndoesn't exist will be skipped as well.\n\nFor the object path, it'll look (in order) for\n``_plone.app.transmogrifier.atschemaupdater_[sectionname]_path``,\n``_plone.app.transmogrifier.atschemaupdater_path``, ``_[sectionname]_path`` and\n``_path``, where ``[sectionname]`` is replaced with the name given to the\ncurrent section. This allows you to target the right section precisely if\nneeded. Alternatively, you can specify what key to use for the path by\nspecifying the ``path-key`` option, which should be a list of keys to try (one\nkey per line, use a ``re:`` or ``regexp:`` prefix to specify regular\nexpressions).\n\nFor the transitions, use the ``transitions-key`` option (same interpretation\nas ``path-key``), defaulting to\n``_plone.app.transmogrifier.atschemaupdater_[sectionname]_transitions``,\n``_plone.app.transmogrifier.atschemaupdater_transitions``,\n``_[sectionname]_transitions`` and ``_transitions``.\n\nUnicode paths are encoded to ASCII. Paths to objects are always interpreted as\nrelative to the context object. Transitions are specified as a sequence of\ntransition names, or as a string specifying one transition, or a list of\ndictionaries containing 'action' as transition id, 'review_state' as state id\nand 'time' as a DateTime representing the transition time (if so, the worflow\nhistory will be updated with the provided date). Transitions are executed in\norder, failing transitions are silently ignored.\n\n::\n\n    >>> import pprint\n    >>> workflow = \"\"\"\n    ... [transmogrifier]\n    ... pipeline =\n    ...     workflowsource\n    ...     workflowupdater\n    ...     printer\n    ...\n    ... [workflowsource]\n    ... blueprint = plone.app.transmogrifier.tests.workflowsource\n    ...\n    ... [workflowupdater]\n    ... blueprint = plone.app.transmogrifier.workflowupdater\n    ...\n    ... [printer]\n    ... blueprint = collective.transmogrifier.sections.logger\n    ... name = logger\n    ... \"\"\"\n    >>> registerConfig(u'plone.app.transmogrifier.tests.workflow',\n    ...                workflow)\n    >>> transmogrifier(u'plone.app.transmogrifier.tests.workflow')\n    >>> print(handler)\n    logger INFO\n      {'_path': '/spam/eggs/foo', '_transitions': 'spam'}\n    logger INFO\n      {'_path': '/spam/eggs/baz', '_transitions': ('spam', 'eggs')}\n    logger INFO\n        {'_path': 'not/existing/bar',\n       '_transitions': ('spam', 'eggs'),\n       'title': 'Should not be updated, not an existing path'}\n    logger INFO\n        {'_path': 'spam/eggs/incomplete',\n       'title': 'Should not be updated, no transitions'}\n    logger INFO\n        {'_path': '/spam/eggs/nosuchtransition',\n       '_transitions': ('nonsuch',),\n       'title': 'Should not be updated, no such transition'}\n    logger INFO\n        {'_path': '/spam/eggs/bla',\n       '_transitions': ({'action': 'spam',\n                         'review_state': 'spammed',\n                         'time': DateTime('2014/06/20 00:00:00 GMT+0')},)}\n\n    >>> pprint.pprint(plone.updated)\n    [('spam/eggs/foo', 'spam'),\n     ('spam/eggs/baz', 'spam'),\n     ('spam/eggs/baz', 'eggs'),\n     ('spam/eggs/bla', 'spam')]\n\n\nChangelog\n=========\n\n3.0.1 (2023-06-02)\n------------------\n\n- Remove Python 2.4 compatibility code.\n  [wesleybl]\n\n- Add Python 3.10 and 3.11 support.\n  [wesleybl]\n\n- Remove ``z3c.autoinclude`` of entry_points.\n  [wesleybl]\n\n- Remove dependency on ``zest.releaser`` in extra test.\n  [wesleybl]\n\n- Fix ``ModuleNotFoundError: No module named 'Products.CMFDynamicViewFTI.interface'`` in Plone 6.\n  [wesleybl]\n\n\n3.0.0 (2022-06-29)\n------------------\n\n- Implement plone/code-analysis-action\n  [ericof]\n\n- Drop support to Plone versions 4.3, 5.0 and 5.1\n  [ericof]\n\n- Drop support to Python 2.7, Python 3.6 and Products.Archetypes\n  [ericof]\n\n\n2.0.0 (2021-09-17)\n------------------\n\n- Raise exception in pathfixer if path is not ascii.\n  [wesleybl]\n\n- Add support for Python 3.6, 3.7 and 3.8.\n  [wesleybl]\n\n- Add support for Plone 5.0, 5.1 and 5.2.\n  [wesleybl]\n\n- Remove supports to Plone 4.0, 4.1 and 4.2.\n  [wesleybl]\n\n- Remove Python 2.6 support.\n  [wesleybl]\n\n\n1.4.2 (2019-09-24)\n------------------\n\n- ``plone.app.transmogrifier.atschemaupdater`` updates fields in fixed order\n  (field names) for bette debuggability.\n  [gotcha]\n\n- ``plone.app.transmogrifier.pathfixer`` now also converts a path into ``str`` and removes any invalid characters from it;\n  this avoids ``UnicodeEncodeError`` in many blueprint sections.\n  [hvelarde]\n\n\n1.4.1 (2018-02-27)\n------------------\n\n- Avoid failures on redirector section when there is no object in referenced path.\n  [hvelarde]\n\n- Fix ``plone.app.transmogrifier.browserdefault`` blueprint section:\n  ``default_page`` and ``layout`` properties should be string, not unicode.\n  [sunew]\n\n\n1.4 (2015-10-23)\n----------------\n\n- Support updating effective and expiration dates on ``plone.app.transmogrifier.datesupdater`` blueprint.\n  Fix field discovering logic to avoid skipping the ones set as ``None``.\n  Fix documentation.\n  [hvelarde]\n\n- Support indexing of individual indexes for the\n  ``plone.app.transmogrifier.reindexobject`` blueprint.\n  [thet]\n\n\n1.3 (2015-01-22)\n----------------\n\n- Ignore if workflow_history is not available on objects when running the\n  workflowupdater blueprint.\n  [thet]\n\n- Add datesupdater section to set creation_date and modification_date on\n  objects.\n  [thet]\n\n- Add pathfixer section to remove/prepend parts of the path.\n  [thet]\n\n- PEP 8.\n  [thet]\n\n- Fix uidsection for dexterity.\n  [shylux]\n\n- Allow to import transition date in the worflow history\n  [ebrehault]\n\n- Fix field accessor and mutator for updating schemaextended field values\n  with schemaupdater.\n  In some cases when using fields extended by schemaextender it defines\n  an accessor attribute which is not accessable. To cover all fields, its\n  better to access and mutate over the getAccessor and getMutator methods on\n  archetype fields.\n  [elioschmutz]\n\n- Add a section to manage `plone.app.redirector` and to use it to\n  update paths.\n  [rpatterson]\n\n- Support field accessor and mutator for updating field values with\n  schemaupdater.\n  [phgross]\n\n\n1.2 (2011-05-23)\n----------------\n\n- Sections to disable and enable versioning within the pipeline.\n  [elro]\n\n- Convert paths to strings.\n  [elro]\n\n- Add a 'verbose' option to reindexobject blueprint\n  that logs the object currently reindexed and number of objects reindexed.\n  [thomasdesvenain]\n\n- Check for CatalogAware base class when reindexing an object instead of\n  CMFCatalogAware because in Plone 4 folders do not inherit from\n  CMFCatalogAware.\n  [buchi]\n\n\n1.1 (2010-03-30)\n----------------\n\n- Added Indexing section. See reindexobject.rst.\n  [sylvainb]\n\n- Added UID updated section. See uidupdater.rst.\n  [optilude]\n\n- Fixed tests for Plone 4, in the same way that they were fixed in\n  collective.transmogrifier.\n  [optilude]\n\n\n1.0 (2009-08-09)\n----------------\n\n- Initial package.\n  [mj]\n\n",
    "bugtrack_url": null,
    "license": "GPL",
    "summary": "Plone blueprints for collective.transmogrifier pipelines",
    "version": "3.0.1",
    "project_urls": {
        "Homepage": "https://github.com/collective/plone.app.transmogrifier",
        "PyPI": "https://pypi.python.org/pypi/plone.app.transmogrifier",
        "Source": "https://github.com/collective/plone.app.transmogrifier",
        "Tracker": "https://github.com/collective/plone.app.transmogrifier/issues"
    },
    "split_keywords": [
        "content",
        "import",
        "filtering",
        "plone"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "fa9d6cc032b0ae153debaf6a23cd709da4a854f1b98d065516f40d29efcebf37",
                "md5": "c5990bad2c0b1f9098b0fd93168a9aee",
                "sha256": "3e7177fdba8c1550a4ea1db729df17171adac7ad017e8e8dedbb3a6df199444c"
            },
            "downloads": -1,
            "filename": "plone.app.transmogrifier-3.0.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "c5990bad2c0b1f9098b0fd93168a9aee",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 48195,
            "upload_time": "2023-06-02T17:48:45",
            "upload_time_iso_8601": "2023-06-02T17:48:45.943860Z",
            "url": "https://files.pythonhosted.org/packages/fa/9d/6cc032b0ae153debaf6a23cd709da4a854f1b98d065516f40d29efcebf37/plone.app.transmogrifier-3.0.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "649e2d062e3c4b14853259d2c9e3da80a6da87dc1b3e8a59caaf013b8ca6e5cd",
                "md5": "86ad235c87de3b223e3960034c61395f",
                "sha256": "fb8d4c8aeac7b6181c9b0696bdb3db69bb14937876a41a4ad1d4a22052b19271"
            },
            "downloads": -1,
            "filename": "plone.app.transmogrifier-3.0.1.tar.gz",
            "has_sig": false,
            "md5_digest": "86ad235c87de3b223e3960034c61395f",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 64050,
            "upload_time": "2023-06-02T17:48:48",
            "upload_time_iso_8601": "2023-06-02T17:48:48.433186Z",
            "url": "https://files.pythonhosted.org/packages/64/9e/2d062e3c4b14853259d2c9e3da80a6da87dc1b3e8a59caaf013b8ca6e5cd/plone.app.transmogrifier-3.0.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-06-02 17:48:48",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "collective",
    "github_project": "plone.app.transmogrifier",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "plone.app.transmogrifier"
}
        
Elapsed time: 0.09375s