[CMF-checkins] SVN: CMF/trunk/ Merge tseaver-refactor_content_export_import branch to trunk.

Tres Seaver tseaver at palladion.com
Tue Nov 15 13:53:20 EST 2005


Log message for revision 40140:
  Merge tseaver-refactor_content_export_import branch to trunk.

Changed:
  U   CMF/trunk/CHANGES.txt
  U   CMF/trunk/CMFCore/exportimport/configure.zcml
  U   CMF/trunk/CMFCore/exportimport/content.py
  U   CMF/trunk/CMFCore/exportimport/tests/test_content.py
  U   CMF/trunk/CMFCore/interfaces/__init__.py
  D   CMF/trunk/CMFCore/interfaces/_exportimport.py
  U   CMF/trunk/CMFDefault/Document.py
  U   CMF/trunk/CMFDefault/File.py
  U   CMF/trunk/CMFDefault/Image.py
  U   CMF/trunk/CMFDefault/Link.py
  U   CMF/trunk/CMFDefault/tests/common.py
  U   CMF/trunk/GenericSetup/configure.zcml
  A   CMF/trunk/GenericSetup/content.py
  U   CMF/trunk/GenericSetup/interfaces.py
  U   CMF/trunk/GenericSetup/tests/conformance.py
  A   CMF/trunk/GenericSetup/tests/faux_objects.py
  A   CMF/trunk/GenericSetup/tests/test_content.py

-=-
Modified: CMF/trunk/CHANGES.txt
===================================================================
--- CMF/trunk/CHANGES.txt	2005-11-15 17:48:34 UTC (rev 40139)
+++ CMF/trunk/CHANGES.txt	2005-11-15 18:53:19 UTC (rev 40140)
@@ -137,6 +137,9 @@
 
   Others
 
+    - CMFCore and GenericSetup: Moved mechanisms for content export / import
+      to GenericSetup/content.py, and made more generic.
+
     - CMFDefault: Removed PortalGenerator and manage_addCMFSite.
 
     - Portal Types: Removed factory_type_information data.

Modified: CMF/trunk/CMFCore/exportimport/configure.zcml
===================================================================
--- CMF/trunk/CMFCore/exportimport/configure.zcml	2005-11-15 17:48:34 UTC (rev 40139)
+++ CMF/trunk/CMFCore/exportimport/configure.zcml	2005-11-15 18:53:19 UTC (rev 40140)
@@ -66,53 +66,17 @@
 
   <adapter
       factory=".content.StructureFolderWalkingAdapter"
-      provides="Products.CMFCore.interfaces.IFilesystemExporter"
+      provides="Products.GenericSetup.interfaces.IFilesystemExporter"
       for="Products.CMFCore.interfaces.IFolderish"
       />
 
   <adapter
       factory=".content.StructureFolderWalkingAdapter"
-      provides="Products.CMFCore.interfaces.IFilesystemImporter"
+      provides="Products.GenericSetup.interfaces.IFilesystemImporter"
       for="Products.CMFCore.interfaces.IFolderish"
       />
 
   <adapter
-      factory=".content.CSVAwareFileAdapter"
-      provides="Products.CMFCore.interfaces.IFilesystemExporter"
-      for="Products.CMFCore.interfaces.ICSVAware"
-      />
-
-  <adapter
-      factory=".content.CSVAwareFileAdapter"
-      provides="Products.CMFCore.interfaces.IFilesystemImporter"
-      for="Products.CMFCore.interfaces.ICSVAware"
-      />
-
-  <adapter
-      factory=".content.INIAwareFileAdapter"
-      provides="Products.CMFCore.interfaces.IFilesystemExporter"
-      for="Products.CMFCore.interfaces.IINIAware"
-      />
-
-  <adapter
-      factory=".content.INIAwareFileAdapter"
-      provides="Products.CMFCore.interfaces.IFilesystemImporter"
-      for="Products.CMFCore.interfaces.IINIAware"
-      />
-
-  <adapter
-      factory=".content.DAVAwareFileAdapter"
-      provides="Products.CMFCore.interfaces.IFilesystemExporter"
-      for="Products.CMFCore.interfaces.IDAVAware"
-      />
-
-  <adapter
-      factory=".content.DAVAwareFileAdapter"
-      provides="Products.CMFCore.interfaces.IFilesystemImporter"
-      for="Products.CMFCore.interfaces.IDAVAware"
-      />
-
-  <adapter
       factory=".contenttyperegistry.ContentTypeRegistryNodeAdapter"
       provides="Products.GenericSetup.interfaces.INodeExporter"
       for="Products.CMFCore.interfaces.IContentTypeRegistry"

