Faceted XML Adapter
===================
XML Adapter for Faceted Navigable objects to be used within GenericSetup profiles.

Imports
-------

    >>> from zope.component import getMultiAdapter
    >>> from eea.facetednavigation.tests.utils import preparefile

Set up
------

Setup sandbox

    >>> portal = layer["portal"]
    >>> sandbox = portal['sandbox']
    >>> sandbox.setTitle('Faceted navigation sandbox')
    >>> request = sandbox.REQUEST

    >>> from zope.component import getMultiAdapter
    >>> subtyper = getMultiAdapter((sandbox, request), name=u'faceted_subtyper')
    >>> subtyper.enable()

Let's cleanup default criteria to avoid surprises

    >>> from eea.facetednavigation.interfaces import ICriteria
    >>> cids = ICriteria(sandbox).keys()
    >>> for cid in cids:
    ...     ICriteria(sandbox).delete(cid)
    >>> ICriteria(sandbox).keys()
    []

Get generic setup xml adapter

    >>> from zope.component import queryMultiAdapter
    >>> from Products.GenericSetup.interfaces import IBody
    >>> from Products.GenericSetup.testing import DummySetupEnviron
    >>> exporter = queryMultiAdapter((sandbox, DummySetupEnviron()), IBody)
    >>> exporter
    <...FacetedNavigableXMLAdapter...>

XML Export
----------

Export empty criteria

    >>> print(exporter.body.decode('utf-8').strip())
    <?xml...?>
    <object name="sandbox" meta_type="...">
     <criteria/>
    </object>

Let's add some criteria

    >>> handler = sandbox.unrestrictedTraverse('@@faceted_configure')

Checkboxes

    >>> _ = handler(addPropertiesWidget_button='Add',
    ...     wtype='checkbox', wposition='left',
    ...     title="Type d'element",
    ...     index='portal_type', default=[u'Folder', u'Document'],
    ...     count=True)

    >>> _ = handler(addPropertiesWidget_button='Add',
    ...     wtype='tagscloud', wposition='right',
    ...     title="Meta-type d'element",
    ...     index='portal_type',
    ...     cloud='sphere', sizemin=15, sizemax=25,
    ...     count=False)

    >>> print(exporter.body.decode('utf-8').strip())
    <?xml...?>
    <object name="sandbox" meta_type="...">
     <criteria>
      <criterion name="c0">
       <property name="widget">checkbox</property>
       <property name="title">Type d...element</property>
       <property name="index">portal_type</property>
       <property name="vocabulary"></property>
       <property name="default">
        <element value="Folder"/>
        <element value="Document"/>
       </property>
       <property name="position">left</property>
       <property name="section">default</property>
       <property name="hidden">False</property>
       <property name="custom_css"></property>
       <property name="count">True</property>
      </criterion>
      <criterion name="c1">
       <property name="widget">tagscloud</property>
       <property name="title">Meta-type d...element</property>
       <property name="index">portal_type</property>
       <property name="vocabulary"></property>
       <property name="position">right</property>
       <property name="section">default</property>
       <property name="hidden">False</property>
       <property name="custom_css"></property>
       <property name="count">False</property>
       <property name="cloud">sphere</property>
       <property name="sizemin">15</property>
       <property name="sizemax">25</property>
      </criterion>
     </criteria>
    </object>


XML Import
----------

Let's add a new folder to import criteria configuration

    >>> fid = portal.invokeFactory('Document', 'new_sandbox')
    >>> new_sandbox = portal._getOb(fid)
    >>> new_sandbox.setTitle('Faceted navigation new sandbox')
    >>> subtyper = getMultiAdapter((new_sandbox, new_sandbox.REQUEST), name=u'faceted_subtyper')
    >>> subtyper.enable()

And cleanup default widgets

    >>> cids = ICriteria(new_sandbox).keys()
    >>> for cid in cids:
    ...     ICriteria(new_sandbox).delete(cid)
    >>> ICriteria(new_sandbox).keys()
    []

