[CMF-checkins] SVN: CMF/branches/mj-cmf14-compat-branch/CMFSetup/
Backport content export / import framework.
Tres Seaver
tseaver at palladion.com
Mon Mar 10 17:57:33 EDT 2008
Log message for revision 84578:
Backport content export / import framework.
Changed:
A CMF/branches/mj-cmf14-compat-branch/CMFSetup/content.py
U CMF/branches/mj-cmf14-compat-branch/CMFSetup/interfaces.py
U CMF/branches/mj-cmf14-compat-branch/CMFSetup/tests/common.py
U CMF/branches/mj-cmf14-compat-branch/CMFSetup/tests/conformance.py
A CMF/branches/mj-cmf14-compat-branch/CMFSetup/tests/faux_objects.py
A CMF/branches/mj-cmf14-compat-branch/CMFSetup/tests/test_content.py
-=-
Added: CMF/branches/mj-cmf14-compat-branch/CMFSetup/content.py
===================================================================
--- CMF/branches/mj-cmf14-compat-branch/CMFSetup/content.py (rev 0)
+++ CMF/branches/mj-cmf14-compat-branch/CMFSetup/content.py 2008-03-10 21:57:32 UTC (rev 84578)
@@ -0,0 +1,415 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Filesystem exporter / importer adapters.
+
+$Id: content.py 66587 2006-04-06 10:47:06Z yuppie $
+"""
+
+from csv import reader
+from csv import register_dialect
+from csv import writer
+from ConfigParser import ConfigParser
+import re
+from StringIO import StringIO
+
+from zope.component import getAdapter
+from zope.component import queryAdapter
+from zope.component import queryNamedAdapter
+from zope.interface import implements
+from zope.interface import directlyProvides
+import zLOG
+
+from interfaces import IContentFactory
+from interfaces import IContentFactoryName
+from interfaces import IFilesystemExporter
+from interfaces import IFilesystemImporter
+from interfaces import IINIAware
+from interfaces import ISetupTool
+from utils import _getDottedName
+from utils import _resolveDottedName
+
+#
+# setup_tool handlers
+#
+def exportSiteStructure(context):
+ adapter = getAdapter(context.getSite(), IFilesystemExporter)
+ adapter.export(context, 'structure', True)
+
+def importSiteStructure(context):
+ adapter = getAdapter(context.getSite(), IFilesystemImporter)
+ adapter.import_(context, 'structure', True)
+
+
+#
+# Filesystem export/import adapters
+#
+class FolderishExporterImporter(object):
+ """ Tree-walking exporter / importer for "folderish" types.
+
+ Folderish instances are mapped to directories within the 'structure'
+ portion of the profile, where the folder's relative path within the site
+ corresponds to the path of its directory under 'structure'.
+
+ The subobjects of a folderish instance are enumerated in the '.objects'
+ file in the corresponding directory. This file is a CSV file, with one
+ row per subobject, with the following wtructure::
+
+ "<subobject id>","<subobject portal_type>"
+
+ Subobjects themselves are represented as individual files or
+ subdirectories within the parent's directory.
+ """
+
+ implements(IFilesystemExporter, IFilesystemImporter)
+
+ def __init__(self, context):
+ self.context = context
+
+ def listExportableItems(self):
+ """ See IFilesystemExporter.
+ """
+ exportable = self.context.objectItems()
+ exportable = [x for x in exportable
+ if not ISetupTool.isImplementedBy(x[1])]
+ exportable = [x + (queryAdapter(x[1], IFilesystemExporter, None),)
+ for x in exportable]
+ return exportable
+
+ def export(self, export_context, subdir, root=False):
+ """ See IFilesystemExporter.
+ """
+ context = self.context
+
+ if not root:
+ subdir = '%s/%s' % (subdir, context.getId())
+
+ exportable = self.listExportableItems()
+
+ stream = StringIO()
+ csv_writer = writer(stream)
+
+ for object_id, object, adapter in exportable:
+
+ factory_namer = queryAdapter(object, IContentFactoryName, None)
+ if factory_namer is None:
+ factory_name = _getDottedName(object.__class__)
+ else:
+ factory_name = factory_namer()
+
+ csv_writer.writerow((object_id, factory_name))
+
+ export_context.writeDataFile('.objects',
+ text=stream.getvalue(),
+ content_type='text/comma-separated-values',
+ subdir=subdir,
+ )
+
+ prop_adapter = queryAdapter(context, IINIAware, None)
+
+ if prop_adapter is not None:
+ export_context.writeDataFile('.properties',
+ text=prop_adapter.as_ini(),
+ content_type='text/plain',
+ subdir=subdir,
+ )
+
+ for object_id, object, adapter in exportable:
+ if adapter is not None:
+ adapter.export(export_context, subdir)
+
+ def import_(self, import_context, subdir, root=False):
+ """ See IFilesystemImporter.
+ """
+ context = self.context
+ if not root:
+ subdir = '%s/%s' % (subdir, context.getId())
+
+ prop_adapter = queryAdapter(context, IINIAware, None)
+
+ if prop_adapter is not None:
+ prop_text = import_context.readDataFile('.properties',
+ subdir=subdir,
+ )
+ if prop_text is not None:
+ prop_adapter.put_ini(prop_text)
+
+ preserve = import_context.readDataFile('.preserve', subdir)
+ must_preserve = self._mustPreserve()
+
+ prior = context.objectIds()
+
+ if not preserve:
+ preserve = []
+ else:
+ preserve = _globtest(preserve, prior)
+
+ preserve.extend([x[0] for x in must_preserve])
+
+ for id in prior:
+ if id not in preserve:
+ context._delObject(id)
+
+ objects = import_context.readDataFile('.objects', subdir)
+ if objects is None:
+ return
+
+ dialect = 'excel'
+ stream = StringIO(objects)
+
+ rowiter = reader(stream, dialect)
+
+ existing = context.objectIds()
+
+ for object_id, type_name in rowiter:
+
+ if object_id not in existing:
+ object = self._makeInstance(object_id, type_name,
+ subdir, import_context)
+ if object is None:
+ zLOG.LOG('SFWA', zLOG.WARNING,
+ "Couldn't make instance: %s/%s" %
+ (subdir, object_id))
+ continue
+
+ wrapped = context._getOb(object_id)
+
+ adapter = getAdapter(wrapped, IFilesystemImporter)
+ adapter.import_(import_context, subdir)
+
+ def _makeInstance(self, instance_id, type_name, subdir, import_context):
+
+ context = self.context
+ class _OldStyleClass:
+ pass
+
+ if '.' in type_name:
+
+ factory = _resolveDottedName(type_name)
+
+ if getattr(factory, '__bases__', None) is not None:
+
+ def _factory(instance_id,
+ container=self.context,
+ klass=factory):
+ try:
+ instance = klass(instance_id)
+ except (TypeError, ValueError):
+ instance = klass()
+ instance._setId(instance_id)
+ container._setObject(instance_id, instance)
+
+ return instance
+
+ factory = _factory
+
+ else:
+ factory = queryNamedAdapter(self.context,
+ IContentFactory,
+ name=type_name,
+ )
+ if factory is None:
+ return None
+
+ try:
+ instance = factory(instance_id)
+ except ValueError: # invalid type
+ return None
+
+ if context._getOb(instance_id, None) is None:
+ context._setObject(instance_id, instance)
+
+ return context._getOb(instance_id)
+
+ def _mustPreserve(self):
+ return [x for x in self.context.objectItems()
+ if ISetupTool.isImplementedBy(x[1])]
+
+
+def _globtest(globpattern, namelist):
+ """ Filter names in 'namelist', returning those which match 'globpattern'.
+ """
+ import re
+ pattern = globpattern.replace(".", r"\.") # mask dots
+ pattern = pattern.replace("*", r".*") # change glob sequence
+ pattern = pattern.replace("?", r".") # change glob char
+ pattern = '|'.join(pattern.split()) # 'or' each line
+
+ compiled = re.compile(pattern)
+
+ return filter(compiled.match, namelist)
+
+
+class CSVAwareFileAdapter(object):
+ """ Adapter for content whose "natural" representation is CSV.
+ """
+ implements(IFilesystemExporter, IFilesystemImporter)
+
+ def __init__(self, context):
+ self.context = context
+
+ def export(self, export_context, subdir, root=False):
+ """ See IFilesystemExporter.
+ """
+ export_context.writeDataFile('%s.csv' % self.context.getId(),
+ self.context.as_csv(),
+ 'text/comma-separated-values',
+ subdir,
+ )
+
+ def listExportableItems(self):
+ """ See IFilesystemExporter.
+ """
+ return ()
+
+ def import_(self, import_context, subdir, root=False):
+ """ See IFilesystemImporter.
+ """
+ cid = self.context.getId()
+ data = import_context.readDataFile('%s.csv' % cid, subdir)
+ if data is None:
+ zLOG.LOG('CSAFA', zLOG.INFO,
+ 'no .csv file for %s/%s' % (subdir, cid))
+ else:
+ stream = StringIO(data)
+ self.context.put_csv(stream)
+
+class INIAwareFileAdapter(object):
+ """ Exporter/importer for content whose "natural" representation is an
+ '.ini' file.
+ """
+ implements(IFilesystemExporter, IFilesystemImporter)
+
+ def __init__(self, context):
+ self.context = context
+
+ def export(self, export_context, subdir, root=False):
+ """ See IFilesystemExporter.
+ """
+ export_context.writeDataFile('%s.ini' % self.context.getId(),
+ self.context.as_ini(),
+ 'text/plain',
+ subdir,
+ )
+
+ def listExportableItems(self):
+ """ See IFilesystemExporter.
+ """
+ return ()
+
+ def import_(self, import_context, subdir, root=False):
+ """ See IFilesystemImporter.
+ """
+ cid = self.context.getId()
+ data = import_context.readDataFile('%s.ini' % cid, subdir)
+ if data is None:
+ zLOG.LOG('SGAIFA', zLOG.INFO,
+ 'no .ini file for %s/%s' % (subdir, cid))
+ else:
+ self.context.put_ini(data)
+
+class SimpleINIAware(object):
+ """ Exporter/importer for content which doesn't know from INI.
+ """
+ implements(IINIAware,)
+
+ def __init__(self, context):
+ self.context = context
+
+ def getId(self):
+ return self.context.getId()
+
+ def as_ini(self):
+ """
+ """
+ context = self.context
+ parser = ConfigParser()
+ stream = StringIO()
+ for k, v in context.propertyItems():
+ parser.set('DEFAULT', k, str(v))
+ parser.write(stream)
+ return stream.getvalue()
+
+ def put_ini(self, text):
+ """
+ """
+ context = self.context
+ parser = ConfigParser()
+ parser.readfp(StringIO(text))
+ for option, value in parser.defaults().items():
+ prop_type = context.getPropertyType(option)
+ if prop_type is None:
+ context._setProperty(option, value, 'string')
+ else:
+ context._updateProperty(option, value)
+
+class FauxDAVRequest:
+
+ def __init__(self, **kw):
+ self._data = {}
+ self._headers = {}
+ self._data.update(kw)
+
+ def __getitem__(self, key):
+ return self._data[key]
+
+ def get(self, key, default=None):
+ return self._data.get(key, default)
+
+ def get_header(self, key, default=None):
+ return self._headers.get(key, default)
+
+class FauxDAVResponse:
+ def setHeader(self, key, value, lock=False):
+ pass # stub this out to mollify webdav.Resource
+ def setStatus(self, value, reason=None):
+ pass # stub this out to mollify webdav.Resource
+
+class DAVAwareFileAdapter(object):
+ """ Exporter/importer for content who handle their own FTP / DAV PUTs.
+ """
+ implements(IFilesystemExporter, IFilesystemImporter)
+
+ def __init__(self, context):
+ self.context = context
+
+ def _getFileName(self):
+ """ Return the name under which our file data is stored.
+ """
+ return '%s' % self.context.getId()
+
+ def export(self, export_context, subdir, root=False):
+ """ See IFilesystemExporter.
+ """
+ export_context.writeDataFile(self._getFileName(),
+ self.context.manage_FTPget(),
+ 'text/plain',
+ subdir,
+ )
+
+ def listExportableItems(self):
+ """ See IFilesystemExporter.
+ """
+ return ()
+
+ def import_(self, import_context, subdir, root=False):
+ """ See IFilesystemImporter.
+ """
+ cid = self.context.getId()
+ data = import_context.readDataFile(self._getFileName(), subdir)
+ if data is None:
+ zLOG.LOG('SGAIFA', zLOG.INFO,
+ 'no .ini file for %s/%s' % (subdir, cid))
+ else:
+ request = FauxDAVRequest(BODY=data, BODYFILE=StringIO(data))
+ response = FauxDAVResponse()
+ self.context.PUT(request, response)
Modified: CMF/branches/mj-cmf14-compat-branch/CMFSetup/interfaces.py
===================================================================
--- CMF/branches/mj-cmf14-compat-branch/CMFSetup/interfaces.py 2008-03-10 21:02:40 UTC (rev 84577)
+++ CMF/branches/mj-cmf14-compat-branch/CMFSetup/interfaces.py 2008-03-10 21:57:32 UTC (rev 84578)
@@ -514,3 +514,133 @@
o If 'ignore_whitespace', then suppress diffs due only to whitespace
(c.f: 'diff -wbB')
"""
+
+
+class IFilesystemExporter(Interface):
+ """ Plugin interface for site structure export.
+ """
+ def export(export_context, subdir, root=False):
+ """ Export our 'context' using the API of 'export_context'.
+
+ o 'export_context' must implement
+ Products.GenericSupport.interfaces.IExportContext.
+
+ o 'subdir', if passed, is the relative subdirectory containing our
+ context within the site.
+
+ o 'root', if true, indicates that the current context is the
+ "root" of an import (this may be used to adjust paths when
+ interacting with the context).
+ """
+
+ def listExportableItems():
+ """ Return a sequence of the child items to be exported.
+
+ o Each item in the returned sequence will be a tuple,
+ (id, object, adapter) where adapter must implement
+ IFilesystemExporter.
+ """
+
+class IFilesystemImporter(Interface):
+ """ Plugin interface for site structure export.
+ """
+ def import_(import_context, subdir, root=False):
+ """ Import our 'context' using the API of 'import_context'.
+
+ o 'import_context' must implement
+ Products.GenericSupport.interfaces.IImportContext.
+
+ o 'subdir', if passed, is the relative subdirectory containing our
+ context within the site.
+
+ o 'root', if true, indicates that the current context is the
+ "root" of an import (this may be used to adjust paths when
+ interacting with the context).
+ """
+
+class IContentFactory(Interface):
+ """ Adapter interface for factories specific to a container.
+ """
+ def __call__(id):
+ """ Return a new instance, seated in the context under 'id'.
+ """
+
+class IContentFactoryName(Interface):
+ """ Adapter interface for finding the name of the ICF for an object.
+ """
+ def __call__():
+ """ Return a string, suitable for looking up an IContentFactory.
+
+ o The string should allow finding a factory for our context's
+ container which would create an "empty" instance of the same
+ type as our context.
+ """
+
+class ICSVAware(Interface):
+ """ Interface for objects which dump / load 'text/comma-separated-values'.
+ """
+ def getId():
+ """ Return the Zope id of the object.
+ """
+
+ def as_csv():
+ """ Return a string representing the object as CSV.
+ """
+
+ def put_csv(fd):
+ """ Parse CSV and update the object.
+
+ o 'fd' must be a file-like object whose 'read' method returns
+ CSV text parseable by the 'csv.reader'.
+ """
+
+class IINIAware(Interface):
+ """ Interface for objects which dump / load INI-format files..
+ """
+ def getId():
+ """ Return the Zope id of the object.
+ """
+
+ def as_ini():
+ """ Return a string representing the object as INI.
+ """
+
+ def put_ini(stream_or_text):
+ """ Parse INI-formatted text and update the object.
+
+ o 'stream_or_text' must be either a string, or else a stream
+ directly parseable by ConfigParser.
+ """
+
+class IDAVAware(Interface):
+ """ Interface for objects which handle their own FTP / DAV operations.
+ """
+ def getId():
+ """ Return the Zope id of the object.
+ """
+
+ def manage_FTPget():
+ """ Return a string representing the object as a file.
+ """
+
+ def PUT(REQUEST, RESPONSE):
+ """ Parse file content and update the object.
+
+ o 'REQUEST' will have a 'get' method, which will have the
+ content object in its "BODY" key. It will also have 'get_header'
+ method, whose headers (e.g., "Content-Type") may affect the
+ processing of the body.
+ """
+
+# Ugn
+class IObjectManager(Interface):
+ """ Shim.
+ """
+
+class ISimpleItem(Interface):
+ """ Shim.
+ """
+
+class IPropertyManager(Interface):
+ """ Shim.
+ """
Modified: CMF/branches/mj-cmf14-compat-branch/CMFSetup/tests/common.py
===================================================================
--- CMF/branches/mj-cmf14-compat-branch/CMFSetup/tests/common.py 2008-03-10 21:02:40 UTC (rev 84577)
+++ CMF/branches/mj-cmf14-compat-branch/CMFSetup/tests/common.py 2008-03-10 21:57:32 UTC (rev 84578)
@@ -176,7 +176,7 @@
def readDataFile( self, filename, subdir=None ):
if subdir is not None:
- filename = '/'.join( subdir, filename )
+ filename = '/'.join( ( subdir, filename ) )
return self._files.get( filename )
Modified: CMF/branches/mj-cmf14-compat-branch/CMFSetup/tests/conformance.py
===================================================================
--- CMF/branches/mj-cmf14-compat-branch/CMFSetup/tests/conformance.py 2008-03-10 21:02:40 UTC (rev 84577)
+++ CMF/branches/mj-cmf14-compat-branch/CMFSetup/tests/conformance.py 2008-03-10 21:57:32 UTC (rev 84578)
@@ -98,3 +98,30 @@
from Interface.Verify import verifyClass
verifyClass( ISetupTool, self._getTargetClass() )
+
+class ConformsToIFilesystemExporter:
+
+ def test_conforms_to_IFilesystemExporter(self):
+
+ from Products.CMFSetup.interfaces import IFilesystemExporter
+ from Interface.Verify import verifyClass
+
+ verifyClass( IFilesystemExporter, self._getTargetClass() )
+
+class ConformsToIFilesystemImporter:
+
+ def test_conforms_to_IFilesystemImporter(self):
+
+ from Products.CMFSetup.interfaces import IFilesystemImporter
+ from Interface.Verify import verifyClass
+
+ verifyClass( IFilesystemImporter, self._getTargetClass() )
+
+class ConformsToIINIAware:
+
+ def test_conforms_to_IINIAware(self):
+
+ from Products.CMFSetup.interfaces import IINIAware
+ from Interface.Verify import verifyClass
+
+ verifyClass (IINIAware, self._getTargetClass() )
Added: CMF/branches/mj-cmf14-compat-branch/CMFSetup/tests/faux_objects.py
===================================================================
--- CMF/branches/mj-cmf14-compat-branch/CMFSetup/tests/faux_objects.py (rev 0)
+++ CMF/branches/mj-cmf14-compat-branch/CMFSetup/tests/faux_objects.py 2008-03-10 21:57:32 UTC (rev 84578)
@@ -0,0 +1,76 @@
+""" Simple, importable content classes.
+
+$Id: faux_objects.py 40140 2005-11-15 18:53:19Z tseaver $
+"""
+from OFS.Folder import Folder
+from OFS.PropertyManager import PropertyManager
+from OFS.SimpleItem import SimpleItem
+
+from Products.CMFSetup.interfaces import IObjectManager
+from Products.CMFSetup.interfaces import ISimpleItem
+from Products.CMFSetup.interfaces import IPropertyManager
+
+class TestSimpleItem(SimpleItem):
+ __implements__ = (ISimpleItem, )
+
+class TestSimpleItemWithProperties(SimpleItem, PropertyManager):
+ __implements__ = (ISimpleItem, IPropertyManager)
+
+KNOWN_CSV = """\
+one,two,three
+four,five,six
+"""
+
+from Products.CMFSetup.interfaces import ICSVAware
+class TestCSVAware(SimpleItem):
+ __implements__ = (ICSVAware, )
+ _was_put = None
+ _csv = KNOWN_CSV
+
+ def as_csv(self):
+ return self._csv
+
+ def put_csv(self, text):
+ self._was_put = text
+
+KNOWN_INI = """\
+[DEFAULT]
+title = %s
+description = %s
+"""
+
+from Products.CMFSetup.interfaces import IINIAware
+class TestINIAware(SimpleItem):
+ __implements__ = (IINIAware, )
+ _was_put = None
+ title = 'INI title'
+ description = 'INI description'
+
+ def as_ini(self):
+ return KNOWN_INI % (self.title, self.description)
+
+ def put_ini(self, text):
+ self._was_put = text
+
+KNOWN_DAV = """\
+Title: %s
+Description: %s
+
+%s
+"""
+
+from Products.CMFSetup.interfaces import IDAVAware
+class TestDAVAware(SimpleItem):
+ __implements__ = (IDAVAware, )
+ _was_put = None
+ title = 'DAV title'
+ description = 'DAV description'
+ body = 'DAV body'
+
+ def manage_FTPget(self):
+ return KNOWN_DAV % (self.title, self.description, self.body)
+
+ def PUT(self, REQUEST, RESPONSE):
+ self._was_put = REQUEST.get('BODY', '')
+ stream = REQUEST.get('BODYFILE', None)
+ self._was_put_as_read = stream.read()
Added: CMF/branches/mj-cmf14-compat-branch/CMFSetup/tests/test_content.py
===================================================================
--- CMF/branches/mj-cmf14-compat-branch/CMFSetup/tests/test_content.py (rev 0)
+++ CMF/branches/mj-cmf14-compat-branch/CMFSetup/tests/test_content.py 2008-03-10 21:57:32 UTC (rev 84578)
@@ -0,0 +1,898 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Filesystem exporter / importer adapter unit tests.
+
+$Id: test_content.py 40234 2005-11-18 21:22:13Z tseaver $
+"""
+
+import unittest
+import Testing
+#import Zope2
+#Zope2.startup()
+import Zope
+Zope.startup()
+
+from csv import reader
+from ConfigParser import ConfigParser
+from StringIO import StringIO
+
+from Products.CMFSetup.interfaces import IObjectManager
+from Products.CMFSetup.interfaces import ISimpleItem
+from Products.CMFSetup.interfaces import IPropertyManager
+
+#from Products.GenericSetup.testing import PlacelessSetup
+from Products.CMFSetup.tests.common import DummyExportContext
+from Products.CMFSetup.tests.common import DummyImportContext
+
+from conformance import ConformsToIINIAware
+from conformance import ConformsToIFilesystemExporter
+from conformance import ConformsToIFilesystemImporter
+
+class SimpleINIAwareTests(unittest.TestCase, ConformsToIINIAware):
+
+ def _getTargetClass(self):
+ from Products.CMFSetup.content import SimpleINIAware
+ return SimpleINIAware
+
+ def test_as_ini_no_properties(self):
+ context = _makePropertied('no_properties')
+ context._properties = ()
+ adapter = self._getTargetClass()(context)
+ text = adapter.as_ini()
+ parser = ConfigParser()
+ parser.readfp(StringIO(text))
+ self.failIf(parser.sections())
+ default_options = parser.defaults()
+ self.assertEqual(len(default_options), 0)
+
+ def test_as_ini_string_property(self):
+ TITLE = 'String Property'
+ DESCR = 'Another property'
+ context = _makePropertied('string_property')
+ context.title = TITLE
+ context._setProperty('description', DESCR)
+ adapter = self._getTargetClass()(context)
+ text = adapter.as_ini()
+ parser = ConfigParser()
+ parser.readfp(StringIO(text))
+ self.failIf(parser.sections())
+ default_options = parser.defaults()
+ self.assertEqual(len(default_options), 2)
+ self.assertEqual(default_options['title'].strip(), TITLE)
+ self.assertEqual(default_options['description'].strip(), DESCR)
+
+ def test_as_ini_other_properties(self):
+ from DateTime.DateTime import DateTime
+ INTPROP = 42
+ FLOATPROP = 3.1415926
+ DATESTR = '2005-11-07T12:00:00.000Z'
+ context = _makePropertied('string_property')
+ context._properties = ()
+ context._setProperty('int_prop', INTPROP, 'int')
+ context._setProperty('float_prop', FLOATPROP, 'float')
+ context._setProperty('date_prop', DateTime(DATESTR), 'date')
+ adapter = self._getTargetClass()(context)
+ text = adapter.as_ini()
+ parser = ConfigParser()
+ parser.readfp(StringIO(text))
+ self.failIf(parser.sections())
+ default_options = parser.defaults()
+ self.assertEqual(len(default_options), 3)
+ self.assertEqual(default_options['int_prop'], str(INTPROP))
+ self.assertEqual(default_options['float_prop'], str(FLOATPROP))
+ self.assertEqual(default_options['date_prop'], str(DateTime(DATESTR)))
+
+ def test_put_ini_empty(self):
+ context = _makePropertied('empty_ini')
+ adapter = self._getTargetClass()(context)
+ context._properties = ()
+ self.failIf(context.propertyItems())
+ adapter.put_ini('')
+ self.failIf(context.propertyItems())
+
+ def test_put_ini_with_values_stripped(self):
+ context = _makePropertied('empty_ini')
+ adapter = self._getTargetClass()(context)
+ adapter.put_ini('[DEFAULT]\ntitle = Foo \ndescription = bar ')
+ props = context.propdict()
+ self.assertEqual(len(props), 2)
+ self.failUnless('title' in props)
+ self.failUnless('description' in props)
+ self.assertEqual(context.title, 'Foo')
+ self.assertEqual(context.description, 'bar')
+
+ def test_put_ini_other_properties(self):
+ from DateTime.DateTime import DateTime
+ INTPROP = 42
+ FLOATPROP = 3.1415926
+ DATESTR = '2005-11-07T12:00:00.000Z'
+ DATESTR2 = '2005-11-09T12:00:00.000Z'
+ context = _makePropertied('string_property')
+ context._properties = ()
+ context._setProperty('int_prop', INTPROP, 'int')
+ context._setProperty('float_prop', FLOATPROP, 'float')
+ context._setProperty('date_prop', DateTime(DATESTR), 'date')
+ adapter = self._getTargetClass()(context)
+ adapter.put_ini('''\
+[DEFAULT]
+int_prop = 13
+\nfloat_prop = 2.818
+\ndate_prop = %s''' % DATESTR2)
+ self.assertEqual(len(context.propertyIds()), 3)
+ self.assertEqual(context.int_prop, 13)
+ self.assertEqual(context.float_prop, 2.818)
+ self.assertEqual(context.date_prop, DateTime(DATESTR2))
+
+class FolderishExporterImporterTests(#PlacelessSetup,
+ unittest.TestCase,
+ ):
+
+ def _getExporter(self):
+ from Products.CMFSetup.content import exportSiteStructure
+ return exportSiteStructure
+
+ def _getImporter(self):
+ from Products.CMFSetup.content import importSiteStructure
+ return importSiteStructure
+
+ def _makeSetupTool(self):
+ from Products.CMFSetup.tool import SetupTool
+ return SetupTool()
+
+ def _setUpAdapters(self):
+ from OFS.Folder import Folder
+ from zope.app.tests import ztapi
+
+ from zope.interface.declarations import classImplements
+ classImplements(Folder, IObjectManager)
+
+ #from OFS.Image import File
+
+ from Products.CMFSetup.interfaces import IFilesystemExporter
+ from Products.CMFSetup.interfaces import IFilesystemImporter
+ from Products.CMFSetup.interfaces import ICSVAware
+ from Products.CMFSetup.interfaces import IINIAware
+ from Products.CMFSetup.interfaces import IDAVAware
+
+ from Products.CMFSetup.content import \
+ SimpleINIAware
+ from Products.CMFSetup.content import \
+ FolderishExporterImporter
+ from Products.CMFSetup.content import \
+ CSVAwareFileAdapter
+ from Products.CMFSetup.content import \
+ INIAwareFileAdapter
+ from Products.CMFSetup.content import \
+ DAVAwareFileAdapter
+
+ ztapi.provideAdapter(IObjectManager,
+ IFilesystemExporter,
+ FolderishExporterImporter,
+ )
+
+ ztapi.provideAdapter(IObjectManager,
+ IFilesystemImporter,
+ FolderishExporterImporter,
+ )
+
+ ztapi.provideAdapter(IPropertyManager,
+ IINIAware,
+ SimpleINIAware,
+ )
+
+ ztapi.provideAdapter(ICSVAware,
+ IFilesystemExporter,
+ CSVAwareFileAdapter,
+ )
+
+ ztapi.provideAdapter(ICSVAware,
+ IFilesystemImporter,
+ CSVAwareFileAdapter,
+ )
+
+ ztapi.provideAdapter(IINIAware,
+ IFilesystemExporter,
+ INIAwareFileAdapter,
+ )
+
+ ztapi.provideAdapter(IINIAware,
+ IFilesystemImporter,
+ INIAwareFileAdapter,
+ )
+
+ ztapi.provideAdapter(IDAVAware,
+ IFilesystemExporter,
+ DAVAwareFileAdapter,
+ )
+
+ ztapi.provideAdapter(IDAVAware,
+ IFilesystemImporter,
+ DAVAwareFileAdapter,
+ )
+
+
+ def test_export_empty_site(self):
+ self._setUpAdapters()
+ site = _makeFolder('site')
+ site.title = 'test_export_empty_site'
+ site.description = 'Testing export of an empty site.'
+ context = DummyExportContext(site)
+ exporter = self._getExporter()
+ exporter(context)
+
+ self.assertEqual(len(context._wrote), 2)
+ filename, text, content_type = context._wrote[0]
+ self.assertEqual(filename, 'structure/.objects')
+ self.assertEqual(content_type, 'text/comma-separated-values')
+
+ objects = [x for x in reader(StringIO(text))]
+ self.assertEqual(len(objects), 0)
+
+ filename, text, content_type = context._wrote[1]
+ self.assertEqual(filename, 'structure/.properties')
+ self.assertEqual(content_type, 'text/plain')
+
+ parser = ConfigParser()
+ parser.readfp(StringIO(text))
+
+ defaults = parser.defaults()
+ self.assertEqual(len(defaults), 1)
+ self.assertEqual(defaults['title'], site.title)
+
+ def test_export_empty_site_with_setup_tool(self):
+ self._setUpAdapters()
+ site = _makeFolder('site')
+ site._setObject('setup_tool', self._makeSetupTool())
+ site._updateProperty('title', 'test_export_empty_site_with_setup_tool')
+ site._setProperty('description',
+ 'Testing export of an empty site with setup tool.')
+ context = DummyExportContext(site)
+ exporter = self._getExporter()
+ exporter(context)
+
+ self.assertEqual(len(context._wrote), 2)
+ filename, text, content_type = context._wrote[0]
+ self.assertEqual(filename, 'structure/.objects')
+ self.assertEqual(content_type, 'text/comma-separated-values')
+
+ objects = [x for x in reader(StringIO(text))]
+ self.assertEqual(len(objects), 0)
+
+ filename, text, content_type = context._wrote[1]
+ self.assertEqual(filename, 'structure/.properties')
+ self.assertEqual(content_type, 'text/plain')
+
+ parser = ConfigParser()
+ parser.readfp(StringIO(text))
+
+ defaults = parser.defaults()
+ self.assertEqual(len(defaults), 2)
+ self.assertEqual(defaults['title'], site.title)
+ self.assertEqual(defaults['description'], site.description)
+
+ def test_export_site_with_non_exportable_simple_items(self):
+ from Products.CMFSetup.utils import _getDottedName
+ self._setUpAdapters()
+ ITEM_IDS = ('foo', 'bar', 'baz')
+
+ site = _makeFolder('site')
+ site.title = 'AAA'
+ site._setProperty('description', 'BBB')
+ item = _makeItem('aside')
+ dotted = _getDottedName(item.__class__)
+ for id in ITEM_IDS:
+ site._setObject(id, _makeItem(id))
+
+ context = DummyExportContext(site)
+ exporter = self._getExporter()
+ exporter(context)
+
+ self.assertEqual(len(context._wrote), 2)
+ filename, text, content_type = context._wrote[0]
+ self.assertEqual(filename, 'structure/.objects')
+ self.assertEqual(content_type, 'text/comma-separated-values')
+
+ objects = [x for x in reader(StringIO(text))]
+ self.assertEqual(len(objects), 3)
+ for index in range(len(ITEM_IDS)):
+ self.assertEqual(objects[index][0], ITEM_IDS[index])
+ self.assertEqual(objects[index][1], dotted)
+
+ filename, text, content_type = context._wrote[1]
+ self.assertEqual(filename, 'structure/.properties')
+ self.assertEqual(content_type, 'text/plain')
+ parser = ConfigParser()
+ parser.readfp(StringIO(text))
+
+ defaults = parser.defaults()
+ self.assertEqual(len(defaults), 2)
+ self.assertEqual(defaults['title'], 'AAA')
+ self.assertEqual(defaults['description'], 'BBB')
+
+ def test_export_site_with_exportable_simple_items(self):
+ from Products.CMFSetup.utils import _getDottedName
+ self._setUpAdapters()
+ ITEM_IDS = ('foo', 'bar', 'baz')
+
+ site = _makeFolder('site')
+ site.title = 'AAA'
+ site._setProperty('description', 'BBB')
+ aware = _makeINIAware('aside')
+ dotted = _getDottedName(aware.__class__)
+ for id in ITEM_IDS:
+ site._setObject(id, _makeINIAware(id))
+
+ context = DummyExportContext(site)
+ exporter = self._getExporter()
+ exporter(context)
+
+ self.assertEqual(len(context._wrote), 2 + len(ITEM_IDS))
+ filename, text, content_type = context._wrote[0]
+ self.assertEqual(filename, 'structure/.objects')
+ self.assertEqual(content_type, 'text/comma-separated-values')
+
+ objects = [x for x in reader(StringIO(text))]
+ self.assertEqual(len(objects), 3)
+ for index in range(len(ITEM_IDS)):
+ self.assertEqual(objects[index][0], ITEM_IDS[index])
+ self.assertEqual(objects[index][1], dotted)
+
+ filename, text, content_type = context._wrote[index+2]
+ self.assertEqual(filename, 'structure/%s.ini' % ITEM_IDS[index])
+ object = site._getOb(ITEM_IDS[index])
+ self.assertEqual(text.strip(),
+ object.as_ini().strip())
+ self.assertEqual(content_type, 'text/plain')
+
+ filename, text, content_type = context._wrote[1]
+ self.assertEqual(filename, 'structure/.properties')
+ self.assertEqual(content_type, 'text/plain')
+ parser = ConfigParser()
+ parser.readfp(StringIO(text))
+
+ defaults = parser.defaults()
+ self.assertEqual(len(defaults), 2)
+ self.assertEqual(defaults['title'], 'AAA')
+ self.assertEqual(defaults['description'], 'BBB')
+
+ def test_export_site_with_subfolders(self):
+ from Products.CMFSetup.utils import _getDottedName
+ self._setUpAdapters()
+ FOLDER_IDS = ('foo', 'bar', 'baz')
+
+ site = _makeFolder('site')
+ site.title = 'AAA'
+ site._setProperty('description', 'BBB')
+ aside = _makeFolder('aside')
+ dotted = _getDottedName(aside.__class__)
+ for id in FOLDER_IDS:
+ folder = _makeFolder(id)
+ folder.title = 'Title: %s' % id
+ site._setObject(id, folder)
+
+ context = DummyExportContext(site)
+ exporter = self._getExporter()
+ exporter(context)
+
+ self.assertEqual(len(context._wrote), 2 + (2 *len(FOLDER_IDS)))
+ filename, text, content_type = context._wrote[0]
+ self.assertEqual(filename, 'structure/.objects')
+ self.assertEqual(content_type, 'text/comma-separated-values')
+
+ objects = [x for x in reader(StringIO(text))]
+ self.assertEqual(len(objects), 3)
+
+ for index in range(len(FOLDER_IDS)):
+ id = FOLDER_IDS[index]
+ self.assertEqual(objects[index][0], id)
+ self.assertEqual(objects[index][1], dotted)
+
+ filename, text, content_type = context._wrote[2 + (2 * index)]
+ self.assertEqual(filename, '/'.join(('structure', id, '.objects')))
+ self.assertEqual(content_type, 'text/comma-separated-values')
+ subobjects = [x for x in reader(StringIO(text))]
+ self.assertEqual(len(subobjects), 0)
+
+ filename, text, content_type = context._wrote[2 + (2 * index) + 1]
+ self.assertEqual(filename,
+ '/'.join(('structure', id, '.properties')))
+ self.assertEqual(content_type, 'text/plain')
+ parser = ConfigParser()
+ parser.readfp(StringIO(text))
+
+ defaults = parser.defaults()
+ self.assertEqual(len(defaults), 1)
+ self.assertEqual(defaults['title'], 'Title: %s' % id)
+
+ filename, text, content_type = context._wrote[1]
+ self.assertEqual(filename, 'structure/.properties')
+ self.assertEqual(content_type, 'text/plain')
+
+ parser = ConfigParser()
+ parser.readfp(StringIO(text))
+
+ defaults = parser.defaults()
+ self.assertEqual(len(defaults), 2)
+ self.assertEqual(defaults['title'], 'AAA')
+ self.assertEqual(defaults['description'], 'BBB')
+
+ def test_export_site_with_csvaware(self):
+ from Products.CMFSetup.utils import _getDottedName
+ self._setUpAdapters()
+
+ site = _makeFolder('site')
+ site.title = 'test_export_site_with_csvaware'
+ site._setProperty('description',
+ 'Testing export of an site with CSV-aware content.')
+
+ aware = _makeCSVAware('aware')
+ site._setObject('aware', aware)
+
+ context = DummyExportContext(site)
+ exporter = self._getExporter()
+ exporter(context)
+
+ self.assertEqual(len(context._wrote), 3)
+ filename, text, content_type = context._wrote[0]
+ self.assertEqual(filename, 'structure/.objects')
+ self.assertEqual(content_type, 'text/comma-separated-values')
+
+ objects = [x for x in reader(StringIO(text))]
+ self.assertEqual(len(objects), 1)
+ self.assertEqual(objects[0][0], 'aware')
+ self.assertEqual(objects[0][1], _getDottedName(aware.__class__))
+
+ filename, text, content_type = context._wrote[1]
+ self.assertEqual(filename, 'structure/.properties')
+ self.assertEqual(content_type, 'text/plain')
+
+ parser = ConfigParser()
+ parser.readfp(StringIO(text))
+
+ defaults = parser.defaults()
+ self.assertEqual(len(defaults), 2)
+ self.assertEqual(defaults['title'], site.title)
+ self.assertEqual(defaults['description'], site.description)
+
+ filename, text, content_type = context._wrote[2]
+ self.assertEqual(filename, 'structure/aware.csv')
+ self.assertEqual(content_type, 'text/comma-separated-values')
+ rows = [x for x in reader(StringIO(text))]
+ self.assertEqual(len(rows), 2)
+ self.assertEqual(rows[0][0], 'one')
+ self.assertEqual(rows[0][1], 'two')
+ self.assertEqual(rows[0][2], 'three')
+ self.assertEqual(rows[1][0], 'four')
+ self.assertEqual(rows[1][1], 'five')
+ self.assertEqual(rows[1][2], 'six')
+
+ def test_import_empty_site(self):
+ self._setUpAdapters()
+ site = _makeFolder('site')
+ context = DummyImportContext(site)
+ context._files['structure/.objects'] = ''
+ importer = self._getImporter()
+ self.assertEqual(len(site.objectIds()), 0)
+ importer(context)
+ self.assertEqual(len(site.objectIds()), 0)
+
+ def test_import_empty_site_with_setup_tool(self):
+ self._setUpAdapters()
+ site = _makeFolder('site')
+ site._setObject('setup_tool', self._makeSetupTool())
+ context = DummyImportContext(site)
+ importer = self._getImporter()
+
+ self.assertEqual(len(site.objectIds()), 1)
+ self.assertEqual(site.objectIds()[0], 'setup_tool')
+ importer(context)
+ self.assertEqual(len(site.objectIds()), 1)
+ self.assertEqual(site.objectIds()[0], 'setup_tool')
+
+ def test_import_site_with_subfolders(self):
+ from Products.CMFSetup.utils import _getDottedName
+ self._setUpAdapters()
+ FOLDER_IDS = ('foo', 'bar', 'baz')
+
+ site = _makeFolder('site')
+ dotted = _getDottedName(site.__class__)
+
+ context = DummyImportContext(site)
+
+ for id in FOLDER_IDS:
+ context._files['structure/%s/.objects' % id] = ''
+ context._files['structure/%s/.properties' % id] = (
+ _PROPERTIES_TEMPLATE % id )
+
+ _ROOT_OBJECTS = '\n'.join(['%s,%s' % (id, dotted)
+ for id in FOLDER_IDS])
+
+ context._files['structure/.objects'] = _ROOT_OBJECTS
+ context._files['structure/.properties'] = (
+ _PROPERTIES_TEMPLATE % 'Test Site')
+
+ importer = self._getImporter()
+ importer(context)
+
+ content = site.objectValues()
+ self.assertEqual(len(content), len(FOLDER_IDS))
+
+ def test_import_site_with_subitems(self):
+ from Products.CMFSetup.utils import _getDottedName
+ from faux_objects import KNOWN_INI
+ from faux_objects import TestINIAware
+ dotted = _getDottedName(TestINIAware)
+ self._setUpAdapters()
+ ITEM_IDS = ('foo', 'bar', 'baz')
+
+ site = _makeFolder('site')
+
+ context = DummyImportContext(site)
+ # We want to add 'baz' to 'foo', without losing 'bar'
+ context._files['structure/.objects'] = '\n'.join(
+ ['%s,%s' % (x, dotted) for x in ITEM_IDS])
+ for index in range(len(ITEM_IDS)):
+ id = ITEM_IDS[index]
+ context._files[
+ 'structure/%s.ini' % id] = KNOWN_INI % ('Title: %s' % id,
+ 'xyzzy',
+ )
+ importer = self._getImporter()
+ importer(context)
+
+ after = site.objectIds()
+ self.assertEqual(len(after), len(ITEM_IDS))
+ for found_id, expected_id in zip(after, ITEM_IDS):
+ self.assertEqual(found_id, expected_id)
+
+ def test_import_site_with_subitem_unknown_portal_type(self):
+ from faux_objects import KNOWN_INI
+ self._setUpAdapters()
+ ITEM_IDS = ('foo', 'bar', 'baz')
+
+ site = _makeFolder('site')
+
+ context = DummyImportContext(site)
+ # We want to add 'baz' to 'foo', without losing 'bar'
+ context._files['structure/.objects'] = '\n'.join(
+ ['%s,Unknown Type' % x for x in ITEM_IDS])
+ for index in range(len(ITEM_IDS)):
+ id = ITEM_IDS[index]
+ context._files[
+ 'structure/%s.ini' % id] = KNOWN_INI % ('Title: %s' % id,
+ 'xyzzy',
+ )
+
+ importer = self._getImporter()
+ importer(context)
+
+ after = site.objectIds()
+ self.assertEqual(len(after), 0)
+ #self.assertEqual(len(context._notes), len(ITEM_IDS))
+ #for level, component, message in context._notes:
+ # self.assertEqual(component, 'SFWA')
+ # self.failUnless(message.startswith("Couldn't make"))
+
+ def test_import_site_with_subitems_and_no_preserve(self):
+ self._setUpAdapters()
+ ITEM_IDS = ('foo', 'bar', 'baz')
+
+ site = _makeFolder('site')
+ for id in ITEM_IDS:
+ site._setObject(id, _makeItem(id))
+
+ context = DummyImportContext(site)
+ # We want to add 'baz' to 'foo', without losing 'bar'
+ context._files['structure/.objects'] = ''
+
+ importer = self._getImporter()
+ importer(context)
+
+ self.assertEqual(len(site.objectIds()), 0)
+
+ def test_import_site_with_subitemss_and_preserve(self):
+ self._setUpAdapters()
+ ITEM_IDS = ('foo', 'bar', 'baz')
+
+ site = _makeFolder('site')
+ for id in ITEM_IDS:
+ site._setObject(id, _makeItem(id))
+
+ context = DummyImportContext(site)
+ # We want to add 'baz' to 'foo', without losing 'bar'
+ context._files['structure/.objects'] = ''
+ context._files['structure/.preserve'] = '*'
+
+ importer = self._getImporter()
+ importer(context)
+
+ after = site.objectIds()
+ self.assertEqual(len(after), len(ITEM_IDS))
+ for i in range(len(ITEM_IDS)):
+ self.assertEqual(after[i], ITEM_IDS[i])
+
+ def test_import_site_with_subitemss_and_preserve_partial(self):
+ self._setUpAdapters()
+ ITEM_IDS = ('foo', 'bar', 'baz')
+
+ site = _makeFolder('site')
+ for id in ITEM_IDS:
+ site._setObject(id, _makeItem(id))
+
+ context = DummyImportContext(site)
+ # We want to add 'baz' to 'foo', without losing 'bar'
+ context._files['structure/.objects'] = ''
+ context._files['structure/.preserve'] = 'b*'
+
+ importer = self._getImporter()
+ importer(context)
+
+ after = site.objectIds()
+ self.assertEqual(len(after), 2)
+ self.assertEqual(after[0], 'bar')
+ self.assertEqual(after[1], 'baz')
+
+ def test_import_site_with_subfolders_and_preserve(self):
+ from Products.CMFSetup.utils import _getDottedName
+ self._setUpAdapters()
+
+ site = _makeFolder('site')
+ site._setObject('foo', _makeFolder('foo'))
+ foo = site._getOb('foo')
+ foo._setObject('bar', _makeFolder('bar'))
+ bar = foo._getOb('bar')
+
+ context = DummyImportContext(site)
+ # We want to add 'baz' to 'foo', without losing 'bar'
+ context._files['structure/.objects'
+ ] = 'foo,%s' % _getDottedName(foo.__class__)
+ context._files['structure/.preserve'] = '*'
+ context._files['structure/foo/.objects'
+ ] = 'baz,%s' % _getDottedName(bar.__class__)
+ context._files['structure/foo/.preserve'] = '*'
+ context._files['structure/foo/baz/.objects'] = ''
+
+ importer = self._getImporter()
+ importer(context)
+
+ self.assertEqual(len(site.objectIds()), 1)
+ self.assertEqual(site.objectIds()[0], 'foo')
+
+ self.assertEqual(len(foo.objectIds()), 2, site.foo.objectIds())
+ self.assertEqual(foo.objectIds()[0], 'bar')
+ self.assertEqual(foo.objectIds()[1], 'baz')
+
+
+class Test_globpattern(unittest.TestCase):
+
+ NAMELIST = ('foo', 'bar', 'baz', 'bam', 'qux', 'quxx', 'quxxx')
+
+ def _checkResults(self, globpattern, namelist, expected):
+ from Products.CMFSetup.content import _globtest
+ found = _globtest(globpattern, namelist)
+ self.assertEqual(len(found), len(expected))
+ for found_item, expected_item in zip(found, expected):
+ self.assertEqual(found_item, expected_item)
+
+ def test_star(self):
+ self._checkResults('*', self.NAMELIST, self.NAMELIST)
+
+ def test_simple(self):
+ self._checkResults('b*', self.NAMELIST,
+ [x for x in self.NAMELIST if x.startswith('b')])
+
+ def test_multiple(self):
+ self._checkResults('b*\n*x', self.NAMELIST,
+ [x for x in self.NAMELIST
+ if x.startswith('b') or x.endswith('x')])
+
+
+class CSVAwareFileAdapterTests(unittest.TestCase,
+ ConformsToIFilesystemExporter,
+ ConformsToIFilesystemImporter,
+ ):
+
+ def _getTargetClass(self):
+ from Products.CMFSetup.content import CSVAwareFileAdapter
+ return CSVAwareFileAdapter
+
+ def _makeOne(self, context, *args, **kw):
+ return self._getTargetClass()(context, *args, **kw)
+
+ def test_export_with_known_CSV(self):
+ from faux_objects import KNOWN_CSV
+ sheet = _makeCSVAware('config')
+
+ adapter = self._makeOne(sheet)
+ context = DummyExportContext(None)
+ adapter.export(context, 'subpath/to/sheet')
+
+ self.assertEqual(len(context._wrote), 1)
+ filename, text, content_type = context._wrote[0]
+ self.assertEqual(filename, 'subpath/to/sheet/config.csv')
+ self.assertEqual(content_type, 'text/comma-separated-values')
+
+ self.assertEqual(text.strip(), KNOWN_CSV.strip())
+
+ def test_import_with_known_CSV(self):
+ ORIG_CSV = """\
+one,two,three
+four,five,six
+"""
+ NEW_CSV = """\
+four,five,six
+one,two,three
+"""
+ sheet = _makeCSVAware('config', ORIG_CSV)
+
+ adapter = self._makeOne(sheet)
+ context = DummyImportContext(None)
+ context._files['subpath/to/sheet/config.csv'] = NEW_CSV
+ adapter.import_(context, 'subpath/to/sheet')
+
+ self.assertEqual(sheet._was_put.getvalue().strip(), NEW_CSV.strip())
+
+
+_PROPERTIES_TEMPLATE = """
+[DEFAULT]
+Title = %s
+Description = This is a test
+"""
+
+class INIAwareFileAdapterTests(unittest.TestCase,
+ ConformsToIFilesystemExporter,
+ ConformsToIFilesystemImporter,
+ ):
+
+ def _getTargetClass(self):
+ from Products.CMFSetup.content import INIAwareFileAdapter
+ return INIAwareFileAdapter
+
+ def _makeOne(self, context, *args, **kw):
+ return self._getTargetClass()(context, *args, **kw)
+
+ def test_export_ini_file(self):
+ ini_file = _makeINIAware('ini_file.html')
+ adapter = self._makeOne(ini_file)
+ context = DummyExportContext(None)
+ adapter.export(context, 'subpath/to')
+
+ self.assertEqual(len(context._wrote), 1)
+ filename, text, content_type = context._wrote[0]
+ self.assertEqual(filename, 'subpath/to/ini_file.html.ini')
+ self.assertEqual(content_type, 'text/plain')
+
+ self.assertEqual(text.strip(), ini_file.as_ini().strip())
+
+ def test_import_ini_file(self):
+ from faux_objects import KNOWN_INI
+ ini_file = _makeINIAware('ini_file.html')
+ adapter = self._makeOne(ini_file)
+ context = DummyImportContext(None)
+ context._files['subpath/to/ini_file.html.ini'] = (
+ KNOWN_INI % ('Title: ini_file', 'abc'))
+
+ adapter.import_(context, 'subpath/to')
+ text = ini_file._was_put
+ parser = ConfigParser()
+ parser.readfp(StringIO(text))
+ self.assertEqual(parser.get('DEFAULT', 'title'), 'Title: ini_file')
+ self.assertEqual(parser.get('DEFAULT', 'description'), 'abc')
+
+
+class DAVAwareFileAdapterTests(unittest.TestCase,
+ ConformsToIFilesystemExporter,
+ ConformsToIFilesystemImporter,
+ ):
+
+ def _getTargetClass(self):
+ from Products.CMFSetup.content import DAVAwareFileAdapter
+ return DAVAwareFileAdapter
+
+ def _makeOne(self, context, *args, **kw):
+ return self._getTargetClass()(context, *args, **kw)
+
+ def test_export_dav_file(self):
+ dav_file = _makeDAVAware('dav_file.html')
+ adapter = self._makeOne(dav_file)
+ context = DummyExportContext(None)
+ adapter.export(context, 'subpath/to')
+
+ self.assertEqual(len(context._wrote), 1)
+ filename, text, content_type = context._wrote[0]
+ self.assertEqual(filename, 'subpath/to/dav_file.html')
+ self.assertEqual(content_type, 'text/plain')
+ self.assertEqual(text.strip(), dav_file.manage_FTPget().strip())
+
+ def test_import_dav_file(self):
+ from faux_objects import KNOWN_DAV
+ VALUES = ('Title: dav_file', 'Description: abc', 'body goes here')
+ dav_file = _makeDAVAware('dav_file.html')
+ adapter = self._makeOne(dav_file)
+ context = DummyImportContext(None)
+ context._files['subpath/to/dav_file.html'] = KNOWN_DAV % VALUES
+
+ adapter.import_(context, 'subpath/to')
+ text = dav_file._was_put == KNOWN_DAV % VALUES
+
+
+def _makePropertied(id):
+ from faux_objects import TestSimpleItemWithProperties
+
+ propertied = TestSimpleItemWithProperties()
+ propertied._setId(id)
+
+ return propertied
+
+def _makeCSVAware(id, csv=None):
+ from faux_objects import TestCSVAware
+
+ aware = TestCSVAware()
+ aware._setId(id)
+ if csv is not None:
+ aware._csv = csv
+
+ return aware
+
+
+def _makeINIAware(id):
+ from faux_objects import TestINIAware
+
+ aware = TestINIAware()
+ aware._setId(id)
+
+ return aware
+
+
+def _makeDAVAware(id):
+ from faux_objects import TestDAVAware
+
+ aware = TestDAVAware()
+ aware._setId(id)
+
+ return aware
+
+
+def _makeItem(id):
+ from faux_objects import TestSimpleItem
+
+ aware = TestSimpleItem()
+ aware._setId(id)
+
+ return aware
+
+
+def _makeFolder(id):
+ from OFS.Folder import Folder
+ from zope.interface import directlyProvides
+ from zope.interface import providedBy
+
+ folder = Folder(id)
+ directlyProvides(folder, providedBy(folder)
+ + IObjectManager + IPropertyManager)
+
+ return folder
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(SimpleINIAwareTests))
+ suite.addTest(unittest.makeSuite(FolderishExporterImporterTests))
+ suite.addTest(unittest.makeSuite(Test_globpattern))
+ suite.addTest(unittest.makeSuite(CSVAwareFileAdapterTests))
+ suite.addTest(unittest.makeSuite(INIAwareFileAdapterTests))
+ suite.addTest(unittest.makeSuite(DAVAwareFileAdapterTests))
+ return suite
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
More information about the CMF-checkins
mailing list