Modified: CMF/trunk/CMFCore/exportimport/content.py
===================================================================
--- CMF/trunk/CMFCore/exportimport/content.py	2005-11-15 17:48:34 UTC (rev 40139)
+++ CMF/trunk/CMFCore/exportimport/content.py	2005-11-15 18:53:19 UTC (rev 40140)
@@ -25,19 +25,19 @@
 from zope.interface import implements
 from zope.interface import directlyProvides
 
-from Products.CMFCore.interfaces import IFilesystemExporter
-from Products.CMFCore.interfaces import IFilesystemImporter
-from Products.CMFCore.interfaces import ISiteRoot
+from Products.GenericSetup.interfaces import IFilesystemExporter
+from Products.GenericSetup.interfaces import IFilesystemImporter
+from Products.GenericSetup.content import _globtest
 from Products.CMFCore.utils import getToolByName
 
 #
 #   setup_tool handlers
 #
 def exportSiteStructure(context):
-    IFilesystemExporter(context.getSite()).export(context, 'structure')
+    IFilesystemExporter(context.getSite()).export(context, 'structure', True)
 
 def importSiteStructure(context):
-    IFilesystemImporter(context.getSite()).import_(context, 'structure')
+    IFilesystemImporter(context.getSite()).import_(context, 'structure', True)
 
 
 #
@@ -65,7 +65,7 @@
     def __init__(self, context):
         self.context = context
 
-    def export(self, export_context, subdir):
+    def export(self, export_context, subdir, root=False):
         """ See IFilesystemExporter.
         """
         # Enumerate exportable children
@@ -79,7 +79,7 @@
         for object_id, object, ignored in exportable:
             csv_writer.writerow((object_id, object.getPortalTypeName()))
 