Let's check exported

    >>> importer = queryMultiAdapter((new_sandbox, DummySetupEnviron()), IBody)
    >>> importer.body = exporter.body
    >>> print(importer.body.decode('utf-8').strip())
    <?xml...?>
    <object name="new_sandbox" meta_type="...">
     <criteria>
      <criterion name="c0">
       <property name="widget">checkbox</property>
       <property name="title">Type d...element</property>
       <property name="index">portal_type</property>
       <property name="vocabulary"></property>
       <property name="default">
        <element value="Folder"/>
        <element value="Document"/>
       </property>
       <property name="position">left</property>
       <property name="section">default</property>
       <property name="hidden">False</property>
       <property name="custom_css"></property>
       <property name="count">True</property>
      </criterion>
      <criterion name="c1">
       <property name="widget">tagscloud</property>
       <property name="title">Meta-type d...element</property>
       <property name="index">portal_type</property>
       <property name="vocabulary"></property>
       <property name="position">right</property>
       <property name="section">default</property>
       <property name="hidden">False</property>
       <property name="custom_css"></property>
       <property name="count">False</property>
       <property name="cloud">sphere</property>
       <property name="sizemin">15</property>
       <property name="sizemax">25</property>
      </criterion>
     </criteria>
    </object>


Now let's see if it works if there is already confgurated.

Cleanup

    >>> cids = ICriteria(new_sandbox).keys()
    >>> for cid in cids:
    ...     ICriteria(new_sandbox).delete(cid)
    >>> ICriteria(new_sandbox).keys()
    []

Add one widget

    >>> handler = new_sandbox.unrestrictedTraverse('@@faceted_configure')
    >>> _ = handler(addPropertiesWidget_button='Add',
    ...     wtype='tagscloud', wposition='right',
    ...     title="Meta-type d'element",
    ...     index='portal_type', default='Folder',
    ...     cloud='sphere', sizemin=15, sizemax=25,
    ...     count=False)

Check configuration

    >>> print(importer.body.decode('utf-8').strip())
    <?xml...>
    <object name="new_sandbox" meta_type="...">
     <criteria>
      <criterion name="c0">
       <property name="widget">tagscloud</property>
       <property name="title">Meta-type d...element</property>
       <property name="index">portal_type</property>
       <property name="vocabulary"></property>
       <property name="default">Folder</property>
       <property name="position">right</property>
       <property name="section">default</property>
       <property name="hidden">False</property>
       <property name="custom_css"></property>
       <property name="count">False</property>
       <property name="cloud">sphere</property>
       <property name="sizemin">15</property>
       <property name="sizemax">25</property>
      </criterion>
     </criteria>
    </object>


Now update it

    >>> importer.body = exporter.body
    >>> print(importer.body.decode('utf-8').strip())
    <?xml...?>
    <object name="new_sandbox" meta_type="...">
     <criteria>
      <criterion name="c0">
       <property name="widget">checkbox</property>
       <property name="title">Type d...element</property>
       <property name="index">portal_type</property>
       <property name="vocabulary"></property>
       <property name="default">
        <element value="Folder"/>
        <element value="Document"/>
       </property>
       <property name="position">left</property>
       <property name="section">default</property>
       <property name="hidden">False</property>
       <property name="custom_css"></property>
       <property name="count">True</property>
      </criterion>
      <criterion name="c1">
       <property name="widget">tagscloud</property>
       <property name="title">Meta-type d...element</property>
       <property name="index">portal_type</property>
       <property name="vocabulary"></property>
       <property name="position">right</property>
       <property name="section">default</property>
       <property name="hidden">False</property>
       <property name="custom_css"></property>
       <property name="count">False</property>
       <property name="cloud">sphere</property>
       <property name="sizemin">15</property>
       <property name="sizemax">25</property>
      </criterion>
     </criteria>
    </object>


Using the browser interface
---------------------------

Cleanup

    >>> cids = ICriteria(new_sandbox).keys()
    >>> for cid in cids:
    ...     ICriteria(new_sandbox).delete(cid)
    >>> ICriteria(new_sandbox).keys()
    []

    >>> view = new_sandbox.unrestrictedTraverse('@@faceted_exportimport')