-        if not ISiteRoot.providedBy(self.context):
+        if not root:
             subdir = '%s/%s' % (subdir, self.context.getId())
 
         export_context.writeDataFile('.objects',
@@ -108,11 +108,11 @@
             if adapter is not None:
                 adapter.export(export_context, subdir)
 
-    def import_(self, import_context, subdir):
+    def import_(self, import_context, subdir, root=False):
         """ See IFilesystemImporter.
         """
         context = self.context
-        if not ISiteRoot.providedBy(context):
+        if not root:
             subdir = '%s/%s' % (subdir, context.getId())
 
         preserve = import_context.readDataFile('.preserve', subdir)
@@ -183,142 +183,3 @@
 
         return content
 
-
-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):
-        """ 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):
-        """ See IFilesystemImporter.
-        """
-        cid = self.context.getId()
-        data = import_context.readDataFile('%s.csv' % cid, subdir)
-        if data is None:
-            import_context.note('CSAFA',
-                                '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 CSV.
-    """
-    implements(IFilesystemExporter, IFilesystemImporter)
-
-    def __init__(self, context):
-        self.context = context
-
-    def export(self, export_context, subdir):
-        """ 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):
-        """ See IFilesystemImporter.
-        """
-        cid = self.context.getId()
-        data = import_context.readDataFile('%s.ini' % cid, subdir)
-        if data is None:
-            import_context.note('SGAIFA',
-                                'no .ini file for %s/%s' % (subdir, cid))
-        else:
-            self.context.put_ini(data)
-
-
-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 export(self, export_context, subdir):
-        """ See IFilesystemExporter.
-        """
-        export_context.writeDataFile('%s' % self.context.getId(),
-                                     self.context.manage_FTPget(),
-                                     'text/plain',
-                                     subdir,
-                                    )
-
-    def listExportableItems(self):
-        """ See IFilesystemExporter.
-        """
-        return ()
-
-    def import_(self, import_context, subdir):
-        """ See IFilesystemImporter.
-        """
-        cid = self.context.getId()
-        data = import_context.readDataFile('%s' % cid, subdir)
-        if data is None:
-            import_context.note('SGAIFA',
-                                '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/trunk/CMFCore/exportimport/tests/test_content.py
===================================================================
--- CMF/trunk/CMFCore/exportimport/tests/test_content.py	2005-11-15 17:48:34 UTC (rev 40139)
+++ CMF/trunk/CMFCore/exportimport/tests/test_content.py	2005-11-15 18:53:19 UTC (rev 40140)
@@ -50,17 +50,17 @@
         from zope.app.tests import ztapi
         #from OFS.Image import File
 
-        from Products.CMFCore.interfaces import IFilesystemExporter
-        from Products.CMFCore.interfaces import IFilesystemImporter
+        from Products.GenericSetup.interfaces import IFilesystemExporter
+        from Products.GenericSetup.interfaces import IFilesystemImporter
+        from Products.GenericSetup.interfaces import ICSVAware
+        from Products.GenericSetup.interfaces import IINIAware
         from Products.CMFCore.interfaces import IFolderish
-        from Products.CMFCore.interfaces import ICSVAware
-        from Products.CMFCore.interfaces import IINIAware
 
         from Products.CMFCore.exportimport.content import \
              StructureFolderWalkingAdapter
-        from Products.CMFCore.exportimport.content import \
+        from Products.GenericSetup.content import \
              CSVAwareFileAdapter
-        from Products.CMFCore.exportimport.content import \
+        from Products.GenericSetup.content import \
              INIAwareFileAdapter
 
         #from Products.CMFCore.exportimport.content import \
@@ -357,6 +357,8 @@
         self.assertEqual(site.objectIds()[0], 'setup_tool')
 
     def test_import_site_with_subfolders(self):
+        from Products.GenericSetup.tests.test_content \
+            import _PROPERTIES_TEMPLATE
         self._setUpAdapters()
         FOLDER_IDS = ('foo', 'bar', 'baz')
 
@@ -518,177 +520,6 @@
         self.assertEqual(site.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.CMFCore.exportimport.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.CMFCore.exportimport.content import CSVAwareFileAdapter
-        return CSVAwareFileAdapter
-
-    def _makeOne(self, context, *args, **kw):
-        return self._getTargetClass()(context, *args, **kw)
-
-    def _makeCSVAware(self, sheet_id, csv=''):
-        class Foo:
-            def getId(self):
-                return self._id
-            def as_csv(self):
-                return self.csv
-            def put_csv(self, stream):
-                self.new_csv = stream.getvalue()
-
-        foo = Foo()
-        foo._id = sheet_id
-        foo.csv = csv
-        foo.new_csv = None
-
-        return foo
-
-
-    def test_export_with_known_CSV(self):
-        KNOWN_CSV = """\
-one,two,three
-four,five,six
-"""
-        sheet = self._makeCSVAware('config', KNOWN_CSV)
-
-        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 = self._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.new_csv.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.CMFCore.exportimport.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):
-        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.CMFCore.exportimport.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):
-        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
-
-
 TEST_CSV_AWARE = 'Test CSV Aware'
 KNOWN_CSV = """\
 one,two,three
@@ -699,7 +530,7 @@
     from OFS.SimpleItem import SimpleItem
     from zope.interface import implements
     from Products.CMFCore.interfaces import IDynamicType
-    from Products.CMFCore.interfaces import ICSVAware
+    from Products.GenericSetup.interfaces import ICSVAware
 
     class _TestCSVAware(SimpleItem):
         implements(IDynamicType, ICSVAware)
@@ -732,7 +563,7 @@
     from OFS.SimpleItem import SimpleItem
     from zope.interface import implements
     from Products.CMFCore.interfaces import IDynamicType
-    from Products.CMFCore.interfaces import IINIAware
+    from Products.GenericSetup.interfaces import IINIAware
 
     class _TestINIAware(SimpleItem):
         implements(IDynamicType, IINIAware)
@@ -768,7 +599,7 @@
     from OFS.SimpleItem import SimpleItem
     from zope.interface import implements
     from Products.CMFCore.interfaces import IDynamicType
-    from Products.CMFCore.interfaces import IDAVAware
+    from Products.GenericSetup.interfaces import IDAVAware
 
     class _TestDAVAware(SimpleItem):
         implements(IDynamicType, IDAVAware)
@@ -818,10 +649,7 @@
 TEST_FOLDER = 'Test Folder'
 
 def _makeFolder(id, site_folder=False):
-    from zope.interface import directlyProvides
-    from zope.interface import providedBy
     from Products.CMFCore.PortalFolder import PortalFolder
-    from Products.CMFCore.interfaces import ISiteRoot
     from Products.CMFCore.TypesTool import TypesTool
     from Products.CMFCore.tests.base.dummy import DummyType
 
@@ -848,7 +676,6 @@
     folder = PortalFolder(id)
     folder.portal_type = TEST_FOLDER
     if site_folder:
-        directlyProvides(folder, ISiteRoot + providedBy(folder))
         tool = folder.portal_types = TypesTool()
         tool._setObject(TEST_CSV_AWARE, _TypeInfo(TEST_CSV_AWARE))
         tool._setObject(TEST_INI_AWARE, _TypeInfo(TEST_INI_AWARE))
@@ -861,10 +688,6 @@
 def test_suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(SiteStructureExporterTests))
-    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__':

Modified: CMF/trunk/CMFCore/interfaces/__init__.py
===================================================================
--- CMF/trunk/CMFCore/interfaces/__init__.py	2005-11-15 17:48:34 UTC (rev 40139)
+++ CMF/trunk/CMFCore/interfaces/__init__.py	2005-11-15 18:53:19 UTC (rev 40140)
@@ -17,7 +17,6 @@
 
 from _content import *
 from _tools import *
-from _exportimport import *
 
 import CachingPolicyManager
 import Contentish