Handle empty or wrong xml files

    >>> query = {
    ...   'import_button': 'Import',
    ...   'import_file': None,
    ...   'redirect': '',
    ... }

    >>> view(**query)
    'Please provide a valid xml file'

Import a valid xml file

    >>> query['import_file'] = preparefile('data/exportimport.xml')
    >>> view(**query)
    'Configuration imported'

Let's see our new configuration

    >>> query = {
    ...   'export_button': 'Export',
    ...   'redirect': '',
    ... }
    >>> print(view(**query).decode('utf-8').strip())
    <?xml...?>
    <object name="new_sandbox" meta_type="...">
     <criteria>
      <criterion name="c2">
       <property name="widget">tagscloud</property>
       <property name="title">Meta-type d...element</property>
       <property name="index">portal_type</property>
       <property name="vocabulary"></property>
       <property name="default">Document</property>
       <property name="position">top</property>
       <property name="section">default</property>
       <property name="hidden">False</property>
       <property name="custom_css"></property>
       <property name="count">False</property>
       <property name="cloud">sphere</property>
      </criterion>
      <criterion name="c7">
       <property name="widget">checkbox</property>
       <property name="title">Type d...element</property>
       <property name="index">portal_type</property>
       <property name="vocabulary"></property>
       <property name="default">
        <element value="Folder"/>
        <element value="Document"/>
       </property>
       <property name="position">left</property>
       <property name="section">default</property>
       <property name="hidden">True</property>
       <property name="custom_css"></property>
       <property name="count">True</property>
      </criterion>
     </criteria>
    </object>


Test the criteria purge attribute
---------------------------------

Import a xml file with purge set to False, new criteria will be added to existing ones

    >>> query = {
    ...   'import_button': 'Import',
    ...   'import_file': preparefile('data/criteria_with_purge_false.xml'),
    ...   'redirect': '',
    ... }
    >>> view(**query)
    'Configuration imported'

    >>> query = {
    ...   'export_button': 'Export',
    ...   'redirect': '',
    ... }
    >>> print(view(**query).decode('utf-8').strip())
    <?xml...?>
    <object name="new_sandbox" meta_type="...">
     <criteria>
      <criterion name="c2">
       <property name="widget">tagscloud</property>
       <property name="title">Meta-type d...element</property>
       <property name="index">portal_type</property>
       <property name="vocabulary"></property>
       <property name="default">Document</property>
       <property name="position">top</property>
       <property name="section">default</property>
       <property name="hidden">False</property>
       <property name="custom_css"></property>
       <property name="count">False</property>
       <property name="cloud">sphere</property>
      </criterion>
      <criterion name="c10">
       <property name="widget">daterange</property>
       <property name="title">Modified</property>
       <property name="index">Date</property>
       <property name="default"></property>
       <property name="position">top</property>
       <property name="section">advanced</property>
       <property name="hidden">False</property>
       <property name="custom_css"></property>
       <property name="calYearRange">c-10:c+10</property>
      </criterion>
      <criterion name="c7">
       <property name="widget">checkbox</property>
       <property name="title">Type d...element</property>
       <property name="index">portal_type</property>
       <property name="vocabulary"></property>
       <property name="default">
        <element value="Folder"/>
        <element value="Document"/>
       </property>
       <property name="position">left</property>
       <property name="section">default</property>
       <property name="hidden">True</property>
       <property name="custom_css"></property>
       <property name="count">True</property>
      </criterion>
     </criteria>
    </object>