Deleted: CMF/trunk/CMFCore/interfaces/_exportimport.py
===================================================================
--- CMF/trunk/CMFCore/interfaces/_exportimport.py	2005-11-15 17:48:34 UTC (rev 40139)
+++ CMF/trunk/CMFCore/interfaces/_exportimport.py	2005-11-15 18:53:19 UTC (rev 40140)
@@ -1,96 +0,0 @@
-""" Interfaces for content import / export, based on GenericSetup
-
-$Id$
-"""
-from zope.interface import Interface
-from zope.interface import Attribute
-
-class IFilesystemExporter(Interface):
-    """ Plugin interface for site structure export.
-    """
-    def export(export_context, subdir):
-        """ 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.
-        """
-
-    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):
-        """ 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.
-        """
-
-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.
-        """

Modified: CMF/trunk/CMFDefault/Document.py
===================================================================
--- CMF/trunk/CMFDefault/Document.py	2005-11-15 17:48:34 UTC (rev 40139)
+++ CMF/trunk/CMFDefault/Document.py	2005-11-15 18:53:19 UTC (rev 40140)
@@ -25,10 +25,10 @@
 from StructuredText.StructuredText import HTML
 from zope.interface import implements
 
-from Products.CMFCore.interfaces import IDAVAware
 from Products.CMFCore.PortalContent import PortalContent
 from Products.CMFCore.utils import contributorsplitter
 from Products.CMFCore.utils import keywordsplitter
+from Products.GenericSetup.interfaces import IDAVAware
 
 from DublinCore import DefaultDublinCoreImpl
 from exceptions import EditingConflict

Modified: CMF/trunk/CMFDefault/File.py
===================================================================
--- CMF/trunk/CMFDefault/File.py	2005-11-15 17:48:34 UTC (rev 40139)
+++ CMF/trunk/CMFDefault/File.py	2005-11-15 18:53:19 UTC (rev 40140)
@@ -22,8 +22,8 @@
 from AccessControl import ClassSecurityInfo
 from zope.interface import implements
 
-from Products.CMFCore.interfaces import IDAVAware
 from Products.CMFCore.PortalContent import PortalContent
+from Products.GenericSetup.interfaces import IDAVAware
 
 from DublinCore import DefaultDublinCoreImpl
 from permissions import ModifyPortalContent

Modified: CMF/trunk/CMFDefault/Image.py
===================================================================
--- CMF/trunk/CMFDefault/Image.py	2005-11-15 17:48:34 UTC (rev 40139)
+++ CMF/trunk/CMFDefault/Image.py	2005-11-15 18:53:19 UTC (rev 40140)
@@ -21,8 +21,8 @@
 from Globals import InitializeClass
 from zope.interface import implements
 
-from Products.CMFCore.interfaces import IDAVAware
 from Products.CMFCore.PortalContent import PortalContent
+from Products.GenericSetup.interfaces import IDAVAware
 
 from DublinCore import DefaultDublinCoreImpl
 from permissions import ModifyPortalContent

Modified: CMF/trunk/CMFDefault/Link.py
===================================================================
--- CMF/trunk/CMFDefault/Link.py	2005-11-15 17:48:34 UTC (rev 40139)
+++ CMF/trunk/CMFDefault/Link.py	2005-11-15 18:53:19 UTC (rev 40140)
@@ -23,10 +23,10 @@
 from Globals import InitializeClass
 from zope.interface import implements
 
-from Products.CMFCore.interfaces import IDAVAware
 from Products.CMFCore.PortalContent import PortalContent
 from Products.CMFCore.utils import contributorsplitter
 from Products.CMFCore.utils import keywordsplitter
+from Products.GenericSetup.interfaces import IDAVAware
 
 from DublinCore import DefaultDublinCoreImpl
 from exceptions import ResourceLockedError

Modified: CMF/trunk/CMFDefault/tests/common.py
===================================================================
--- CMF/trunk/CMFDefault/tests/common.py	2005-11-15 17:48:34 UTC (rev 40139)
+++ CMF/trunk/CMFDefault/tests/common.py	2005-11-15 18:53:19 UTC (rev 40140)
@@ -40,10 +40,10 @@
         from zope.interface.verify import verifyClass
         from Products.CMFCore.interfaces import ICatalogableDublinCore
         from Products.CMFCore.interfaces import IContentish
-        from Products.CMFCore.interfaces import IDAVAware
         from Products.CMFCore.interfaces import IDublinCore
         from Products.CMFCore.interfaces import IDynamicType
         from Products.CMFCore.interfaces import IMutableDublinCore
+        from Products.GenericSetup.interfaces import IDAVAware
 
         verifyClass(ICatalogableDublinCore, self._getTargetClass())
         verifyClass(IContentish, self._getTargetClass())

Modified: CMF/trunk/GenericSetup/configure.zcml
===================================================================
--- CMF/trunk/GenericSetup/configure.zcml	2005-11-15 17:48:34 UTC (rev 40139)
+++ CMF/trunk/GenericSetup/configure.zcml	2005-11-15 18:53:19 UTC (rev 40140)
@@ -10,4 +10,40 @@
 
   <include package=".ZCTextIndex"/>
 
+  <adapter
+      factory=".content.CSVAwareFileAdapter"
+      provides="Products.GenericSetup.interfaces.IFilesystemExporter"
+      for="Products.GenericSetup.interfaces.ICSVAware"
+      />
+
+  <adapter
+      factory=".content.CSVAwareFileAdapter"
+      provides="Products.GenericSetup.interfaces.IFilesystemImporter"
+      for="Products.GenericSetup.interfaces.ICSVAware"
+      />
+
+  <adapter
+      factory=".content.INIAwareFileAdapter"
+      provides="Products.GenericSetup.interfaces.IFilesystemExporter"
+      for="Products.GenericSetup.interfaces.IINIAware"
+      />
+
+  <adapter
+      factory=".content.INIAwareFileAdapter"
+      provides="Products.GenericSetup.interfaces.IFilesystemImporter"
+      for="Products.GenericSetup.interfaces.IINIAware"
+      />
+
+  <adapter
+      factory=".content.DAVAwareFileAdapter"
+      provides="Products.GenericSetup.interfaces.IFilesystemExporter"
+      for="Products.GenericSetup.interfaces.IDAVAware"
+      />
+
+  <adapter
+      factory=".content.DAVAwareFileAdapter"
+      provides="Products.GenericSetup.interfaces.IFilesystemImporter"
+      for="Products.GenericSetup.interfaces.IDAVAware"
+      />
+
 </configure>

Added: CMF/trunk/GenericSetup/content.py
===================================================================
--- CMF/trunk/GenericSetup/content.py	2005-11-15 17:48:34 UTC (rev 40139)
+++ CMF/trunk/GenericSetup/content.py	2005-11-15 18:53:19 UTC (rev 40140)
@@ -0,0 +1,401 @@
+##############################################################################
+#
+# 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$
+"""
+
+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.interface import implements
+from zope.interface import directlyProvides
+from zope.app import zapi
+
+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):
+    IFilesystemExporter(context.getSite()).export(context, 'structure', True)
+
+def importSiteStructure(context):
+    IFilesystemImporter(context.getSite()).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.providedBy(x[1])]
+        exportable = [x + (IFilesystemExporter(x[1], 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 = IContentFactoryName(object, 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 = IINIAware(context, 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 = IINIAware(context, 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)
+        tool_items = [x for x in context.objectItems()
+                        if ISetupTool.providedBy(x[1])]
+
+        prior = context.objectIds()
+
+        if not preserve:
+            preserve = []
+        else:
+            preserve = _globtest(preserve, prior)
+
+        preserve.extend([x[0] for x in tool_items])
+
+        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:
+                    message = "Couldn't make instance: %s/%s" % (subdir,
+                                                                 object_id)
+                    import_context.note('SFWA', message)
+                    continue
+
+            wrapped = context._getOb(object_id)
+
+            IFilesystemImporter(wrapped).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 = zapi.queryAdapter(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 _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:
+            import_context.note('CSAFA',
+                                '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:
+            import_context.note('SGAIFA',
+                                '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 export(self, export_context, subdir, root=False):
+        """ See IFilesystemExporter.
+        """
+        export_context.writeDataFile('%s' % self.context.getId(),
+                                     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('%s' % cid, subdir)
+        if data is None:
+            import_context.note('SGAIFA',
+                                'no .ini file for %s/%s' % (subdir, cid))
+        else:
+            request = FauxDAVRequest(BODY=data, BODYFILE=StringIO(data))
+            response = FauxDAVResponse()
+            self.context.PUT(request, response)


Property changes on: CMF/trunk/GenericSetup/content.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: CMF/trunk/GenericSetup/interfaces.py
===================================================================
--- CMF/trunk/GenericSetup/interfaces.py	2005-11-15 17:48:34 UTC (rev 40139)
+++ CMF/trunk/GenericSetup/interfaces.py	2005-11-15 18:53:19 UTC (rev 40140)
@@ -544,3 +544,119 @@
     def importNode(node, mode=PURGE):
         """Import the object from the DOM node.
         """
+
+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.
+        """

Modified: CMF/trunk/GenericSetup/tests/conformance.py
===================================================================
--- CMF/trunk/GenericSetup/tests/conformance.py	2005-11-15 17:48:34 UTC (rev 40139)
+++ CMF/trunk/GenericSetup/tests/conformance.py	2005-11-15 18:53:19 UTC (rev 40140)
@@ -98,3 +98,66 @@
         from zope.interface.verify import verifyClass
 
         verifyClass( ISetupTool, self._getTargetClass() )
+
+class ConformsToIContentFactory:
+
+    def test_conforms_to_IContentFactory(self):
+
+        from Products.GenericSetup.interfaces import IContentFactory
+        from zope.interface.verify import verifyClass
+
+        verifyClass( IContentFactory, self._getTargetClass() )
+
+class ConformsToIContentFactoryName:
+
+    def test_conforms_to_IContentFactory(self):
+
+        from Products.GenericSetup.interfaces import IContentFactoryName
+        from zope.interface.verify import verifyClass
+
+        verifyClass( IContentFactoryName, self._getTargetClass() )
+
+class ConformsToIFilesystemExporter:
+
+    def test_conforms_to_IFilesystemExporter(self):
+
+        from Products.GenericSetup.interfaces import IFilesystemExporter
+        from zope.interface.verify import verifyClass
+
+        verifyClass( IFilesystemExporter, self._getTargetClass() )
+
+class ConformsToIFilesystemImporter:
+
+    def test_conforms_to_IFilesystemImporter(self):
+
+        from Products.GenericSetup.interfaces import IFilesystemImporter
+        from zope.interface.verify import verifyClass
+
+        verifyClass( IFilesystemImporter, self._getTargetClass() )
+
+class ConformsToIINIAware:
+
+    def test_conforms_to_IINIAware(self):
+
+        from Products.GenericSetup.interfaces import IINIAware
+        from zope.interface.verify import verifyClass
+
+        verifyClass (IINIAware, self._getTargetClass() )
+
+class ConformsToICSVAware:
+
+    def test_conforms_to_ICSVAware(self):
+
+        from Products.GenericSetup.interfaces import ICSVAware
+        from zope.interface.verify import verifyClass
+
+        verifyClass( ICSVAware, self._getTargetClass() )
+
+class ConformsToIDAVAware:
+
+    def test_conforms_to_IDAVAware(self):
+
+        from Products.GenericSetup.interfaces import IDAVAware
+        from zope.interface.verify import verifyClass
+
+        verifyClass( IDAVAware, self._getTargetClass() )

Added: CMF/trunk/GenericSetup/tests/faux_objects.py
===================================================================
--- CMF/trunk/GenericSetup/tests/faux_objects.py	2005-11-15 17:48:34 UTC (rev 40139)
+++ CMF/trunk/GenericSetup/tests/faux_objects.py	2005-11-15 18:53:19 UTC (rev 40140)
@@ -0,0 +1,82 @@
+""" Simple, importable content classes.
+
+$Id$
+"""
+from OFS.Folder import Folder
+from OFS.PropertyManager import PropertyManager
+from OFS.SimpleItem import SimpleItem
+from zope.interface import implements
+
+try:
+    from OFS.interfaces import IObjectManager
+    from OFS.interfaces import ISimpleItem
+    from OFS.interfaces import IPropertyManager
+except ImportError: # BBB
+    from Products.Five.interfaces import IObjectManager
+    from Products.Five.interfaces import ISimpleItem
+    from Products.Five.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.GenericSetup.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.GenericSetup.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.GenericSetup.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()


Property changes on: CMF/trunk/GenericSetup/tests/faux_objects.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: CMF/trunk/GenericSetup/tests/test_content.py
===================================================================
--- CMF/trunk/GenericSetup/tests/test_content.py	2005-11-15 17:48:34 UTC (rev 40139)
+++ CMF/trunk/GenericSetup/tests/test_content.py	2005-11-15 18:53:19 UTC (rev 40140)
@@ -0,0 +1,895 @@
+##############################################################################
+#
+# 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$
+"""
+
+import unittest
+import Testing
+
+from csv import reader
+from ConfigParser import ConfigParser
+from StringIO import StringIO
+
+try:
+    from OFS.interfaces import IObjectManager
+    from OFS.interfaces import ISimpleItem
+    from OFS.interfaces import IPropertyManager
+except ImportError: # BBB
+    from Products.Five.interfaces import IObjectManager
+    from Products.Five.interfaces import ISimpleItem
+    from Products.Five.interfaces import IPropertyManager
+
+from Products.GenericSetup.testing import PlacelessSetup
+from Products.GenericSetup.tests.common import DummyExportContext
+from Products.GenericSetup.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.GenericSetup.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.GenericSetup.content import exportSiteStructure
+        return exportSiteStructure
+
+    def _getImporter(self):
+        from Products.GenericSetup.content import importSiteStructure
+        return importSiteStructure
+
+    def _makeSetupTool(self):
+        from Products.GenericSetup.tool import SetupTool
+        return SetupTool('portal_setup')
+
+    def _setUpAdapters(self):
+        from OFS.Folder import Folder
+        from zope.app.tests import ztapi
+        #from OFS.Image import File
+
+        from Products.GenericSetup.interfaces import IFilesystemExporter
+        from Products.GenericSetup.interfaces import IFilesystemImporter
+        from Products.GenericSetup.interfaces import ICSVAware
+        from Products.GenericSetup.interfaces import IINIAware
+        from Products.GenericSetup.interfaces import IDAVAware
+
+        from Products.GenericSetup.content import \
+             SimpleINIAware
+        from Products.GenericSetup.content import \
+             FolderishExporterImporter
+        from Products.GenericSetup.content import \
+             CSVAwareFileAdapter
+        from Products.GenericSetup.content import \
+             INIAwareFileAdapter
+        from Products.GenericSetup.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.GenericSetup.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.GenericSetup.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.GenericSetup.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.GenericSetup.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.GenericSetup.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.GenericSetup.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 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.GenericSetup.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.GenericSetup.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.GenericSetup.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.GenericSetup.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.GenericSetup.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')


Property changes on: CMF/trunk/GenericSetup/tests/test_content.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native



More information about the CMF-checkins mailing list