Importing an already existing criterion will not fail, it is just ignored and a warning is added to the Zope log

    >>> query = {
    ...   'import_button': 'Import',
    ...   'import_file': preparefile('data/criteria_with_purge_false.xml'),
    ...   'redirect': '',
    ... }
    >>> view(**query)
    'Configuration imported'
    >>> query = {
    ...   'export_button': 'Export',
    ...   'redirect': '',
    ... }

    >>> print(view(**query).decode('utf-8').strip())
    <?xml...?>
    <object name="new_sandbox" meta_type="...">
     <criteria>
      <criterion name="c2">
       <property name="widget">tagscloud</property>
       <property name="title">Meta-type d...element</property>
       <property name="index">portal_type</property>
       <property name="vocabulary"></property>
       <property name="default">Document</property>
       <property name="position">top</property>
       <property name="section">default</property>
       <property name="hidden">False</property>
       <property name="custom_css"></property>
       <property name="count">False</property>
       <property name="cloud">sphere</property>
      </criterion>
      <criterion name="c10">
        <property name="widget">daterange</property>
        <property name="title">Modified</property>
        <property name="index">Date</property>
        <property name="default"></property>
        <property name="position">top</property>
        <property name="section">advanced</property>
        <property name="hidden">False</property>
        <property name="custom_css"></property>
        <property name="calYearRange">c-10:c+10</property>
       </criterion>
      <criterion name="c7">
       <property name="widget">checkbox</property>
       <property name="title">Type d...element</property>
       <property name="index">portal_type</property>
       <property name="vocabulary"></property>
       <property name="default">
        <element value="Folder"/>
        <element value="Document"/>
       </property>
       <property name="position">left</property>
       <property name="section">default</property>
       <property name="hidden">True</property>
       <property name="custom_css"></property>
       <property name="count">True</property>
      </criterion>
     </criteria>
    </object>

Import a xml file with purge set to True (default), existing criteria are removed and the new is imported

    >>> query = {
    ...   'import_button': 'Import',
    ...   'import_file': preparefile('data/criteria_with_purge_true.xml'),
    ...   'redirect': '',
    ... }
    >>> view(**query)
    'Configuration imported'

    >>> query = {
    ...   'export_button': 'Export',
    ...   'redirect': '',
    ... }
    >>> print(view(**query).decode('utf-8').strip())
    <?xml...?>
    <object name="new_sandbox" meta_type="...">
     <criteria>
      <criterion name="c22">
       <property name="widget">daterange</property>
       <property name="title">Created</property>
       <property name="index">Date</property>
       <property name="default"></property>
       <property name="position">top</property>
       <property name="section">advanced</property>
       <property name="hidden">False</property>
       <property name="custom_css"></property>
       <property name="calYearRange">c-10:c+10</property>
      </criterion>
     </criteria>
    </object>

Import a xml file with criterion purge set to False, existing criterion is left and the new is not imported

    >>> query = {
    ...   'import_button': 'Import',
    ...   'import_file': preparefile('data/criterion_with_purge_false.xml'),
    ...   'redirect': '',
    ... }
    >>> view(**query)
    'Configuration imported'

    >>> query = {
    ...   'export_button': 'Export',
    ...   'redirect': '',
    ... }
    >>> print(view(**query).decode('utf-8').strip())
    <?xml...?>
    <object name="new_sandbox" meta_type="...">
     <criteria>
      <criterion name="c22">
       <property name="widget">daterange</property>
       <property name="title">Created</property>
       <property name="index">Date</property>
       <property name="default"></property>
       <property name="position">top</property>
       <property name="section">advanced</property>
       <property name="hidden">False</property>
       <property name="custom_css"></property>
       <property name="calYearRange">c-10:c+10</property>
      </criterion>
     </criteria>
    </object>

Import a xml file with criterion purge set to True, existing criterion is removed and the new is imported

    >>> query = {
    ...   'import_button': 'Import',
    ...   'import_file': preparefile('data/criterion_with_purge_true.xml'),
    ...   'redirect': '',
    ... }
    >>> view(**query)
    'Configuration imported'

    >>> query = {
    ...   'export_button': 'Export',
    ...   'redirect': '',
    ... }
    >>> print(view(**query).decode('utf-8').strip())
    <?xml...?>
    <object name="new_sandbox" meta_type="...">
     <criteria>
      <criterion name="c22">
       <property name="widget">daterange</property>
       <property name="title">Created title changed</property>
       <property name="index">Date</property>
       <property name="default"></property>
       <property name="position">top</property>
       <property name="section">advanced</property>
       <property name="hidden">False</property>
       <property name="custom_css"></property>
       <property name="calYearRange">c-20:c+20</property>
      </criterion>
     </criteria>
    </object>
