[CMF-checkins] SVN: CMF/branches/goldegg-phase-1/ Merge GenericSetup 1.0, stripping out redundant activity in CMFSetup.

Tres Seaver tseaver at palladion.com
Fri Sep 23 17:33:02 EDT 2005


Log message for revision 38573:
  Merge GenericSetup 1.0, stripping out redundant activity in CMFSetup.
  

Changed:
  U   CMF/branches/goldegg-phase-1/CMFSetup/context.py
  U   CMF/branches/goldegg-phase-1/CMFSetup/differ.py
  U   CMF/branches/goldegg-phase-1/CMFSetup/interfaces.py
  U   CMF/branches/goldegg-phase-1/CMFSetup/registry.py
  U   CMF/branches/goldegg-phase-1/CMFSetup/rolemap.py
  D   CMF/branches/goldegg-phase-1/CMFSetup/tests/test_context.py
  D   CMF/branches/goldegg-phase-1/CMFSetup/tests/test_differ.py
  D   CMF/branches/goldegg-phase-1/CMFSetup/tests/test_registry.py
  D   CMF/branches/goldegg-phase-1/CMFSetup/tests/test_rolemap.py
  D   CMF/branches/goldegg-phase-1/CMFSetup/tests/test_tool.py
  U   CMF/branches/goldegg-phase-1/CMFSetup/tool.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/
  A   CMF/branches/goldegg-phase-1/GenericSetup/CHANGES.txt
  A   CMF/branches/goldegg-phase-1/GenericSetup/CREDITS.txt
  A   CMF/branches/goldegg-phase-1/GenericSetup/DEPENDENCIES.txt
  A   CMF/branches/goldegg-phase-1/GenericSetup/PROFILES.txt
  A   CMF/branches/goldegg-phase-1/GenericSetup/README.txt
  A   CMF/branches/goldegg-phase-1/GenericSetup/__init__.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/context.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/differ.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/exceptions.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/factory.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/interfaces.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/permissions.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/properties.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/registry.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/rolemap.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/__init__.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/common.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/conformance.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/default_profile/
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/default_profile/export_steps.xml
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/default_profile/import_steps.xml
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/default_profile/profile.ini
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/default_profile/toolset.xml
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/four/
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/four/placeholder.txt
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/one/
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/one/placeholder.txt
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/simple.png
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/test_context.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/test_differ.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/test_properties.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/test_registry.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/test_rolemap.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/test_tool.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/test_utils.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/three/
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/three/placeholder.txt
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/two/
  A   CMF/branches/goldegg-phase-1/GenericSetup/tests/two/placeholder.txt
  A   CMF/branches/goldegg-phase-1/GenericSetup/tool.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/utils.py
  A   CMF/branches/goldegg-phase-1/GenericSetup/version.txt
  A   CMF/branches/goldegg-phase-1/GenericSetup/www/
  A   CMF/branches/goldegg-phase-1/GenericSetup/www/siteAddForm.zpt
  A   CMF/branches/goldegg-phase-1/GenericSetup/www/sutCompare.zpt
  A   CMF/branches/goldegg-phase-1/GenericSetup/www/sutExportSteps.zpt
  A   CMF/branches/goldegg-phase-1/GenericSetup/www/sutImportSteps.zpt
  A   CMF/branches/goldegg-phase-1/GenericSetup/www/sutProperties.zpt
  A   CMF/branches/goldegg-phase-1/GenericSetup/www/sutSnapshots.zpt
  A   CMF/branches/goldegg-phase-1/GenericSetup/www/tool.png
  A   CMF/branches/goldegg-phase-1/GenericSetup/www/toolAdd.zpt
  A   CMF/branches/goldegg-phase-1/GenericSetup/xml/
  A   CMF/branches/goldegg-phase-1/GenericSetup/xml/esrExport.xml
  A   CMF/branches/goldegg-phase-1/GenericSetup/xml/isrExport.xml
  A   CMF/branches/goldegg-phase-1/GenericSetup/xml/object_nodes.xml
  A   CMF/branches/goldegg-phase-1/GenericSetup/xml/property_nodes.xml
  A   CMF/branches/goldegg-phase-1/GenericSetup/xml/rmeExport.xml
  A   CMF/branches/goldegg-phase-1/GenericSetup/xml/spcExport.xml
  A   CMF/branches/goldegg-phase-1/GenericSetup/xml/tscExport.xml

-=-
Modified: CMF/branches/goldegg-phase-1/CMFSetup/context.py
===================================================================
--- CMF/branches/goldegg-phase-1/CMFSetup/context.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/CMFSetup/context.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -17,446 +17,9 @@
 $Id$
 """
 
-import os
-import time
-from StringIO import StringIO
-from tarfile import TarFile
-from tarfile import TarInfo
-
-from AccessControl import ClassSecurityInfo
-from Acquisition import aq_inner
-from Acquisition import aq_parent
-from Acquisition import aq_self
-from Acquisition import Implicit
-from DateTime.DateTime import DateTime
-from Globals import InitializeClass
-from OFS.DTMLDocument import DTMLDocument
-from OFS.Folder import Folder
-from OFS.Image import File
-from OFS.Image import Image
-from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
-from Products.PythonScripts.PythonScript import PythonScript
-
-from interfaces import IExportContext
-from interfaces import IImportContext
-from permissions import ManagePortal
-
-
-class DirectoryImportContext( Implicit ):
-
-    __implements__ = ( IImportContext, )
-
-    security = ClassSecurityInfo()
-
-    def __init__( self
-                , tool
-                , profile_path
-                , should_purge=False
-                , encoding=None
-                ):
-
-        self._site = aq_parent( aq_inner( tool ) )
-        self._profile_path = profile_path
-        self._should_purge = bool( should_purge )
-        self._encoding = encoding
-
-    security.declareProtected( ManagePortal, 'getSite' )
-    def getSite( self ):
-
-        """ See ISetupContext.
-        """
-        return aq_self(self._site)
-
-    security.declareProtected( ManagePortal, 'getEncoding' )
-    def getEncoding( self ):
-
-        """ See IImportContext.
-        """
-        return self._encoding
-
-    security.declareProtected( ManagePortal, 'readDataFile' )
-    def readDataFile( self, filename, subdir=None ):
-
-        """ See IImportContext.
-        """
-        if subdir is None:
-            full_path = os.path.join( self._profile_path, filename )
-        else:
-            full_path = os.path.join( self._profile_path, subdir, filename )
-
-        if not os.path.exists( full_path ):
-            return None
-
-        file = open( full_path, 'rb' )
-        result = file.read()
-        file.close()
-
-        return result
-
-    security.declareProtected( ManagePortal, 'getLastModified' )
-    def getLastModified( self, path ):
-
-        """ See IImportContext.
-        """
-        full_path = os.path.join( self._profile_path, path )
-
-        if not os.path.exists( full_path ):
-            return None
-
-        return DateTime( os.path.getmtime( full_path ) )
-
-    security.declareProtected( ManagePortal, 'isDirectory' )
-    def isDirectory( self, path ):
-
-        """ See IImportContext.
-        """
-        full_path = os.path.join( self._profile_path, path )
-
-        if not os.path.exists( full_path ):
-            return None
-
-        return os.path.isdir( full_path )
-
-    security.declareProtected( ManagePortal, 'listDirectory' )
-    def listDirectory( self, path, skip=('CVS', '.svn') ):
-
-        """ See IImportContext.
-        """
-        if path is None:
-            path = ''
-
-        full_path = os.path.join( self._profile_path, path )
-
-        if not os.path.exists( full_path ) or not os.path.isdir( full_path ):
-            return None
-
-        names = os.listdir( full_path )
-
-        return [ name for name in names if name not in skip ]
-
-    security.declareProtected( ManagePortal, 'shouldPurge' )
-    def shouldPurge( self ):
-
-        """ See IImportContext.
-        """
-        return self._should_purge
-
-InitializeClass( DirectoryImportContext )
-
-
-class DirectoryExportContext( Implicit ):
-
-    __implements__ = ( IExportContext, )
-
-    security = ClassSecurityInfo()
-
-    def __init__( self, tool, profile_path ):
-
-        self._site = aq_parent( aq_inner( tool ) )
-        self._profile_path = profile_path
-
-    security.declareProtected( ManagePortal, 'getSite' )
-    def getSite( self ):
-
-        """ See ISetupContext.
-        """
-        return aq_self(self._site)
-
-    security.declareProtected( ManagePortal, 'writeDataFile' )
-    def writeDataFile( self, filename, text, content_type, subdir=None ):
-
-        """ See IExportContext.
-        """
-        if subdir is None:
-            prefix = self._profile_path
-        else:
-            prefix = os.path.join( self._profile_path, subdir )
-
-        full_path = os.path.join( prefix, filename )
-
-        if not os.path.exists( prefix ):
-            os.makedirs( prefix )
-
-        mode = content_type.startswith( 'text/' ) and 'w' or 'wb'
-
-        file = open( full_path, mode )
-        file.write( text )
-        file.close()
-
-InitializeClass( DirectoryExportContext )
-
-
-class TarballExportContext( Implicit ):
-
-    __implements__ = ( IExportContext, )
-
-    security = ClassSecurityInfo()
-
-    def __init__( self, tool ):
-
-        self._site = aq_parent( aq_inner( tool ) )
-        timestamp = time.gmtime()
-        archive_name = ( 'portal_setup-%4d%02d%02d%02d%02d%02d.tar.gz'
-                       % timestamp[:6] )
-
-        self._archive_stream = StringIO()
-        self._archive_filename = archive_name
-        self._archive = TarFile.open( archive_name, 'w:gz'
-                                    , self._archive_stream )
-
-    security.declareProtected( ManagePortal, 'getSite' )
-    def getSite( self ):
-
-        """ See ISetupContext.
-        """
-        return aq_self(self._site)
-
-    security.declareProtected( ManagePortal, 'writeDataFile' )
-    def writeDataFile( self, filename, text, content_type, subdir=None ):
-
-        """ See IExportContext.
-        """
-        if subdir is not None:
-            filename = os.path.join( subdir, filename )
-
-        stream = StringIO( text )
-        info = TarInfo( filename )
-        info.size = len( text )
-        info.mtime = time.time()
-        self._archive.addfile( info, stream )
-
-    security.declareProtected( ManagePortal, 'getArchive' )
-    def getArchive( self ):
-
-        """ Close the archive, and return it as a big string.
-        """
-        self._archive.close()
-        return self._archive_stream.getvalue()
-
-    security.declareProtected( ManagePortal, 'getArchiveFilename' )
-    def getArchiveFilename( self ):
-
-        """ Close the archive, and return it as a big string.
-        """
-        return self._archive_filename
-
-InitializeClass( TarballExportContext )
-
-
-class SnapshotExportContext( Implicit ):
-
-    __implements__ = ( IExportContext, )
-
-    security = ClassSecurityInfo()
-
-    def __init__( self, tool, snapshot_id ):
-
-        self._tool = tool = aq_inner( tool )
-        self._site = aq_parent( tool )
-        self._snapshot_id = snapshot_id
-
-    security.declareProtected( ManagePortal, 'getSite' )
-    def getSite( self ):
-
-        """ See ISetupContext.
-        """
-        return aq_self(self._site)
-
-    security.declareProtected( ManagePortal, 'writeDataFile' )
-    def writeDataFile( self, filename, text, content_type, subdir=None ):
-
-        """ See IExportContext.
-        """
-        folder = self._ensureSnapshotsFolder( subdir )
-
-        # TODO: switch on content_type
-        ob = self._createObjectByType( filename, text, content_type )
-        folder._setObject( str( filename ), ob ) # No Unicode IDs!
-
-    security.declareProtected( ManagePortal, 'getSnapshotURL' )
-    def getSnapshotURL( self ):
-
-        """ See IExportContext.
-        """
-        return '%s/%s' % ( self._tool.absolute_url(), self._snapshot_id )
-
-    security.declareProtected( ManagePortal, 'getSnapshotFolder' )
-    def getSnapshotFolder( self ):
-
-        """ See IExportContext.
-        """
-        return self._ensureSnapshotsFolder()
-
-    #
-    #   Helper methods
-    #
-    security.declarePrivate( '_createObjectByType' )
-    def _createObjectByType( self, name, body, content_type ):
-
-        if name.endswith('.py'):
-
-            ob = PythonScript( name )
-            ob.write( body )
-
-        elif name.endswith('.dtml'):
-
-            ob = DTMLDocument( '', __name__=name )
-            ob.munge( body )
-
-        elif content_type in ('text/html', 'text/xml' ):
-
-            ob = ZopePageTemplate( name, str( body )
-                                 , content_type=content_type )
-
-        elif content_type[:6]=='image/':
-
-            ob=Image( name, '', body, content_type=content_type )
-
-        else:
-            ob=File( name, '', body, content_type=content_type )
-
-        return ob
-
-    security.declarePrivate( '_ensureSnapshotsFolder' )
-    def _ensureSnapshotsFolder( self, subdir=None ):
-
-        """ Ensure that the appropriate snapshot folder exists.
-        """
-        path = [ 'snapshots', self._snapshot_id ]
-
-        if subdir is not None:
-            path.extend( subdir.split( '/' ) )
-
-        current = self._tool
-
-        for element in path:
-
-            if element not in current.objectIds():
-                # No Unicode IDs!
-                current._setObject( str( element ), Folder( element ) )
-
-            current = current._getOb( element )
-
-        return current
-
-InitializeClass( SnapshotExportContext )
-
-
-class SnapshotImportContext( Implicit ):
-
-    __implements__ = ( IImportContext, )
-
-    security = ClassSecurityInfo()
-
-    def __init__( self
-                , tool
-                , snapshot_id
-                , should_purge=False
-                , encoding=None
-                ):
-
-        self._tool = tool = aq_inner( tool )
-        self._site = aq_parent( tool )
-        self._snapshot_id = snapshot_id
-        self._encoding = encoding
-        self._should_purge = bool( should_purge )
-
-    security.declareProtected( ManagePortal, 'getSite' )
-    def getSite( self ):
-
-        """ See ISetupContext.
-        """
-        return aq_self(self._site)
-
-    security.declareProtected( ManagePortal, 'getEncoding' )
-    def getEncoding( self ):
-
-        """ Return the encoding used in data files.
-
-        o Return None if the data should not be encoded.
-        """
-        return self._encoding
-
-    security.declareProtected( ManagePortal, 'readDataFile' )
-    def readDataFile( self, filename, subdir=None ):
-
-        """ See IImportContext.
-        """
-        try:
-            snapshot = self._getSnapshotFolder( subdir )
-            object = snapshot._getOb( filename )
-        except ( AttributeError, KeyError ):
-            return None
-
-        try:
-            return object.read()
-        except AttributeError:
-            return object.manage_FTPget()
-
-    security.declareProtected( ManagePortal, 'getLastModified' )
-    def getLastModified( self, path ):
-
-        """ See IImportContext.
-        """
-        try:
-            snapshot = self._getSnapshotFolder()
-            object = snapshot.restrictedTraverse( path )
-        except ( AttributeError, KeyError ):
-            return None
-        else:
-            return object.bobobase_modification_time()
-
-    security.declareProtected( ManagePortal, 'isDirectory' )
-    def isDirectory( self, path ):
-
-        """ See IImportContext.
-        """
-        try:
-            snapshot = self._getSnapshotFolder()
-            object = snapshot.restrictedTraverse( path )
-        except ( AttributeError, KeyError ):
-            return None
-        else:
-            folderish = getattr( object, 'isPrincipiaFolderish', False )
-            return bool( folderish )
-
-    security.declareProtected( ManagePortal, 'listDirectory' )
-    def listDirectory( self, path, skip=() ):
-
-        """ See IImportContext.
-        """
-        try:
-            snapshot = self._getSnapshotFolder()
-            subdir = snapshot.restrictedTraverse( path )
-        except ( AttributeError, KeyError ):
-            return None
-        else:
-            if not getattr( subdir, 'isPrincipiaFolderish', False ):
-                return None
-
-            object_ids = subdir.objectIds()
-            return [ x for x in object_ids if x not in skip ]
-
-    security.declareProtected( ManagePortal, 'shouldPurge' )
-    def shouldPurge( self ):
-
-        """ See IImportContext.
-        """
-        return self._should_purge
-
-    #
-    #   Helper methods
-    #
-    security.declarePrivate( '_getSnapshotFolder' )
-    def _getSnapshotFolder( self, subdir=None ):
-
-        """ Return the appropriate snapshot (sub)folder.
-        """
-        path = [ 'snapshots', self._snapshot_id ]
-
-        if subdir is not None:
-            path.extend( subdir.split( '/' ) )
-
-        return self._tool.restrictedTraverse( path )
-
-InitializeClass( SnapshotImportContext )
+# BBB
+from Products.GenericSetup.context import DirectoryImportContext
+from Products.GenericSetup.context import DirectoryExportContext
+from Products.GenericSetup.context import TarballExportContext
+from Products.GenericSetup.context import SnapshotExportContext
+from Products.GenericSetup.context import SnapshotImportContext

Modified: CMF/branches/goldegg-phase-1/CMFSetup/differ.py
===================================================================
--- CMF/branches/goldegg-phase-1/CMFSetup/differ.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/CMFSetup/differ.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -15,215 +15,6 @@
 $Id$
 """
 
-from difflib import unified_diff
-import re
-
-from Globals import InitializeClass
-from AccessControl import ClassSecurityInfo
-
-BLANKS_REGEX = re.compile( r'^\s*$' )
-
-def unidiff( a
-           , b
-           , filename_a='original'
-           , timestamp_a=None
-           , filename_b='modified'
-           , timestamp_b=None
-           , ignore_blanks=False
-           ):
-    r"""Compare two sequences of lines; generate the resulting delta.
-
-    Each sequence must contain individual single-line strings
-    ending with newlines. Such sequences can be obtained from the
-    `readlines()` method of file-like objects.  The delta
-    generated also consists of newline-terminated strings, ready
-    to be printed as-is via the writeline() method of a file-like
-    object.
-
-    Note that the last line of a file may *not* have a newline;
-    this is reported in the same way that GNU diff reports this.
-    *This method only supports UNIX line ending conventions.*
-
-        filename_a and filename_b are used to generate the header,
-        allowing other tools to determine what 'files' were used
-        to generate this output.
-
-        timestamp_a and timestamp_b, when supplied, are expected
-        to be last-modified timestamps to be inserted in the
-        header, as floating point values since the epoch.
-
-    Example:
-
-    >>> print ''.join(UniDiffer().compare(
-    ...     'one\ntwo\nthree\n'.splitlines(1),
-    ...     'ore\ntree\nemu\n'.splitlines(1))),
-    +++ original
-    --- modified
-    @@ -1,3 +1,3 @@
-    -one
-    +ore
-    -two
-    -three
-    +tree
-    +emu
-    """
-    if isinstance( a, basestring ):
-        a = a.splitlines()
-
-    if isinstance( b, basestring ):
-        b = b.splitlines()
-
-    if ignore_blanks:
-        a = [ x for x in a if not BLANKS_REGEX.match( x ) ]
-        b = [ x for x in b if not BLANKS_REGEX.match( x ) ]
-
-    return unified_diff( a
-                       , b
-                       , filename_a
-                       , filename_b
-                       , timestamp_a
-                       , timestamp_b
-                       , lineterm=""
-                       )
-
-class ConfigDiff:
-
-    security = ClassSecurityInfo()
-
-    def __init__( self
-                , lhs
-                , rhs
-                , missing_as_empty=False
-                , ignore_blanks=False
-                , skip=('CVS','.svn')
-                ):
-        self._lhs = lhs
-        self._rhs = rhs
-        self._missing_as_empty = missing_as_empty
-        self._ignore_blanks=ignore_blanks
-        self._skip = skip
-
-    security.declarePrivate( 'compareDirectories' )
-    def compareDirectories( self, subdir=None ):
-
-        lhs_files = self._lhs.listDirectory( subdir, self._skip )
-        if lhs_files is None:
-            lhs_files = []
-
-        rhs_files = self._rhs.listDirectory( subdir, self._skip )
-        if rhs_files is None:
-            rhs_files = []
-
-        added = [ f for f in rhs_files if f not in lhs_files ]
-        removed = [ f for f in lhs_files if f not in rhs_files ]
-        all_files = lhs_files + added
-        all_files.sort()
-
-        result = []
-
-        for filename in all_files:
-
-            if subdir is None:
-                pathname = filename
-            else:
-                pathname = '%s/%s' % ( subdir, filename )
-
-            if filename not in added:
-                isDirectory = self._lhs.isDirectory( pathname )
-            else:
-                isDirectory = self._rhs.isDirectory( pathname )
-
-            if not self._missing_as_empty and filename in removed:
-
-                if isDirectory:
-                    result.append( '** Directory %s removed\n' % pathname )
-                    result.extend( self.compareDirectories( pathname ) )
-                else:
-                    result.append( '** File %s removed\n' % pathname )
-
-            elif not self._missing_as_empty and filename in added:
-
-                if isDirectory:
-                    result.append( '** Directory %s added\n' % pathname )
-                    result.extend( self.compareDirectories( pathname ) )
-                else:
-                    result.append( '** File %s added\n' % pathname )
-
-            elif isDirectory:
-
-                result.extend( self.compareDirectories( pathname ) )
-
-                if ( filename not in added + removed and
-                    not self._rhs.isDirectory( pathname ) ):
-
-                    result.append( '** Directory %s replaced with a file of '
-                                   'the same name\n' % pathname )
-
-                    if self._missing_as_empty:
-                        result.extend( self.compareFiles( filename, subdir ) )
-            else:
-                if ( filename not in added + removed and
-                     self._rhs.isDirectory( pathname ) ):
-
-                    result.append( '** File %s replaced with a directory of '
-                                   'the same name\n' % pathname )
-
-                    if self._missing_as_empty:
-                        result.extend( self.compareFiles( filename, subdir ) )
-
-                    result.extend( self.compareDirectories( pathname ) )
-                else:
-                    result.extend( self.compareFiles( filename, subdir ) )
-
-        return result
-
-    security.declarePrivate( 'compareFiles' )
-    def compareFiles( self, filename, subdir=None ):
-
-        if subdir is None:
-            path = filename
-        else:
-            path = '%s/%s' % ( subdir, filename )
-
-        lhs_file = self._lhs.readDataFile( filename, subdir )
-        lhs_time = self._lhs.getLastModified( path )
-
-        if lhs_file is None:
-            assert self._missing_as_empty
-            lhs_file = ''
-            lhs_time = 0
-
-        rhs_file = self._rhs.readDataFile( filename, subdir )
-        rhs_time = self._rhs.getLastModified( path )
-
-        if rhs_file is None:
-            assert self._missing_as_empty
-            rhs_file = ''
-            rhs_time = 0
-
-        if lhs_file == rhs_file:
-            diff_lines = []
-        else:
-            diff_lines = unidiff( lhs_file
-                                , rhs_file
-                                , filename_a=path
-                                , timestamp_a=lhs_time
-                                , filename_b=path
-                                , timestamp_b=rhs_time
-                                , ignore_blanks=self._ignore_blanks
-                                )
-            diff_lines = list( diff_lines ) # generator
-
-        if len( diff_lines ) == 0: # No *real* difference found
-            return []
-
-        diff_lines.insert( 0, 'Index: %s' % path )
-        diff_lines.insert( 1, '=' * 67 )
-
-        return diff_lines
-
-    security.declarePrivate( 'compare' )
-    def compare( self ):
-        return '\n'.join( self.compareDirectories() )
-
-InitializeClass( ConfigDiff )
+# BBB
+from Products.GenericSetup.context import unidiff
+from Products.GenericSetup.context import ConfigDiff

Modified: CMF/branches/goldegg-phase-1/CMFSetup/interfaces.py
===================================================================
--- CMF/branches/goldegg-phase-1/CMFSetup/interfaces.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/CMFSetup/interfaces.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -15,494 +15,18 @@
 $Id$
 """
 
-from Interface import Interface
-
-
-BASE, EXTENSION = range(2)
-
-
-class IPseudoInterface( Interface ):
-
-    """ API documentation;  not testable / enforceable.
-    """
-
-class ISetupContext( Interface ):
-
-    """ Context used for export / import plugins.
-    """
-    def getSite():
-
-        """ Return the site object being configured / dumped.
-        """
-
-class IImportContext( ISetupContext ):
-
-    def getEncoding():
-
-        """ Get the encoding used for configuration data within the site.
-
-        o Return None if the data should not be encoded.
-        """
-
-    def readDataFile( filename, subdir=None ):
-
-        """ Search the current configuration for the requested file.
-
-        o 'filename' is the name (without path elements) of the file.
-
-        o 'subdir' is an optional subdirectory;  if not supplied, search
-          only the "root" directory.
-
-        o Return the file contents as a string, or None if the
-          file cannot be found.
-        """
-
-    def getLastModified( path ):
-
-        """ Return the modification timestamp of the item at 'path'.
-
-        o Result will be a DateTime instance.
-
-        o Search profiles in the configuration in order.
-
-        o If the context is filesystem based, return the 'stat' timestamp
-          of the file / directory to which 'path' points.
-
-        o If the context is ZODB-based, return the Zope modification time
-          of the object to which 'path' points.
-
-        o Return None if 'path' does not point to any object.
-        """
-
-    def isDirectory( path ):
-
-        """ Test whether path points to a directory / folder.
-
-        o If the context is filesystem based, check that 'path' points to
-          a subdirectory within the "root" directory.
-
-        o If the context is ZODB-based, check that 'path' points to a
-          "container" under the context's tool.
-
-        o Return None if 'path' does not resolve;  otherwise, return a
-          bool.
-        """
-
-    def listDirectory( path, skip=('CVS', '.svn') ):
-
-        """ List IDs of the contents of a  directory / folder.
-
-        o Omit names in 'skip'.
-
-        o If 'path' does not point to a directory / folder, return None.
-        """
-
-    def shouldPurge():
-
-        """ When installing, should the existing setup be purged?
-        """
-
-class IImportPlugin( IPseudoInterface ):
-
-    """ Signature for callables used to import portions of site configuration.
-    """
-    def __call__( context ):
-
-        """ Perform the setup step.
-
-        o Return a message describing the work done.
-
-        o 'context' must implement IImportContext.
-        """
-
-class IExportContext( ISetupContext ):
-
-    def writeDataFile( filename, text, content_type, subdir=None ):
-
-        """ Write data into the specified location.
-
-        o 'filename' is the unqualified name of the file.
-
-        o 'text' is the content of the file.
-
-        o 'content_type' is the MIMEtype of the file.
-
-        o 'subdir', if passed, is a path to a subdirectory / folder in
-          which to write the file;  if not passed, write the file to the
-          "root" of the target.
-        """
-
-class IExportPlugin( IPseudoInterface ):
-
-    """ Signature for callables used to export portions of site configuration.
-    """
-    def __call__( context ):
-
-        """ Write export data for the site wrapped by context.
-
-        o Return a message describing the work done.
-
-        o 'context' must implement IExportContext.  The plugin will use
-          its 'writeDataFile' method for each file to be exported.
-        """
-
-class IStepRegistry( Interface ):
-
-    """ Base interface for step registries.
-    """
-    def listSteps():
-
-        """ Return a sequence of IDs of registered steps.
-
-        o Order is not significant.
-        """
-
-    def listStepMetadata():
-
-        """ Return a sequence of mappings describing registered steps.
-
-        o Mappings will be ordered alphabetically.
-        """
-
-    def getStepMetadata( key, default=None ):
-
-        """ Return a mapping of metadata for the step identified by 'key'.
-
-        o Return 'default' if no such step is registered.
-
-        o The 'handler' metadata is available via 'getStep'.
-        """
-
-    def generateXML():
-
-        """ Return a round-trippable XML representation of the registry.
-
-        o 'handler' values are serialized using their dotted names.
-        """
-
-    def parseXML( text ):
-
-        """ Parse 'text'.
-        """
-
-class IImportStepRegistry( IStepRegistry ):
-
-    """ API for import step registry.
-    """
-    def sortSteps():
-
-        """ Return a sequence of registered step IDs
-
-        o Sequence is sorted topologically by dependency, with the dependent
-          steps *after* the steps they depend on.
-        """
-
-    def checkComplete():
-
-        """ Return a sequence of ( node, edge ) tuples for unsatisifed deps.
-        """
-
-    def getStep( key, default=None ):
-
-        """ Return the IImportPlugin registered for 'key'.
-
-        o Return 'default' if no such step is registered.
-        """
-
-    def registerStep( id
-                    , version
-                    , handler
-                    , dependencies=()
-                    , title=None
-                    , description=None
-                    ):
-        """ Register a setup step.
-
-        o 'id' is a unique name for this step,
-
-        o 'version' is a string for comparing versions, it is preferred to
-          be a yyyy/mm/dd-ii formatted string (date plus two-digit
-          ordinal).  when comparing two version strings, the version with
-          the lower sort order is considered the older version.
-
-          - Newer versions of a step supplant older ones.
-
-          - Attempting to register an older one after a newer one results
-            in a KeyError.
-
-        o 'handler' should implement IImportPlugin.
-
-        o 'dependencies' is a tuple of step ids which have to run before
-          this step in order to be able to run at all. Registration of
-          steps that have unmet dependencies are deferred until the
-          dependencies have been registered.
-
-        o 'title' is a one-line UI description for this step.
-          If None, the first line of the documentation string of the handler
-          is used, or the id if no docstring can be found.
-
-        o 'description' is a one-line UI description for this step.
-          If None, the remaining line of the documentation string of
-          the handler is used, or default to ''.
-        """
-
-class IExportStepRegistry( IStepRegistry ):
-
-    """ API for export step registry.
-    """
-    def getStep( key, default=None ):
-
-        """ Return the IExportPlugin registered for 'key'.
-
-        o Return 'default' if no such step is registered.
-        """
-
-    def registerStep( id, handler, title=None, description=None ):
-
-        """ Register an export step.
-
-        o 'id' is the unique identifier for this step
-
-        o 'handler' should implement IExportPlugin.
-
-        o 'title' is a one-line UI description for this step.
-          If None, the first line of the documentation string of the step
-          is used, or the id if no docstring can be found.
-
-        o 'description' is a one-line UI description for this step.
-          If None, the remaining line of the documentation string of
-          the step is used, or default to ''.
-        """
-
-class IToolsetRegistry( Interface ):
-
-    """ API for toolset registry.
-    """
-    def listForbiddenTools():
-
-        """ Return a list of IDs of tools which must be removed, if present.
-        """
-
-    def addForbiddenTool(tool_id ):
-
-        """ Add 'tool_id' to the list of forbidden tools.
-
-        o Raise KeyError if 'tool_id' is already in the list.
-
-        o Raise ValueError if 'tool_id' is in the "required" list.
-        """
-
-    def listRequiredTools():
-
-        """ Return a list of IDs of tools which must be present.
-        """
-
-    def getRequiredToolInfo( tool_id ):
-
-        """ Return a mapping describing a partiuclar required tool.
-
-        o Keys include:
-
-          'id' -- the ID of the tool
-
-          'class' -- a dotted path to its class
-
-        o Raise KeyError if 'tool_id' id not a known tool.
-        """
-
-    def listRequiredToolInfo():
-
-        """ Return a list of IDs of tools which must be present.
-        """
-
-    def addRequiredTool( tool_id, dotted_name ):
-
-        """ Add a tool to our "required" list.
-
-        o 'tool_id' is the tool's ID.
-
-        o 'dotted_name' is a dotted (importable) name of the tool's class.
-
-        o Raise KeyError if we have already registered a class for 'tool_id'.
-
-        o Raise ValueError if 'tool_id' is in the "forbidden" list.
-        """
-
-class IProfileRegistry( Interface ):
-
-    """ API for profile registry.
-    """
-    def getProfileInfo( profile_id ):
-
-        """ Return a mapping describing a registered filesystem profile.
-
-        o Keys include:
-
-          'id' -- the ID of the profile
-
-          'title' -- its title
-
-          'description' -- a textual description of the profile
-
-          'path' -- a path to the profile on the filesystem.
-
-          'product' -- the name of the product to which 'path' is
-             relative (None for absolute paths).
-
-          'type' -- either BASE or EXTENSION
-        """
-
-    def listProfiles():
-
-        """ Return a list of IDs for registered profiles.
-        """
-
-    def listProfileInfo():
-
-        """ Return a list of mappings describing registered profiles.
-
-        o See 'getProfileInfo' for a description of the mappings' keys.
-        """
-
-    def registerProfile( name
-                       , title
-                       , description
-                       , path
-                       , product=None
-                       , profile_type=BASE
-                       ):
-        """ Add a new profile to the registry.
-
-        o If an existing profile is already registered for 'product:name',
-          raise KeyError.
-
-        o If 'product' is passed, then 'path' should be interpreted as
-          relative to the corresponding product directory.
-        """
-
-class ISetupTool( Interface ):
-
-    """ API for SetupTool.
-    """
-
-    def getEncoding():
-
-        """ Get the encoding used for configuration data within the site.
-
-        o Return None if the data should not be encoded.
-        """
-
-    def getImportContextID():
-
-        """ Get the ID of the active import context.
-        """
-
-    def setImportContext( context_id ):
-
-        """ Set the ID of the active import context and update the registries.
-        """
-
-    def getImportStepRegistry():
-
-        """ Return the IImportStepRegistry for the tool.
-        """
-
-    def getExportStepRegistry():
-
-        """ Return the IExportStepRegistry for the tool.
-        """
-
-    def getToolsetRegistry():
-
-        """ Return the IToolsetRegistry for the tool.
-        """
-
-    def runImportStep( step_id, run_dependencies=True, purge_old=None ):
-
-        """ Execute a given setup step
-
-        o 'step_id' is the ID of the step to run.
-
-        o If 'purge_old' is True, then run the step after purging any
-          "old" setup first (this is the responsibility of the step,
-          which must check the context we supply).
-
-        o If 'run_dependencies' is True, then run any out-of-date
-          dependency steps first.
-
-        o Return a mapping, with keys:
-
-          'steps' -- a sequence of IDs of the steps run.
-
-          'messages' -- a dictionary holding messages returned from each
-            step
-        """
-
-    def runAllImportSteps( purge_old=None ):
-
-        """ Run all setup steps in dependency order.
-
-        o If 'purge_old' is True, then run each step after purging any
-          "old" setup first (this is the responsibility of the step,
-          which must check the context we supply).
-
-        o Return a mapping, with keys:
-
-          'steps' -- a sequence of IDs of the steps run.
-
-          'messages' -- a dictionary holding messages returned from each
-            step
-        """
-
-    def runExportStep( step_id ):
-
-        """ Generate a tarball containing artifacts from one export step.
-
-        o 'step_id' identifies the export step.
-
-        o Return a mapping, with keys:
-
-          'steps' -- a sequence of IDs of the steps run.
-
-          'messages' -- a dictionary holding messages returned from each
-            step
-
-          'tarball' -- the stringified tar-gz data.
-        """
-
-    def runAllExportSteps():
-
-        """ Generate a tarball containing artifacts from all export steps.
-
-        o Return a mapping, with keys:
-
-          'steps' -- a sequence of IDs of the steps run.
-
-          'messages' -- a dictionary holding messages returned from each
-            step
-
-          'tarball' -- the stringified tar-gz data.
-        """
-
-    def createSnapshot( snapshot_id ):
-
-        """ Create a snapshot folder using all steps.
-
-        o 'snapshot_id' is the ID of the new folder.
-        """
-
-    def compareConfigurations( lhs_context
-                             , rhs_context
-                             , missing_as_empty=False
-                             , ignore_whitespace=False
-                             ):
-        """ Compare two configurations.
-
-        o 'lhs_context' and 'rhs_context' must implement IImportContext.
-
-        o If 'missing_as_empty', then compare files not present as though
-          they were zero-length;  otherwise, omit such files.
-
-        o If 'ignore_whitespace', then suppress diffs due only to whitespace
-          (c.f:  'diff -wbB')
-        """
+# BBB
+from Products.GenericSetup.interfaces import BASE
+from Products.GenericSetup.interfaces import EXTENSION
+from Products.GenericSetup.interfaces import IPseudoInterface
+from Products.GenericSetup.interfaces import ISetupContext
+from Products.GenericSetup.interfaces import IImportContext
+from Products.GenericSetup.interfaces import IImportPlugin
+from Products.GenericSetup.interfaces import IExportContext
+from Products.GenericSetup.interfaces import IExportPlugin
+from Products.GenericSetup.interfaces import IStepRegistry
+from Products.GenericSetup.interfaces import IImportStepRegistry
+from Products.GenericSetup.interfaces import IExportStepRegistry
+from Products.GenericSetup.interfaces import IToolsetRegistry
+from Products.GenericSetup.interfaces import IProfileRegistry
+from Products.GenericSetup.interfaces import ISetupTool

Modified: CMF/branches/goldegg-phase-1/CMFSetup/registry.py
===================================================================
--- CMF/branches/goldegg-phase-1/CMFSetup/registry.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/CMFSetup/registry.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -15,739 +15,9 @@
 $Id$
 """
 
-from xml.sax import parseString
-
-from AccessControl import ClassSecurityInfo
-from Acquisition import Implicit
-from Globals import InitializeClass
-from Products.PageTemplates.PageTemplateFile import PageTemplateFile
-
-from interfaces import BASE
-from interfaces import IImportStepRegistry
-from interfaces import IExportStepRegistry
-from interfaces import IToolsetRegistry
-from interfaces import IProfileRegistry
-from permissions import ManagePortal
-from utils import HandlerBase
-from utils import _xmldir
-from utils import _getDottedName
-from utils import _resolveDottedName
-from utils import _extractDocstring
-
-
-class ImportStepRegistry( Implicit ):
-
-    """ Manage knowledge about steps to create / configure site.
-
-    o Steps are composed together to define a site profile.
-    """
-    __implements__ = ( IImportStepRegistry, )
-
-    security = ClassSecurityInfo()
-
-    def __init__( self ):
-
-        self.clear()
-
-    security.declareProtected( ManagePortal, 'listSteps' )
-    def listSteps( self ):
-
-        """ Return a sequence of IDs of registered steps.
-
-        o Order is not significant.
-        """
-        return self._registered.keys()
-
-    security.declareProtected( ManagePortal, 'sortSteps' )
-    def sortSteps( self ):
-
-        """ Return a sequence of registered step IDs
-
-        o Sequence is sorted topologically by dependency, with the dependent
-          steps *after* the steps they depend on.
-        """
-        return self._computeTopologicalSort()
-
-    security.declareProtected( ManagePortal, 'checkComplete' )
-    def checkComplete( self ):
-
-        """ Return a sequence of ( node, edge ) tuples for unsatisifed deps.
-        """
-        result = []
-        seen = {}
-
-        graph = self._computeTopologicalSort()
-
-        for node in graph:
-
-            dependencies = self.getStepMetadata( node )[ 'dependencies' ]
-
-            for dependency in dependencies:
-
-                if seen.get( dependency ) is None:
-                    result.append( ( node, dependency ) )
-
-            seen[ node ] = 1
-
-        return result
-
-    security.declareProtected( ManagePortal, 'getStepMetadata' )
-    def getStepMetadata( self, key, default=None ):
-
-        """ Return a mapping of metadata for the step identified by 'key'.
-
-        o Return 'default' if no such step is registered.
-
-        o The 'handler' metadata is available via 'getStep'.
-        """
-        result = {}
-
-        info = self._registered.get( key )
-
-        if info is None:
-            return default
-
-        return info.copy()
-
-    security.declareProtected( ManagePortal, 'listStepMetadata' )
-    def listStepMetadata( self ):
-
-        """ Return a sequence of mappings describing registered steps.
-
-        o Mappings will be ordered alphabetically.
-        """
-        step_ids = self.listSteps()
-        step_ids.sort()
-        return [ self.getStepMetadata( x ) for x in step_ids ]
-
-    security.declareProtected( ManagePortal, 'generateXML' )
-    def generateXML( self ):
-
-        """ Return a round-trippable XML representation of the registry.
-
-        o 'handler' values are serialized using their dotted names.
-        """
-        return self._exportTemplate()
-
-    security.declarePrivate( 'getStep' )
-    def getStep( self, key, default=None ):
-
-        """ Return the IImportPlugin registered for 'key'.
-
-        o Return 'default' if no such step is registered.
-        """
-        marker = object()
-        info = self._registered.get( key, marker )
-
-        if info is marker:
-            return default
-
-        return _resolveDottedName( info[ 'handler' ] )
-
-    security.declarePrivate( 'registerStep' )
-    def registerStep( self
-                    , id
-                    , version
-                    , handler
-                    , dependencies=()
-                    , title=None
-                    , description=None
-                    ):
-        """ Register a setup step.
-
-        o 'id' is a unique name for this step,
-
-        o 'version' is a string for comparing versions, it is preferred to
-          be a yyyy/mm/dd-ii formatted string (date plus two-digit
-          ordinal).  when comparing two version strings, the version with
-          the lower sort order is considered the older version.
-
-          - Newer versions of a step supplant older ones.
-
-          - Attempting to register an older one after a newer one results
-            in a KeyError.
-
-        o 'handler' should implement IImportPlugin.
-
-        o 'dependencies' is a tuple of step ids which have to run before
-          this step in order to be able to run at all. Registration of
-          steps that have unmet dependencies are deferred until the
-          dependencies have been registered.
-
-        o 'title' is a one-line UI description for this step.
-          If None, the first line of the documentation string of the handler
-          is used, or the id if no docstring can be found.
-
-        o 'description' is a one-line UI description for this step.
-          If None, the remaining line of the documentation string of
-          the handler is used, or default to ''.
-        """
-        already = self.getStepMetadata( id )
-
-        if already and already[ 'version' ] > version:
-            raise KeyError( 'Existing registration for step %s, version %s'
-                          % ( id, already[ 'version' ] ) )
-
-        if title is None or description is None:
-
-            t, d = _extractDocstring( handler, id, '' )
-
-            title = title or t
-            description = description or d
-
-        info = { 'id'           : id
-               , 'version'      : version
-               , 'handler'      : _getDottedName( handler )
-               , 'dependencies' : dependencies
-               , 'title'        : title
-               , 'description'  : description
-               }
-
-        self._registered[ id ] = info
-
-    security.declarePrivate( 'parseXML' )
-    def parseXML( self, text, encoding=None ):
-
-        """ Parse 'text'.
-        """
-        reader = getattr( text, 'read', None )
-
-        if reader is not None:
-            text = reader()
-
-        parser = _ImportStepRegistryParser( encoding )
-        parseString( text, parser )
-
-        return parser._parsed
-
-    security.declarePrivate( 'clear' )
-    def clear( self ):
-
-        self._registered = {}
-
-    #
-    #   Helper methods
-    #
-    security.declarePrivate( '_computeTopologicalSort' )
-    def _computeTopologicalSort( self ):
-
-        result = []
-
-        graph = [ ( x[ 'id' ], x[ 'dependencies' ] )
-                    for x in self._registered.values() ]
-
-        for node, edges in graph:
-
-            after = -1
-
-            for edge in edges:
-
-                if edge in result:
-                    after = max( after, result.index( edge ) )
-
-            result.insert( after + 1, node )
-
-        return result
-
-    security.declarePrivate( '_exportTemplate' )
-    _exportTemplate = PageTemplateFile( 'isrExport.xml', _xmldir )
-
-InitializeClass( ImportStepRegistry )
-
-
-class ExportStepRegistry( Implicit ):
-
-    """ Registry of known site-configuration export steps.
-
-    o Each step is registered with a unique id.
-
-    o When called, with the portal object passed in as an argument,
-      the step must return a sequence of three-tuples,
-      ( 'data', 'content_type', 'filename' ), one for each file exported
-      by the step.
-
-      - 'data' is a string containing the file data;
-
-      - 'content_type' is the MIME type of the data;
-
-      - 'filename' is a suggested filename for use when downloading.
-
-    """
-    __implements__ = ( IExportStepRegistry, )
-
-    security = ClassSecurityInfo()
-
-    def __init__( self ):
-
-        self.clear()
-
-    security.declareProtected( ManagePortal, 'listSteps' )
-    def listSteps( self ):
-
-        """ Return a list of registered step IDs.
-        """
-        return self._registered.keys()
-
-    security.declareProtected( ManagePortal, 'getStepMetadata' )
-    def getStepMetadata( self, key, default=None ):
-
-        """ Return a mapping of metadata for the step identified by 'key'.
-
-        o Return 'default' if no such step is registered.
-
-        o The 'handler' metadata is available via 'getStep'.
-        """
-        info = self._registered.get( key )
-
-        if info is None:
-            return default
-
-        return info.copy()
-
-    security.declareProtected( ManagePortal, 'listStepMetadata' )
-    def listStepMetadata( self ):
-
-        """ Return a sequence of mappings describing registered steps.
-
-        o Steps will be alphabetical by ID.
-        """
-        step_ids = self.listSteps()
-        step_ids.sort()
-        return [ self.getStepMetadata( x ) for x in step_ids ]
-
-    security.declareProtected( ManagePortal, 'generateXML' )
-    def generateXML( self ):
-
-        """ Return a round-trippable XML representation of the registry.
-
-        o 'handler' values are serialized using their dotted names.
-        """
-        return self._exportTemplate()
-
-    security.declarePrivate( 'getStep' )
-    def getStep( self, key, default=None ):
-
-        """ Return the IExportPlugin registered for 'key'.
-
-        o Return 'default' if no such step is registered.
-        """
-        marker = object()
-        info = self._registered.get( key, marker )
-
-        if info is marker:
-            return default
-
-        return _resolveDottedName( info[ 'handler' ] )
-
-    security.declarePrivate( 'registerStep' )
-    def registerStep( self, id, handler, title=None, description=None ):
-
-        """ Register an export step.
-
-        o 'id' is the unique identifier for this step
-
-        o 'step' should implement IExportPlugin.
-
-        o 'title' is a one-line UI description for this step.
-          If None, the first line of the documentation string of the step
-          is used, or the id if no docstring can be found.
-
-        o 'description' is a one-line UI description for this step.
-          If None, the remaining line of the documentation string of
-          the step is used, or default to ''.
-        """
-        if title is None or description is None:
-
-            t, d = _extractDocstring( handler, id, '' )
-
-            title = title or t
-            description = description or d
-
-        info = { 'id'           : id
-               , 'handler'      : _getDottedName( handler )
-               , 'title'        : title
-               , 'description'  : description
-               }
-
-        self._registered[ id ] = info
-
-    security.declarePrivate( 'parseXML' )
-    def parseXML( self, text, encoding=None ):
-
-        """ Parse 'text'.
-        """
-        reader = getattr( text, 'read', None )
-
-        if reader is not None:
-            text = reader()
-
-        parser = _ExportStepRegistryParser( encoding )
-        parseString( text, parser )
-
-        return parser._parsed
-
-    security.declarePrivate( 'clear' )
-    def clear( self ):
-
-        self._registered = {}
-
-    #
-    #   Helper methods
-    #
-    security.declarePrivate( '_exportTemplate' )
-    _exportTemplate = PageTemplateFile( 'esrExport.xml', _xmldir )
-
-InitializeClass( ExportStepRegistry )
-
-class ToolsetRegistry( Implicit ):
-
-    """ Track required / forbidden tools.
-    """
-    __implements__ = ( IToolsetRegistry, )
-
-    security = ClassSecurityInfo()
-    security.setDefaultAccess( 'allow' )
-
-    def __init__( self ):
-
-        self.clear()
-
-    #
-    #   Toolset API
-    #
-    security.declareProtected( ManagePortal, 'listForbiddenTools' )
-    def listForbiddenTools( self ):
-
-        """ See IToolsetRegistry.
-        """
-        result = list( self._forbidden )
-        result.sort()
-        return result
-
-    security.declareProtected( ManagePortal, 'addForbiddenTool' )
-    def addForbiddenTool( self, tool_id ):
-
-        """ See IToolsetRegistry.
-        """
-        if tool_id in self._forbidden:
-            return
-
-        if self._required.get( tool_id ) is not None:
-            raise ValueError, 'Tool %s is required!' % tool_id
-
-        self._forbidden.append( tool_id )
-
-    security.declareProtected( ManagePortal, 'listRequiredTools' )
-    def listRequiredTools( self ):
-
-        """ See IToolsetRegistry.
-        """
-        result = list( self._required.keys() )
-        result.sort()
-        return result
-
-    security.declareProtected( ManagePortal, 'getRequiredToolInfo' )
-    def getRequiredToolInfo( self, tool_id ):
-
-        """ See IToolsetRegistry.
-        """
-        return self._required[ tool_id ]
-
-    security.declareProtected( ManagePortal, 'listRequiredToolInfo' )
-    def listRequiredToolInfo( self ):
-
-        """ See IToolsetRegistry.
-        """
-        return [ self.getRequiredToolInfo( x )
-                        for x in self.listRequiredTools() ]
-
-    security.declareProtected( ManagePortal, 'addRequiredTool' )
-    def addRequiredTool( self, tool_id, dotted_name ):
-
-        """ See IToolsetRegistry.
-        """
-        if tool_id in self._forbidden:
-            raise ValueError, "Forbidden tool ID: %s" % tool_id
-
-        self._required[ tool_id ] = { 'id' : tool_id
-                                    , 'class' : dotted_name
-                                    }
-
-    security.declareProtected( ManagePortal, 'generateXML' )
-    def generateXML( self ):
-
-        """ Pseudo API.
-        """
-        return self._toolsetConfig()
-
-    security.declareProtected( ManagePortal, 'parseXML' )
-    def parseXML( self, text, encoding=None ):
-
-        """ Pseudo-API
-        """
-        reader = getattr( text, 'read', None )
-
-        if reader is not None:
-            text = reader()
-
-        parser = _ToolsetParser( encoding )
-        parseString( text, parser )
-
-        for tool_id in parser._forbidden:
-            self.addForbiddenTool( tool_id )
-
-        for tool_id, dotted_name in parser._required.items():
-            self.addRequiredTool( tool_id, dotted_name )
-
-    security.declarePrivate( 'clear' )
-    def clear( self ):
-
-        self._forbidden = []
-        self._required = {}
-
-    #
-    #   Helper methods.
-    #
-    security.declarePrivate( '_toolsetConfig' )
-    _toolsetConfig = PageTemplateFile( 'tscExport.xml'
-                                     , _xmldir
-                                     , __name__='toolsetConfig'
-                                     )
-
-InitializeClass( ToolsetRegistry )
-
-class ProfileRegistry( Implicit ):
-
-    """ Track registered profiles.
-    """
-    __implements__ = ( IProfileRegistry, )
-
-    security = ClassSecurityInfo()
-    security.setDefaultAccess( 'allow' )
-
-    def __init__( self ):
-
-        self.clear()
-
-    security.declareProtected( ManagePortal, '' )
-    def getProfileInfo( self, profile_id ):
-
-        """ See IProfileRegistry.
-        """
-        result = self._profile_info[ profile_id ]
-        return result.copy()
-
-    security.declareProtected( ManagePortal, 'listProfiles' )
-    def listProfiles( self ):
-
-        """ See IProfileRegistry.
-        """
-        return tuple( self._profile_ids )
-
-    security.declareProtected( ManagePortal, 'listProfileInfo' )
-    def listProfileInfo( self ):
-
-        """ See IProfileRegistry.
-        """
-        return [ self.getProfileInfo( id ) for id in self.listProfiles() ]
-
-    security.declareProtected( ManagePortal, 'registerProfile' )
-    def registerProfile( self
-                       , name
-                       , title
-                       , description
-                       , path
-                       , product=None
-                       , profile_type=BASE
-                       ):
-        """ See IProfileRegistry.
-        """
-        profile_id = '%s:%s' % (product or 'other', name)
-        if self._profile_info.get( profile_id ) is not None:
-            raise KeyError, 'Duplicate profile ID: %s' % profile_id
-
-        self._profile_ids.append( profile_id )
-
-        info = { 'id' : profile_id
-               , 'title' : title
-               , 'description' : description
-               , 'path' : path
-               , 'product' : product
-               , 'type': profile_type
-               }
-
-        self._profile_info[ profile_id ] = info
-
-    security.declarePrivate( 'clear' )
-    def clear( self ):
-
-        self._profile_info = {}
-        self._profile_ids = []
-
-InitializeClass( ProfileRegistry )
-
-_profile_registry = ProfileRegistry()
-
-class _ImportStepRegistryParser( HandlerBase ):
-
-    security = ClassSecurityInfo()
-    security.declareObjectPrivate()
-    security.setDefaultAccess( 'deny' )
-
-    def __init__( self, encoding ):
-
-        self._encoding = encoding
-        self._started = False
-        self._pending = None
-        self._parsed = []
-
-    def startElement( self, name, attrs ):
-
-        if name == 'import-steps':
-
-            if self._started:
-                raise ValueError, 'Duplicated setup-steps element: %s' % name
-
-            self._started = True
-
-        elif name == 'import-step':
-
-            if self._pending is not None:
-                raise ValueError, 'Cannot nest setup-step elements'
-
-            self._pending = dict( [ ( k, self._extract( attrs, k ) )
-                                    for k in attrs.keys() ] )
-
-            self._pending[ 'dependencies' ] = []
-
-        elif name == 'dependency':
-
-            if not self._pending:
-                raise ValueError, 'Dependency outside of step'
-
-            depended = self._extract( attrs, 'step' )
-            self._pending[ 'dependencies' ].append( depended )
-
-        else:
-            raise ValueError, 'Unknown element %s' % name
-
-    def characters( self, content ):
-
-        if self._pending is not None:
-            content = self._encode( content )
-            self._pending.setdefault( 'description', [] ).append( content )
-
-    def endElement(self, name):
-
-        if name == 'import-steps':
-            pass
-
-        elif name == 'import-step':
-
-            if self._pending is None:
-                raise ValueError, 'No pending step!'
-
-            deps = tuple( self._pending[ 'dependencies' ] )
-            self._pending[ 'dependencies' ] = deps
-
-            desc = ''.join( self._pending[ 'description' ] )
-            self._pending[ 'description' ] = desc
-
-            self._parsed.append( self._pending )
-            self._pending = None
-
-InitializeClass( _ImportStepRegistryParser )
-
-class _ExportStepRegistryParser( HandlerBase ):
-
-    security = ClassSecurityInfo()
-    security.declareObjectPrivate()
-    security.setDefaultAccess( 'deny' )
-
-    def __init__( self, encoding ):
-
-        self._encoding = encoding
-        self._started = False
-        self._pending = None
-        self._parsed = []
-
-    def startElement( self, name, attrs ):
-
-        if name == 'export-steps':
-
-            if self._started:
-                raise ValueError, 'Duplicated export-steps element: %s' % name
-
-            self._started = True
-
-        elif name == 'export-step':
-
-            if self._pending is not None:
-                raise ValueError, 'Cannot nest export-step elements'
-
-            self._pending = dict( [ ( k, self._extract( attrs, k ) )
-                                    for k in attrs.keys() ] )
-
-        else:
-            raise ValueError, 'Unknown element %s' % name
-
-    def characters( self, content ):
-
-        if self._pending is not None:
-            content = self._encode( content )
-            self._pending.setdefault( 'description', [] ).append( content )
-
-    def endElement(self, name):
-
-        if name == 'export-steps':
-            pass
-
-        elif name == 'export-step':
-
-            if self._pending is None:
-                raise ValueError, 'No pending step!'
-
-            desc = ''.join( self._pending[ 'description' ] )
-            self._pending[ 'description' ] = desc
-
-            self._parsed.append( self._pending )
-            self._pending = None
-
-InitializeClass( _ExportStepRegistryParser )
-
-
-class _ToolsetParser( HandlerBase ):
-
-    security = ClassSecurityInfo()
-    security.declareObjectPrivate()
-    security.setDefaultAccess( 'deny' )
-
-    def __init__( self, encoding ):
-
-        self._encoding = encoding
-        self._required = {}
-        self._forbidden = []
-
-    def startElement( self, name, attrs ):
-
-        if name == 'tool-setup':
-            pass
-
-        elif name == 'forbidden':
-
-            tool_id = self._extract( attrs, 'tool_id' )
-
-            if tool_id not in self._forbidden:
-                self._forbidden.append( tool_id )
-
-        elif name == 'required':
-
-            tool_id = self._extract( attrs, 'tool_id' )
-            dotted_name = self._extract( attrs, 'class' )
-            self._required[ tool_id ] = dotted_name
-
-        else:
-            raise ValueError, 'Unknown element %s' % name
-
-
-InitializeClass( _ToolsetParser )
+# BBB
+from Products.GenericSetup.registry import ImportStepRegistry
+from Products.GenericSetup.registry import ExportStepRegistry
+from Products.GenericSetup.registry import ToolsetRegistry
+from Products.GenericSetup.registry import ProfileRegistry
+from Products.GenericSetup.registry import _profile_registry

Modified: CMF/branches/goldegg-phase-1/CMFSetup/rolemap.py
===================================================================
--- CMF/branches/goldegg-phase-1/CMFSetup/rolemap.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/CMFSetup/rolemap.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -15,198 +15,6 @@
 $Id$
 """
 
-from AccessControl import ClassSecurityInfo
-from AccessControl.Permission import Permission
-from Globals import InitializeClass
-from Products.PageTemplates.PageTemplateFile import PageTemplateFile
-
-from permissions import ManagePortal
-from utils import _xmldir
-from utils import ConfiguratorBase
-from utils import CONVERTER, DEFAULT, KEY
-
-
-#
-#   Configurator entry points
-#
-_FILENAME = 'rolemap.xml'
-
-def importRolemap( context ):
-
-    """ Import roles / permission map from an XML file.
-
-    o 'context' must implement IImportContext.
-
-    o Register via Python:
-
-      registry = site.portal_setup.setup_steps
-      registry.registerStep( 'importRolemap'
-                           , '20040518-01'
-                           , Products.CMFSetup.rolemap.importRolemap
-                           , ()
-                           , 'Role / Permission import'
-                           , 'Import additional roles, and map '
-                           'roles to permissions'
-                           )
-
-    o Register via XML:
-
-      <setup-step id="importRolemap"
-                  version="20040518-01"
-                  handler="Products.CMFSetup.rolemap.importRolemap"
-                  title="Role / Permission import"
-      >Import additional roles, and map roles to permissions.</setup-step>
-
-    """
-    site = context.getSite()
-    encoding = context.getEncoding()
-
-    if context.shouldPurge():
-
-        items = site.__dict__.items()
-
-        for k, v in items: # XXX: WAAA
-
-            if k == '__ac_roles__':
-                delattr( site, k )
-
-            if k.startswith( '_' ) and k.endswith( '_Permission' ):
-                delattr( site, k )
-
-    text = context.readDataFile( _FILENAME )
-
-    if text is not None:
-
-        rc = RolemapConfigurator( site, encoding )
-        rolemap_info = rc.parseXML( text )
-
-        immediate_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
-        already = {}
-
-        for role in site.valid_roles():
-            already[ role ] = 1
-
-        for role in rolemap_info[ 'roles' ]:
-
-            if already.get( role ) is None:
-                immediate_roles.append( role )
-                already[ role ] = 1
-
-        immediate_roles.sort()
-        site.__ac_roles__ = tuple( immediate_roles )
-
-        for permission in rolemap_info[ 'permissions' ]:
-
-            site.manage_permission( permission[ 'name' ]
-                                  , permission[ 'roles' ]
-                                  , permission[ 'acquire' ]
-                                  )
-
-    return 'Role / permission map imported.'
-
-
-def exportRolemap( context ):
-
-    """ Export roles / permission map as an XML file
-
-    o 'context' must implement IExportContext.
-
-    o Register via Python:
-
-      registry = site.portal_setup.export_steps
-      registry.registerStep( 'exportRolemap'
-                           , Products.CMFSetup.rolemap.exportRolemap
-                           , 'Role / Permission export'
-                           , 'Export additional roles, and '
-                             'role / permission map '
-                           )
-
-    o Register via XML:
-
-      <export-script id="exportRolemap"
-                     version="20040518-01"
-                     handler="Products.CMFSetup.rolemap.exportRolemap"
-                     title="Role / Permission export"
-      >Export additional roles, and role / permission map.</export-script>
-
-    """
-    site = context.getSite()
-    rc = RolemapConfigurator( site ).__of__( site )
-    text = rc.generateXML()
-
-    context.writeDataFile( _FILENAME, text, 'text/xml' )
-
-    return 'Role / permission map exported.'
-
-
-class RolemapConfigurator(ConfiguratorBase):
-    """ Synthesize XML description of sitewide role-permission settings.
-    """
-    security = ClassSecurityInfo()
-
-    security.declareProtected( ManagePortal, 'listRoles' )
-    def listRoles( self ):
-
-        """ List the valid role IDs for our site.
-        """
-        return self._site.valid_roles()
-
-    security.declareProtected( ManagePortal, 'listPermissions' )
-    def listPermissions( self ):
-
-        """ List permissions for export.
-
-        o Returns a sqeuence of mappings describing locally-modified
-          permission / role settings.  Keys include:
-
-          'permission' -- the name of the permission
-
-          'acquire' -- a flag indicating whether to acquire roles from the
-              site's container
-
-          'roles' -- the list of roles which have the permission.
-
-        o Do not include permissions which both acquire and which define
-          no local changes to the acquired policy.
-        """
-        permissions = []
-        valid_roles = self.listRoles()
-
-        for perm in self._site.ac_inherited_permissions( 1 ):
-
-            name = perm[ 0 ]
-            p = Permission( name, perm[ 1 ], self._site )
-            roles = p.getRoles( default=[] )
-            acquire = isinstance( roles, list )  # tuple means don't acquire
-            roles = [ r for r in roles if r in valid_roles ]
-
-            if roles or not acquire:
-                permissions.append( { 'name'    : name
-                                    , 'acquire' : acquire
-                                    , 'roles'   : roles
-                                    } )
-
-        return permissions
-
-    def _getExportTemplate(self):
-
-        return PageTemplateFile('rmeExport.xml', _xmldir)
-
-    def _getImportMapping(self):
-
-        return {
-          'rolemap':
-            { 'roles':       {CONVERTER: self._convertToUnique},
-              'permissions': {CONVERTER: self._convertToUnique} },
-          'roles':
-            { 'role':        {KEY: None} },
-          'role':
-            { 'name':        {KEY: None} },
-          'permissions':
-            { 'permission':  {KEY: None, DEFAULT: ()} },
-          'permission':
-            { 'name':        {},
-              'role':        {KEY: 'roles'},
-              'acquire':     {CONVERTER: self._convertToBoolean} } }
-
-InitializeClass(RolemapConfigurator)
+# BBB
+from Products.GenericSetup.rolemap import importRolemap
+from Products.GenericSetup.rolemap import exportRolemap

Deleted: CMF/branches/goldegg-phase-1/CMFSetup/tests/test_context.py
===================================================================
--- CMF/branches/goldegg-phase-1/CMFSetup/tests/test_context.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/CMFSetup/tests/test_context.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -1,988 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 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.
-#
-##############################################################################
-""" Unit tests for import / export contexts.
-
-$Id$
-"""
-
-import unittest
-import Testing
-import Zope2
-Zope2.startup()
-
-import os
-import time
-from StringIO import StringIO
-
-from DateTime.DateTime import DateTime
-from OFS.Folder import Folder
-from OFS.Image import File
-
-from Products.CMFCore.tests.base.testcase import SecurityRequestTest
-
-from common import FilesystemTestBase
-from common import TarballTester
-from common import _makeTestFile
-from conformance import ConformsToISetupContext
-from conformance import ConformsToIImportContext
-from conformance import ConformsToIExportContext
-
-
-class DummySite( Folder ):
-
-    pass
-
-class DummyTool( Folder ):
-
-    pass
-
-class DirectoryImportContextTests( FilesystemTestBase
-                                 , ConformsToISetupContext
-                                 , ConformsToIImportContext
-                                 ):
-
-    _PROFILE_PATH = '/tmp/ICTTexts'
-
-    def _getTargetClass( self ):
-
-        from Products.CMFSetup.context import DirectoryImportContext
-        return DirectoryImportContext
-
-    def test_readDataFile_nonesuch( self ):
-
-        FILENAME = 'nonesuch.txt'
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        self.assertEqual( ctx.readDataFile( FILENAME ), None )
-
-    def test_readDataFile_simple( self ):
-
-        from string import printable
-
-        FILENAME = 'simple.txt'
-        self._makeFile( FILENAME, printable )
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        self.assertEqual( ctx.readDataFile( FILENAME ), printable )
-
-    def test_readDataFile_subdir( self ):
-
-        from string import printable
-
-        FILENAME = 'subdir/nested.txt'
-        self._makeFile( FILENAME, printable )
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        self.assertEqual( ctx.readDataFile( FILENAME ), printable )
-
-    def test_getLastModified_nonesuch( self ):
-
-        FILENAME = 'nonesuch.txt'
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        self.assertEqual( ctx.getLastModified( FILENAME ), None )
-
-    def test_getLastModified_simple( self ):
-
-        from string import printable
-
-        FILENAME = 'simple.txt'
-        fqpath = self._makeFile( FILENAME, printable )
-        timestamp = os.path.getmtime( fqpath )
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        lm = ctx.getLastModified( FILENAME )
-        self.failUnless( isinstance( lm, DateTime ) )
-        self.assertEqual( lm, timestamp )
-
-    def test_getLastModified_subdir( self ):
-
-        from string import printable
-
-        SUBDIR = 'subdir'
-        FILENAME = os.path.join( SUBDIR, 'nested.txt' )
-        fqpath = self._makeFile( FILENAME, printable )
-        timestamp = os.path.getmtime( fqpath )
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        lm = ctx.getLastModified( FILENAME )
-        self.failUnless( isinstance( lm, DateTime ) )
-        self.assertEqual( lm, timestamp )
-
-    def test_getLastModified_directory( self ):
-
-        from string import printable
-
-        SUBDIR = 'subdir'
-        FILENAME = os.path.join( SUBDIR, 'nested.txt' )
-        fqpath = self._makeFile( FILENAME, printable )
-        path, file = os.path.split( fqpath )
-        timestamp = os.path.getmtime( path )
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        lm = ctx.getLastModified( SUBDIR )
-        self.failUnless( isinstance( lm, DateTime ) )
-        self.assertEqual( lm, timestamp )
-
-    def test_isDirectory_nonesuch( self ):
-
-        FILENAME = 'nonesuch.txt'
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        self.assertEqual( ctx.isDirectory( FILENAME ), None )
-
-    def test_isDirectory_simple( self ):
-
-        from string import printable
-
-        FILENAME = 'simple.txt'
-        fqpath = self._makeFile( FILENAME, printable )
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        self.assertEqual( ctx.isDirectory( FILENAME ), False )
-
-    def test_isDirectory_nested( self ):
-
-        from string import printable
-
-        SUBDIR = 'subdir'
-        FILENAME = os.path.join( SUBDIR, 'nested.txt' )
-        fqpath = self._makeFile( FILENAME, printable )
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        self.assertEqual( ctx.isDirectory( FILENAME ), False )
-
-    def test_isDirectory_directory( self ):
-
-        from string import printable
-
-        SUBDIR = 'subdir'
-        FILENAME = os.path.join( SUBDIR, 'nested.txt' )
-        fqpath = self._makeFile( FILENAME, printable )
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        self.assertEqual( ctx.isDirectory( SUBDIR ), True )
-
-    def test_listDirectory_nonesuch( self ):
-
-        FILENAME = 'nonesuch.txt'
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        self.assertEqual( ctx.listDirectory( FILENAME ), None )
-
-    def test_listDirectory_root( self ):
-
-        from string import printable
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        FILENAME = 'simple.txt'
-        self._makeFile( FILENAME, printable )
-
-        self.assertEqual( len( ctx.listDirectory( None ) ), 1 )
-        self.failUnless( FILENAME in ctx.listDirectory( None ) )
-
-    def test_listDirectory_simple( self ):
-
-        from string import printable
-
-        FILENAME = 'simple.txt'
-        self._makeFile( FILENAME, printable )
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        self.assertEqual( ctx.listDirectory( FILENAME ), None )
-
-    def test_listDirectory_nested( self ):
-
-        from string import printable
-
-        SUBDIR = 'subdir'
-        FILENAME = os.path.join( SUBDIR, 'nested.txt' )
-        self._makeFile( FILENAME, printable )
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        self.assertEqual( ctx.listDirectory( FILENAME ), None )
-
-    def test_listDirectory_single( self ):
-
-        from string import printable
-
-        SUBDIR = 'subdir'
-        FILENAME = os.path.join( SUBDIR, 'nested.txt' )
-        self._makeFile( FILENAME, printable )
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        names = ctx.listDirectory( SUBDIR )
-        self.assertEqual( len( names ), 1 )
-        self.failUnless( 'nested.txt' in names )
-
-    def test_listDirectory_multiple( self ):
-
-        from string import printable
-        SUBDIR = 'subdir'
-        FILENAME = os.path.join( SUBDIR, 'nested.txt' )
-        self._makeFile( FILENAME, printable )
-        self._makeFile( os.path.join( SUBDIR, 'another.txt' ), 'ABC' )
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        names = ctx.listDirectory( SUBDIR )
-        self.assertEqual( len( names ), 2 )
-        self.failUnless( 'nested.txt' in names )
-        self.failUnless( 'another.txt' in names )
-
-    def test_listDirectory_skip_implicit( self ):
-
-        from string import printable
-        SUBDIR = 'subdir'
-        FILENAME = os.path.join( SUBDIR, 'nested.txt' )
-        self._makeFile( FILENAME, printable )
-        self._makeFile( os.path.join( SUBDIR, 'another.txt' ), 'ABC' )
-        self._makeFile( os.path.join( SUBDIR, 'CVS/skip.txt' ), 'DEF' )
-        self._makeFile( os.path.join( SUBDIR, '.svn/skip.txt' ), 'GHI' )
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        names = ctx.listDirectory( SUBDIR )
-        self.assertEqual( len( names ), 2 )
-        self.failUnless( 'nested.txt' in names )
-        self.failUnless( 'another.txt' in names )
-        self.failIf( 'CVS' in names )
-        self.failIf( '.svn' in names )
-
-    def test_listDirectory_skip_explicit( self ):
-
-        from string import printable
-        SUBDIR = 'subdir'
-        FILENAME = os.path.join( SUBDIR, 'nested.txt' )
-        self._makeFile( FILENAME, printable )
-        self._makeFile( os.path.join( SUBDIR, 'another.txt' ), 'ABC' )
-        self._makeFile( os.path.join( SUBDIR, 'CVS/skip.txt' ), 'DEF' )
-        self._makeFile( os.path.join( SUBDIR, '.svn/skip.txt' ), 'GHI' )
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        names = ctx.listDirectory( SUBDIR, ( 'nested.txt', ) )
-        self.assertEqual( len( names ), 3 )
-        self.failIf( 'nested.txt' in names )
-        self.failUnless( 'another.txt' in names )
-        self.failUnless( 'CVS' in names )
-        self.failUnless( '.svn' in names )
-
-class DirectoryExportContextTests( FilesystemTestBase
-                                 , ConformsToISetupContext
-                                 , ConformsToIExportContext
-                                 ):
-
-    _PROFILE_PATH = '/tmp/ECTTexts'
-
-    def _getTargetClass( self ):
-
-        from Products.CMFSetup.context import DirectoryExportContext
-        return DirectoryExportContext
-
-    def test_writeDataFile_simple( self ):
-
-        from string import printable, digits
-        FILENAME = 'simple.txt'
-        fqname = self._makeFile( FILENAME, printable )
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        ctx.writeDataFile( FILENAME, digits, 'text/plain' )
-
-        self.assertEqual( open( fqname, 'rb' ).read(), digits )
-
-    def test_writeDataFile_new_subdir( self ):
-
-        from string import printable, digits
-        SUBDIR = 'subdir'
-        FILENAME = 'nested.txt'
-        fqname = os.path.join( self._PROFILE_PATH, SUBDIR, FILENAME )
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        ctx.writeDataFile( FILENAME, digits, 'text/plain', SUBDIR )
-
-        self.assertEqual( open( fqname, 'rb' ).read(), digits )
-
-    def test_writeDataFile_overwrite( self ):
-
-        from string import printable, digits
-        SUBDIR = 'subdir'
-        FILENAME = 'nested.txt'
-        fqname = self._makeFile( os.path.join( SUBDIR, FILENAME )
-                               , printable )
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        ctx.writeDataFile( FILENAME, digits, 'text/plain', SUBDIR )
-
-        self.assertEqual( open( fqname, 'rb' ).read(), digits )
-
-    def test_writeDataFile_existing_subdir( self ):
-
-        from string import printable, digits
-        SUBDIR = 'subdir'
-        FILENAME = 'nested.txt'
-        self._makeFile( os.path.join( SUBDIR, 'another.txt' ), printable )
-        fqname = os.path.join( self._PROFILE_PATH, SUBDIR, FILENAME )
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._makeOne( site, self._PROFILE_PATH )
-
-        ctx.writeDataFile( FILENAME, digits, 'text/plain', SUBDIR )
-
-        self.assertEqual( open( fqname, 'rb' ).read(), digits )
-
-
-class TarballExportContextTests( FilesystemTestBase
-                               , TarballTester
-                               , ConformsToISetupContext
-                               , ConformsToIExportContext
-                               ):
-
-    _PROFILE_PATH = '/tmp/TECT_tests'
-
-    def _getTargetClass( self ):
-
-        from Products.CMFSetup.context import TarballExportContext
-        return TarballExportContext
-
-    def test_writeDataFile_simple( self ):
-
-        from string import printable
-        now = long( time.time() )
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._getTargetClass()( site )
-
-        ctx.writeDataFile( 'foo.txt', printable, 'text/plain' )
-
-        fileish = StringIO( ctx.getArchive() )
-
-        self._verifyTarballContents( fileish, [ 'foo.txt' ], now )
-        self._verifyTarballEntry( fileish, 'foo.txt', printable )
-
-    def test_writeDataFile_multiple( self ):
-
-        from string import printable
-        from string import digits
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._getTargetClass()( site )
-
-        ctx.writeDataFile( 'foo.txt', printable, 'text/plain' )
-        ctx.writeDataFile( 'bar.txt', digits, 'text/plain' )
-
-        fileish = StringIO( ctx.getArchive() )
-
-        self._verifyTarballContents( fileish, [ 'foo.txt', 'bar.txt' ] )
-        self._verifyTarballEntry( fileish, 'foo.txt', printable )
-        self._verifyTarballEntry( fileish, 'bar.txt', digits )
-
-    def test_writeDataFile_subdir( self ):
-
-        from string import printable
-        from string import digits
-
-        site = DummySite( 'site' ).__of__( self.root )
-        ctx = self._getTargetClass()( site )
-
-        ctx.writeDataFile( 'foo.txt', printable, 'text/plain' )
-        ctx.writeDataFile( 'bar/baz.txt', digits, 'text/plain' )
-
-        fileish = StringIO( ctx.getArchive() )
-
-        self._verifyTarballContents( fileish, [ 'foo.txt', 'bar/baz.txt' ] )
-        self._verifyTarballEntry( fileish, 'foo.txt', printable )
-        self._verifyTarballEntry( fileish, 'bar/baz.txt', digits )
-
-
-class SnapshotExportContextTests( SecurityRequestTest
-                                , ConformsToISetupContext
-                                , ConformsToIExportContext
-                                ):
-
-    def _getTargetClass( self ):
-
-        from Products.CMFSetup.context import SnapshotExportContext
-        return SnapshotExportContext
-
-    def _makeOne( self, *args, **kw ):
-
-        return self._getTargetClass()( *args, **kw )
-
-    def test_writeDataFile_simple_image( self ):
-
-        from OFS.Image import Image
-        FILENAME = 'simple.txt'
-        _CONTENT_TYPE = 'image/png'
-        png_filename = os.path.join( os.path.split( __file__ )[0]
-                                   , 'simple.png' )
-        png_file = open( png_filename, 'rb' )
-        png_data = png_file.read()
-        png_file.close()
-
-        site = DummySite( 'site' ).__of__( self.root )
-        site.portal_setup = DummyTool( 'portal_setup' )
-        tool = site.portal_setup
-        ctx = self._makeOne( tool, 'simple' )
-
-        ctx.writeDataFile( FILENAME, png_data, _CONTENT_TYPE )
-
-        snapshot = tool.snapshots._getOb( 'simple' )
-
-        self.assertEqual( len( snapshot.objectIds() ), 1 )
-        self.failUnless( FILENAME in snapshot.objectIds() )
-
-        fileobj = snapshot._getOb( FILENAME )
-
-        self.assertEqual( fileobj.getId(), FILENAME )
-        self.assertEqual( fileobj.meta_type, Image.meta_type )
-        self.assertEqual( fileobj.getContentType(), _CONTENT_TYPE )
-        self.assertEqual( fileobj.data, png_data )
-
-    def test_writeDataFile_simple_plain_text( self ):
-
-        from string import digits
-        from OFS.Image import File
-        FILENAME = 'simple.txt'
-        _CONTENT_TYPE = 'text/plain'
-
-        site = DummySite( 'site' ).__of__( self.root )
-        site.portal_setup = DummyTool( 'portal_setup' )
-        tool = site.portal_setup
-        ctx = self._makeOne( tool, 'simple' )
-
-        ctx.writeDataFile( FILENAME, digits, _CONTENT_TYPE )
-
-        snapshot = tool.snapshots._getOb( 'simple' )
-
-        self.assertEqual( len( snapshot.objectIds() ), 1 )
-        self.failUnless( FILENAME in snapshot.objectIds() )
-
-        fileobj = snapshot._getOb( FILENAME )
-
-        self.assertEqual( fileobj.getId(), FILENAME )
-        self.assertEqual( fileobj.meta_type, File.meta_type )
-        self.assertEqual( fileobj.getContentType(), _CONTENT_TYPE )
-        self.assertEqual( str( fileobj ), digits )
-
-    def test_writeDataFile_simple_xml( self ):
-
-        from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
-        FILENAME = 'simple.xml'
-        _CONTENT_TYPE = 'text/xml'
-        _XML = """<?xml version="1.0"?><simple />"""
-
-        site = DummySite( 'site' ).__of__( self.root )
-        site.portal_setup = DummyTool( 'portal_setup' )
-        tool = site.portal_setup
-        ctx = self._makeOne( tool, 'simple' )
-
-        ctx.writeDataFile( FILENAME, _XML, _CONTENT_TYPE )
-
-        snapshot = tool.snapshots._getOb( 'simple' )
-
-        self.assertEqual( len( snapshot.objectIds() ), 1 )
-        self.failUnless( FILENAME in snapshot.objectIds() )
-
-        template = snapshot._getOb( FILENAME )
-
-        self.assertEqual( template.getId(), FILENAME )
-        self.assertEqual( template.meta_type, ZopePageTemplate.meta_type )
-        self.assertEqual( template.read(), _XML )
-        self.failIf( template.html() )
-
-    def test_writeDataFile_unicode_xml( self ):
-
-        from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
-        FILENAME = 'simple.xml'
-        _CONTENT_TYPE = 'text/xml'
-        _XML = u"""<?xml version="1.0"?><simple />"""
-
-        site = DummySite( 'site' ).__of__( self.root )
-        site.portal_setup = DummyTool( 'portal_setup' )
-        tool = site.portal_setup
-        ctx = self._makeOne( tool, 'simple' )
-
-        ctx.writeDataFile( FILENAME, _XML, _CONTENT_TYPE )
-
-        snapshot = tool.snapshots._getOb( 'simple' )
-
-        self.assertEqual( len( snapshot.objectIds() ), 1 )
-        self.failUnless( FILENAME in snapshot.objectIds() )
-
-        template = snapshot._getOb( FILENAME )
-
-        self.assertEqual( template.getId(), FILENAME )
-        self.assertEqual( template.meta_type, ZopePageTemplate.meta_type )
-        self.assertEqual( template.read(), _XML )
-        self.failIf( template.html() )
-
-    def test_writeDataFile_subdir_dtml( self ):
-
-        from OFS.DTMLDocument import DTMLDocument
-        FILENAME = 'simple.dtml'
-        _CONTENT_TYPE = 'text/html'
-        _HTML = """<html><body><h1>HTML</h1></body></html>"""
-
-        site = DummySite( 'site' ).__of__( self.root )
-        site.portal_setup = DummyTool( 'portal_setup' )
-        tool = site.portal_setup
-        ctx = self._makeOne( tool, 'simple' )
-
-        ctx.writeDataFile( FILENAME, _HTML, _CONTENT_TYPE, 'sub1' )
-
-        snapshot = tool.snapshots._getOb( 'simple' )
-        sub1 = snapshot._getOb( 'sub1' )
-
-        self.assertEqual( len( sub1.objectIds() ), 1 )
-        self.failUnless( FILENAME in sub1.objectIds() )
-
-        template = sub1._getOb( FILENAME )
-
-        self.assertEqual( template.getId(), FILENAME )
-        self.assertEqual( template.meta_type, DTMLDocument.meta_type )
-        self.assertEqual( template.read(), _HTML )
-
-    def test_writeDataFile_nested_subdirs_html( self ):
-
-        from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
-        FILENAME = 'simple.html'
-        _CONTENT_TYPE = 'text/html'
-        _HTML = """<html><body><h1>HTML</h1></body></html>"""
-
-        site = DummySite( 'site' ).__of__( self.root )
-        site.portal_setup = DummyTool( 'portal_setup' )
-        tool = site.portal_setup
-        ctx = self._makeOne( tool, 'simple' )
-
-        ctx.writeDataFile( FILENAME, _HTML, _CONTENT_TYPE, 'sub1/sub2' )
-
-        snapshot = tool.snapshots._getOb( 'simple' )
-        sub1 = snapshot._getOb( 'sub1' )
-        sub2 = sub1._getOb( 'sub2' )
-
-        self.assertEqual( len( sub2.objectIds() ), 1 )
-        self.failUnless( FILENAME in sub2.objectIds() )
-
-        template = sub2._getOb( FILENAME )
-
-        self.assertEqual( template.getId(), FILENAME )
-        self.assertEqual( template.meta_type, ZopePageTemplate.meta_type )
-        self.assertEqual( template.read(), _HTML )
-        self.failUnless( template.html() )
-
-    def test_writeDataFile_multiple( self ):
-
-        from string import printable
-        from string import digits
-
-        site = DummySite( 'site' ).__of__( self.root )
-        site.portal_setup = DummyTool( 'portal_setup' )
-        tool = site.portal_setup
-        ctx = self._makeOne( tool, 'multiple' )
-
-        ctx.writeDataFile( 'foo.txt', printable, 'text/plain' )
-        ctx.writeDataFile( 'bar.txt', digits, 'text/plain' )
-
-        snapshot = tool.snapshots._getOb( 'multiple' )
-
-        self.assertEqual( len( snapshot.objectIds() ), 2 )
-
-        for id in [ 'foo.txt', 'bar.txt' ]:
-            self.failUnless( id in snapshot.objectIds() )
-
-
-class SnapshotImportContextTests( SecurityRequestTest
-                                , ConformsToISetupContext
-                                , ConformsToIImportContext
-                                ):
-
-    def _getTargetClass( self ):
-
-        from Products.CMFSetup.context import SnapshotImportContext
-        return SnapshotImportContext
-
-    def _makeOne( self, context_id, *args, **kw ):
-
-        site = DummySite( 'site' ).__of__( self.root )
-        site._setObject( 'portal_setup', Folder( 'portal_setup' ) )
-        tool = site._getOb( 'portal_setup' )
-
-        tool._setObject( 'snapshots', Folder( 'snapshots' ) )
-        tool.snapshots._setObject( context_id, Folder( context_id ) )
-
-        ctx = self._getTargetClass()( tool, context_id, *args, **kw )
-
-        return site, tool, ctx.__of__( tool )
-
-    def _makeFile( self
-                 , tool
-                 , snapshot_id
-                 , filename
-                 , contents
-                 , content_type='text/plain'
-                 , mod_time=None
-                 , subdir=None
-                 ):
-
-        snapshots = tool._getOb( 'snapshots' )
-        folder = snapshot = snapshots._getOb( snapshot_id )
-
-        if subdir is not None:
-
-            for element in subdir.split( '/' ):
-
-                try:
-                    folder = folder._getOb( element )
-                except AttributeError:
-                    folder._setObject( element, Folder( element ) )
-                    folder = folder._getOb( element )
-
-        file = File( filename, '', contents, content_type )
-        folder._setObject( filename, file )
-
-        if mod_time is not None:
-
-            def __faux_mod_time():
-                return mod_time
-
-            folder.bobobase_modification_time = \
-            file.bobobase_modification_time = __faux_mod_time
-
-        return folder._getOb( filename )
-
-    def test_ctorparms( self ):
-
-        SNAPSHOT_ID = 'ctorparms'
-        ENCODING = 'latin-1'
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID
-                                       , encoding=ENCODING
-                                       , should_purge=True
-                                       )
-
-        self.assertEqual( ctx.getEncoding(), ENCODING )
-        self.assertEqual( ctx.shouldPurge(), True )
-
-    def test_empty( self ):
-
-        SNAPSHOT_ID = 'empty'
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
-
-        self.assertEqual( ctx.getSite(), site )
-        self.assertEqual( ctx.getEncoding(), None )
-        self.assertEqual( ctx.shouldPurge(), False )
-
-        # These methods are all specified to return 'None' for non-existing
-        # paths / entities
-        self.assertEqual( ctx.isDirectory( 'nonesuch/path' ), None )
-        self.assertEqual( ctx.listDirectory( 'nonesuch/path' ), None )
-
-    def test_readDataFile_nonesuch( self ):
-
-        SNAPSHOT_ID = 'readDataFile_nonesuch'
-        FILENAME = 'nonesuch.txt'
-
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
-
-        self.assertEqual( ctx.readDataFile( FILENAME ), None )
-        self.assertEqual( ctx.readDataFile( FILENAME, 'subdir' ), None )
-
-    def test_readDataFile_simple( self ):
-
-        from string import printable
-
-        SNAPSHOT_ID = 'readDataFile_simple'
-        FILENAME = 'simple.txt'
-
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
-        self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable )
-
-        self.assertEqual( ctx.readDataFile( FILENAME ), printable )
-
-    def test_readDataFile_subdir( self ):
-
-        from string import printable
-
-        SNAPSHOT_ID = 'readDataFile_subdir'
-        FILENAME = 'subdir.txt'
-        SUBDIR = 'subdir'
-
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
-        self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable
-                      , subdir=SUBDIR )
-
-        self.assertEqual( ctx.readDataFile( FILENAME, SUBDIR ), printable )
-
-    def test_getLastModified_nonesuch( self ):
-
-        SNAPSHOT_ID = 'getLastModified_nonesuch'
-        FILENAME = 'nonesuch.txt'
-
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
-
-        self.assertEqual( ctx.getLastModified( FILENAME ), None )
-
-    def test_getLastModified_simple( self ):
-
-        from string import printable
-
-        SNAPSHOT_ID = 'getLastModified_simple'
-        FILENAME = 'simple.txt'
-        WHEN = DateTime( '2004-01-01T00:00:00Z' )
-
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
-        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable
-                             , mod_time=WHEN )
-
-        self.assertEqual( ctx.getLastModified( FILENAME ), WHEN )
-
-    def test_getLastModified_subdir( self ):
-
-        from string import printable
-
-        SNAPSHOT_ID = 'getLastModified_subdir'
-        FILENAME = 'subdir.txt'
-        SUBDIR = 'subdir'
-        PATH = '%s/%s' % ( SUBDIR, FILENAME )
-        WHEN = DateTime( '2004-01-01T00:00:00Z' )
-
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
-        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable
-                             , mod_time=WHEN, subdir=SUBDIR )
-
-        self.assertEqual( ctx.getLastModified( PATH ), WHEN )
-
-    def test_getLastModified_directory( self ):
-
-        from string import printable
-
-        SNAPSHOT_ID = 'readDataFile_subdir'
-        FILENAME = 'subdir.txt'
-        SUBDIR = 'subdir'
-        WHEN = DateTime( '2004-01-01T00:00:00Z' )
-
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
-        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable
-                             , mod_time=WHEN, subdir=SUBDIR )
-
-        self.assertEqual( ctx.getLastModified( SUBDIR ), WHEN )
-
-    def test_isDirectory_nonesuch( self ):
-
-        SNAPSHOT_ID = 'isDirectory_nonesuch'
-        FILENAME = 'nonesuch.txt'
-
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
-
-        self.assertEqual( ctx.isDirectory( FILENAME ), None )
-
-    def test_isDirectory_simple( self ):
-
-        from string import printable
-
-        SNAPSHOT_ID = 'isDirectory_simple'
-        FILENAME = 'simple.txt'
-
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
-        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable )
-
-        self.assertEqual( ctx.isDirectory( FILENAME ), False )
-
-    def test_isDirectory_nested( self ):
-
-        from string import printable
-
-        SNAPSHOT_ID = 'isDirectory_nested'
-        SUBDIR = 'subdir'
-        FILENAME = 'nested.txt'
-        PATH = '%s/%s' % ( SUBDIR, FILENAME )
-
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
-        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable
-                             , subdir=SUBDIR )
-
-        self.assertEqual( ctx.isDirectory( PATH ), False )
-
-    def test_isDirectory_subdir( self ):
-
-        from string import printable
-
-        SNAPSHOT_ID = 'isDirectory_subdir'
-        SUBDIR = 'subdir'
-        FILENAME = 'nested.txt'
-        PATH = '%s/%s' % ( SUBDIR, FILENAME )
-
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
-        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable
-                             , subdir=SUBDIR )
-
-        self.assertEqual( ctx.isDirectory( SUBDIR ), True )
-
-    def test_listDirectory_nonesuch( self ):
-
-        SNAPSHOT_ID = 'listDirectory_nonesuch'
-        SUBDIR = 'nonesuch/path'
-
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
-
-        self.assertEqual( ctx.listDirectory( SUBDIR ), None )
-
-    def test_listDirectory_root( self ):
-
-        from string import printable
-
-        SNAPSHOT_ID = 'listDirectory_root'
-        FILENAME = 'simple.txt'
-
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
-        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable )
-
-        self.assertEqual( len( ctx.listDirectory( None ) ), 1 )
-        self.failUnless( FILENAME in ctx.listDirectory( None ) )
-
-    def test_listDirectory_simple( self ):
-
-        from string import printable
-
-        SNAPSHOT_ID = 'listDirectory_simple'
-        FILENAME = 'simple.txt'
-
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
-        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable )
-
-        self.assertEqual( ctx.listDirectory( FILENAME ), None )
-
-    def test_listDirectory_nested( self ):
-
-        from string import printable
-
-        SNAPSHOT_ID = 'listDirectory_nested'
-        SUBDIR = 'subdir'
-        FILENAME = 'nested.txt'
-        PATH = '%s/%s' % ( SUBDIR, FILENAME )
-
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
-        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable
-                             , subdir=SUBDIR )
-
-        self.assertEqual( ctx.listDirectory( PATH ), None )
-
-    def test_listDirectory_single( self ):
-
-        from string import printable
-
-        SNAPSHOT_ID = 'listDirectory_nested'
-        SUBDIR = 'subdir'
-        FILENAME = 'nested.txt'
-
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
-        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable
-                             , subdir=SUBDIR )
-
-        names = ctx.listDirectory( SUBDIR )
-        self.assertEqual( len( names ), 1 )
-        self.failUnless( FILENAME in names )
-
-    def test_listDirectory_multiple( self ):
-
-        from string import printable, uppercase
-
-        SNAPSHOT_ID = 'listDirectory_nested'
-        SUBDIR = 'subdir'
-        FILENAME1 = 'nested.txt'
-        FILENAME2 = 'another.txt'
-
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
-        file1 = self._makeFile( tool, SNAPSHOT_ID, FILENAME1, printable
-                              , subdir=SUBDIR )
-        file2 = self._makeFile( tool, SNAPSHOT_ID, FILENAME2, uppercase
-                              , subdir=SUBDIR )
-
-        names = ctx.listDirectory( SUBDIR )
-        self.assertEqual( len( names ), 2 )
-        self.failUnless( FILENAME1 in names )
-        self.failUnless( FILENAME2 in names )
-
-    def test_listDirectory_skip( self ):
-
-        from string import printable, uppercase
-
-        SNAPSHOT_ID = 'listDirectory_nested'
-        SUBDIR = 'subdir'
-        FILENAME1 = 'nested.txt'
-        FILENAME2 = 'another.txt'
-
-        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
-        file1 = self._makeFile( tool, SNAPSHOT_ID, FILENAME1, printable
-                              , subdir=SUBDIR )
-        file2 = self._makeFile( tool, SNAPSHOT_ID, FILENAME2, uppercase
-                              , subdir=SUBDIR )
-
-        names = ctx.listDirectory( SUBDIR, skip=( FILENAME1, ) )
-        self.assertEqual( len( names ), 1 )
-        self.failIf( FILENAME1 in names )
-        self.failUnless( FILENAME2 in names )
-
-
-def test_suite():
-    return unittest.TestSuite((
-        unittest.makeSuite( DirectoryImportContextTests ),
-        unittest.makeSuite( DirectoryExportContextTests ),
-        unittest.makeSuite( TarballExportContextTests ),
-        unittest.makeSuite( SnapshotExportContextTests ),
-        unittest.makeSuite( SnapshotImportContextTests ),
-        ))
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='test_suite')

Deleted: CMF/branches/goldegg-phase-1/CMFSetup/tests/test_differ.py
===================================================================
--- CMF/branches/goldegg-phase-1/CMFSetup/tests/test_differ.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/CMFSetup/tests/test_differ.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -1,406 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 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.
-#
-##############################################################################
-""" Unit tests for differ module.
-
-$Id$
-"""
-
-import unittest
-import Testing
-import Zope2
-Zope2.startup()
-
-from OFS.Folder import Folder
-from OFS.Image import File
-
-from DateTime.DateTime import DateTime
-from Products.CMFCore.tests.base.testcase import SecurityRequestTest
-
-
-class DummySite( Folder ):
-
-    pass
-
-
-class Test_unidiff( unittest.TestCase ):
-
-    def test_unidiff_both_text( self ):
-
-        from Products.CMFSetup.differ import unidiff
-
-        diff_lines = unidiff( ONE_FOUR, ZERO_FOUR )
-        diff_text = '\n'.join( diff_lines )
-        self.assertEqual( diff_text, DIFF_TEXT )
-
-    def test_unidiff_both_lines( self ):
-
-        from Products.CMFSetup.differ import unidiff
-
-        diff_lines = unidiff( ONE_FOUR.splitlines(), ZERO_FOUR.splitlines() )
-        diff_text = '\n'.join( diff_lines )
-        self.assertEqual( diff_text, DIFF_TEXT )
-
-    def test_unidiff_mixed( self ):
-
-        from Products.CMFSetup.differ import unidiff
-
-        diff_lines = unidiff( ONE_FOUR, ZERO_FOUR.splitlines() )
-        diff_text = '\n'.join( diff_lines )
-        self.assertEqual( diff_text, DIFF_TEXT )
-
-    def test_unidiff_ignore_blanks( self ):
-
-        from Products.CMFSetup.differ import unidiff
-
-        double_spaced = ONE_FOUR.replace( '\n', '\n\n' )
-        diff_lines = unidiff( double_spaced
-                            , ZERO_FOUR.splitlines()
-                            , ignore_blanks=True
-                            )
-
-        diff_text = '\n'.join( diff_lines )
-        self.assertEqual( diff_text, DIFF_TEXT )
-
-ZERO_FOUR = """\
-zero
-one
-tree
-four
-"""
-
-ONE_FOUR = """\
-one
-two
-three
-four
-"""
-
-DIFF_TEXT = """\
---- original None
-+++ modified None
-@@ -1,4 +1,4 @@
-+zero
- one
--two
--three
-+tree
- four\
-"""
-
-class ConfigDiffTests( SecurityRequestTest ):
-
-    site = None
-    tool = None
-
-    def _getTargetClass( self ):
-
-        from Products.CMFSetup.differ import ConfigDiff
-        return ConfigDiff
-
-    def _makeOne( self, lhs, rhs, *args, **kw ):
-
-        return self._getTargetClass()( lhs, rhs, *args, **kw )
-
-    def _makeSite( self ):
-
-        if self.site is not None:
-            return
-
-        site = self.site = DummySite( 'site' ).__of__( self.root )
-        site._setObject( 'portal_setup', Folder( 'portal_setup' ) )
-        self.tool = tool = site._getOb( 'portal_setup' )
-
-        tool._setObject( 'snapshots', Folder( 'snapshots' ) )
-
-    def _makeContext( self, context_id ):
-
-        from Products.CMFSetup.context import SnapshotImportContext
-
-        self._makeSite()
-
-        if context_id not in self.tool.snapshots.objectIds():
-            self.tool.snapshots._setObject( context_id, Folder( context_id ) )
-
-        ctx = SnapshotImportContext( self.tool, context_id )
-
-        return ctx.__of__( self.tool )
-
-    def _makeDirectory( self, snapshot_id, subdir ):
-
-        self._makeSite()
-        folder = self.tool.snapshots._getOb( snapshot_id )
-
-        for element in subdir.split( '/' ):
-
-            try:
-                folder = folder._getOb( element )
-            except AttributeError:
-                folder._setObject( element, Folder( element ) )
-                folder = folder._getOb( element )
-
-        return folder
-
-    def _makeFile( self
-                 , snapshot_id
-                 , filename
-                 , contents
-                 , content_type='text/plain'
-                 , mod_time=None
-                 , subdir=None
-                 ):
-
-        self._makeSite()
-        snapshots = self.tool.snapshots
-        snapshot = snapshots._getOb( snapshot_id )
-
-        if subdir is not None:
-            folder = self._makeDirectory( snapshot_id, subdir )
-        else:
-            folder = snapshot
-
-        file = File( filename, '', contents, content_type )
-        folder._setObject( filename, file )
-
-        if mod_time is not None:
-
-            def __faux_mod_time():
-                return mod_time
-
-            folder.bobobase_modification_time = \
-            file.bobobase_modification_time = __faux_mod_time
-
-        return folder._getOb( filename )
-
-    def test_compare_empties( self ):
-
-        lhs = self._makeContext( 'lhs' )
-        rhs = self._makeContext( 'rhs' )
-
-        cd = self._makeOne( lhs, rhs )
-
-        diffs = cd.compare()
-
-        self.assertEqual( diffs, '' )
-
-    def test_compare_identical( self ):
-
-        lhs = self._makeContext( 'lhs' )
-        rhs = self._makeContext( 'rhs' )
-
-        self._makeFile( 'lhs', 'test.txt', 'ABCDEF' )
-        self._makeFile( 'lhs', 'again.txt', 'GHIJKL', subdir='sub' )
-        self._makeFile( 'rhs', 'test.txt', 'ABCDEF' )
-        self._makeFile( 'rhs', 'again.txt', 'GHIJKL', subdir='sub' )
-
-        cd = self._makeOne( lhs, rhs )
-
-        diffs = cd.compare()
-
-        self.assertEqual( diffs, '' )
-
-    def test_compare_changed_file( self ):
-
-        BEFORE = DateTime( '2004-01-01T00:00:00Z' )
-        AFTER = DateTime( '2004-02-29T23:59:59Z' )
-
-        lhs = self._makeContext( 'lhs' )
-        rhs = self._makeContext( 'rhs' )
-
-        self._makeFile( 'lhs', 'test.txt', 'ABCDEF\nWXYZ', mod_time=BEFORE )
-        self._makeFile( 'lhs', 'again.txt', 'GHIJKL', subdir='sub' )
-        self._makeFile( 'rhs', 'test.txt', 'ABCDEF\nQRST', mod_time=AFTER )
-        self._makeFile( 'rhs', 'again.txt', 'GHIJKL', subdir='sub' )
-
-        cd = self._makeOne( lhs, rhs )
-
-        diffs = cd.compare()
-
-        self.assertEqual( diffs, TEST_TXT_DIFFS % ( BEFORE, AFTER ) )
-
-    def test_compare_changed_file_ignore_blanks( self ):
-
-        BEFORE = DateTime( '2004-01-01T00:00:00Z' )
-        AFTER = DateTime( '2004-02-29T23:59:59Z' )
-
-        lhs = self._makeContext( 'lhs' )
-        rhs = self._makeContext( 'rhs' )
-
-        self._makeFile( 'lhs', 'test.txt', 'ABCDEF\nWXYZ', mod_time=BEFORE )
-        self._makeFile( 'rhs', 'test.txt', 'ABCDEF\n\n\nWXYZ', mod_time=AFTER )
-
-        cd = self._makeOne( lhs, rhs, ignore_blanks=True )
-
-        diffs = cd.compare()
-
-        self.assertEqual( diffs, '' )
-
-    def test_compare_changed_file_explicit_skip( self ):
-
-        BEFORE = DateTime( '2004-01-01T00:00:00Z' )
-        AFTER = DateTime( '2004-02-29T23:59:59Z' )
-
-        lhs = self._makeContext( 'lhs' )
-        rhs = self._makeContext( 'rhs' )
-
-        self._makeFile( 'lhs', 'test.txt', 'ABCDEF\nWXYZ', subdir='skipme'
-                      , mod_time=BEFORE )
-        self._makeFile( 'lhs', 'again.txt', 'GHIJKL', subdir='sub' )
-        self._makeFile( 'rhs', 'test.txt', 'ABCDEF\nQRST', subdir='skipme'
-                      , mod_time=AFTER )
-        self._makeFile( 'rhs', 'again.txt', 'GHIJKL', subdir='sub' )
-
-        cd = self._makeOne( lhs, rhs, skip=[ 'skipme' ] )
-
-        diffs = cd.compare()
-
-        self.assertEqual( diffs, '' )
-
-    def test_compare_changed_file_implicit_skip( self ):
-
-        BEFORE = DateTime( '2004-01-01T00:00:00Z' )
-        AFTER = DateTime( '2004-02-29T23:59:59Z' )
-
-        lhs = self._makeContext( 'lhs' )
-        rhs = self._makeContext( 'rhs' )
-
-        self._makeFile( 'lhs', 'test.txt', 'ABCDEF\nWXYZ', subdir='CVS'
-                      , mod_time=BEFORE )
-        self._makeFile( 'lhs', 'again.txt', 'GHIJKL', subdir='.svn'
-                      , mod_time=BEFORE )
-
-        self._makeFile( 'rhs', 'test.txt', 'ABCDEF\nQRST', subdir='CVS'
-                      , mod_time=AFTER )
-        self._makeFile( 'rhs', 'again.txt', 'MNOPQR', subdir='.svn'
-                      , mod_time=AFTER )
-
-        cd = self._makeOne( lhs, rhs )
-
-        diffs = cd.compare()
-
-        self.assertEqual( diffs, '' )
-
-    def test_compare_added_file_no_missing_as_empty( self ):
-
-        lhs = self._makeContext( 'lhs' )
-        rhs = self._makeContext( 'rhs' )
-
-        self._makeFile( 'lhs', 'test.txt', 'ABCDEF\nWXYZ' )
-        self._makeDirectory( 'lhs', subdir='sub' )
-        self._makeFile( 'rhs', 'test.txt', 'ABCDEF\nWXYZ' )
-        self._makeFile( 'rhs', 'again.txt', 'GHIJKL', subdir='sub' )
-
-        cd = self._makeOne( lhs, rhs )
-
-        diffs = cd.compare()
-
-        self.assertEqual( diffs, ADDED_FILE_DIFFS_NO_MAE )
-
-    def test_compare_added_file_missing_as_empty( self ):
-
-        AFTER = DateTime( '2004-02-29T23:59:59Z' )
-        lhs = self._makeContext( 'lhs' )
-        rhs = self._makeContext( 'rhs' )
-
-        self._makeFile( 'lhs', 'test.txt', 'ABCDEF\nWXYZ' )
-        self._makeDirectory( 'lhs', subdir='sub' )
-        self._makeFile( 'rhs', 'test.txt', 'ABCDEF\nWXYZ' )
-        self._makeFile( 'rhs', 'again.txt', 'GHIJKL', subdir='sub'
-                      , mod_time=AFTER )
-
-        cd = self._makeOne( lhs, rhs, missing_as_empty=True )
-
-        diffs = cd.compare()
-
-        self.assertEqual( diffs, ADDED_FILE_DIFFS_MAE % AFTER )
-
-    def test_compare_removed_file_no_missing_as_empty( self ):
-
-        lhs = self._makeContext( 'lhs' )
-        rhs = self._makeContext( 'rhs' )
-
-        self._makeFile( 'lhs', 'test.txt', 'ABCDEF\nWXYZ' )
-        self._makeFile( 'lhs', 'again.txt', 'GHIJKL', subdir='sub' )
-        self._makeFile( 'rhs', 'test.txt', 'ABCDEF\nWXYZ' )
-        self._makeDirectory( 'rhs', subdir='sub' )
-
-        cd = self._makeOne( lhs, rhs )
-
-        diffs = cd.compare()
-
-        self.assertEqual( diffs, REMOVED_FILE_DIFFS_NO_MAE )
-
-    def test_compare_removed_file_missing_as_empty( self ):
-
-        BEFORE = DateTime( '2004-01-01T00:00:00Z' )
-        lhs = self._makeContext( 'lhs' )
-        rhs = self._makeContext( 'rhs' )
-
-        self._makeFile( 'lhs', 'test.txt', 'ABCDEF\nWXYZ' )
-        self._makeFile( 'lhs', 'again.txt', 'GHIJKL', subdir='sub'
-                      , mod_time=BEFORE )
-        self._makeFile( 'rhs', 'test.txt', 'ABCDEF\nWXYZ' )
-        self._makeDirectory( 'rhs', subdir='sub' )
-
-        cd = self._makeOne( lhs, rhs, missing_as_empty=True )
-
-        diffs = cd.compare()
-
-        self.assertEqual( diffs, REMOVED_FILE_DIFFS_MAE % BEFORE )
-
-
-TEST_TXT_DIFFS = """\
-Index: test.txt
-===================================================================
---- test.txt %s
-+++ test.txt %s
-@@ -1,2 +1,2 @@
- ABCDEF
--WXYZ
-+QRST\
-"""
-
-ADDED_FILE_DIFFS_NO_MAE = """\
-** File sub/again.txt added
-"""
-
-ADDED_FILE_DIFFS_MAE = """\
-Index: sub/again.txt
-===================================================================
---- sub/again.txt 0
-+++ sub/again.txt %s
-@@ -1,0 +1,1 @@
-+GHIJKL\
-"""
-
-REMOVED_FILE_DIFFS_NO_MAE = """\
-** File sub/again.txt removed
-"""
-
-REMOVED_FILE_DIFFS_MAE = """\
-Index: sub/again.txt
-===================================================================
---- sub/again.txt %s
-+++ sub/again.txt 0
-@@ -1,1 +1,0 @@
--GHIJKL\
-"""
-
-
-def test_suite():
-    return unittest.TestSuite((
-        unittest.makeSuite( Test_unidiff ),
-        unittest.makeSuite( ConfigDiffTests ),
-        ))
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='test_suite')

Deleted: CMF/branches/goldegg-phase-1/CMFSetup/tests/test_registry.py
===================================================================
--- CMF/branches/goldegg-phase-1/CMFSetup/tests/test_registry.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/CMFSetup/tests/test_registry.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -1,1098 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 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.
-#
-##############################################################################
-""" Registry unit tests.
-
-$Id$
-"""
-
-import unittest
-import Testing
-import Zope2
-Zope2.startup()
-
-from OFS.Folder import Folder
-from Products.CMFSetup.tests.common import BaseRegistryTests
-from Products.CMFSetup import EXTENSION
-
-from conformance import ConformsToIStepRegistry
-from conformance import ConformsToIImportStepRegistry
-from conformance import ConformsToIExportStepRegistry
-from conformance import ConformsToIToolsetRegistry
-from conformance import ConformsToIProfileRegistry
-
-
-#==============================================================================
-#   Dummy handlers
-#==============================================================================
-def ONE_FUNC( context ): pass
-def TWO_FUNC( context ): pass
-def THREE_FUNC( context ): pass
-def FOUR_FUNC( context ): pass
-
-ONE_FUNC_NAME = '%s.%s' % ( __name__, ONE_FUNC.__name__ )
-TWO_FUNC_NAME = '%s.%s' % ( __name__, TWO_FUNC.__name__ )
-THREE_FUNC_NAME = '%s.%s' % ( __name__, THREE_FUNC.__name__ )
-FOUR_FUNC_NAME = '%s.%s' % ( __name__, FOUR_FUNC.__name__ )
-
-
-#==============================================================================
-#   SSR tests
-#==============================================================================
-class ImportStepRegistryTests( BaseRegistryTests
-                             , ConformsToIStepRegistry
-                             , ConformsToIImportStepRegistry
-                             ):
-
-    def _getTargetClass( self ):
-
-        from Products.CMFSetup.registry import ImportStepRegistry
-        return ImportStepRegistry
-
-    def test_empty( self ):
-
-        registry = self._makeOne()
-
-        self.assertEqual( len( registry.listSteps() ), 0 )
-        self.assertEqual( len( registry.listStepMetadata() ), 0 )
-        self.assertEqual( len( registry.sortSteps() ), 0 )
-
-    def test_getStep_nonesuch( self ):
-
-        registry = self._makeOne()
-
-        self.assertEqual( registry.getStep( 'nonesuch' ), None )
-        self.assertEqual( registry.getStep( 'nonesuch' ), None )
-        default = object()
-        self.failUnless( registry.getStepMetadata( 'nonesuch'
-                                                 , default ) is default )
-        self.failUnless( registry.getStep( 'nonesuch', default ) is default )
-        self.failUnless( registry.getStepMetadata( 'nonesuch'
-                                                 , default ) is default )
-
-    def test_getStep_defaulted( self ):
-
-        registry = self._makeOne()
-        default = object()
-
-        self.failUnless( registry.getStep( 'nonesuch', default ) is default )
-        self.assertEqual( registry.getStepMetadata( 'nonesuch', {} ), {} )
-
-    def test_registerStep_docstring( self ):
-
-        def func_with_doc( site ):
-            """This is the first line.
-
-            This is the second line.
-            """
-        FUNC_NAME = '%s.%s' % ( __name__, func_with_doc.__name__ )
-
-        registry = self._makeOne()
-
-        registry.registerStep( id='docstring'
-                             , version='1'
-                             , handler=func_with_doc
-                             , dependencies=()
-                             )
-
-        info = registry.getStepMetadata( 'docstring' )
-        self.assertEqual( info[ 'id' ], 'docstring' )
-        self.assertEqual( info[ 'handler' ], FUNC_NAME )
-        self.assertEqual( info[ 'dependencies' ], () )
-        self.assertEqual( info[ 'title' ], 'This is the first line.' )
-        self.assertEqual( info[ 'description' ] , 'This is the second line.' )
-
-    def test_registerStep_docstring_override( self ):
-
-        def func_with_doc( site ):
-            """This is the first line.
-
-            This is the second line.
-            """
-        FUNC_NAME = '%s.%s' % ( __name__, func_with_doc.__name__ )
-
-        registry = self._makeOne()
-
-        registry.registerStep( id='docstring'
-                             , version='1'
-                             , handler=func_with_doc
-                             , dependencies=()
-                             , title='Title'
-                             )
-
-        info = registry.getStepMetadata( 'docstring' )
-        self.assertEqual( info[ 'id' ], 'docstring' )
-        self.assertEqual( info[ 'handler' ], FUNC_NAME )
-        self.assertEqual( info[ 'dependencies' ], () )
-        self.assertEqual( info[ 'title' ], 'Title' )
-        self.assertEqual( info[ 'description' ] , 'This is the second line.' )
-
-    def test_registerStep_single( self ):
-
-        registry = self._makeOne()
-
-        registry.registerStep( id='one'
-                             , version='1'
-                             , handler=ONE_FUNC
-                             , dependencies=( 'two', 'three' )
-                             , title='One Step'
-                             , description='One small step'
-                             )
-
-        steps = registry.listSteps()
-        self.assertEqual( len( steps ), 1 )
-        self.failUnless( 'one' in steps )
-
-        sorted = registry.sortSteps()
-        self.assertEqual( len( sorted ), 1 )
-        self.assertEqual( sorted[ 0 ], 'one' )
-
-        self.assertEqual( registry.getStep( 'one' ), ONE_FUNC )
-
-        info = registry.getStepMetadata( 'one' )
-        self.assertEqual( info[ 'id' ], 'one' )
-        self.assertEqual( info[ 'version' ], '1' )
-        self.assertEqual( info[ 'handler' ], ONE_FUNC_NAME )
-        self.assertEqual( info[ 'dependencies' ], ( 'two', 'three' ) )
-        self.assertEqual( info[ 'title' ], 'One Step' )
-        self.assertEqual( info[ 'description' ], 'One small step' )
-
-        info_list = registry.listStepMetadata()
-        self.assertEqual( len( info_list ), 1 )
-        self.assertEqual( info, info_list[ 0 ] )
-
-    def test_registerStep_conflict( self ):
-
-        registry = self._makeOne()
-
-        registry.registerStep( id='one', version='1', handler=ONE_FUNC )
-
-        self.assertRaises( KeyError
-                         , registry.registerStep
-                         , id='one'
-                         , version='0'
-                         , handler=ONE_FUNC
-                         )
-
-        registry.registerStep( id='one', version='1', handler=ONE_FUNC )
-
-        info_list = registry.listStepMetadata()
-        self.assertEqual( len( info_list ), 1 )
-
-    def test_registerStep_replacement( self ):
-
-        registry = self._makeOne()
-
-        registry.registerStep( id='one'
-                             , version='1'
-                             , handler=ONE_FUNC
-                             , dependencies=( 'two', 'three' )
-                             , title='One Step'
-                             , description='One small step'
-                             )
-
-        registry.registerStep( id='one'
-                             , version='1.1'
-                             , handler=ONE_FUNC
-                             , dependencies=()
-                             , title='Leads to Another'
-                             , description='Another small step'
-                             )
-
-        info = registry.getStepMetadata( 'one' )
-        self.assertEqual( info[ 'id' ], 'one' )
-        self.assertEqual( info[ 'version' ], '1.1' )
-        self.assertEqual( info[ 'dependencies' ], () )
-        self.assertEqual( info[ 'title' ], 'Leads to Another' )
-        self.assertEqual( info[ 'description' ], 'Another small step' )
-
-    def test_registerStep_multiple( self ):
-
-        registry = self._makeOne()
-
-        registry.registerStep( id='one'
-                             , version='1'
-                             , handler=ONE_FUNC
-                             , dependencies=()
-                             )
-
-        registry.registerStep( id='two'
-                             , version='2'
-                             , handler=TWO_FUNC
-                             , dependencies=()
-                             )
-
-        registry.registerStep( id='three'
-                             , version='3'
-                             , handler=THREE_FUNC
-                             , dependencies=()
-                             )
-
-        steps = registry.listSteps()
-        self.assertEqual( len( steps ), 3 )
-        self.failUnless( 'one' in steps )
-        self.failUnless( 'two' in steps )
-        self.failUnless( 'three' in steps )
-
-    def test_sortStep_simple( self ):
-
-        registry = self._makeOne()
-
-        registry.registerStep( id='one'
-                             , version='1'
-                             , handler=ONE_FUNC
-                             , dependencies=( 'two', )
-                             )
-
-        registry.registerStep( id='two'
-                             , version='2'
-                             , handler=TWO_FUNC
-                             , dependencies=()
-                             )
-
-        steps = registry.sortSteps()
-        self.assertEqual( len( steps ), 2 )
-        one = steps.index( 'one' )
-        two = steps.index( 'two' )
-
-        self.failUnless( 0 <= two < one )
-
-    def test_sortStep_chained( self ):
-
-        registry = self._makeOne()
-
-        registry.registerStep( id='one'
-                             , version='1'
-                             , handler=ONE_FUNC
-                             , dependencies=( 'two', )
-                             , title='One small step'
-                             )
-
-        registry.registerStep( id='two'
-                             , version='2'
-                             , handler=TWO_FUNC
-                             , dependencies=( 'three', )
-                             , title='Texas two step'
-                             )
-
-        registry.registerStep( id='three'
-                             , version='3'
-                             , handler=THREE_FUNC
-                             , dependencies=()
-                             , title='Gimme three steps'
-                             )
-
-        steps = registry.sortSteps()
-        self.assertEqual( len( steps ), 3 )
-        one = steps.index( 'one' )
-        two = steps.index( 'two' )
-        three = steps.index( 'three' )
-
-        self.failUnless( 0 <= three < two < one )
-
-    def test_sortStep_complex( self ):
-
-        registry = self._makeOne()
-
-        registry.registerStep( id='one'
-                             , version='1'
-                             , handler=ONE_FUNC
-                             , dependencies=( 'two', )
-                             , title='One small step'
-                             )
-
-        registry.registerStep( id='two'
-                             , version='2'
-                             , handler=TWO_FUNC
-                             , dependencies=( 'four', )
-                             , title='Texas two step'
-                             )
-
-        registry.registerStep( id='three'
-                             , version='3'
-                             , handler=THREE_FUNC
-                             , dependencies=( 'four', )
-                             , title='Gimme three steps'
-                             )
-
-        registry.registerStep( id='four'
-                             , version='4'
-                             , handler=FOUR_FUNC
-                             , dependencies=()
-                             , title='Four step program'
-                             )
-
-        steps = registry.sortSteps()
-        self.assertEqual( len( steps ), 4 )
-        one = steps.index( 'one' )
-        two = steps.index( 'two' )
-        three = steps.index( 'three' )
-        four = steps.index( 'four' )
-
-        self.failUnless( 0 <= four < two < one )
-        self.failUnless( 0 <= four < three )
-
-    def test_sortStep_equivalence( self ):
-
-        registry = self._makeOne()
-
-        registry.registerStep( id='one'
-                             , version='1'
-                             , handler=ONE_FUNC
-                             , dependencies=( 'two', 'three' )
-                             , title='One small step'
-                             )
-
-        registry.registerStep( id='two'
-                             , version='2'
-                             , handler=TWO_FUNC
-                             , dependencies=( 'four', )
-                             , title='Texas two step'
-                             )
-
-        registry.registerStep( id='three'
-                             , version='3'
-                             , handler=THREE_FUNC
-                             , dependencies=( 'four', )
-                             , title='Gimme three steps'
-                             )
-
-        registry.registerStep( id='four'
-                             , version='4'
-                             , handler=FOUR_FUNC
-                             , dependencies=()
-                             , title='Four step program'
-                             )
-
-        steps = registry.sortSteps()
-        self.assertEqual( len( steps ), 4 )
-        one = steps.index( 'one' )
-        two = steps.index( 'two' )
-        three = steps.index( 'three' )
-        four = steps.index( 'four' )
-
-        self.failUnless( 0 <= four < two < one )
-        self.failUnless( 0 <= four < three < one )
-
-    def test_checkComplete_simple( self ):
-
-        registry = self._makeOne()
-
-        registry.registerStep( id='one'
-                             , version='1'
-                             , handler=ONE_FUNC
-                             , dependencies=( 'two', )
-                             )
-
-        incomplete = registry.checkComplete()
-        self.assertEqual( len( incomplete ), 1 )
-        self.failUnless( ( 'one', 'two' ) in incomplete )
-
-        registry.registerStep( id='two'
-                             , version='2'
-                             , handler=TWO_FUNC
-                             , dependencies=()
-                             )
-
-        self.assertEqual( len( registry.checkComplete() ), 0 )
-
-    def test_checkComplete_double( self ):
-
-        registry = self._makeOne()
-
-        registry.registerStep( id='one'
-                             , version='1'
-                             , handler=ONE_FUNC
-                             , dependencies=( 'two', 'three' )
-                             )
-
-        incomplete = registry.checkComplete()
-        self.assertEqual( len( incomplete ), 2 )
-        self.failUnless( ( 'one', 'two' ) in incomplete )
-        self.failUnless( ( 'one', 'three' ) in incomplete )
-
-        registry.registerStep( id='two'
-                             , version='2'
-                             , handler=TWO_FUNC
-                             , dependencies=()
-                             )
-
-        incomplete = registry.checkComplete()
-        self.assertEqual( len( incomplete ), 1 )
-        self.failUnless( ( 'one', 'three' ) in incomplete )
-
-        registry.registerStep( id='three'
-                             , version='3'
-                             , handler=THREE_FUNC
-                             , dependencies=()
-                             )
-
-        self.assertEqual( len( registry.checkComplete() ), 0 )
-
-        registry.registerStep( id='two'
-                             , version='2.1'
-                             , handler=TWO_FUNC
-                             , dependencies=( 'four', )
-                             )
-
-        incomplete = registry.checkComplete()
-        self.assertEqual( len( incomplete ), 1 )
-        self.failUnless( ( 'two', 'four' ) in incomplete )
-
-    def test_generateXML_empty( self ):
-
-        registry = self._makeOne().__of__( self.root )
-
-        xml = registry.generateXML()
-
-        self._compareDOM( registry.generateXML(), _EMPTY_IMPORT_XML )
-
-    def test_generateXML_single( self ):
-
-        registry = self._makeOne().__of__( self.root )
-
-        registry.registerStep( id='one'
-                             , version='1'
-                             , handler=ONE_FUNC
-                             , dependencies=()
-                             , title='One Step'
-                             , description='One small step'
-                             )
-
-        self._compareDOM( registry.generateXML(), _SINGLE_IMPORT_XML )
-
-    def test_generateXML_ordered( self ):
-
-        registry = self._makeOne().__of__( self.root )
-
-        registry.registerStep( id='one'
-                             , version='1'
-                             , handler=ONE_FUNC
-                             , dependencies=( 'two', )
-                             , title='One Step'
-                             , description='One small step'
-                             )
-
-        registry.registerStep( id='two'
-                             , version='2'
-                             , handler=TWO_FUNC
-                             , dependencies=( 'three', )
-                             , title='Two Steps'
-                             , description='Texas two step'
-                             )
-
-        registry.registerStep( id='three'
-                             , version='3'
-                             , handler=THREE_FUNC
-                             , dependencies=()
-                             , title='Three Steps'
-                             , description='Gimme three steps'
-                             )
-
-        self._compareDOM( registry.generateXML(), _ORDERED_IMPORT_XML )
-
-    def test_parseXML_empty( self ):
-
-        registry = self._makeOne().__of__( self.root )
-
-        registry.registerStep( id='one'
-                             , version='1'
-                             , handler=ONE_FUNC
-                             , dependencies=()
-                             , description='One small step'
-                             )
-
-        info_list = registry.parseXML( _EMPTY_IMPORT_XML )
-
-        self.assertEqual( len( info_list ), 0 )
-
-    def test_parseXML_single( self ):
-
-        registry = self._makeOne().__of__( self.root )
-
-        registry.registerStep( id='two'
-                             , version='2'
-                             , handler=TWO_FUNC
-                             , dependencies=()
-                             , title='Two Steps'
-                             , description='Texas two step'
-                             )
-
-        info_list = registry.parseXML( _SINGLE_IMPORT_XML )
-
-        self.assertEqual( len( info_list ), 1 )
-
-        info = info_list[ 0 ]
-        self.assertEqual( info[ 'id' ], 'one' )
-        self.assertEqual( info[ 'version' ], '1' )
-        self.assertEqual( info[ 'handler' ], ONE_FUNC_NAME )
-        self.assertEqual( info[ 'dependencies' ], () )
-        self.assertEqual( info[ 'title' ], 'One Step' )
-        self.failUnless( 'One small step' in info[ 'description' ] )
-
-
-_EMPTY_IMPORT_XML = """\
-<?xml version="1.0"?>
-<import-steps>
-</import-steps>
-"""
-
-_SINGLE_IMPORT_XML = """\
-<?xml version="1.0"?>
-<import-steps>
- <import-step id="one"
-             version="1"
-             handler="%s"
-             title="One Step">
-  One small step
- </import-step>
-</import-steps>
-""" % ( ONE_FUNC_NAME, )
-
-_ORDERED_IMPORT_XML = """\
-<?xml version="1.0"?>
-<import-steps>
- <import-step id="one"
-             version="1"
-             handler="%s"
-             title="One Step">
-  <dependency step="two" />
-  One small step
- </import-step>
- <import-step id="three"
-             version="3"
-             handler="%s"
-             title="Three Steps">
-  Gimme three steps
- </import-step>
- <import-step id="two"
-             version="2"
-             handler="%s"
-             title="Two Steps">
-  <dependency step="three" />
-  Texas two step
- </import-step>
-</import-steps>
-""" % ( ONE_FUNC_NAME, THREE_FUNC_NAME, TWO_FUNC_NAME )
-
-
-#==============================================================================
-#   ESR tests
-#==============================================================================
-class ExportStepRegistryTests( BaseRegistryTests
-                             , ConformsToIStepRegistry
-                             , ConformsToIExportStepRegistry
-                             ):
-
-    def _getTargetClass( self ):
-
-        from Products.CMFSetup.registry import ExportStepRegistry
-        return ExportStepRegistry
-
-    def _makeOne( self, *args, **kw ):
-
-        return self._getTargetClass()( *args, **kw )
-
-    def test_empty( self ):
-
-        registry = self._makeOne()
-        self.assertEqual( len( registry.listSteps() ), 0 )
-        self.assertEqual( len( registry.listStepMetadata() ), 0 )
-
-    def test_getStep_nonesuch( self ):
-
-        registry = self._makeOne()
-        self.assertEqual( registry.getStep( 'nonesuch' ), None )
-
-    def test_getStep_defaulted( self ):
-
-        registry = self._makeOne()
-        default = lambda x: false
-        self.assertEqual( registry.getStep( 'nonesuch', default ), default )
-
-    def test_getStepMetadata_nonesuch( self ):
-
-        registry = self._makeOne()
-        self.assertEqual( registry.getStepMetadata( 'nonesuch' ), None )
-
-    def test_getStepMetadata_defaulted( self ):
-
-        registry = self._makeOne()
-        self.assertEqual( registry.getStepMetadata( 'nonesuch', {} ), {} )
-
-    def test_registerStep_simple( self ):
-
-        registry = self._makeOne()
-        registry.registerStep( 'one', ONE_FUNC )
-        info = registry.getStepMetadata( 'one', {} )
-
-        self.assertEqual( info[ 'id' ], 'one' )
-        self.assertEqual( info[ 'handler' ], ONE_FUNC_NAME )
-        self.assertEqual( info[ 'title' ], 'one' )
-        self.assertEqual( info[ 'description' ], '' )
-
-    def test_registerStep_docstring( self ):
-
-        def func_with_doc( site ):
-            """This is the first line.
-
-            This is the second line.
-            """
-        FUNC_NAME = '%s.%s' % ( __name__, func_with_doc.__name__ )
-
-        registry = self._makeOne()
-        registry.registerStep( 'one', func_with_doc )
-        info = registry.getStepMetadata( 'one', {} )
-
-        self.assertEqual( info[ 'id' ], 'one' )
-        self.assertEqual( info[ 'handler' ], FUNC_NAME )
-        self.assertEqual( info[ 'title' ], 'This is the first line.' )
-        self.assertEqual( info[ 'description' ] , 'This is the second line.' )
-
-    def test_registerStep_docstring_with_override( self ):
-
-        def func_with_doc( site ):
-            """This is the first line.
-
-            This is the second line.
-            """
-        FUNC_NAME = '%s.%s' % ( __name__, func_with_doc.__name__ )
-
-        registry = self._makeOne()
-        registry.registerStep( 'one', func_with_doc
-                               , description='Description' )
-        info = registry.getStepMetadata( 'one', {} )
-
-        self.assertEqual( info[ 'id' ], 'one' )
-        self.assertEqual( info[ 'handler' ], FUNC_NAME )
-        self.assertEqual( info[ 'title' ], 'This is the first line.' )
-        self.assertEqual( info[ 'description' ], 'Description' )
-
-    def test_registerStep_collision( self ):
-
-        registry = self._makeOne()
-        registry.registerStep( 'one', ONE_FUNC )
-        registry.registerStep( 'one', TWO_FUNC )
-        info = registry.getStepMetadata( 'one', {} )
-
-        self.assertEqual( info[ 'id' ], 'one' )
-        self.assertEqual( info[ 'handler' ], TWO_FUNC_NAME )
-        self.assertEqual( info[ 'title' ], 'one' )
-        self.assertEqual( info[ 'description' ], '' )
-
-    def test_generateXML_empty( self ):
-
-        registry = self._makeOne().__of__( self.root )
-
-        xml = registry.generateXML()
-
-        self._compareDOM( registry.generateXML(), _EMPTY_EXPORT_XML )
-
-    def test_generateXML_single( self ):
-
-        registry = self._makeOne().__of__( self.root )
-
-        registry.registerStep( id='one'
-                             , handler=ONE_FUNC
-                             , title='One Step'
-                             , description='One small step'
-                             )
-
-        self._compareDOM( registry.generateXML(), _SINGLE_EXPORT_XML )
-
-    def test_generateXML_ordered( self ):
-
-        registry = self._makeOne().__of__( self.root )
-
-        registry.registerStep( id='one'
-                             , handler=ONE_FUNC
-                             , title='One Step'
-                             , description='One small step'
-                             )
-
-        registry.registerStep( id='two'
-                             , handler=TWO_FUNC
-                             , title='Two Steps'
-                             , description='Texas two step'
-                             )
-
-        registry.registerStep( id='three'
-                             , handler=THREE_FUNC
-                             , title='Three Steps'
-                             , description='Gimme three steps'
-                             )
-
-        self._compareDOM( registry.generateXML(), _ORDERED_EXPORT_XML )
-
-    def test_parseXML_empty( self ):
-
-        registry = self._makeOne().__of__( self.root )
-
-        registry.registerStep( id='one'
-                             , handler=ONE_FUNC
-                             , description='One small step'
-                             )
-
-        info_list = registry.parseXML( _EMPTY_EXPORT_XML )
-
-        self.assertEqual( len( info_list ), 0 )
-
-    def test_parseXML_single( self ):
-
-        registry = self._makeOne().__of__( self.root )
-
-        registry.registerStep( id='two'
-                             , handler=TWO_FUNC
-                             , title='Two Steps'
-                             , description='Texas two step'
-                             )
-
-        info_list = registry.parseXML( _SINGLE_EXPORT_XML )
-
-        self.assertEqual( len( info_list ), 1 )
-
-        info = info_list[ 0 ]
-        self.assertEqual( info[ 'id' ], 'one' )
-        self.assertEqual( info[ 'handler' ], ONE_FUNC_NAME )
-        self.assertEqual( info[ 'title' ], 'One Step' )
-        self.failUnless( 'One small step' in info[ 'description' ] )
-
-    def test_parseXML_single_as_ascii( self ):
-
-        registry = self._makeOne().__of__( self.root )
-
-        registry.registerStep( id='two'
-                             , handler=TWO_FUNC
-                             , title='Two Steps'
-                             , description='Texas two step'
-                             )
-
-        info_list = registry.parseXML( _SINGLE_EXPORT_XML, encoding='ascii' )
-
-        self.assertEqual( len( info_list ), 1 )
-
-        info = info_list[ 0 ]
-        self.assertEqual( info[ 'id' ], 'one' )
-        self.assertEqual( info[ 'handler' ], ONE_FUNC_NAME )
-        self.assertEqual( info[ 'title' ], 'One Step' )
-        self.failUnless( 'One small step' in info[ 'description' ] )
-
-
-_EMPTY_EXPORT_XML = """\
-<?xml version="1.0"?>
-<export-steps>
-</export-steps>
-"""
-
-_SINGLE_EXPORT_XML = """\
-<?xml version="1.0"?>
-<export-steps>
- <export-step id="one"
-                handler="%s"
-                title="One Step">
-  One small step
- </export-step>
-</export-steps>
-""" % ( ONE_FUNC_NAME, )
-
-_ORDERED_EXPORT_XML = """\
-<?xml version="1.0"?>
-<export-steps>
- <export-step id="one"
-                handler="%s"
-                title="One Step">
-  One small step
- </export-step>
- <export-step id="three"
-                handler="%s"
-                title="Three Steps">
-  Gimme three steps
- </export-step>
- <export-step id="two"
-                handler="%s"
-                title="Two Steps">
-  Texas two step
- </export-step>
-</export-steps>
-""" % ( ONE_FUNC_NAME, THREE_FUNC_NAME, TWO_FUNC_NAME )
-
-#==============================================================================
-#   ToolsetRegistry tests
-#==============================================================================
-class ToolsetRegistryTests( BaseRegistryTests
-                          , ConformsToIToolsetRegistry
-                          ):
-
-    def _getTargetClass( self ):
-
-        from Products.CMFSetup.registry import ToolsetRegistry
-        return ToolsetRegistry
-
-    def _initSite( self ):
-
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-
-        return site
-
-    def test_empty( self ):
-
-        site = self._initSite()
-        configurator = self._makeOne().__of__( site )
-
-        self.assertEqual( len( configurator.listForbiddenTools() ), 0 )
-        self.assertEqual( len( configurator.listRequiredTools() ), 0 )
-        self.assertEqual( len( configurator.listRequiredToolInfo() ), 0 )
-
-        self.assertRaises( KeyError
-                         , configurator.getRequiredToolInfo, 'nonesuch' )
-
-    def test_addForbiddenTool_multiple( self ):
-
-        VERBOTTEN = ( 'foo', 'bar', 'bam' )
-
-        site = self._initSite()
-        configurator = self._makeOne().__of__( site )
-
-        for verbotten in VERBOTTEN:
-            configurator.addForbiddenTool( verbotten )
-
-        self.assertEqual( len( configurator.listForbiddenTools() )
-                        , len( VERBOTTEN ) )
-
-        for verbotten in configurator.listForbiddenTools():
-            self.failUnless( verbotten in VERBOTTEN )
-
-    def test_addForbiddenTool_duplicate( self ):
-
-        site = self._initSite()
-        configurator = self._makeOne().__of__( site )
-
-        configurator.addForbiddenTool( 'once' )
-        configurator.addForbiddenTool( 'once' )
-
-        self.assertEqual( len( configurator.listForbiddenTools() ), 1 )
-
-    def test_addForbiddenTool_but_required( self ):
-
-        site = self._initSite()
-        configurator = self._makeOne().__of__( site )
-
-        configurator.addRequiredTool( 'required', 'some.dotted.name' )
-
-        self.assertRaises( ValueError
-                         , configurator.addForbiddenTool, 'required' )
-
-    def test_addRequiredTool_multiple( self ):
-
-        REQUIRED = ( ( 'one', 'path.to.one' )
-                   , ( 'two', 'path.to.two' )
-                   , ( 'three', 'path.to.three' )
-                   )
-
-        site = self._initSite()
-        configurator = self._makeOne().__of__( site )
-
-        for tool_id, dotted_name in REQUIRED:
-            configurator.addRequiredTool( tool_id, dotted_name )
-
-        self.assertEqual( len( configurator.listRequiredTools() )
-                        , len( REQUIRED ) )
-
-        for id in [ x[0] for x in REQUIRED ]:
-            self.failUnless( id in configurator.listRequiredTools() )
-
-        self.assertEqual( len( configurator.listRequiredToolInfo() )
-                        , len( REQUIRED ) )
-
-        for tool_id, dotted_name in REQUIRED:
-            info = configurator.getRequiredToolInfo( tool_id )
-            self.assertEqual( info[ 'id' ], tool_id )
-            self.assertEqual( info[ 'class' ], dotted_name )
-
-    def test_addRequiredTool_duplicate( self ):
-
-        site = self._initSite()
-        configurator = self._makeOne().__of__( site )
-
-        configurator.addRequiredTool( 'required', 'some.dotted.name' )
-        configurator.addRequiredTool( 'required', 'another.name' )
-
-        info = configurator.getRequiredToolInfo( 'required' )
-        self.assertEqual( info[ 'id' ], 'required' )
-        self.assertEqual( info[ 'class' ], 'another.name' )
-
-    def test_addRequiredTool_but_forbidden( self ):
-
-        site = self._initSite()
-        configurator = self._makeOne().__of__( site )
-
-        configurator.addForbiddenTool( 'forbidden' )
-
-        self.assertRaises( ValueError
-                         , configurator.addRequiredTool
-                         , 'forbidden'
-                         , 'a.name'
-                         )
-
-    def test_generateXML_empty( self ):
-
-        site = self._initSite()
-        configurator = self._makeOne().__of__( site )
-
-        self._compareDOM( configurator.generateXML(), _EMPTY_TOOLSET_XML )
-
-    def test_generateXML_normal( self ):
-
-        site = self._initSite()
-        configurator = self._makeOne().__of__( site )
-
-        configurator.addForbiddenTool( 'doomed' )
-        configurator.addRequiredTool( 'mandatory', 'path.to.one' )
-        configurator.addRequiredTool( 'obligatory', 'path.to.another' )
-
-        configurator.parseXML( _NORMAL_TOOLSET_XML )
-
-    def test_parseXML_empty( self ):
-
-        site = self._initSite()
-        configurator = self._makeOne().__of__( site )
-
-        configurator.parseXML( _EMPTY_TOOLSET_XML )
-
-        self.assertEqual( len( configurator.listForbiddenTools() ), 0 )
-        self.assertEqual( len( configurator.listRequiredTools() ), 0 )
-
-    def test_parseXML_normal( self ):
-
-        site = self._initSite()
-        configurator = self._makeOne().__of__( site )
-
-        configurator.parseXML( _NORMAL_TOOLSET_XML )
-
-        self.assertEqual( len( configurator.listForbiddenTools() ), 1 )
-        self.failUnless( 'doomed' in configurator.listForbiddenTools() )
-
-        self.assertEqual( len( configurator.listRequiredTools() ), 2 )
-
-        self.failUnless( 'mandatory' in configurator.listRequiredTools() )
-        info = configurator.getRequiredToolInfo( 'mandatory' )
-        self.assertEqual( info[ 'class' ], 'path.to.one' )
-
-        self.failUnless( 'obligatory' in configurator.listRequiredTools() )
-        info = configurator.getRequiredToolInfo( 'obligatory' )
-        self.assertEqual( info[ 'class' ], 'path.to.another' )
-
-    def test_parseXML_confused( self ):
-
-        site = self._initSite()
-        configurator = self._makeOne().__of__( site )
-
-        self.assertRaises( ValueError
-                         , configurator.parseXML, _CONFUSED_TOOLSET_XML )
-
-
-_EMPTY_TOOLSET_XML = """\
-<?xml version="1.0"?>
-<tool-setup>
-</tool-setup>
-"""
-
-_NORMAL_TOOLSET_XML = """\
-<?xml version="1.0"?>
-<tool-setup>
- <forbidden tool_id="doomed" />
- <required tool_id="mandatory" class="path.to.one" />
- <required tool_id="obligatory" class="path.to.another" />
-</tool-setup>
-"""
-
-_CONFUSED_TOOLSET_XML = """\
-<?xml version="1.0"?>
-<tool-setup>
- <forbidden tool_id="confused" />
- <required tool_id="confused" class="path.to.one" />
-</tool-setup>
-"""
-
-class ProfileRegistryTests( BaseRegistryTests
-                          , ConformsToIProfileRegistry
-                          ):
-
-    def _getTargetClass( self ):
-
-        from Products.CMFSetup.registry import ProfileRegistry
-        return ProfileRegistry
-
-    def test_empty( self ):
-
-        registry = self._makeOne()
-
-        self.assertEqual( len( registry.listProfiles() ), 0 )
-        self.assertEqual( len( registry.listProfiles() ), 0 )
-        self.assertRaises( KeyError, registry.getProfileInfo, 'nonesuch' )
-
-    def test_registerProfile_normal( self ):
-
-        NAME = 'one'
-        TITLE = 'One'
-        DESCRIPTION = 'One profile'
-        PATH = '/path/to/one'
-        PRODUCT = 'TestProduct'
-        PROFILE_TYPE = EXTENSION
-        PROFILE_ID = 'TestProduct:one'
-
-        registry = self._makeOne()
-        registry.registerProfile( NAME
-                                , TITLE
-                                , DESCRIPTION
-                                , PATH
-                                , PRODUCT
-                                , PROFILE_TYPE
-                                )
-
-        self.assertEqual( len( registry.listProfiles() ), 1 )
-        self.assertEqual( len( registry.listProfileInfo() ), 1 )
-
-        info = registry.getProfileInfo( PROFILE_ID )
-
-        self.assertEqual( info[ 'id' ], PROFILE_ID )
-        self.assertEqual( info[ 'title' ], TITLE )
-        self.assertEqual( info[ 'description' ], DESCRIPTION )
-        self.assertEqual( info[ 'path' ], PATH )
-        self.assertEqual( info[ 'product' ], PRODUCT )
-        self.assertEqual( info[ 'type' ], PROFILE_TYPE )
-
-    def test_registerProfile_duplicate( self ):
-
-        PROFILE_ID = 'one'
-        TITLE = 'One'
-        DESCRIPTION = 'One profile'
-        PATH = '/path/to/one'
-
-        registry = self._makeOne()
-        registry.registerProfile( PROFILE_ID, TITLE, DESCRIPTION, PATH )
-        self.assertRaises( KeyError
-                         , registry.registerProfile
-                         , PROFILE_ID, TITLE, DESCRIPTION, PATH )
-
-
-def test_suite():
-    return unittest.TestSuite((
-        unittest.makeSuite( ImportStepRegistryTests ),
-        unittest.makeSuite( ExportStepRegistryTests ),
-        unittest.makeSuite( ToolsetRegistryTests ),
-        unittest.makeSuite( ProfileRegistryTests ),
-        ))
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='test_suite')

Deleted: CMF/branches/goldegg-phase-1/CMFSetup/tests/test_rolemap.py
===================================================================
--- CMF/branches/goldegg-phase-1/CMFSetup/tests/test_rolemap.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/CMFSetup/tests/test_rolemap.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -1,784 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 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.
-#
-##############################################################################
-""" CMFSetup rolemap export / import unit tests
-
-$Id$
-"""
-
-import unittest
-import Testing
-import Zope2
-Zope2.startup()
-
-from OFS.Folder import Folder
-
-from common import BaseRegistryTests
-from common import DummyExportContext
-from common import DummyImportContext
-
-class RolemapConfiguratorTests( BaseRegistryTests ):
-
-    def _getTargetClass( self ):
-
-        from Products.CMFSetup.rolemap import RolemapConfigurator
-        return RolemapConfigurator
-
-    def test_listRoles_normal( self ):
-
-        EXPECTED = [ 'Anonymous', 'Authenticated', 'Manager', 'Owner' ]
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        configurator = self._makeOne( site )
-
-        roles = list( configurator.listRoles() )
-        self.assertEqual( len( roles ), len( EXPECTED ) )
-
-        roles.sort()
-
-        for found, expected in zip( roles, EXPECTED ):
-            self.assertEqual( found, expected )
-
-    def test_listRoles_added( self ):
-
-        EXPECTED = [ 'Anonymous', 'Authenticated', 'Manager', 'Owner', 'ZZZ' ]
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        existing_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
-        existing_roles.append( 'ZZZ' )
-        site.__ac_roles__ = existing_roles
-
-        configurator = self._makeOne( site )
-
-        roles = list( configurator.listRoles() )
-        self.assertEqual( len( roles ), len( EXPECTED ) )
-
-        roles.sort()
-
-        for found, expected in zip( roles, EXPECTED ):
-            self.assertEqual( found, expected )
-
-    def test_listPermissions_nooverrides( self ):
-
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        configurator = self._makeOne( site )
-
-        self.assertEqual( len( configurator.listPermissions() ), 0 )
-
-    def test_listPermissions_nooverrides( self ):
-
-        site = Folder( id='site' ).__of__( self.root )
-        configurator = self._makeOne( site )
-
-        self.assertEqual( len( configurator.listPermissions() ), 0 )
-
-    def test_listPermissions_acquire( self ):
-
-        ACI = 'Access contents information'
-        ROLES = [ 'Manager', 'Owner' ]
-
-        site = Folder( id='site' ).__of__( self.root )
-        site.manage_permission( ACI, ROLES, acquire=1 )
-        configurator = self._makeOne( site )
-
-        self.assertEqual( len( configurator.listPermissions() ), 1 )
-        info = configurator.listPermissions()[ 0 ]
-        self.assertEqual( info[ 'name' ], ACI )
-        self.assertEqual( info[ 'roles' ], ROLES )
-        self.failUnless( info[ 'acquire' ] )
-
-    def test_listPermissions_no_acquire( self ):
-
-        ACI = 'Access contents information'
-        ROLES = [ 'Manager', 'Owner' ]
-
-        site = Folder( id='site' ).__of__( self.root )
-        site.manage_permission( ACI, ROLES )
-        configurator = self._makeOne( site )
-
-        self.assertEqual( len( configurator.listPermissions() ), 1 )
-        info = configurator.listPermissions()[ 0 ]
-        self.assertEqual( info[ 'name' ], ACI )
-        self.assertEqual( info[ 'roles' ], ROLES )
-        self.failIf( info[ 'acquire' ] )
-
-    def test_generateXML_empty( self ):
-
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        configurator = self._makeOne( site ).__of__( site )
-
-        self._compareDOM( configurator.generateXML(), _EMPTY_EXPORT )
-
-    def test_generateXML_added_role( self ):
-
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        existing_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
-        existing_roles.append( 'ZZZ' )
-        site.__ac_roles__ = existing_roles
-        configurator = self._makeOne( site ).__of__( site )
-
-        self._compareDOM( configurator.generateXML(), _ADDED_ROLE_EXPORT )
-
-    def test_generateEXML_acquired_perm( self ):
-
-        ACI = 'Access contents information'
-        ROLES = [ 'Manager', 'Owner' ]
-
-        site = Folder( id='site' ).__of__( self.root )
-        site.manage_permission( ACI, ROLES, acquire=1 )
-        configurator = self._makeOne( site ).__of__( site )
-
-        self._compareDOM( configurator.generateXML(), _ACQUIRED_EXPORT )
-
-    def test_generateEXML_unacquired_perm( self ):
-
-        ACI = 'Access contents information'
-        ROLES = [ 'Manager', 'Owner', 'ZZZ' ]
-
-        site = Folder( id='site' ).__of__( self.root )
-        existing_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
-        existing_roles.append( 'ZZZ' )
-        site.__ac_roles__ = existing_roles
-        site.manage_permission( ACI, ROLES )
-        configurator = self._makeOne( site ).__of__( site )
-
-        self._compareDOM( configurator.generateXML(), _COMBINED_EXPORT )
-
-    def test_generateEXML_unacquired_perm_added_role( self ):
-
-        ACI = 'Access contents information'
-        ROLES = [ 'Manager', 'Owner' ]
-
-        site = Folder( id='site' ).__of__( self.root )
-        site.manage_permission( ACI, ROLES )
-        configurator = self._makeOne( site ).__of__( site )
-
-        self._compareDOM( configurator.generateXML(), _UNACQUIRED_EXPORT )
-
-    def test_parseXML_empty( self ):
-
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        existing_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
-        configurator = self._makeOne( site )
-
-        rolemap_info = configurator.parseXML( _EMPTY_EXPORT )
-
-        self.assertEqual( len( rolemap_info[ 'roles' ] ), 4 )
-        self.assertEqual( len( rolemap_info[ 'permissions' ] ), 0 )
-
-    def test_parseXML_added_role( self ):
-
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        configurator = self._makeOne( site )
-
-        rolemap_info = configurator.parseXML( _ADDED_ROLE_EXPORT )
-        roles = rolemap_info[ 'roles' ]
-
-        self.assertEqual( len( roles ), 5 )
-        self.failUnless( 'Anonymous' in roles )
-        self.failUnless( 'Authenticated' in roles )
-        self.failUnless( 'Manager' in roles )
-        self.failUnless( 'Owner' in roles )
-        self.failUnless( 'ZZZ' in roles )
-
-    def test_parseXML_acquired_permission( self ):
-
-        ACI = 'Access contents information'
-
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        configurator = self._makeOne( site )
-
-        rolemap_info = configurator.parseXML( _ACQUIRED_EXPORT )
-
-        self.assertEqual( len( rolemap_info[ 'permissions' ] ), 1 )
-        permission = rolemap_info[ 'permissions' ][ 0 ]
-
-        self.assertEqual( permission[ 'name' ], ACI )
-        self.failUnless( permission[ 'acquire' ] )
-
-        p_roles = permission[ 'roles' ]
-        self.assertEqual( len( p_roles ), 2 )
-        self.failUnless( 'Manager' in p_roles )
-        self.failUnless( 'Owner' in p_roles )
-
-    def test_parseXML_unacquired_permission( self ):
-
-        ACI = 'Access contents information'
-
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        configurator = self._makeOne( site )
-
-        rolemap_info = configurator.parseXML( _UNACQUIRED_EXPORT )
-
-        self.assertEqual( len( rolemap_info[ 'permissions' ] ), 1 )
-        permission = rolemap_info[ 'permissions' ][ 0 ]
-
-        self.assertEqual( permission[ 'name' ], ACI )
-        self.failIf( permission[ 'acquire' ] )
-
-        p_roles = permission[ 'roles' ]
-        self.assertEqual( len( p_roles ), 2 )
-        self.failUnless( 'Manager' in p_roles )
-        self.failUnless( 'Owner' in p_roles )
-
-    def test_parseXML_unacquired_permission_added_role( self ):
-
-        ACI = 'Access contents information'
-
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        configurator = self._makeOne( site )
-
-        rolemap_info = configurator.parseXML( _COMBINED_EXPORT )
-        roles = rolemap_info[ 'roles' ]
-
-        self.assertEqual( len( roles ), 5 )
-        self.failUnless( 'Anonymous' in roles )
-        self.failUnless( 'Authenticated' in roles )
-        self.failUnless( 'Manager' in roles )
-        self.failUnless( 'Owner' in roles )
-        self.failUnless( 'ZZZ' in roles )
-
-        self.assertEqual( len( rolemap_info[ 'permissions' ] ), 1 )
-        permission = rolemap_info[ 'permissions' ][ 0 ]
-
-        self.assertEqual( permission[ 'name' ], ACI )
-        self.failIf( permission[ 'acquire' ] )
-
-        p_roles = permission[ 'roles' ]
-        self.assertEqual( len( p_roles ), 3 )
-        self.failUnless( 'Manager' in p_roles )
-        self.failUnless( 'Owner' in p_roles )
-        self.failUnless( 'ZZZ' in p_roles )
-
-
-
-_EMPTY_EXPORT = """\
-<?xml version="1.0"?>
-<rolemap>
-  <roles>
-    <role name="Anonymous"/>
-    <role name="Authenticated"/>
-    <role name="Manager"/>
-    <role name="Owner"/>
-  </roles>
-  <permissions>
-  </permissions>
-</rolemap>
-"""
-
-_ADDED_ROLE_EXPORT = """\
-<?xml version="1.0"?>
-<rolemap>
-  <roles>
-    <role name="Anonymous"/>
-    <role name="Authenticated"/>
-    <role name="Manager"/>
-    <role name="Owner"/>
-    <role name="ZZZ"/>
-  </roles>
-  <permissions>
-  </permissions>
-</rolemap>
-"""
-
-_ACQUIRED_EXPORT = """\
-<?xml version="1.0"?>
-<rolemap>
-  <roles>
-    <role name="Anonymous"/>
-    <role name="Authenticated"/>
-    <role name="Manager"/>
-    <role name="Owner"/>
-  </roles>
-  <permissions>
-    <permission name="Access contents information"
-                acquire="True">
-      <role name="Manager"/>
-      <role name="Owner"/>
-    </permission>
-  </permissions>
-</rolemap>
-"""
-
-_UNACQUIRED_EXPORT = """\
-<?xml version="1.0"?>
-<rolemap>
-  <roles>
-    <role name="Anonymous"/>
-    <role name="Authenticated"/>
-    <role name="Manager"/>
-    <role name="Owner"/>
-  </roles>
-  <permissions>
-    <permission name="Access contents information"
-                acquire="False">
-      <role name="Manager"/>
-      <role name="Owner"/>
-    </permission>
-  </permissions>
-</rolemap>
-"""
-
-_COMBINED_EXPORT = """\
-<?xml version="1.0"?>
-<rolemap>
-  <roles>
-    <role name="Anonymous"/>
-    <role name="Authenticated"/>
-    <role name="Manager"/>
-    <role name="Owner"/>
-    <role name="ZZZ"/>
-  </roles>
-  <permissions>
-    <permission name="Access contents information"
-                acquire="False">
-      <role name="Manager"/>
-      <role name="Owner"/>
-      <role name="ZZZ"/>
-    </permission>
-  </permissions>
-</rolemap>
-"""
-
-class Test_exportRolemap( BaseRegistryTests ):
-
-    def test_unchanged( self ):
-
-        self.root.site = Folder( 'site' )
-        site = self.root.site
-
-        context = DummyExportContext( site )
-
-        from Products.CMFSetup.rolemap import exportRolemap
-        exportRolemap( context )
-
-        self.assertEqual( len( context._wrote ), 1 )
-        filename, text, content_type = context._wrote[ 0 ]
-        self.assertEqual( filename, 'rolemap.xml' )
-        self._compareDOM( text, _EMPTY_EXPORT )
-        self.assertEqual( content_type, 'text/xml' )
-
-    def test_added_role( self ):
-
-        self.root.site = Folder( 'site' )
-        site = self.root.site
-        existing_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
-        existing_roles.append( 'ZZZ' )
-        site.__ac_roles__ = existing_roles
-
-        context = DummyExportContext( site )
-
-        from Products.CMFSetup.rolemap import exportRolemap
-        exportRolemap( context )
-
-        self.assertEqual( len( context._wrote ), 1 )
-        filename, text, content_type = context._wrote[ 0 ]
-        self.assertEqual( filename, 'rolemap.xml' )
-        self._compareDOM( text, _ADDED_ROLE_EXPORT )
-        self.assertEqual( content_type, 'text/xml' )
-
-
-    def test_acquired_perm( self ):
-
-        ACI = 'Access contents information'
-        ROLES = [ 'Manager', 'Owner' ]
-
-        self.root.site = Folder( 'site' )
-        site = self.root.site
-        site.manage_permission( ACI, ROLES, acquire=1 )
-
-        context = DummyExportContext( site )
-
-        from Products.CMFSetup.rolemap import exportRolemap
-        exportRolemap( context )
-
-        self.assertEqual( len( context._wrote ), 1 )
-        filename, text, content_type = context._wrote[ 0 ]
-        self.assertEqual( filename, 'rolemap.xml' )
-        self._compareDOM( text, _ACQUIRED_EXPORT )
-        self.assertEqual( content_type, 'text/xml' )
-
-    def test_unacquired_perm( self ):
-
-        ACI = 'Access contents information'
-        ROLES = [ 'Manager', 'Owner', 'ZZZ' ]
-
-        self.root.site = Folder( 'site' )
-        site = self.root.site
-        existing_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
-        existing_roles.append( 'ZZZ' )
-        site.__ac_roles__ = existing_roles
-        site.manage_permission( ACI, ROLES )
-
-        context = DummyExportContext( site )
-
-        from Products.CMFSetup.rolemap import exportRolemap
-        exportRolemap( context )
-
-        self.assertEqual( len( context._wrote ), 1 )
-        filename, text, content_type = context._wrote[ 0 ]
-        self.assertEqual( filename, 'rolemap.xml' )
-        self._compareDOM( text, _COMBINED_EXPORT )
-        self.assertEqual( content_type, 'text/xml' )
-
-    def test_unacquired_perm_added_role( self ):
-
-        ACI = 'Access contents information'
-        ROLES = [ 'Manager', 'Owner' ]
-
-        self.root.site = Folder( 'site' )
-        site = self.root.site
-        site.manage_permission( ACI, ROLES )
-
-        context = DummyExportContext( site )
-
-        from Products.CMFSetup.rolemap import exportRolemap
-        exportRolemap( context )
-
-        self.assertEqual( len( context._wrote ), 1 )
-        filename, text, content_type = context._wrote[ 0 ]
-        self.assertEqual( filename, 'rolemap.xml' )
-        self._compareDOM( text, _UNACQUIRED_EXPORT )
-        self.assertEqual( content_type, 'text/xml' )
-
-class Test_importRolemap( BaseRegistryTests ):
-
-    def test_empty_default_purge( self ):
-
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        original_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
-        modified_roles = original_roles[:]
-        modified_roles.append( 'ZZZ' )
-        site.__ac_roles__ = modified_roles
-
-        context = DummyImportContext( site )
-        context._files[ 'rolemap.xml' ] = _EMPTY_EXPORT
-
-        from Products.CMFSetup.rolemap import importRolemap
-        importRolemap( context )
-
-        new_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
-
-        original_roles.sort()
-        new_roles.sort()
-
-        self.assertEqual( original_roles, new_roles )
-
-    def test_empty_explicit_purge( self ):
-
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        original_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
-        modified_roles = original_roles[:]
-        modified_roles.append( 'ZZZ' )
-        site.__ac_roles__ = modified_roles
-
-        context = DummyImportContext( site, True )
-        context._files[ 'rolemap.xml' ] = _EMPTY_EXPORT
-
-        from Products.CMFSetup.rolemap import importRolemap
-        importRolemap( context )
-
-        new_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
-
-        original_roles.sort()
-        new_roles.sort()
-
-        self.assertEqual( original_roles, new_roles )
-
-    def test_empty_skip_purge( self ):
-
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        original_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
-        modified_roles = original_roles[:]
-        modified_roles.append( 'ZZZ' )
-        site.__ac_roles__ = modified_roles
-
-        context = DummyImportContext( site, False )
-        context._files[ 'rolemap.xml' ] = _EMPTY_EXPORT
-
-        from Products.CMFSetup.rolemap import importRolemap
-        importRolemap( context )
-
-        new_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
-
-        modified_roles.sort()
-        new_roles.sort()
-
-        self.assertEqual( modified_roles, new_roles )
-
-    def test_acquired_permission_explicit_purge( self ):
-
-        ACI = 'Access contents information'
-        VIEW = 'View'
-
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        site.manage_permission( ACI, () )
-        site.manage_permission( VIEW, () )
-
-        existing_allowed = [ x[ 'name' ]
-                                for x in site.rolesOfPermission( ACI )
-                                if x[ 'selected' ] ]
-
-        self.assertEqual( existing_allowed, [] )
-
-        self.failIf( site.acquiredRolesAreUsedBy( ACI ) )
-        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
-
-        context = DummyImportContext( site, True )
-        context._files[ 'rolemap.xml' ] = _ACQUIRED_EXPORT
-
-        from Products.CMFSetup.rolemap import importRolemap
-        importRolemap( context )
-
-        new_allowed = [ x[ 'name' ]
-                           for x in site.rolesOfPermission( ACI )
-                           if x[ 'selected' ] ]
-
-        self.assertEqual( new_allowed, [ 'Manager', 'Owner' ] )
-
-        # ACI is overwritten by XML, but VIEW was purged
-        self.failUnless( site.acquiredRolesAreUsedBy( ACI ) )
-        self.failUnless( site.acquiredRolesAreUsedBy( VIEW ) )
-
-    def test_acquired_permission_no_purge( self ):
-
-        ACI = 'Access contents information'
-        VIEW = 'View'
-
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        site.manage_permission( ACI, () )
-        site.manage_permission( VIEW, () )
-
-        existing_allowed = [ x[ 'name' ]
-                                for x in site.rolesOfPermission( ACI )
-                                if x[ 'selected' ] ]
-
-        self.assertEqual( existing_allowed, [] )
-
-        self.failIf( site.acquiredRolesAreUsedBy( ACI ) )
-
-        context = DummyImportContext( site, False )
-        context._files[ 'rolemap.xml' ] = _ACQUIRED_EXPORT
-
-        from Products.CMFSetup.rolemap import importRolemap
-        importRolemap( context )
-
-        new_allowed = [ x[ 'name' ]
-                           for x in site.rolesOfPermission( ACI )
-                           if x[ 'selected' ] ]
-
-        self.assertEqual( new_allowed, [ 'Manager', 'Owner' ] )
-
-        # ACI is overwritten by XML, but VIEW is not
-        self.failUnless( site.acquiredRolesAreUsedBy( ACI ) )
-        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
-
-    def test_unacquired_permission_explicit_purge( self ):
-
-        ACI = 'Access contents information'
-        VIEW = 'View'
-
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        site.manage_permission( VIEW, () )
-
-        existing_allowed = [ x[ 'name' ]
-                                for x in site.rolesOfPermission( ACI )
-                                if x[ 'selected' ] ]
-
-        self.assertEqual( existing_allowed, [ 'Manager' ] )
-
-        self.failUnless( site.acquiredRolesAreUsedBy( ACI ) )
-        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
-
-        context = DummyImportContext( site, True )
-        context._files[ 'rolemap.xml' ] = _UNACQUIRED_EXPORT
-
-        from Products.CMFSetup.rolemap import importRolemap
-        importRolemap( context )
-
-        new_allowed = [ x[ 'name' ]
-                           for x in site.rolesOfPermission( ACI )
-                           if x[ 'selected' ] ]
-
-        self.assertEqual( new_allowed, [ 'Manager', 'Owner' ] )
-
-        self.failIf( site.acquiredRolesAreUsedBy( ACI ) )
-        self.failUnless( site.acquiredRolesAreUsedBy( VIEW ) )
-
-    def test_unacquired_permission_skip_purge( self ):
-
-        ACI = 'Access contents information'
-        VIEW = 'View'
-
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        site.manage_permission( VIEW, () )
-
-        existing_allowed = [ x[ 'name' ]
-                                for x in site.rolesOfPermission( ACI )
-                                if x[ 'selected' ] ]
-
-        self.assertEqual( existing_allowed, [ 'Manager' ] )
-
-        self.failUnless( site.acquiredRolesAreUsedBy( ACI ) )
-        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
-
-        context = DummyImportContext( site, False )
-        context._files[ 'rolemap.xml' ] = _UNACQUIRED_EXPORT
-
-        from Products.CMFSetup.rolemap import importRolemap
-        importRolemap( context )
-
-        new_allowed = [ x[ 'name' ]
-                           for x in site.rolesOfPermission( ACI )
-                           if x[ 'selected' ] ]
-
-        self.assertEqual( new_allowed, [ 'Manager', 'Owner' ] )
-
-        self.failIf( site.acquiredRolesAreUsedBy( ACI ) )
-        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
-
-    def test_unacquired_permission_added_role_explicit_purge( self ):
-
-        ACI = 'Access contents information'
-        VIEW = 'View'
-
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        site.manage_permission( VIEW, () )
-
-        existing_allowed = [ x[ 'name' ]
-                                for x in site.rolesOfPermission( ACI )
-                                if x[ 'selected' ] ]
-
-        self.assertEqual( existing_allowed, [ 'Manager' ] )
-
-        self.failUnless( site.acquiredRolesAreUsedBy( ACI ) )
-        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
-
-        self.failIf( site._has_user_defined_role( 'ZZZ' ) )
-
-        context = DummyImportContext( site, True )
-        context._files[ 'rolemap.xml' ] = _COMBINED_EXPORT
-
-        from Products.CMFSetup.rolemap import importRolemap
-        importRolemap( context )
-
-        self.failUnless( site._has_user_defined_role( 'ZZZ' ) )
-
-        new_allowed = [ x[ 'name' ]
-                           for x in site.rolesOfPermission( ACI )
-                           if x[ 'selected' ] ]
-
-        self.assertEqual( new_allowed, [ 'Manager', 'Owner', 'ZZZ' ] )
-
-        self.failIf( site.acquiredRolesAreUsedBy( ACI ) )
-        self.failUnless( site.acquiredRolesAreUsedBy( VIEW ) )
-
-    def test_unacquired_permission_added_role_skip_purge( self ):
-
-        ACI = 'Access contents information'
-        VIEW = 'View'
-
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        site.manage_permission( VIEW, () )
-
-        existing_allowed = [ x[ 'name' ]
-                                for x in site.rolesOfPermission( ACI )
-                                if x[ 'selected' ] ]
-
-        self.assertEqual( existing_allowed, [ 'Manager' ] )
-
-        self.failUnless( site.acquiredRolesAreUsedBy( ACI ) )
-        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
-
-        self.failIf( site._has_user_defined_role( 'ZZZ' ) )
-
-        context = DummyImportContext( site, False )
-        context._files[ 'rolemap.xml' ] = _COMBINED_EXPORT
-
-        from Products.CMFSetup.rolemap import importRolemap
-        importRolemap( context )
-
-        self.failUnless( site._has_user_defined_role( 'ZZZ' ) )
-
-        new_allowed = [ x[ 'name' ]
-                           for x in site.rolesOfPermission( ACI )
-                           if x[ 'selected' ] ]
-
-        self.assertEqual( new_allowed, [ 'Manager', 'Owner', 'ZZZ' ] )
-
-        self.failIf( site.acquiredRolesAreUsedBy( ACI ) )
-        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
-
-    def test_unacquired_permission_added_role_skip_purge_encode_ascii( self ):
-
-        ACI = 'Access contents information'
-        VIEW = 'View'
-
-        self.root.site = Folder( id='site' )
-        site = self.root.site
-        site.manage_permission( VIEW, () )
-
-        existing_allowed = [ x[ 'name' ]
-                                for x in site.rolesOfPermission( ACI )
-                                if x[ 'selected' ] ]
-
-        self.assertEqual( existing_allowed, [ 'Manager' ] )
-
-        self.failUnless( site.acquiredRolesAreUsedBy( ACI ) )
-        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
-
-        self.failIf( site._has_user_defined_role( 'ZZZ' ) )
-
-        context = DummyImportContext( site, False, encoding='ascii' )
-        context._files[ 'rolemap.xml' ] = _COMBINED_EXPORT
-
-        from Products.CMFSetup.rolemap import importRolemap
-        importRolemap( context )
-
-        self.failUnless( site._has_user_defined_role( 'ZZZ' ) )
-
-        new_allowed = [ x[ 'name' ]
-                           for x in site.rolesOfPermission( ACI )
-                           if x[ 'selected' ] ]
-
-        self.assertEqual( new_allowed, [ 'Manager', 'Owner', 'ZZZ' ] )
-
-        self.failIf( site.acquiredRolesAreUsedBy( ACI ) )
-        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
-
-
-def test_suite():
-    return unittest.TestSuite((
-        unittest.makeSuite( RolemapConfiguratorTests ),
-        unittest.makeSuite( Test_exportRolemap ),
-        unittest.makeSuite( Test_importRolemap ),
-        ))
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='test_suite')

Deleted: CMF/branches/goldegg-phase-1/CMFSetup/tests/test_tool.py
===================================================================
--- CMF/branches/goldegg-phase-1/CMFSetup/tests/test_tool.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/CMFSetup/tests/test_tool.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -1,911 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 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.
-#
-##############################################################################
-""" Unit tests for CMFSetup tool.
-
-$Id$
-"""
-
-import unittest
-import Testing
-import Zope2
-Zope2.startup()
-
-from StringIO import StringIO
-
-from Acquisition import aq_base
-from OFS.Folder import Folder
-
-from Products.CMFSetup import profile_registry
-
-from common import DOMComparator
-from common import DummyExportContext
-from common import DummyImportContext
-from common import FilesystemTestBase
-from common import SecurityRequestTest
-from common import TarballTester
-from conformance import ConformsToISetupTool
-
-
-class SetupToolTests( FilesystemTestBase
-                    , TarballTester
-                    , ConformsToISetupTool
-                    ):
-
-    _PROFILE_PATH = '/tmp/STT_test'
-
-    def setUp( self ):
-
-        FilesystemTestBase.setUp( self )
-        self._profile_registry_info = profile_registry._profile_info
-        self._profile_registry_ids = profile_registry._profile_ids
-        profile_registry.clear()
-
-    def tearDown( self ):
-
-        profile_registry._profile_info = self._profile_registry_info
-        profile_registry._profile_ids = self._profile_registry_ids
-        FilesystemTestBase.tearDown( self )
-
-    def _getTargetClass( self ):
-
-        from Products.CMFSetup.tool import SetupTool
-        return SetupTool
-
-    def _makeOne( self, *args, **kw ):
-
-        return self._getTargetClass()( *args, **kw )
-
-    def _makeSite( self, title="Don't care" ):
-
-        site = Folder()
-        site._setId( 'site' )
-        site.title = title
-
-        self.root._setObject( 'site', site )
-        return self.root._getOb( 'site' )
-
-    def test_empty( self ):
-
-        tool = self._makeOne()
-
-        self.assertEqual( tool.getImportContextID(), '' )
-
-        import_registry = tool.getImportStepRegistry()
-        self.assertEqual( len( import_registry.listSteps() ), 0 )
-
-        export_registry = tool.getExportStepRegistry()
-        export_steps = export_registry.listSteps()
-        self.assertEqual( len( export_steps ), 1 )
-        self.assertEqual( export_steps[ 0 ], 'step_registries' )
-
-        toolset_registry = tool.getToolsetRegistry()
-        self.assertEqual( len( toolset_registry.listForbiddenTools() ), 0 )
-        self.assertEqual( len( toolset_registry.listRequiredTools() ), 0 )
-
-    def test_getImportContextID( self ):
-
-        from Products.CMFSetup.tool import IMPORT_STEPS_XML
-        from Products.CMFSetup.tool import EXPORT_STEPS_XML
-        from Products.CMFSetup.tool import TOOLSET_XML
-        from test_registry import _EMPTY_IMPORT_XML
-        from test_registry import _EMPTY_EXPORT_XML
-        from test_registry import _EMPTY_TOOLSET_XML
-        from common import _makeTestFile
-
-        tool = self._makeOne()
-
-        _makeTestFile( IMPORT_STEPS_XML
-                     , self._PROFILE_PATH
-                     , _EMPTY_IMPORT_XML
-                     )
-
-        _makeTestFile( EXPORT_STEPS_XML
-                     , self._PROFILE_PATH
-                     , _EMPTY_EXPORT_XML
-                     )
-
-        _makeTestFile( TOOLSET_XML
-                     , self._PROFILE_PATH
-                     , _EMPTY_TOOLSET_XML
-                     )
-
-        profile_registry.registerProfile('foo', 'Foo', '', self._PROFILE_PATH)
-        tool.setImportContext('profile-other:foo')
-
-        self.assertEqual( tool.getImportContextID(), 'profile-other:foo' )
-
-    def test_setImportContext_invalid( self ):
-
-        tool = self._makeOne()
-
-        self.assertRaises( KeyError
-                         , tool.setImportContext
-                         , 'profile-foo'
-                         )
-
-    def test_setImportContext( self ):
-
-        from Products.CMFSetup.tool import IMPORT_STEPS_XML
-        from Products.CMFSetup.tool import EXPORT_STEPS_XML
-        from Products.CMFSetup.tool import TOOLSET_XML
-        from test_registry import _SINGLE_IMPORT_XML
-        from test_registry import _SINGLE_EXPORT_XML
-        from test_registry import _NORMAL_TOOLSET_XML
-        from test_registry import ONE_FUNC
-        from common import _makeTestFile
-
-        tool = self._makeOne()
-        tool.getExportStepRegistry().clear()
-
-        _makeTestFile( IMPORT_STEPS_XML
-                     , self._PROFILE_PATH
-                     , _SINGLE_IMPORT_XML
-                     )
-
-        _makeTestFile( EXPORT_STEPS_XML
-                     , self._PROFILE_PATH
-                     , _SINGLE_EXPORT_XML
-                     )
-
-        _makeTestFile( TOOLSET_XML
-                     , self._PROFILE_PATH
-                     , _NORMAL_TOOLSET_XML
-                     )
-
-        profile_registry.registerProfile('foo', 'Foo', '', self._PROFILE_PATH)
-        tool.setImportContext('profile-other:foo')
-
-        self.assertEqual( tool.getImportContextID(), 'profile-other:foo' )
-
-        import_registry = tool.getImportStepRegistry()
-        self.assertEqual( len( import_registry.listSteps() ), 1 )
-        self.failUnless( 'one' in import_registry.listSteps() )
-        info = import_registry.getStepMetadata( 'one' )
-        self.assertEqual( info[ 'id' ], 'one' )
-        self.assertEqual( info[ 'title' ], 'One Step' )
-        self.assertEqual( info[ 'version' ], '1' )
-        self.failUnless( 'One small step' in info[ 'description' ] )
-        self.assertEqual( info[ 'handler' ]
-                        , 'Products.CMFSetup.tests.test_registry.ONE_FUNC' )
-
-        self.assertEqual( import_registry.getStep( 'one' ), ONE_FUNC )
-
-        export_registry = tool.getExportStepRegistry()
-        self.assertEqual( len( export_registry.listSteps() ), 1 )
-        self.failUnless( 'one' in import_registry.listSteps() )
-        info = export_registry.getStepMetadata( 'one' )
-        self.assertEqual( info[ 'id' ], 'one' )
-        self.assertEqual( info[ 'title' ], 'One Step' )
-        self.failUnless( 'One small step' in info[ 'description' ] )
-        self.assertEqual( info[ 'handler' ]
-                        , 'Products.CMFSetup.tests.test_registry.ONE_FUNC' )
-
-        self.assertEqual( export_registry.getStep( 'one' ), ONE_FUNC )
-
-        toolset = tool.getToolsetRegistry()
-        self.assertEqual( len( toolset.listForbiddenTools() ), 1 )
-        self.failUnless( 'doomed' in toolset.listForbiddenTools() )
-        self.assertEqual( len( toolset.listRequiredTools() ), 2 )
-        self.failUnless( 'mandatory' in toolset.listRequiredTools() )
-        info = toolset.getRequiredToolInfo( 'mandatory' )
-        self.assertEqual( info[ 'class' ], 'path.to.one' )
-        self.failUnless( 'obligatory' in toolset.listRequiredTools() )
-        info = toolset.getRequiredToolInfo( 'obligatory' )
-        self.assertEqual( info[ 'class' ], 'path.to.another' )
-
-    def test_runImportStep_nonesuch( self ):
-
-        site = self._makeSite()
-
-        tool = self._makeOne().__of__( site )
-
-        self.assertRaises( ValueError, tool.runImportStep, 'nonesuch' )
-
-    def test_runImportStep_simple( self ):
-
-        TITLE = 'original title'
-        site = self._makeSite( TITLE )
-
-        tool = self._makeOne().__of__( site )
-
-        registry = tool.getImportStepRegistry()
-        registry.registerStep( 'simple', '1', _uppercaseSiteTitle )
-
-        result = tool.runImportStep( 'simple' )
-
-        self.assertEqual( len( result[ 'steps' ] ), 1 )
-
-        self.assertEqual( result[ 'steps' ][ 0 ], 'simple' )
-        self.assertEqual( result[ 'messages' ][ 'simple' ]
-                        , 'Uppercased title' )
-
-        self.assertEqual( site.title, TITLE.upper() )
-
-    def test_runImportStep_dependencies( self ):
-
-        TITLE = 'original title'
-        site = self._makeSite( TITLE )
-
-        tool = self._makeOne().__of__( site )
-
-        registry = tool.getImportStepRegistry()
-        registry.registerStep( 'dependable', '1', _underscoreSiteTitle )
-        registry.registerStep( 'dependent', '1'
-                             , _uppercaseSiteTitle, ( 'dependable', ) )
-
-        result = tool.runImportStep( 'dependent' )
-
-        self.assertEqual( len( result[ 'steps' ] ), 2 )
-
-        self.assertEqual( result[ 'steps' ][ 0 ], 'dependable' )
-        self.assertEqual( result[ 'messages' ][ 'dependable' ]
-                        , 'Underscored title' )
-
-        self.assertEqual( result[ 'steps' ][ 1 ], 'dependent' )
-        self.assertEqual( result[ 'messages' ][ 'dependent' ]
-                        , 'Uppercased title' )
-        self.assertEqual( site.title, TITLE.replace( ' ', '_' ).upper() )
-
-    def test_runImportStep_skip_dependencies( self ):
-
-        TITLE = 'original title'
-        site = self._makeSite( TITLE )
-
-        tool = self._makeOne().__of__( site )
-
-        registry = tool.getImportStepRegistry()
-        registry.registerStep( 'dependable', '1', _underscoreSiteTitle )
-        registry.registerStep( 'dependent', '1'
-                             , _uppercaseSiteTitle, ( 'dependable', ) )
-
-        result = tool.runImportStep( 'dependent', run_dependencies=False )
-
-        self.assertEqual( len( result[ 'steps' ] ), 1 )
-
-        self.assertEqual( result[ 'steps' ][ 0 ], 'dependent' )
-        self.assertEqual( result[ 'messages' ][ 'dependent' ]
-                        , 'Uppercased title' )
-
-        self.assertEqual( site.title, TITLE.upper() )
-
-    def test_runImportStep_default_purge( self ):
-
-        site = self._makeSite()
-
-        tool = self._makeOne().__of__( site )
-        registry = tool.getImportStepRegistry()
-        registry.registerStep( 'purging', '1', _purgeIfRequired )
-
-        result = tool.runImportStep( 'purging' )
-
-        self.assertEqual( len( result[ 'steps' ] ), 1 )
-        self.assertEqual( result[ 'steps' ][ 0 ], 'purging' )
-        self.assertEqual( result[ 'messages' ][ 'purging' ], 'Purged' )
-        self.failUnless( site.purged )
-
-    def test_runImportStep_explicit_purge( self ):
-
-        site = self._makeSite()
-
-        tool = self._makeOne().__of__( site )
-        registry = tool.getImportStepRegistry()
-        registry.registerStep( 'purging', '1', _purgeIfRequired )
-
-        result = tool.runImportStep( 'purging', purge_old=True )
-
-        self.assertEqual( len( result[ 'steps' ] ), 1 )
-        self.assertEqual( result[ 'steps' ][ 0 ], 'purging' )
-        self.assertEqual( result[ 'messages' ][ 'purging' ], 'Purged' )
-        self.failUnless( site.purged )
-
-    def test_runImportStep_skip_purge( self ):
-
-        site = self._makeSite()
-
-        tool = self._makeOne().__of__( site )
-        registry = tool.getImportStepRegistry()
-        registry.registerStep( 'purging', '1', _purgeIfRequired )
-
-        result = tool.runImportStep( 'purging', purge_old=False )
-
-        self.assertEqual( len( result[ 'steps' ] ), 1 )
-        self.assertEqual( result[ 'steps' ][ 0 ], 'purging' )
-        self.assertEqual( result[ 'messages' ][ 'purging' ], 'Unpurged' )
-        self.failIf( site.purged )
-
-    def test_runImportStep_consistent_context( self ):
-
-        site = self._makeSite()
-
-        tool = self._makeOne().__of__( site )
-
-        registry = tool.getImportStepRegistry()
-        registry.registerStep( 'purging', '1', _purgeIfRequired )
-        registry.registerStep( 'dependent', '1'
-                             , _uppercaseSiteTitle, ( 'purging', ) )
-
-        result = tool.runImportStep( 'dependent', purge_old=False )
-        self.failIf( site.purged )
-
-    def test_runAllImportSteps_empty( self ):
-
-        site = self._makeSite()
-        tool = self._makeOne().__of__( site )
-
-        result = tool.runAllImportSteps()
-
-        self.assertEqual( len( result[ 'steps' ] ), 0 )
-
-    def test_runAllImportSteps_sorted_default_purge( self ):
-
-        TITLE = 'original title'
-        site = self._makeSite( TITLE )
-        tool = self._makeOne().__of__( site )
-
-        registry = tool.getImportStepRegistry()
-        registry.registerStep( 'dependable', '1'
-                             , _underscoreSiteTitle, ( 'purging', ) )
-        registry.registerStep( 'dependent', '1'
-                             , _uppercaseSiteTitle, ( 'dependable', ) )
-        registry.registerStep( 'purging', '1'
-                             , _purgeIfRequired )
-
-        result = tool.runAllImportSteps()
-
-        self.assertEqual( len( result[ 'steps' ] ), 3 )
-
-        self.assertEqual( result[ 'steps' ][ 0 ], 'purging' )
-        self.assertEqual( result[ 'messages' ][ 'purging' ]
-                        , 'Purged' )
-
-        self.assertEqual( result[ 'steps' ][ 1 ], 'dependable' )
-        self.assertEqual( result[ 'messages' ][ 'dependable' ]
-                        , 'Underscored title' )
-
-        self.assertEqual( result[ 'steps' ][ 2 ], 'dependent' )
-        self.assertEqual( result[ 'messages' ][ 'dependent' ]
-                        , 'Uppercased title' )
-
-        self.assertEqual( site.title, TITLE.replace( ' ', '_' ).upper() )
-        self.failUnless( site.purged )
-
-    def test_runAllImportSteps_sorted_explicit_purge( self ):
-
-        site = self._makeSite()
-        tool = self._makeOne().__of__( site )
-
-        registry = tool.getImportStepRegistry()
-        registry.registerStep( 'dependable', '1'
-                             , _underscoreSiteTitle, ( 'purging', ) )
-        registry.registerStep( 'dependent', '1'
-                             , _uppercaseSiteTitle, ( 'dependable', ) )
-        registry.registerStep( 'purging', '1'
-                             , _purgeIfRequired )
-
-        result = tool.runAllImportSteps( purge_old=True )
-
-        self.assertEqual( len( result[ 'steps' ] ), 3 )
-
-        self.assertEqual( result[ 'steps' ][ 0 ], 'purging' )
-        self.assertEqual( result[ 'messages' ][ 'purging' ]
-                        , 'Purged' )
-
-        self.assertEqual( result[ 'steps' ][ 1 ], 'dependable' )
-        self.assertEqual( result[ 'steps' ][ 2 ], 'dependent' )
-        self.failUnless( site.purged )
-
-    def test_runAllImportSteps_sorted_skip_purge( self ):
-
-        site = self._makeSite()
-        tool = self._makeOne().__of__( site )
-
-        registry = tool.getImportStepRegistry()
-        registry.registerStep( 'dependable', '1'
-                             , _underscoreSiteTitle, ( 'purging', ) )
-        registry.registerStep( 'dependent', '1'
-                             , _uppercaseSiteTitle, ( 'dependable', ) )
-        registry.registerStep( 'purging', '1'
-                             , _purgeIfRequired )
-
-        result = tool.runAllImportSteps( purge_old=False )
-
-        self.assertEqual( len( result[ 'steps' ] ), 3 )
-
-        self.assertEqual( result[ 'steps' ][ 0 ], 'purging' )
-        self.assertEqual( result[ 'messages' ][ 'purging' ]
-                        , 'Unpurged' )
-
-        self.assertEqual( result[ 'steps' ][ 1 ], 'dependable' )
-        self.assertEqual( result[ 'steps' ][ 2 ], 'dependent' )
-        self.failIf( site.purged )
-
-    def test_runExportStep_nonesuch( self ):
-
-        site = self._makeSite()
-        tool = self._makeOne().__of__( site )
-
-        self.assertRaises( ValueError, tool.runExportStep, 'nonesuch' )
-
-    def test_runExportStep_step_registry( self ):
-
-        from test_registry import _EMPTY_IMPORT_XML
-
-        site = self._makeSite()
-        site.portal_setup = self._makeOne()
-        tool = site.portal_setup
-
-        result = tool.runExportStep( 'step_registries' )
-
-        self.assertEqual( len( result[ 'steps' ] ), 1 )
-        self.assertEqual( result[ 'steps' ][ 0 ], 'step_registries' )
-        self.assertEqual( result[ 'messages' ][ 'step_registries' ]
-                        , 'Step registries exported'
-                        )
-        fileish = StringIO( result[ 'tarball' ] )
-
-        self._verifyTarballContents( fileish, [ 'import_steps.xml'
-                                              , 'export_steps.xml'
-                                              ] )
-        self._verifyTarballEntryXML( fileish, 'import_steps.xml'
-                                   , _EMPTY_IMPORT_XML )
-        self._verifyTarballEntryXML( fileish, 'export_steps.xml'
-                                   , _DEFAULT_STEP_REGISTRIES_EXPORT_XML )
-
-    def test_runAllExportSteps_default( self ):
-
-        from test_registry import _EMPTY_IMPORT_XML
-
-        site = self._makeSite()
-        site.portal_setup = self._makeOne()
-        tool = site.portal_setup
-
-        result = tool.runAllExportSteps()
-
-        self.assertEqual( len( result[ 'steps' ] ), 1 )
-        self.assertEqual( result[ 'steps' ][ 0 ], 'step_registries' )
-        self.assertEqual( result[ 'messages' ][ 'step_registries' ]
-                        , 'Step registries exported'
-                        )
-        fileish = StringIO( result[ 'tarball' ] )
-
-        self._verifyTarballContents( fileish, [ 'import_steps.xml'
-                                              , 'export_steps.xml'
-                                              ] )
-        self._verifyTarballEntryXML( fileish, 'import_steps.xml'
-                                   , _EMPTY_IMPORT_XML )
-        self._verifyTarballEntryXML( fileish, 'export_steps.xml'
-                                   , _DEFAULT_STEP_REGISTRIES_EXPORT_XML )
-
-    def test_runAllExportSteps_extras( self ):
-
-        from test_registry import _EMPTY_IMPORT_XML
-
-        site = self._makeSite()
-        site.portal_setup = self._makeOne()
-        tool = site.portal_setup
-
-        import_reg = tool.getImportStepRegistry()
-        import_reg.registerStep( 'dependable', '1'
-                               , _underscoreSiteTitle, ( 'purging', ) )
-        import_reg.registerStep( 'dependent', '1'
-                               , _uppercaseSiteTitle, ( 'dependable', ) )
-        import_reg.registerStep( 'purging', '1'
-                               , _purgeIfRequired )
-
-        export_reg = tool.getExportStepRegistry()
-        export_reg.registerStep( 'properties'
-                               , _exportPropertiesINI )
-
-        result = tool.runAllExportSteps()
-
-        self.assertEqual( len( result[ 'steps' ] ), 2 )
-
-        self.failUnless( 'properties' in result[ 'steps' ] )
-        self.assertEqual( result[ 'messages' ][ 'properties' ]
-                        , 'Exported properties'
-                        )
-
-        self.failUnless( 'step_registries' in result[ 'steps' ] )
-        self.assertEqual( result[ 'messages' ][ 'step_registries' ]
-                        , 'Step registries exported'
-                        )
-
-        fileish = StringIO( result[ 'tarball' ] )
-
-        self._verifyTarballContents( fileish, [ 'import_steps.xml'
-                                              , 'export_steps.xml'
-                                              , 'properties.ini'
-                                              ] )
-        self._verifyTarballEntryXML( fileish, 'import_steps.xml'
-                                   , _EXTRAS_STEP_REGISTRIES_IMPORT_XML )
-        self._verifyTarballEntryXML( fileish, 'export_steps.xml'
-                                   , _EXTRAS_STEP_REGISTRIES_EXPORT_XML )
-        self._verifyTarballEntry( fileish, 'properties.ini'
-                                , _PROPERTIES_INI % site.title  )
-
-    def test_createSnapshot_default( self ):
-
-        from test_registry import _EMPTY_IMPORT_XML
-
-        _EXPECTED = [ ( 'import_steps.xml', _EMPTY_IMPORT_XML )
-                    , ( 'export_steps.xml'
-                      , _DEFAULT_STEP_REGISTRIES_EXPORT_XML
-                      )
-                    ]
-
-        site = self._makeSite()
-        site.portal_setup = self._makeOne()
-        tool = site.portal_setup
-
-        self.assertEqual( len( tool.listSnapshotInfo() ), 0 )
-
-        result = tool.createSnapshot( 'default' )
-
-        self.assertEqual( len( result[ 'steps' ] ), 1 )
-        self.assertEqual( result[ 'steps' ][ 0 ], 'step_registries' )
-        self.assertEqual( result[ 'messages' ][ 'step_registries' ]
-                        , 'Step registries exported'
-                        )
-
-        snapshot = result[ 'snapshot' ]
-
-        self.assertEqual( len( snapshot.objectIds() ), len( _EXPECTED ) )
-
-        for id in [ x[0] for x in _EXPECTED ]:
-            self.failUnless( id in snapshot.objectIds() )
-
-        def normalize_xml(xml):
-            # using this might mask a real problem on windows, but so far the
-            # different newlines just caused problems in this test
-            lines = [ line for line in xml.splitlines() if line ]
-            return '\n'.join(lines) + '\n'
-
-        fileobj = snapshot._getOb( 'import_steps.xml' )
-        self.assertEqual( normalize_xml( fileobj.read() ),
-                          _EMPTY_IMPORT_XML )
-
-        fileobj = snapshot._getOb( 'export_steps.xml' )
-
-        self.assertEqual( normalize_xml( fileobj.read() ),
-                          _DEFAULT_STEP_REGISTRIES_EXPORT_XML )
-
-        self.assertEqual( len( tool.listSnapshotInfo() ), 1 )
-
-        info = tool.listSnapshotInfo()[ 0 ]
-
-        self.assertEqual( info[ 'id' ], 'default' )
-        self.assertEqual( info[ 'title' ], 'default' )
-
-
-_DEFAULT_STEP_REGISTRIES_EXPORT_XML = """\
-<?xml version="1.0"?>
-<export-steps>
- <export-step id="step_registries"
-              handler="Products.CMFSetup.tool.exportStepRegistries"
-              title="Export import / export steps.">
-  
- </export-step>
-</export-steps>
-"""
-
-_EXTRAS_STEP_REGISTRIES_EXPORT_XML = """\
-<?xml version="1.0"?>
-<export-steps>
- <export-step id="properties"
-              handler="Products.CMFSetup.tests.test_tool._exportPropertiesINI"
-              title="properties">
-
- </export-step>
- <export-step id="step_registries"
-              handler="Products.CMFSetup.tool.exportStepRegistries"
-              title="Export import / export steps.">
-
- </export-step>
-</export-steps>
-"""
-
-_EXTRAS_STEP_REGISTRIES_IMPORT_XML = """\
-<?xml version="1.0"?>
-<import-steps>
- <import-step id="dependable"
-              version="1"
-              handler="Products.CMFSetup.tests.test_tool._underscoreSiteTitle"
-              title="dependable">
-  <dependency step="purging" />
-
- </import-step>
- <import-step id="dependent"
-              version="1"
-              handler="Products.CMFSetup.tests.test_tool._uppercaseSiteTitle"
-              title="dependent">
-  <dependency step="dependable" />
-
- </import-step>
- <import-step id="purging"
-              version="1"
-              handler="Products.CMFSetup.tests.test_tool._purgeIfRequired"
-              title="purging">
-
- </import-step>
-</import-steps>
-"""
-
-_PROPERTIES_INI = """\
-[Default]
-Title=%s
-"""
-
-def _underscoreSiteTitle( context ):
-
-    site = context.getSite()
-    site.title = site.title.replace( ' ', '_' )
-    return 'Underscored title'
-
-def _uppercaseSiteTitle( context ):
-
-    site = context.getSite()
-    site.title = site.title.upper()
-    return 'Uppercased title'
-
-def _purgeIfRequired( context ):
-
-    site = context.getSite()
-    purged = site.purged = context.shouldPurge()
-    return purged and 'Purged' or 'Unpurged'
-
-def _exportPropertiesINI( context ):
-
-    site = context.getSite()
-    text = _PROPERTIES_INI % site.title
-
-    context.writeDataFile( 'properties.ini', text, 'text/plain' )
-
-    return 'Exported properties'
-
-class _ToolsetSetup( SecurityRequestTest ):
-
-    def _initSite( self ):
-
-        from Products.CMFSetup.tool import SetupTool
-        site = Folder()
-        site._setId( 'site' )
-        self.root._setObject( 'site', site )
-        site = self.root._getOb( 'site' )
-        site._setObject( 'portal_setup', SetupTool() )
-        return site
-
-class Test_exportToolset( _ToolsetSetup
-                        , DOMComparator
-                        ):
-
-    def test_empty( self ):
-
-        from Products.CMFSetup.tool import TOOLSET_XML
-        from Products.CMFSetup.tool import exportToolset
-
-        site = self._initSite()
-        context = DummyExportContext( site )
-
-        exportToolset( context )
-
-        self.assertEqual( len( context._wrote ), 1 )
-        filename, text, content_type = context._wrote[ 0 ]
-        self.assertEqual( filename, TOOLSET_XML )
-        self._compareDOM( text, _EMPTY_TOOLSET_XML )
-        self.assertEqual( content_type, 'text/xml' )
-
-    def test_normal( self ):
-
-        from Products.CMFSetup.tool import TOOLSET_XML
-        from Products.CMFSetup.tool import exportToolset
-
-        site = self._initSite()
-        toolset = site.portal_setup.getToolsetRegistry()
-        toolset.addForbiddenTool( 'doomed' )
-        toolset.addRequiredTool( 'mandatory', 'path.to.one' )
-        toolset.addRequiredTool( 'obligatory', 'path.to.another' )
-
-        context = DummyExportContext( site )
-
-        exportToolset( context )
-
-        self.assertEqual( len( context._wrote ), 1 )
-        filename, text, content_type = context._wrote[ 0 ]
-        self.assertEqual( filename, TOOLSET_XML )
-        self._compareDOM( text, _NORMAL_TOOLSET_XML )
-        self.assertEqual( content_type, 'text/xml' )
-
-class Test_importToolset( _ToolsetSetup ):
-
-    def test_tool_ids( self ):
-        # The tool import mechanism used to rely on the fact that all tools
-        # have unique IDs set at the class level and that you can call their
-        # constructor with no arguments. However, there might be tools 
-        # that need IDs set.
-        from Products.CMFSetup.tool import TOOLSET_XML
-        from Products.CMFSetup.tool import importToolset
-
-        site = self._initSite()
-        context = DummyImportContext( site )
-        context._files[ TOOLSET_XML ] = _REQUIRED_TOOLSET_XML
-
-        importToolset( context )
-
-        for tool_id in ( 'mandatory', 'obligatory' ):
-            tool = getattr( site, tool_id )
-            self.assertEqual( tool.getId(), tool_id )
-
-    def test_forbidden_tools( self ):
-
-        from Products.CMFSetup.tool import TOOLSET_XML
-        from Products.CMFSetup.tool import importToolset
-        TOOL_IDS = ( 'doomed', 'blasted', 'saved' )
-
-        site = self._initSite()
-
-        for tool_id in TOOL_IDS:
-            pseudo = Folder()
-            pseudo._setId( tool_id )
-            site._setObject( tool_id, pseudo )
-
-        self.assertEqual( len( site.objectIds() ), len( TOOL_IDS ) + 1 )
-
-        for tool_id in TOOL_IDS:
-            self.failUnless( tool_id in site.objectIds() )
-
-        context = DummyImportContext( site )
-        context._files[ TOOLSET_XML ] = _FORBIDDEN_TOOLSET_XML
-
-        importToolset( context )
-
-        self.assertEqual( len( site.objectIds() ), 2 )
-        self.failUnless( 'portal_setup' in site.objectIds() )
-        self.failUnless( 'saved' in site.objectIds() )
-
-    def test_required_tools_missing( self ):
-
-        from Products.CMFSetup.tool import TOOLSET_XML
-        from Products.CMFSetup.tool import importToolset
-
-        site = self._initSite()
-        self.assertEqual( len( site.objectIds() ), 1 )
-
-        context = DummyImportContext( site )
-        context._files[ TOOLSET_XML ] = _REQUIRED_TOOLSET_XML
-
-        importToolset( context )
-
-        self.assertEqual( len( site.objectIds() ), 3 )
-        self.failUnless( isinstance( aq_base( site._getOb( 'mandatory' ) )
-                                   , DummyTool ) )
-        self.failUnless( isinstance( aq_base( site._getOb( 'obligatory' ) )
-                                   , DummyTool ) )
-
-    def test_required_tools_no_replacement( self ):
-
-        from Products.CMFSetup.tool import TOOLSET_XML
-        from Products.CMFSetup.tool import importToolset
-
-        site = self._initSite()
-
-        mandatory = DummyTool()
-        mandatory._setId( 'mandatory' )
-        site._setObject( 'mandatory', mandatory )
-
-        obligatory = DummyTool()
-        obligatory._setId( 'obligatory' )
-        site._setObject( 'obligatory', obligatory )
-
-        self.assertEqual( len( site.objectIds() ), 3 )
-
-        context = DummyImportContext( site )
-        context._files[ TOOLSET_XML ] = _REQUIRED_TOOLSET_XML
-
-        importToolset( context )
-
-        self.assertEqual( len( site.objectIds() ), 3 )
-        self.failUnless( aq_base( site._getOb( 'mandatory' ) ) is mandatory )
-        self.failUnless( aq_base( site._getOb( 'obligatory' ) ) is obligatory )
-
-    def test_required_tools_with_replacement( self ):
-
-        from Products.CMFSetup.tool import TOOLSET_XML
-        from Products.CMFSetup.tool import importToolset
-
-        site = self._initSite()
-
-        mandatory = AnotherDummyTool()
-        mandatory._setId( 'mandatory' )
-        site._setObject( 'mandatory', mandatory )
-
-        obligatory = AnotherDummyTool()
-        obligatory._setId( 'obligatory' )
-        site._setObject( 'obligatory', obligatory )
-
-        self.assertEqual( len( site.objectIds() ), 3 )
-
-        context = DummyImportContext( site )
-        context._files[ TOOLSET_XML ] = _REQUIRED_TOOLSET_XML
-
-        importToolset( context )
-
-        self.assertEqual( len( site.objectIds() ), 3 )
-
-        self.failIf( aq_base( site._getOb( 'mandatory' ) ) is mandatory )
-        self.failUnless( isinstance( aq_base( site._getOb( 'mandatory' ) )
-                                   , DummyTool ) )
-
-        self.failIf( aq_base( site._getOb( 'obligatory' ) ) is obligatory )
-        self.failUnless( isinstance( aq_base( site._getOb( 'obligatory' ) )
-                                   , DummyTool ) )
-
-
-class DummyTool( Folder ):
-
-    pass
-
-class AnotherDummyTool( Folder ):
-
-    pass
-
-
-_EMPTY_TOOLSET_XML = """\
-<?xml version="1.0"?>
-<tool-setup>
-</tool-setup>
-"""
-
-_NORMAL_TOOLSET_XML = """\
-<?xml version="1.0" ?>
-<tool-setup>
-<forbidden tool_id="doomed"/>
-<required class="path.to.one" tool_id="mandatory"/>
-<required class="path.to.another" tool_id="obligatory"/>
-</tool-setup>
-"""
-
-_FORBIDDEN_TOOLSET_XML = """\
-<?xml version="1.0"?>
-<tool-setup>
- <forbidden tool_id="doomed" />
- <forbidden tool_id="damned" />
- <forbidden tool_id="blasted" />
-</tool-setup>
-"""
-
-_REQUIRED_TOOLSET_XML = """\
-<?xml version="1.0"?>
-<tool-setup>
- <required
-    tool_id="mandatory"
-    class="Products.CMFSetup.tests.test_tool.DummyTool" />
- <required
-    tool_id="obligatory"
-    class="Products.CMFSetup.tests.test_tool.DummyTool" />
-</tool-setup>
-"""
-
-def test_suite():
-    # reimport to make sure tests are run from Products
-    from Products.CMFSetup.tests.test_tool import SetupToolTests
-    from Products.CMFSetup.tests.test_tool import Test_exportToolset
-    from Products.CMFSetup.tests.test_tool import Test_importToolset
-
-    return unittest.TestSuite((
-        unittest.makeSuite( SetupToolTests ),
-        unittest.makeSuite( Test_exportToolset ),
-        unittest.makeSuite( Test_importToolset ),
-        ))
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='test_suite')

Modified: CMF/branches/goldegg-phase-1/CMFSetup/tool.py
===================================================================
--- CMF/branches/goldegg-phase-1/CMFSetup/tool.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/CMFSetup/tool.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -15,715 +15,7 @@
 $Id$
 """
 
-import os
-import time
-from cgi import escape
-
-from AccessControl import ClassSecurityInfo
-from Acquisition import aq_base
-from Globals import InitializeClass
-from OFS.Folder import Folder
-from Products.PageTemplates.PageTemplateFile import PageTemplateFile
-
-from Products.CMFCore.utils import UniqueObject
-from Products.CMFCore.utils import ImmutableId
-from Products.CMFCore.utils import getToolByName
-
-from interfaces import EXTENSION
-from interfaces import ISetupTool
-from permissions import ManagePortal
-from context import DirectoryImportContext
-from context import SnapshotImportContext
-from context import TarballExportContext
-from context import SnapshotExportContext
-from differ import ConfigDiff
-from registry import ImportStepRegistry
-from registry import ExportStepRegistry
-from registry import ToolsetRegistry
-from registry import _profile_registry
-
-from utils import _resolveDottedName
-from utils import _wwwdir
-
-IMPORT_STEPS_XML = 'import_steps.xml'
-EXPORT_STEPS_XML = 'export_steps.xml'
-TOOLSET_XML = 'toolset.xml'
-
-def exportStepRegistries( context ):
-
-    """ Built-in handler for exporting import / export step registries.
-    """
-    site = context.getSite()
-    tool = getToolByName( site, 'portal_setup' )
-
-    import_steps_xml = tool.getImportStepRegistry().generateXML()
-    context.writeDataFile( 'import_steps.xml', import_steps_xml, 'text/xml' )
-
-    export_steps_xml = tool.getExportStepRegistry().generateXML()
-    context.writeDataFile( 'export_steps.xml', export_steps_xml, 'text/xml' )
-
-    return 'Step registries exported'
-
-def importToolset( context ):
-
-    """ Import required / forbidden tools from XML file.
-    """
-    site = context.getSite()
-    encoding = context.getEncoding()
-
-    xml = context.readDataFile(TOOLSET_XML)
-    if xml is None:
-        return 'Toolset: Nothing to import.'
-
-    setup_tool = getToolByName( site, 'portal_setup' )
-    toolset = setup_tool.getToolsetRegistry()
-
-    toolset.parseXML(xml, encoding)
-
-    existing_ids = site.objectIds()
-    existing_values = site.objectValues()
-
-    for tool_id in toolset.listForbiddenTools():
-
-        if tool_id in existing_ids:
-            site._delObject( tool_id )
-
-    for info in toolset.listRequiredToolInfo():
-
-        tool_id = str( info[ 'id' ] )
-        tool_class = _resolveDottedName( info[ 'class' ] )
-
-        existing = getToolByName( site, tool_id, None )
-        new_tool = tool_class()
-
-        if not isinstance( new_tool, ImmutableId ):
-            new_tool._setId( tool_id )
-
-        if existing is None:
-            site._setObject( tool_id, new_tool )
-
-        else:
-            unwrapped = aq_base( existing )
-            if not isinstance( unwrapped, tool_class ):
-                site._delObject( tool_id )
-                site._setObject( tool_id, new_tool )
-
-    return 'Toolset imported.'
-
-def exportToolset( context ):
-
-    """ Export required / forbidden tools to XML file.
-    """
-    site = context.getSite()
-    setup_tool = getToolByName( site, 'portal_setup' )
-    toolset = setup_tool.getToolsetRegistry()
-
-    xml = toolset.generateXML()
-    context.writeDataFile( TOOLSET_XML, xml, 'text/xml' )
-
-    return 'Toolset exported.'
-
-
-class SetupTool( UniqueObject, Folder ):
-
-    """ Profile-based site configuration manager.
-    """
-    __implements__ = ( ISetupTool, ) + Folder.__implements__
-
-    id = 'portal_setup'
-    meta_type = 'Portal Setup Tool'
-
-    _import_context_id = ''
-
-    security = ClassSecurityInfo()
-
-    def __init__( self ):
-
-        self._import_registry = ImportStepRegistry()
-        self._export_registry = ExportStepRegistry()
-        self._export_registry.registerStep( 'step_registries'
-                                          , exportStepRegistries
-                                          , 'Export import / export steps.'
-                                          )
-        self._toolset_registry = ToolsetRegistry()
-
-    #
-    #   ISetupTool API
-    #
-    security.declareProtected( ManagePortal, 'getEncoding' )
-    def getEncoding( self ):
-
-        """ See ISetupTool.
-        """
-        return 'ascii'
-
-    security.declareProtected( ManagePortal, 'getImportContextId' )
-    def getImportContextID( self ):
-
-        """ See ISetupTool.
-        """
-        return self._import_context_id
-
-    security.declareProtected(ManagePortal, 'setImportContext')
-    def setImportContext(self, context_id, encoding=None):
-
-        """ See ISetupTool.
-        """
-        self._import_context_id = context_id
-
-        self._updateImportStepsRegistry( encoding )
-        self._updateExportStepsRegistry( encoding )
-        self._updateToolsetRegistry( encoding )
-
-    security.declareProtected( ManagePortal, 'getImportStepRegistry' )
-    def getImportStepRegistry( self ):
-
-        """ See ISetupTool.
-        """
-        return self._import_registry
-
-    security.declareProtected( ManagePortal, 'getImportStepRegistry' )
-    def getExportStepRegistry( self ):
-
-        """ See ISetupTool.
-        """
-        return self._export_registry
-
-    security.declareProtected( ManagePortal, 'getToolsetRegistry' )
-    def getToolsetRegistry( self ):
-
-        """ See ISetupTool.
-        """
-        return self._toolset_registry
-
-    security.declareProtected( ManagePortal, 'executeStep' )
-    def runImportStep( self, step_id, run_dependencies=True, purge_old=None ):
-
-        """ See ISetupTool.
-        """
-        context = self._getImportContext(self._import_context_id, purge_old)
-
-        info = self._import_registry.getStepMetadata( step_id )
-
-        if info is None:
-            raise ValueError, 'No such import step: %s' % step_id
-
-        dependencies = info.get( 'dependencies', () )
-
-        messages = {}
-        steps = []
-        if run_dependencies:
-            for dependency in dependencies:
-
-                if dependency not in steps:
-                    message = self._doRunImportStep( dependency, context )
-                    messages[ dependency ] = message
-                    steps.append( dependency )
-
-        message = self._doRunImportStep( step_id, context )
-        messages[ step_id ] = message
-        steps.append( step_id )
-
-        return { 'steps' : steps, 'messages' : messages }
-
-    security.declareProtected( ManagePortal, 'runAllSetupSteps')
-    def runAllImportSteps( self, purge_old=None ):
-
-        """ See ISetupTool.
-        """
-        context = self._getImportContext(self._import_context_id, purge_old)
-
-        steps = self._import_registry.sortSteps()
-        messages = {}
-
-        for step in steps:
-            message = self._doRunImportStep( step, context )
-            messages[ step ] = message
-
-        return { 'steps' : steps, 'messages' : messages }
-
-    security.declareProtected( ManagePortal, 'runExportStep')
-    def runExportStep( self, step_id ):
-
-        """ See ISetupTool.
-        """
-        return self._doRunExportSteps( [ step_id ] )
-
-    security.declareProtected(ManagePortal, 'runAllExportSteps')
-    def runAllExportSteps( self ):
-
-        """ See ISetupTool.
-        """
-        return self._doRunExportSteps( self._export_registry.listSteps() )
-
-    security.declareProtected( ManagePortal, 'createSnapshot')
-    def createSnapshot( self, snapshot_id ):
-
-        """ See ISetupTool.
-        """
-        context = SnapshotExportContext( self, snapshot_id )
-        messages = {}
-        steps = self._export_registry.listSteps()
-
-        for step_id in steps:
-
-            handler = self._export_registry.getStep( step_id )
-
-            if handler is None:
-                raise ValueError( 'Invalid export step: %s' % step_id )
-
-            messages[ step_id ] = handler( context )
-
-
-        return { 'steps' : steps
-               , 'messages' : messages
-               , 'url' : context.getSnapshotURL()
-               , 'snapshot' : context.getSnapshotFolder()
-               }
-
-    security.declareProtected(ManagePortal, 'compareConfigurations')
-    def compareConfigurations( self
-                             , lhs_context
-                             , rhs_context
-                             , missing_as_empty=False
-                             , ignore_blanks=False
-                             , skip=( 'CVS', '.svn' )
-                             ):
-        """ See ISetupTool.
-        """
-        differ = ConfigDiff( lhs_context
-                           , rhs_context
-                           , missing_as_empty
-                           , ignore_blanks
-                           , skip
-                           )
-
-        return differ.compare()
-
-    security.declareProtected( ManagePortal, 'markupComparison')
-    def markupComparison(self, lines):
-
-        """ See ISetupTool.
-        """
-        result = []
-
-        for line in lines.splitlines():
-
-            if line.startswith('** '):
-
-                if line.find('File') > -1:
-                    if line.find('replaced') > -1:
-                        result.append( ( 'file-to-dir', line ) )
-                    elif line.find('added') > -1:
-                        result.append( ( 'file-added', line ) )
-                    else:
-                        result.append( ( 'file-removed', line ) )
-                else:
-                    if line.find('replaced') > -1:
-                        result.append( ( 'dir-to-file', line ) )
-                    elif line.find('added') > -1:
-                        result.append( ( 'dir-added', line ) )
-                    else:
-                        result.append( ( 'dir-removed', line ) )
-
-            elif line.startswith('@@'):
-                result.append( ( 'diff-range', line ) )
-
-            elif line.startswith(' '):
-                result.append( ( 'diff-context', line ) )
-
-            elif line.startswith('+'):
-                result.append( ( 'diff-added', line ) )
-
-            elif line.startswith('-'):
-                result.append( ( 'diff-removed', line ) )
-
-            elif line == '\ No newline at end of file':
-                result.append( ( 'diff-context', line ) )
-
-            else:
-                result.append( ( 'diff-header', line ) )
-
-        return '<pre>\n%s\n</pre>' % (
-            '\n'.join( [ ( '<span class="%s">%s</span>' % ( cl, escape( l ) ) )
-                                  for cl, l in result] ) )
-
-    #
-    #   ZMI
-    #
-    manage_options = ( Folder.manage_options[ :1 ]
-                     + ( { 'label' : 'Properties'
-                         , 'action' : 'manage_tool'
-                         }
-                       , { 'label' : 'Import'
-                         , 'action' : 'manage_importSteps'
-                         }
-                       , { 'label' : 'Export'
-                         , 'action' : 'manage_exportSteps'
-                         }
-                       , { 'label' : 'Snapshots'
-                         , 'action' : 'manage_snapshots'
-                         }
-                       , { 'label' : 'Comparison'
-                         , 'action' : 'manage_showDiff'
-                         }
-                       )
-                     + Folder.manage_options[ 3: ] # skip "View", "Properties"
-                     )
-
-    security.declareProtected( ManagePortal, 'manage_tool' )
-    manage_tool = PageTemplateFile( 'sutProperties', _wwwdir )
-
-    security.declareProtected( ManagePortal, 'manage_updateToolProperties' )
-    def manage_updateToolProperties(self, context_id, RESPONSE):
-        """ Update the tool's settings.
-        """
-        self.setImportContext(context_id)
-
-        RESPONSE.redirect( '%s/manage_tool?manage_tabs_message=%s'
-                         % ( self.absolute_url(), 'Properties+updated.' )
-                         )
-
-    security.declareProtected( ManagePortal, 'manage_importSteps' )
-    manage_importSteps = PageTemplateFile( 'sutImportSteps', _wwwdir )
-
-    security.declareProtected( ManagePortal, 'manage_importSelectedSteps' )
-    def manage_importSelectedSteps( self
-                                  , ids
-                                  , run_dependencies
-                                  , RESPONSE
-                                  ):
-        """ Import the steps selected by the user.
-        """
-        if not ids:
-            message = 'No+steps+selected.'
-
-        else:
-            steps_run = []
-            for step_id in ids:
-                result = self.runImportStep( step_id, run_dependencies )
-                steps_run.extend( result[ 'steps' ] )
-
-            message = 'Steps+run:%s' % '+,'.join( steps_run )
-
-        RESPONSE.redirect( '%s/manage_importSteps?manage_tabs_message=%s'
-                         % ( self.absolute_url(), message )
-                         )
-
-    security.declareProtected( ManagePortal, 'manage_importSelectedSteps' )
-    def manage_importAllSteps( self, RESPONSE ):
-
-        """ Import all steps.
-        """
-        result = self.runAllImportSteps()
-        message = 'Steps+run:%s' % '+,'.join( result[ 'steps' ] )
-
-        RESPONSE.redirect( '%s/manage_importSteps?manage_tabs_message=%s'
-                         % ( self.absolute_url(), message )
-                         )
-
-    security.declareProtected( ManagePortal, 'manage_exportSteps' )
-    manage_exportSteps = PageTemplateFile( 'sutExportSteps', _wwwdir )
-
-    security.declareProtected( ManagePortal, 'manage_exportSelectedSteps' )
-    def manage_exportSelectedSteps( self, ids, RESPONSE ):
-
-        """ Export the steps selected by the user.
-        """
-        if not ids:
-            RESPONSE.redirect( '%s/manage_exportSteps?manage_tabs_message=%s'
-                             % ( self.absolute_url(), 'No+steps+selected.' )
-                             )
-
-        result = self._doRunExportSteps( ids )
-        RESPONSE.setHeader( 'Content-type', 'application/x-gzip')
-        RESPONSE.setHeader( 'Content-disposition'
-                          , 'attachment; filename=%s' % result[ 'filename' ]
-                          )
-        return result[ 'tarball' ]
-
-    security.declareProtected( ManagePortal, 'manage_exportAllSteps' )
-    def manage_exportAllSteps( self, RESPONSE ):
-
-        """ Export all steps.
-        """
-        result = self.runAllExportSteps()
-        RESPONSE.setHeader( 'Content-type', 'application/x-gzip')
-        RESPONSE.setHeader( 'Content-disposition'
-                          , 'attachment; filename=%s' % result[ 'filename' ]
-                          )
-        return result[ 'tarball' ]
-
-    security.declareProtected( ManagePortal, 'manage_snapshots' )
-    manage_snapshots = PageTemplateFile( 'sutSnapshots', _wwwdir )
-
-    security.declareProtected( ManagePortal, 'listSnapshotInfo' )
-    def listSnapshotInfo( self ):
-
-        """ Return a list of mappings describing available snapshots.
-
-        o Keys include:
-
-          'id' -- snapshot ID
-
-          'title' -- snapshot title or ID
-
-          'url' -- URL of the snapshot folder
-        """
-        result = []
-        snapshots = self._getOb( 'snapshots', None )
-
-        if snapshots:
-
-            for id, folder in snapshots.objectItems( 'Folder' ):
-
-                result.append( { 'id' : id
-                               , 'title' : folder.title_or_id()
-                               , 'url' : folder.absolute_url()
-                               } )
-        return result
-
-    security.declareProtected( ManagePortal, 'listProfileInfo' )
-    def listProfileInfo( self ):
-
-        """ Return a list of mappings describing registered profiles.
-
-        o Keys include:
-
-          'id' -- profile ID
-
-          'title' -- profile title or ID
-
-          'description' -- description of the profile
-
-          'path' -- path to the profile within its product
-
-          'product' -- name of the registering product
-        """
-        return _profile_registry.listProfileInfo()
-
-    security.declareProtected(ManagePortal, 'listContextInfos')
-    def listContextInfos(self):
-
-        """ List registered profiles and snapshots.
-        """
-
-        s_infos = [ { 'id': 'snapshot-%s' % info['id'],
-                      'title': info['title'] }
-                    for info in self.listSnapshotInfo() ]
-        p_infos = [ { 'id': 'profile-%s' % info['id'],
-                      'title': info['title'] }
-                    for info in self.listProfileInfo() ]
-
-        return tuple(s_infos + p_infos)
-
-    security.declareProtected( ManagePortal, 'manage_createSnapshot' )
-    def manage_createSnapshot( self, RESPONSE, snapshot_id=None ):
-
-        """ Create a snapshot with the given ID.
-
-        o If no ID is passed, generate one.
-        """
-        if snapshot_id is None:
-            timestamp = time.gmtime()
-            snapshot_id = 'snapshot-%4d%02d%02d%02d%02d%02d' % timestamp[:6]
-
-        self.createSnapshot( snapshot_id )
-
-        RESPONSE.redirect( '%s/manage_snapshots?manage_tabs_message=%s'
-                         % ( self.absolute_url(), 'Snapshot+created.' ) )
-
-    security.declareProtected( ManagePortal, 'manage_showDiff' )
-    manage_showDiff = PageTemplateFile( 'sutCompare', _wwwdir )
-
-    def manage_downloadDiff( self
-                           , lhs
-                           , rhs
-                           , missing_as_empty
-                           , ignore_blanks
-                           , RESPONSE
-                           ):
-        """ Crack request vars and call compareConfigurations.
-
-        o Return the result as a 'text/plain' stream, suitable for framing.
-        """
-        comparison = self.manage_compareConfigurations( lhs
-                                                      , rhs
-                                                      , missing_as_empty
-                                                      , ignore_blanks
-                                                      )
-        RESPONSE.setHeader( 'Content-Type', 'text/plain' )
-        return _PLAINTEXT_DIFF_HEADER % ( lhs, rhs, comparison )
-
-    security.declareProtected( ManagePortal, 'manage_compareConfigurations' )
-    def manage_compareConfigurations( self
-                                    , lhs
-                                    , rhs
-                                    , missing_as_empty
-                                    , ignore_blanks
-                                    ):
-        """ Crack request vars and call compareConfigurations.
-        """
-        lhs_context = self._getImportContext( lhs )
-        rhs_context = self._getImportContext( rhs )
-
-        return self.compareConfigurations( lhs_context
-                                         , rhs_context
-                                         , missing_as_empty
-                                         , ignore_blanks
-                                         )
-
-
-    #
-    #   Helper methods
-    #
-    security.declarePrivate( '_getProductPath' )
-    def _getProductPath( self, product_name ):
-
-        """ Return the absolute path of the product's directory.
-        """
-        try:
-            product = __import__( 'Products.%s' % product_name
-                                , globals(), {}, ['initialize' ] )
-        except ImportError:
-            raise ValueError, 'Not a valid product name: %s' % product_name
-
-        return product.__path__[0]
-
-    security.declarePrivate( '_getImportContext' )
-    def _getImportContext( self, context_id, should_purge=None ):
-
-        """ Crack ID and generate appropriate import context.
-        """
-        encoding = self.getEncoding()
-
-        if context_id.startswith( 'profile-' ):
-
-            context_id = context_id[ len( 'profile-' ): ]
-            info = _profile_registry.getProfileInfo( context_id )
-
-            if info.get( 'product' ):
-                path = os.path.join( self._getProductPath( info[ 'product' ] )
-                                   , info[ 'path' ] )
-            else:
-                path = info[ 'path' ]
-            if should_purge is None:
-                should_purge = (info.get('type') != EXTENSION)
-            return DirectoryImportContext(self, path, should_purge, encoding)
-
-        # else snapshot
-        context_id = context_id[ len( 'snapshot-' ): ]
-        if should_purge is None:
-            should_purge = True
-        return SnapshotImportContext(self, context_id, should_purge, encoding)
-
-    security.declarePrivate( '_updateImportStepsRegistry' )
-    def _updateImportStepsRegistry( self, encoding ):
-
-        """ Update our import steps registry from our profile.
-        """
-        context = self._getImportContext(self._import_context_id)
-        xml = context.readDataFile(IMPORT_STEPS_XML)
-        if xml is None:
-            return
-
-        info_list = self._import_registry.parseXML( xml, encoding )
-
-        for step_info in info_list:
-
-            id = step_info[ 'id' ]
-            version = step_info[ 'version' ]
-            handler = _resolveDottedName( step_info[ 'handler' ] )
-
-            dependencies = tuple( step_info.get( 'dependencies', () ) )
-            title = step_info.get( 'title', id )
-            description = ''.join( step_info.get( 'description', [] ) )
-
-            self._import_registry.registerStep( id=id
-                                              , version=version
-                                              , handler=handler
-                                              , dependencies=dependencies
-                                              , title=title
-                                              , description=description
-                                              )
-
-    security.declarePrivate( '_updateExportStepsRegistry' )
-    def _updateExportStepsRegistry( self, encoding ):
-
-        """ Update our export steps registry from our profile.
-        """
-        context = self._getImportContext(self._import_context_id)
-        xml = context.readDataFile(EXPORT_STEPS_XML)
-        if xml is None:
-            return
-
-        info_list = self._export_registry.parseXML( xml, encoding )
-
-        for step_info in info_list:
-
-            id = step_info[ 'id' ]
-            handler = _resolveDottedName( step_info[ 'handler' ] )
-
-            title = step_info.get( 'title', id )
-            description = ''.join( step_info.get( 'description', [] ) )
-
-            self._export_registry.registerStep( id=id
-                                              , handler=handler
-                                              , title=title
-                                              , description=description
-                                              )
-
-    security.declarePrivate( '_updateToolsetRegistry' )
-    def _updateToolsetRegistry( self, encoding ):
-
-        """ Update our toolset registry from our profile.
-        """
-        context = self._getImportContext(self._import_context_id)
-        xml = context.readDataFile(TOOLSET_XML)
-        if xml is None:
-            return
-
-        self._toolset_registry.parseXML( xml, encoding )
-
-    security.declarePrivate( '_doRunImportStep' )
-    def _doRunImportStep( self, step_id, context ):
-
-        """ Run a single import step, using a pre-built context.
-        """
-        handler = self._import_registry.getStep( step_id )
-
-        if handler is None:
-            raise ValueError( 'Invalid import step: %s' % step_id )
-
-        return handler( context )
-
-    security.declarePrivate( '_doRunExportSteps')
-    def _doRunExportSteps( self, steps ):
-
-        """ See ISetupTool.
-        """
-        context = TarballExportContext( self )
-        messages = {}
-
-        for step_id in steps:
-
-            handler = self._export_registry.getStep( step_id )
-
-            if handler is None:
-                raise ValueError( 'Invalid export step: %s' % step_id )
-
-            messages[ step_id ] = handler( context )
-
-
-        return { 'steps' : steps
-               , 'messages' : messages
-               , 'tarball' : context.getArchive()
-               , 'filename' : context.getArchiveFilename()
-               }
-
-InitializeClass( SetupTool )
-
-_PLAINTEXT_DIFF_HEADER ="""\
-Comparing configurations: '%s' and '%s'
-
-%s"""
+from Products.GenericSetup.tool import exportStepRegistries
+from Products.GenericSetup.tool import importToolset
+from Products.GenericSetup.tool import exportToolset
+from Products.GenericSetup.tool import SetupTool

Added: CMF/branches/goldegg-phase-1/GenericSetup/CHANGES.txt
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/CHANGES.txt	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/CHANGES.txt	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,24 @@
+GenericSetup Product Changelog
+
+  GenericSetup 1.0 (2005/09/23)
+
+    - CVS tag:  GenericSetup-1_0
+
+    - Forward-ported i18n support from CMF 1.5 branch.
+
+    - Forward ported BBB for old instances that stored properties as
+      lists from CMFSetup.
+
+    - Forward ported fix for tools with non unique IDs from CMFSetup.
+
+  GenericSetup 0.10 (2005/08/11)
+
+    - CVS tag:  GenericSetup-0_10
+
+    - Added TarballImportContext, including full test suite.
+
+  GenericSetup 0.9 (2005/08/08)
+
+    - CVS tag:  GenericSetup-0_9
+
+    - Initial version, cut down from CMFSetup-1.5.3

Added: CMF/branches/goldegg-phase-1/GenericSetup/CREDITS.txt
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/CREDITS.txt	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/CREDITS.txt	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,7 @@
+GenericSetup Credits
+
+  - Martijn Pieters <mj at zopatista.com> wrote the original version of this
+    software while working at Zope Corporation.
+
+  - He developed his version as part of the "Bonzai" CMS which Zope corp.
+    built Boston.com using its Zope4Media Print product.

Added: CMF/branches/goldegg-phase-1/GenericSetup/DEPENDENCIES.txt
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/DEPENDENCIES.txt	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/DEPENDENCIES.txt	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1 @@
+Zope >= 2.8.0

Added: CMF/branches/goldegg-phase-1/GenericSetup/PROFILES.txt
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/PROFILES.txt	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/PROFILES.txt	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,40 @@
+Profiles
+
+  Overview
+
+    There are two different kinds of profiles: Base profiles and extension
+    profiles. Base profiles have no dependencies. Extension profiles are
+    profile fragments used to modify base profiles. They can be shipped with
+    add-on products or used for customization steps. Importing an extension
+    profile adds or overwrites existing settings in a fine-grained way. You
+    can't export extension profiles. Snapshots and exports always represent
+    the merged settings.
+
+  Update Directives
+
+    For some XML elements there are additional attributes and values to
+    specify update directives. They are only useful for extension profiles and
+    you will never see them in snapshots and exports.
+
+    'insert-before' and 'insert-after'
+
+      applies to: object (generic); layer (skins.xml)
+
+      'insert-before' and 'insert-after' specify the position of a new item
+      relative to an existing item. If they are omitted or not valid, items
+      are appended. You can also use '*' as wildcard. This will insert the new
+      item at the top (before all existing items) or the bottom (after all
+      existing items). If an item with the given ID exists already, it is
+      moved to the specified position.
+
+    'id="*"' wildcard
+
+      applies to: skin-path (skins.xml)
+
+      Updates all existing items in the container with the same settings.
+
+    'remove'
+
+      applies to: action-provider, skin layer (actions.xml, skins.xml)
+
+      Removes the specified item if it exists.

Added: CMF/branches/goldegg-phase-1/GenericSetup/README.txt
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/README.txt	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/README.txt	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,48 @@
+GenericSetup Product README
+
+  Overview
+
+    This product is a "cut-down" version of the CMFSetup product, shipped
+    with CMF 1.5.x:  it removes all CMF dependencies.  In the ideal world,
+    CMFSetup would depend on this product, and supply the CMF-specific bits
+    (handlers for actions, types, workflows, etc.).
+
+    This product provides a mini-framework for expressing the configured
+    state of a Zope Site as a set of filesystem artifacts.  These artifacts
+    consist of declarative XML files, which spell out the configuration
+    settings for each "tool" in the site , and supporting scripts / templates,
+    in their "canonical" filesystem representations.
+
+  Configurations Included
+
+    The 'setup_tool' knows how to export / import configurations and scripts
+    for the following tools:
+
+      - (x) removal / creation of specified tools
+
+      - (x) itself :)
+
+      - (x) the role / permission map on the "site" object (its parent)
+
+      - (x) properties of the site object
+
+  Extending The Tool
+
+    Third-party products extend the tool by registering handlers for
+    import / export of their unique tools.
+
+  Glossary
+
+    Site --
+      The instance in the Zope URL space which defines a "zone of service"
+      for a set of tools.
+
+    Profile --
+      A "preset" configuration of a site, defined on the filesystem
+
+    Snapshot --
+      "Frozen" site configuration, captured within the setup tool
+
+    "dotted name" --
+      The Pythonic representation of the "path" to a given function /
+      module, e.g. 'Products.CMFSetup.utils.ConfiguratorBase'.

Added: CMF/branches/goldegg-phase-1/GenericSetup/__init__.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/__init__.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/__init__.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,26 @@
+""" GenericSetup product initialization.
+
+$Id: __init__.py,v 1.1.1.1 2005/08/08 19:38:37 tseaver Exp $
+"""
+
+from AccessControl import ModuleSecurityInfo
+
+from interfaces import BASE, EXTENSION
+from permissions import ManagePortal
+from registry import _profile_registry as profile_registry
+
+security = ModuleSecurityInfo('Products.GenericSetup')
+security.declareProtected(ManagePortal, 'profile_registry')
+
+def initialize(context):
+
+    import tool
+
+    context.registerClass(tool.SetupTool,
+                          constructors=(#tool.addSetupToolForm,
+                                        tool.addSetupTool,
+                                        ),
+                          permissions=(ManagePortal,),
+                          interfaces=None,
+                          icon='www/tool.png',
+                         )

Added: CMF/branches/goldegg-phase-1/GenericSetup/context.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/context.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/context.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,561 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" Various context implementations for export / import of configurations.
+
+Wrappers representing the state of an import / export operation.
+
+$Id: context.py,v 1.2 2005/08/11 21:41:36 tseaver Exp $
+"""
+
+import os
+import time
+from StringIO import StringIO
+from tarfile import TarFile
+from tarfile import TarInfo
+
+from AccessControl import ClassSecurityInfo
+from Acquisition import aq_inner
+from Acquisition import aq_parent
+from Acquisition import aq_self
+from Acquisition import Implicit
+from DateTime.DateTime import DateTime
+from Globals import InitializeClass
+from OFS.DTMLDocument import DTMLDocument
+from OFS.Folder import Folder
+from OFS.Image import File
+from OFS.Image import Image
+from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
+from Products.PythonScripts.PythonScript import PythonScript
+from zope.interface import implements
+
+from interfaces import IExportContext
+from interfaces import IImportContext
+from permissions import ManagePortal
+
+
+class DirectoryImportContext( Implicit ):
+
+    implements(IImportContext)
+
+    security = ClassSecurityInfo()
+
+    def __init__( self
+                , tool
+                , profile_path
+                , should_purge=False
+                , encoding=None
+                ):
+
+        self._site = aq_parent( aq_inner( tool ) )
+        self._profile_path = profile_path
+        self._should_purge = bool( should_purge )
+        self._encoding = encoding
+
+    security.declareProtected( ManagePortal, 'getSite' )
+    def getSite( self ):
+
+        """ See ISetupContext.
+        """
+        return aq_self(self._site)
+
+    security.declareProtected( ManagePortal, 'getEncoding' )
+    def getEncoding( self ):
+
+        """ See IImportContext.
+        """
+        return self._encoding
+
+    security.declareProtected( ManagePortal, 'readDataFile' )
+    def readDataFile( self, filename, subdir=None ):
+
+        """ See IImportContext.
+        """
+        if subdir is None:
+            full_path = os.path.join( self._profile_path, filename )
+        else:
+            full_path = os.path.join( self._profile_path, subdir, filename )
+
+        if not os.path.exists( full_path ):
+            return None
+
+        file = open( full_path, 'rb' )
+        result = file.read()
+        file.close()
+
+        return result
+
+    security.declareProtected( ManagePortal, 'getLastModified' )
+    def getLastModified( self, path ):
+
+        """ See IImportContext.
+        """
+        full_path = os.path.join( self._profile_path, path )
+
+        if not os.path.exists( full_path ):
+            return None
+
+        return DateTime( os.path.getmtime( full_path ) )
+
+    security.declareProtected( ManagePortal, 'isDirectory' )
+    def isDirectory( self, path ):
+
+        """ See IImportContext.
+        """
+        full_path = os.path.join( self._profile_path, path )
+
+        if not os.path.exists( full_path ):
+            return None
+
+        return os.path.isdir( full_path )
+
+    security.declareProtected( ManagePortal, 'listDirectory' )
+    def listDirectory( self, path, skip=('CVS', '.svn') ):
+
+        """ See IImportContext.
+        """
+        if path is None:
+            path = ''
+
+        full_path = os.path.join( self._profile_path, path )
+
+        if not os.path.exists( full_path ) or not os.path.isdir( full_path ):
+            return None
+
+        names = os.listdir( full_path )
+
+        return [ name for name in names if name not in skip ]
+
+    security.declareProtected( ManagePortal, 'shouldPurge' )
+    def shouldPurge( self ):
+
+        """ See IImportContext.
+        """
+        return self._should_purge
+
+InitializeClass( DirectoryImportContext )
+
+
+class DirectoryExportContext( Implicit ):
+
+    implements(IExportContext)
+
+    security = ClassSecurityInfo()
+
+    def __init__( self, tool, profile_path ):
+
+        self._site = aq_parent( aq_inner( tool ) )
+        self._profile_path = profile_path
+
+    security.declareProtected( ManagePortal, 'getSite' )
+    def getSite( self ):
+
+        """ See ISetupContext.
+        """
+        return aq_self(self._site)
+
+    security.declareProtected( ManagePortal, 'writeDataFile' )
+    def writeDataFile( self, filename, text, content_type, subdir=None ):
+
+        """ See IExportContext.
+        """
+        if subdir is None:
+            prefix = self._profile_path
+        else:
+            prefix = os.path.join( self._profile_path, subdir )
+
+        full_path = os.path.join( prefix, filename )
+
+        if not os.path.exists( prefix ):
+            os.makedirs( prefix )
+
+        mode = content_type.startswith( 'text/' ) and 'w' or 'wb'
+
+        file = open( full_path, mode )
+        file.write( text )
+        file.close()
+
+InitializeClass( DirectoryExportContext )
+
+
+class TarballImportContext( Implicit ):
+
+    implements(IImportContext)
+
+    security = ClassSecurityInfo()
+
+    def __init__( self, tool, archive_bits, encoding=None, should_purge=False ):
+
+        self._site = aq_parent( aq_inner( tool ) )
+        timestamp = time.gmtime()
+        self._archive_stream = StringIO(archive_bits)
+        self._archive = TarFile.open( 'foo.bar', 'r:gz'
+                                    , self._archive_stream )
+        self._encoding = encoding
+        self._should_purge = bool( should_purge )
+
+    security.declareProtected( ManagePortal, 'getSite' )
+    def getSite( self ):
+
+        """ See ISetupContext.
+        """
+        return aq_self(self._site)
+
+    def getEncoding( self ):
+
+        """ See IImportContext.
+        """
+        return self._encoding
+
+    def readDataFile( self, filename, subdir=None ):
+
+        """ See IImportContext.
+        """
+        if subdir is not None:
+            filename = os.path.join( subdir, filename )
+
+        try:
+            file = self._archive.extractfile( filename )
+        except KeyError:
+            return None
+
+        return file.read()
+
+    def getLastModified( self, path ):
+
+        """ See IImportContext.
+        """
+        info = self._getTarInfo( path )
+        return info and info.mtime or None
+
+    def isDirectory( self, path ):
+
+        """ See IImportContext.
+        """
+        info = self._getTarInfo( path )
+
+        if info is not None:
+            return info.isdir()
+
+    def listDirectory( self, path, skip=('CVS', '.svn') ):
+
+        """ See IImportContext.
+        """
+        if path is None:  # root is special case:  no leading '/'
+            path = ''
+        else:
+            if not self.isDirectory(path):
+                return None
+
+            if path[-1] != '/':
+                path = path + '/'
+
+        pfx_len = len(path)
+
+        beneath = [x[pfx_len:] for x in self._archive.getnames()
+                                if x.startswith(path) and x != path]
+
+        return [x for x in beneath if '/' not in x and x not in skip]
+
+    def shouldPurge( self ):
+
+        """ See IImportContext.
+        """
+        return self._should_purge
+
+    def _getTarInfo( self, path ):
+        if path[-1] == '/':
+            path = path[:-1]
+        try:
+            return self._archive.getmember( path )
+        except KeyError:
+            pass
+        try:
+            return self._archive.getmember( path + '/' )
+        except KeyError:
+            return None
+
+
+class TarballExportContext( Implicit ):
+
+    implements(IExportContext)
+
+    security = ClassSecurityInfo()
+
+    def __init__( self, tool ):
+
+        self._site = aq_parent( aq_inner( tool ) )
+        timestamp = time.gmtime()
+        archive_name = ( 'setup_tool-%4d%02d%02d%02d%02d%02d.tar.gz'
+                       % timestamp[:6] )
+
+        self._archive_stream = StringIO()
+        self._archive_filename = archive_name
+        self._archive = TarFile.open( archive_name, 'w:gz'
+                                    , self._archive_stream )
+
+    security.declareProtected( ManagePortal, 'getSite' )
+    def getSite( self ):
+
+        """ See ISetupContext.
+        """
+        return aq_self(self._site)
+
+    security.declareProtected( ManagePortal, 'writeDataFile' )
+    def writeDataFile( self, filename, text, content_type, subdir=None ):
+
+        """ See IExportContext.
+        """
+        if subdir is not None:
+            filename = os.path.join( subdir, filename )
+
+        stream = StringIO( text )
+        info = TarInfo( filename )
+        info.size = len( text )
+        info.mtime = time.time()
+        self._archive.addfile( info, stream )
+
+    security.declareProtected( ManagePortal, 'getArchive' )
+    def getArchive( self ):
+
+        """ Close the archive, and return it as a big string.
+        """
+        self._archive.close()
+        return self._archive_stream.getvalue()
+
+    security.declareProtected( ManagePortal, 'getArchiveFilename' )
+    def getArchiveFilename( self ):
+
+        """ Close the archive, and return it as a big string.
+        """
+        return self._archive_filename
+
+InitializeClass( TarballExportContext )
+
+
+class SnapshotExportContext( Implicit ):
+
+    implements(IExportContext)
+
+    security = ClassSecurityInfo()
+
+    def __init__( self, tool, snapshot_id ):
+
+        self._tool = tool = aq_inner( tool )
+        self._site = aq_parent( tool )
+        self._snapshot_id = snapshot_id
+
+    security.declareProtected( ManagePortal, 'getSite' )
+    def getSite( self ):
+
+        """ See ISetupContext.
+        """
+        return aq_self(self._site)
+
+    security.declareProtected( ManagePortal, 'writeDataFile' )
+    def writeDataFile( self, filename, text, content_type, subdir=None ):
+
+        """ See IExportContext.
+        """
+        folder = self._ensureSnapshotsFolder( subdir )
+
+        # TODO: switch on content_type
+        ob = self._createObjectByType( filename, text, content_type )
+        folder._setObject( str( filename ), ob ) # No Unicode IDs!
+
+    security.declareProtected( ManagePortal, 'getSnapshotURL' )
+    def getSnapshotURL( self ):
+
+        """ See IExportContext.
+        """
+        return '%s/%s' % ( self._tool.absolute_url(), self._snapshot_id )
+
+    security.declareProtected( ManagePortal, 'getSnapshotFolder' )
+    def getSnapshotFolder( self ):
+
+        """ See IExportContext.
+        """
+        return self._ensureSnapshotsFolder()
+
+    #
+    #   Helper methods
+    #
+    security.declarePrivate( '_createObjectByType' )
+    def _createObjectByType( self, name, body, content_type ):
+
+        if name.endswith('.py'):
+
+            ob = PythonScript( name )
+            ob.write( body )
+
+        elif name.endswith('.dtml'):
+
+            ob = DTMLDocument( '', __name__=name )
+            ob.munge( body )
+
+        elif content_type in ('text/html', 'text/xml' ):
+
+            ob = ZopePageTemplate( name, str( body )
+                                 , content_type=content_type )
+
+        elif content_type[:6]=='image/':
+
+            ob=Image( name, '', body, content_type=content_type )
+
+        else:
+            ob=File( name, '', body, content_type=content_type )
+
+        return ob
+
+    security.declarePrivate( '_ensureSnapshotsFolder' )
+    def _ensureSnapshotsFolder( self, subdir=None ):
+
+        """ Ensure that the appropriate snapshot folder exists.
+        """
+        path = [ 'snapshots', self._snapshot_id ]
+
+        if subdir is not None:
+            path.extend( subdir.split( '/' ) )
+
+        current = self._tool
+
+        for element in path:
+
+            if element not in current.objectIds():
+                # No Unicode IDs!
+                current._setObject( str( element ), Folder( element ) )
+
+            current = current._getOb( element )
+
+        return current
+
+InitializeClass( SnapshotExportContext )
+
+
+class SnapshotImportContext( Implicit ):
+
+    implements(IImportContext)
+
+    security = ClassSecurityInfo()
+
+    def __init__( self
+                , tool
+                , snapshot_id
+                , should_purge=False
+                , encoding=None
+                ):
+
+        self._tool = tool = aq_inner( tool )
+        self._site = aq_parent( tool )
+        self._snapshot_id = snapshot_id
+        self._encoding = encoding
+        self._should_purge = bool( should_purge )
+
+    security.declareProtected( ManagePortal, 'getSite' )
+    def getSite( self ):
+
+        """ See ISetupContext.
+        """
+        return aq_self(self._site)
+
+    security.declareProtected( ManagePortal, 'getEncoding' )
+    def getEncoding( self ):
+
+        """ Return the encoding used in data files.
+
+        o Return None if the data should not be encoded.
+        """
+        return self._encoding
+
+    security.declareProtected( ManagePortal, 'readDataFile' )
+    def readDataFile( self, filename, subdir=None ):
+
+        """ See IImportContext.
+        """
+        try:
+            snapshot = self._getSnapshotFolder( subdir )
+            object = snapshot._getOb( filename )
+        except ( AttributeError, KeyError ):
+            return None
+
+        try:
+            return object.read()
+        except AttributeError:
+            return object.manage_FTPget()
+
+    security.declareProtected( ManagePortal, 'getLastModified' )
+    def getLastModified( self, path ):
+
+        """ See IImportContext.
+        """
+        try:
+            snapshot = self._getSnapshotFolder()
+            object = snapshot.restrictedTraverse( path )
+        except ( AttributeError, KeyError ):
+            return None
+        else:
+            return object.bobobase_modification_time()
+
+    security.declareProtected( ManagePortal, 'isDirectory' )
+    def isDirectory( self, path ):
+
+        """ See IImportContext.
+        """
+        try:
+            snapshot = self._getSnapshotFolder()
+            object = snapshot.restrictedTraverse( path )
+        except ( AttributeError, KeyError ):
+            return None
+        else:
+            folderish = getattr( object, 'isPrincipiaFolderish', False )
+            return bool( folderish )
+
+    security.declareProtected( ManagePortal, 'listDirectory' )
+    def listDirectory( self, path, skip=() ):
+
+        """ See IImportContext.
+        """
+        try:
+            snapshot = self._getSnapshotFolder()
+            subdir = snapshot.restrictedTraverse( path )
+        except ( AttributeError, KeyError ):
+            return None
+        else:
+            if not getattr( subdir, 'isPrincipiaFolderish', False ):
+                return None
+
+            object_ids = subdir.objectIds()
+            return [ x for x in object_ids if x not in skip ]
+
+    security.declareProtected( ManagePortal, 'shouldPurge' )
+    def shouldPurge( self ):
+
+        """ See IImportContext.
+        """
+        return self._should_purge
+
+    #
+    #   Helper methods
+    #
+    security.declarePrivate( '_getSnapshotFolder' )
+    def _getSnapshotFolder( self, subdir=None ):
+
+        """ Return the appropriate snapshot (sub)folder.
+        """
+        path = [ 'snapshots', self._snapshot_id ]
+
+        if subdir is not None:
+            path.extend( subdir.split( '/' ) )
+
+        return self._tool.restrictedTraverse( path )
+
+InitializeClass( SnapshotImportContext )

Added: CMF/branches/goldegg-phase-1/GenericSetup/differ.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/differ.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/differ.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,229 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" Diff utilities for comparing configurations.
+
+$Id: differ.py,v 1.1.1.1 2005/08/08 19:38:37 tseaver Exp $
+"""
+
+from difflib import unified_diff
+import re
+
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+
+BLANKS_REGEX = re.compile( r'^\s*$' )
+
+def unidiff( a
+           , b
+           , filename_a='original'
+           , timestamp_a=None
+           , filename_b='modified'
+           , timestamp_b=None
+           , ignore_blanks=False
+           ):
+    r"""Compare two sequences of lines; generate the resulting delta.
+
+    Each sequence must contain individual single-line strings
+    ending with newlines. Such sequences can be obtained from the
+    `readlines()` method of file-like objects.  The delta
+    generated also consists of newline-terminated strings, ready
+    to be printed as-is via the writeline() method of a file-like
+    object.
+
+    Note that the last line of a file may *not* have a newline;
+    this is reported in the same way that GNU diff reports this.
+    *This method only supports UNIX line ending conventions.*
+
+        filename_a and filename_b are used to generate the header,
+        allowing other tools to determine what 'files' were used
+        to generate this output.
+
+        timestamp_a and timestamp_b, when supplied, are expected
+        to be last-modified timestamps to be inserted in the
+        header, as floating point values since the epoch.
+
+    Example:
+
+    >>> print ''.join(UniDiffer().compare(
+    ...     'one\ntwo\nthree\n'.splitlines(1),
+    ...     'ore\ntree\nemu\n'.splitlines(1))),
+    +++ original
+    --- modified
+    @@ -1,3 +1,3 @@
+    -one
+    +ore
+    -two
+    -three
+    +tree
+    +emu
+    """
+    if isinstance( a, basestring ):
+        a = a.splitlines()
+
+    if isinstance( b, basestring ):
+        b = b.splitlines()
+
+    if ignore_blanks:
+        a = [ x for x in a if not BLANKS_REGEX.match( x ) ]
+        b = [ x for x in b if not BLANKS_REGEX.match( x ) ]
+
+    return unified_diff( a
+                       , b
+                       , filename_a
+                       , filename_b
+                       , timestamp_a
+                       , timestamp_b
+                       , lineterm=""
+                       )
+
+class ConfigDiff:
+
+    security = ClassSecurityInfo()
+
+    def __init__( self
+                , lhs
+                , rhs
+                , missing_as_empty=False
+                , ignore_blanks=False
+                , skip=('CVS','.svn')
+                ):
+        self._lhs = lhs
+        self._rhs = rhs
+        self._missing_as_empty = missing_as_empty
+        self._ignore_blanks=ignore_blanks
+        self._skip = skip
+
+    security.declarePrivate( 'compareDirectories' )
+    def compareDirectories( self, subdir=None ):
+
+        lhs_files = self._lhs.listDirectory( subdir, self._skip )
+        if lhs_files is None:
+            lhs_files = []
+
+        rhs_files = self._rhs.listDirectory( subdir, self._skip )
+        if rhs_files is None:
+            rhs_files = []
+
+        added = [ f for f in rhs_files if f not in lhs_files ]
+        removed = [ f for f in lhs_files if f not in rhs_files ]
+        all_files = lhs_files + added
+        all_files.sort()
+
+        result = []
+
+        for filename in all_files:
+
+            if subdir is None:
+                pathname = filename
+            else:
+                pathname = '%s/%s' % ( subdir, filename )
+
+            if filename not in added:
+                isDirectory = self._lhs.isDirectory( pathname )
+            else:
+                isDirectory = self._rhs.isDirectory( pathname )
+
+            if not self._missing_as_empty and filename in removed:
+
+                if isDirectory:
+                    result.append( '** Directory %s removed\n' % pathname )
+                    result.extend( self.compareDirectories( pathname ) )
+                else:
+                    result.append( '** File %s removed\n' % pathname )
+
+            elif not self._missing_as_empty and filename in added:
+
+                if isDirectory:
+                    result.append( '** Directory %s added\n' % pathname )
+                    result.extend( self.compareDirectories( pathname ) )
+                else:
+                    result.append( '** File %s added\n' % pathname )
+
+            elif isDirectory:
+
+                result.extend( self.compareDirectories( pathname ) )
+
+                if ( filename not in added + removed and
+                    not self._rhs.isDirectory( pathname ) ):
+
+                    result.append( '** Directory %s replaced with a file of '
+                                   'the same name\n' % pathname )
+
+                    if self._missing_as_empty:
+                        result.extend( self.compareFiles( filename, subdir ) )
+            else:
+                if ( filename not in added + removed and
+                     self._rhs.isDirectory( pathname ) ):
+
+                    result.append( '** File %s replaced with a directory of '
+                                   'the same name\n' % pathname )
+
+                    if self._missing_as_empty:
+                        result.extend( self.compareFiles( filename, subdir ) )
+
+                    result.extend( self.compareDirectories( pathname ) )
+                else:
+                    result.extend( self.compareFiles( filename, subdir ) )
+
+        return result
+
+    security.declarePrivate( 'compareFiles' )
+    def compareFiles( self, filename, subdir=None ):
+
+        if subdir is None:
+            path = filename
+        else:
+            path = '%s/%s' % ( subdir, filename )
+
+        lhs_file = self._lhs.readDataFile( filename, subdir )
+        lhs_time = self._lhs.getLastModified( path )
+
+        if lhs_file is None:
+            assert self._missing_as_empty
+            lhs_file = ''
+            lhs_time = 0
+
+        rhs_file = self._rhs.readDataFile( filename, subdir )
+        rhs_time = self._rhs.getLastModified( path )
+
+        if rhs_file is None:
+            assert self._missing_as_empty
+            rhs_file = ''
+            rhs_time = 0
+
+        if lhs_file == rhs_file:
+            diff_lines = []
+        else:
+            diff_lines = unidiff( lhs_file
+                                , rhs_file
+                                , filename_a=path
+                                , timestamp_a=lhs_time
+                                , filename_b=path
+                                , timestamp_b=rhs_time
+                                , ignore_blanks=self._ignore_blanks
+                                )
+            diff_lines = list( diff_lines ) # generator
+
+        if len( diff_lines ) == 0: # No *real* difference found
+            return []
+
+        diff_lines.insert( 0, 'Index: %s' % path )
+        diff_lines.insert( 1, '=' * 67 )
+
+        return diff_lines
+
+    security.declarePrivate( 'compare' )
+    def compare( self ):
+        return '\n'.join( self.compareDirectories() )
+
+InitializeClass( ConfigDiff )

Added: CMF/branches/goldegg-phase-1/GenericSetup/exceptions.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/exceptions.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/exceptions.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,22 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" GenericSetup product exceptions.
+
+$Id: exceptions.py,v 1.1.1.1 2005/08/08 19:38:37 tseaver Exp $
+"""
+
+from AccessControl import ModuleSecurityInfo
+security = ModuleSecurityInfo('Products.GenericSetup.exceptions')
+
+security.declarePublic('BadRequest')
+from zExceptions import BadRequest

Added: CMF/branches/goldegg-phase-1/GenericSetup/factory.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/factory.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/factory.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,76 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" Configured site factory implementation.
+
+$Id: factory.py,v 1.1.1.1 2005/08/08 19:38:37 tseaver Exp $
+"""
+
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+
+from interfaces import EXTENSION
+from registry import _profile_registry as profile_registry
+from tool import SetupTool
+from utils import _wwwdir
+
+def addConfiguredSiteForm( dispatcher ):
+
+    """ Wrap the PTF in 'dispatcher', including 'profile_registry' in options.
+    """
+    wrapped = PageTemplateFile( 'siteAddForm', _wwwdir ).__of__( dispatcher )
+
+    base_profiles = []
+    extension_profiles = []
+
+    for info in profile_registry.listProfileInfo():
+        if info.get('type') == EXTENSION:
+            extension_profiles.append(info)
+        else:
+            base_profiles.append(info)
+
+    return wrapped( base_profiles=tuple(base_profiles),
+                    extension_profiles =tuple(extension_profiles) )
+
+def addConfiguredSite( dispatcher
+                     , site_id
+                     , profile_id
+                     , snapshot=True
+                     , RESPONSE=None 
+                     , extension_ids=()
+                     ):
+
+    """ Add a CMFSite to 'dispatcher', configured according to 'profile_id'.
+    """
+    from Products.CMFCore.utils import getToolByName
+    from Products.CMFDefault.Portal import CMFSite # XXX:  store in profile!
+
+    site = CMFSite( site_id )
+    dispatcher._setObject( site_id, site )
+    site = dispatcher._getOb( site_id )
+
+    setup_tool = SetupTool()
+    site._setObject( 'setup_tool', setup_tool )
+    setup_tool = getToolByName( site, 'setup_tool' )
+
+    setup_tool.setImportContext( 'profile-%s' % profile_id )
+    setup_tool.runAllImportSteps()
+    for extension_id in extension_ids:
+        setup_tool.setImportContext( 'profile-%s' % extension_id )
+        setup_tool.runAllImportSteps()
+    setup_tool.setImportContext( 'profile-%s' % profile_id )
+
+    if snapshot is True:
+        setup_tool.createSnapshot( 'initial_configuration' )
+
+    if RESPONSE is not None:
+        RESPONSE.redirect( '%s/manage_main?update_menu=1'
+                         % dispatcher.absolute_url() )

Added: CMF/branches/goldegg-phase-1/GenericSetup/interfaces.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/interfaces.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/interfaces.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,508 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" GenericSetup product interfaces
+
+$Id: interfaces.py,v 1.1.1.1 2005/08/08 19:38:37 tseaver Exp $
+"""
+
+from zope.interface import Interface
+
+
+BASE, EXTENSION = range(2)
+
+
+class IPseudoInterface( Interface ):
+
+    """ API documentation;  not testable / enforceable.
+    """
+
+class ISetupContext( Interface ):
+
+    """ Context used for export / import plugins.
+    """
+    def getSite():
+
+        """ Return the site object being configured / dumped.
+        """
+
+class IImportContext( ISetupContext ):
+
+    def getEncoding():
+
+        """ Get the encoding used for configuration data within the site.
+
+        o Return None if the data should not be encoded.
+        """
+
+    def readDataFile( filename, subdir=None ):
+
+        """ Search the current configuration for the requested file.
+
+        o 'filename' is the name (without path elements) of the file.
+
+        o 'subdir' is an optional subdirectory;  if not supplied, search
+          only the "root" directory.
+
+        o Return the file contents as a string, or None if the
+          file cannot be found.
+        """
+
+    def getLastModified( path ):
+
+        """ Return the modification timestamp of the item at 'path'.
+
+        o Result will be a DateTime instance.
+
+        o Search profiles in the configuration in order.
+
+        o If the context is filesystem based, return the 'stat' timestamp
+          of the file / directory to which 'path' points.
+
+        o If the context is ZODB-based, return the Zope modification time
+          of the object to which 'path' points.
+
+        o Return None if 'path' does not point to any object.
+        """
+
+    def isDirectory( path ):
+
+        """ Test whether path points to a directory / folder.
+
+        o If the context is filesystem based, check that 'path' points to
+          a subdirectory within the "root" directory.
+
+        o If the context is ZODB-based, check that 'path' points to a
+          "container" under the context's tool.
+
+        o Return None if 'path' does not resolve;  otherwise, return a
+          bool.
+        """
+
+    def listDirectory( path, skip=('CVS', '.svn') ):
+
+        """ List IDs of the contents of a  directory / folder.
+
+        o Omit names in 'skip'.
+
+        o If 'path' does not point to a directory / folder, return None.
+        """
+
+    def shouldPurge():
+
+        """ When installing, should the existing setup be purged?
+        """
+
+class IImportPlugin( IPseudoInterface ):
+
+    """ Signature for callables used to import portions of site configuration.
+    """
+    def __call__( context ):
+
+        """ Perform the setup step.
+
+        o Return a message describing the work done.
+
+        o 'context' must implement IImportContext.
+        """
+
+class IExportContext( ISetupContext ):
+
+    def writeDataFile( filename, text, content_type, subdir=None ):
+
+        """ Write data into the specified location.
+
+        o 'filename' is the unqualified name of the file.
+
+        o 'text' is the content of the file.
+
+        o 'content_type' is the MIMEtype of the file.
+
+        o 'subdir', if passed, is a path to a subdirectory / folder in
+          which to write the file;  if not passed, write the file to the
+          "root" of the target.
+        """
+
+class IExportPlugin( IPseudoInterface ):
+
+    """ Signature for callables used to export portions of site configuration.
+    """
+    def __call__( context ):
+
+        """ Write export data for the site wrapped by context.
+
+        o Return a message describing the work done.
+
+        o 'context' must implement IExportContext.  The plugin will use
+          its 'writeDataFile' method for each file to be exported.
+        """
+
+class IStepRegistry( Interface ):
+
+    """ Base interface for step registries.
+    """
+    def listSteps():
+
+        """ Return a sequence of IDs of registered steps.
+
+        o Order is not significant.
+        """
+
+    def listStepMetadata():
+
+        """ Return a sequence of mappings describing registered steps.
+
+        o Mappings will be ordered alphabetically.
+        """
+
+    def getStepMetadata( key, default=None ):
+
+        """ Return a mapping of metadata for the step identified by 'key'.
+
+        o Return 'default' if no such step is registered.
+
+        o The 'handler' metadata is available via 'getStep'.
+        """
+
+    def generateXML():
+
+        """ Return a round-trippable XML representation of the registry.
+
+        o 'handler' values are serialized using their dotted names.
+        """
+
+    def parseXML( text ):
+
+        """ Parse 'text'.
+        """
+
+class IImportStepRegistry( IStepRegistry ):
+
+    """ API for import step registry.
+    """
+    def sortSteps():
+
+        """ Return a sequence of registered step IDs
+
+        o Sequence is sorted topologically by dependency, with the dependent
+          steps *after* the steps they depend on.
+        """
+
+    def checkComplete():
+
+        """ Return a sequence of ( node, edge ) tuples for unsatisifed deps.
+        """
+
+    def getStep( key, default=None ):
+
+        """ Return the IImportPlugin registered for 'key'.
+
+        o Return 'default' if no such step is registered.
+        """
+
+    def registerStep( id
+                    , version
+                    , handler
+                    , dependencies=()
+                    , title=None
+                    , description=None
+                    ):
+        """ Register a setup step.
+
+        o 'id' is a unique name for this step,
+
+        o 'version' is a string for comparing versions, it is preferred to
+          be a yyyy/mm/dd-ii formatted string (date plus two-digit
+          ordinal).  when comparing two version strings, the version with
+          the lower sort order is considered the older version.
+
+          - Newer versions of a step supplant older ones.
+
+          - Attempting to register an older one after a newer one results
+            in a KeyError.
+
+        o 'handler' should implement IImportPlugin.
+
+        o 'dependencies' is a tuple of step ids which have to run before
+          this step in order to be able to run at all. Registration of
+          steps that have unmet dependencies are deferred until the
+          dependencies have been registered.
+
+        o 'title' is a one-line UI description for this step.
+          If None, the first line of the documentation string of the handler
+          is used, or the id if no docstring can be found.
+
+        o 'description' is a one-line UI description for this step.
+          If None, the remaining line of the documentation string of
+          the handler is used, or default to ''.
+        """
+
+class IExportStepRegistry( IStepRegistry ):
+
+    """ API for export step registry.
+    """
+    def getStep( key, default=None ):
+
+        """ Return the IExportPlugin registered for 'key'.
+
+        o Return 'default' if no such step is registered.
+        """
+
+    def registerStep( id, handler, title=None, description=None ):
+
+        """ Register an export step.
+
+        o 'id' is the unique identifier for this step
+
+        o 'handler' should implement IExportPlugin.
+
+        o 'title' is a one-line UI description for this step.
+          If None, the first line of the documentation string of the step
+          is used, or the id if no docstring can be found.
+
+        o 'description' is a one-line UI description for this step.
+          If None, the remaining line of the documentation string of
+          the step is used, or default to ''.
+        """
+
+class IToolsetRegistry( Interface ):
+
+    """ API for toolset registry.
+    """
+    def listForbiddenTools():
+
+        """ Return a list of IDs of tools which must be removed, if present.
+        """
+
+    def addForbiddenTool(tool_id ):
+
+        """ Add 'tool_id' to the list of forbidden tools.
+
+        o Raise KeyError if 'tool_id' is already in the list.
+
+        o Raise ValueError if 'tool_id' is in the "required" list.
+        """
+
+    def listRequiredTools():
+
+        """ Return a list of IDs of tools which must be present.
+        """
+
+    def getRequiredToolInfo( tool_id ):
+
+        """ Return a mapping describing a partiuclar required tool.
+
+        o Keys include:
+
+          'id' -- the ID of the tool
+
+          'class' -- a dotted path to its class
+
+        o Raise KeyError if 'tool_id' id not a known tool.
+        """
+
+    def listRequiredToolInfo():
+
+        """ Return a list of IDs of tools which must be present.
+        """
+
+    def addRequiredTool( tool_id, dotted_name ):
+
+        """ Add a tool to our "required" list.
+
+        o 'tool_id' is the tool's ID.
+
+        o 'dotted_name' is a dotted (importable) name of the tool's class.
+
+        o Raise KeyError if we have already registered a class for 'tool_id'.
+
+        o Raise ValueError if 'tool_id' is in the "forbidden" list.
+        """
+
+class IProfileRegistry( Interface ):
+
+    """ API for profile registry.
+    """
+    def getProfileInfo( profile_id ):
+
+        """ Return a mapping describing a registered filesystem profile.
+
+        o Keys include:
+
+          'id' -- the ID of the profile
+
+          'title' -- its title
+
+          'description' -- a textual description of the profile
+
+          'path' -- a path to the profile on the filesystem.
+
+          'product' -- the name of the product to which 'path' is
+             relative (None for absolute paths).
+
+          'type' -- either BASE or EXTENSION
+        """
+
+    def listProfiles():
+
+        """ Return a list of IDs for registered profiles.
+        """
+
+    def listProfileInfo():
+
+        """ Return a list of mappings describing registered profiles.
+
+        o See 'getProfileInfo' for a description of the mappings' keys.
+        """
+
+    def registerProfile( name
+                       , title
+                       , description
+                       , path
+                       , product=None
+                       , profile_type=BASE
+                       ):
+        """ Add a new profile to the registry.
+
+        o If an existing profile is already registered for 'product:name',
+          raise KeyError.
+
+        o If 'product' is passed, then 'path' should be interpreted as
+          relative to the corresponding product directory.
+        """
+
+class ISetupTool( Interface ):
+
+    """ API for SetupTool.
+    """
+
+    def getEncoding():
+
+        """ Get the encoding used for configuration data within the site.
+
+        o Return None if the data should not be encoded.
+        """
+
+    def getImportContextID():
+
+        """ Get the ID of the active import context.
+        """
+
+    def setImportContext( context_id ):
+
+        """ Set the ID of the active import context and update the registries.
+        """
+
+    def getImportStepRegistry():
+
+        """ Return the IImportStepRegistry for the tool.
+        """
+
+    def getExportStepRegistry():
+
+        """ Return the IExportStepRegistry for the tool.
+        """
+
+    def getToolsetRegistry():
+
+        """ Return the IToolsetRegistry for the tool.
+        """
+
+    def runImportStep( step_id, run_dependencies=True, purge_old=None ):
+
+        """ Execute a given setup step
+
+        o 'step_id' is the ID of the step to run.
+
+        o If 'purge_old' is True, then run the step after purging any
+          "old" setup first (this is the responsibility of the step,
+          which must check the context we supply).
+
+        o If 'run_dependencies' is True, then run any out-of-date
+          dependency steps first.
+
+        o Return a mapping, with keys:
+
+          'steps' -- a sequence of IDs of the steps run.
+
+          'messages' -- a dictionary holding messages returned from each
+            step
+        """
+
+    def runAllImportSteps( purge_old=None ):
+
+        """ Run all setup steps in dependency order.
+
+        o If 'purge_old' is True, then run each step after purging any
+          "old" setup first (this is the responsibility of the step,
+          which must check the context we supply).
+
+        o Return a mapping, with keys:
+
+          'steps' -- a sequence of IDs of the steps run.
+
+          'messages' -- a dictionary holding messages returned from each
+            step
+        """
+
+    def runExportStep( step_id ):
+
+        """ Generate a tarball containing artifacts from one export step.
+
+        o 'step_id' identifies the export step.
+
+        o Return a mapping, with keys:
+
+          'steps' -- a sequence of IDs of the steps run.
+
+          'messages' -- a dictionary holding messages returned from each
+            step
+
+          'tarball' -- the stringified tar-gz data.
+        """
+
+    def runAllExportSteps():
+
+        """ Generate a tarball containing artifacts from all export steps.
+
+        o Return a mapping, with keys:
+
+          'steps' -- a sequence of IDs of the steps run.
+
+          'messages' -- a dictionary holding messages returned from each
+            step
+
+          'tarball' -- the stringified tar-gz data.
+        """
+
+    def createSnapshot( snapshot_id ):
+
+        """ Create a snapshot folder using all steps.
+
+        o 'snapshot_id' is the ID of the new folder.
+        """
+
+    def compareConfigurations( lhs_context
+                             , rhs_context
+                             , missing_as_empty=False
+                             , ignore_whitespace=False
+                             ):
+        """ Compare two configurations.
+
+        o 'lhs_context' and 'rhs_context' must implement IImportContext.
+
+        o If 'missing_as_empty', then compare files not present as though
+          they were zero-length;  otherwise, omit such files.
+
+        o If 'ignore_whitespace', then suppress diffs due only to whitespace
+          (c.f:  'diff -wbB')
+        """

Added: CMF/branches/goldegg-phase-1/GenericSetup/permissions.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/permissions.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/permissions.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,18 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" GenericSetup product permissions.
+
+$Id: permissions.py,v 1.1.1.1 2005/08/08 19:38:37 tseaver Exp $
+"""
+
+ManagePortal = 'Manage Site'

Added: CMF/branches/goldegg-phase-1/GenericSetup/properties.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/properties.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/properties.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,97 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" Site properties setup handlers.
+
+$Id: properties.py,v 1.1.1.1 2005/08/08 19:38:37 tseaver Exp $
+"""
+
+from AccessControl import ClassSecurityInfo
+from Globals import InitializeClass
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+
+from permissions import ManagePortal
+from utils import _xmldir
+from utils import ConfiguratorBase
+from utils import DEFAULT, KEY
+
+
+#
+#   Configurator entry points
+#
+_FILENAME = 'properties.xml'
+
+def importSiteProperties(context):
+    """ Import site properties from an XML file.
+    """
+    site = context.getSite()
+    encoding = context.getEncoding()
+
+    if context.shouldPurge():
+
+        for prop_map in site._propertyMap():
+            prop_id = prop_map['id']
+            if 'd' in prop_map.get('mode', 'wd') and \
+                    prop_id not in ('title', 'description'):
+                site._delProperty(prop_id)
+            else:
+                if prop_map.get('type') == 'multiple selection':
+                    prop_value = ()
+                else:
+                    prop_value = ''
+                site._updateProperty(prop_id, prop_value)
+
+    xml = context.readDataFile(_FILENAME)
+    if xml is None:
+        return 'Site properties: Nothing to import.'
+
+    spc = SitePropertiesConfigurator(site, encoding)
+    site_info = spc.parseXML(xml)
+
+    for prop_info in site_info['properties']:
+        spc.initProperty(site, prop_info)
+
+    return 'Site properties imported.'
+
+def exportSiteProperties(context):
+    """ Export site properties as an XML file.
+    """
+    site = context.getSite()
+    spc = SitePropertiesConfigurator(site).__of__(site)
+
+    xml = spc.generateXML()
+    context.writeDataFile(_FILENAME, xml, 'text/xml')
+
+    return 'Site properties exported.'
+
+
+class SitePropertiesConfigurator(ConfiguratorBase):
+    """ Synthesize XML description of site's properties.
+    """
+    security = ClassSecurityInfo()
+
+    security.declareProtected(ManagePortal, 'listSiteInfos')
+    def listSiteInfos(self):
+        """ Get a sequence of mappings for site properties.
+        """
+        return tuple( [ self._extractProperty(self._site, prop_map)
+                        for prop_map in self._site._propertyMap() ] )
+
+    def _getExportTemplate(self):
+
+        return PageTemplateFile('spcExport.xml', _xmldir)
+
+    def _getImportMapping(self):
+
+        return { 'site': { 'property': {KEY: 'properties', DEFAULT: () } } }
+
+InitializeClass(SitePropertiesConfigurator)

Added: CMF/branches/goldegg-phase-1/GenericSetup/registry.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/registry.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/registry.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,754 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" Classes:  ImportStepRegistry, ExportStepRegistry
+
+$Id: registry.py,v 1.1.1.1 2005/08/08 19:38:37 tseaver Exp $
+"""
+
+from xml.sax import parseString
+
+from AccessControl import ClassSecurityInfo
+from Acquisition import Implicit
+from Globals import InitializeClass
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+from zope.interface import implements
+
+from interfaces import BASE
+from interfaces import IImportStepRegistry
+from interfaces import IExportStepRegistry
+from interfaces import IToolsetRegistry
+from interfaces import IProfileRegistry
+from permissions import ManagePortal
+from utils import HandlerBase
+from utils import _xmldir
+from utils import _getDottedName
+from utils import _resolveDottedName
+from utils import _extractDocstring
+
+
+class ImportStepRegistry( Implicit ):
+
+    """ Manage knowledge about steps to create / configure site.
+
+    o Steps are composed together to define a site profile.
+    """
+    implements(IImportStepRegistry)
+
+    security = ClassSecurityInfo()
+
+    def __init__( self ):
+
+        self.clear()
+
+    security.declareProtected( ManagePortal, 'listSteps' )
+    def listSteps( self ):
+
+        """ Return a sequence of IDs of registered steps.
+
+        o Order is not significant.
+        """
+        return self._registered.keys()
+
+    security.declareProtected( ManagePortal, 'sortSteps' )
+    def sortSteps( self ):
+
+        """ Return a sequence of registered step IDs
+
+        o Sequence is sorted topologically by dependency, with the dependent
+          steps *after* the steps they depend on.
+        """
+        return self._computeTopologicalSort()
+
+    security.declareProtected( ManagePortal, 'checkComplete' )
+    def checkComplete( self ):
+
+        """ Return a sequence of ( node, edge ) tuples for unsatisifed deps.
+        """
+        result = []
+        seen = {}
+
+        graph = self._computeTopologicalSort()
+
+        for node in graph:
+
+            dependencies = self.getStepMetadata( node )[ 'dependencies' ]
+
+            for dependency in dependencies:
+
+                if seen.get( dependency ) is None:
+                    result.append( ( node, dependency ) )
+
+            seen[ node ] = 1
+
+        return result
+
+    security.declareProtected( ManagePortal, 'getStepMetadata' )
+    def getStepMetadata( self, key, default=None ):
+
+        """ Return a mapping of metadata for the step identified by 'key'.
+
+        o Return 'default' if no such step is registered.
+
+        o The 'handler' metadata is available via 'getStep'.
+        """
+        result = {}
+
+        info = self._registered.get( key )
+
+        if info is None:
+            return default
+
+        return info.copy()
+
+    security.declareProtected( ManagePortal, 'listStepMetadata' )
+    def listStepMetadata( self ):
+
+        """ Return a sequence of mappings describing registered steps.
+
+        o Mappings will be ordered alphabetically.
+        """
+        step_ids = self.listSteps()
+        step_ids.sort()
+        return [ self.getStepMetadata( x ) for x in step_ids ]
+
+    security.declareProtected( ManagePortal, 'generateXML' )
+    def generateXML( self ):
+
+        """ Return a round-trippable XML representation of the registry.
+
+        o 'handler' values are serialized using their dotted names.
+        """
+        return self._exportTemplate()
+
+    security.declarePrivate( 'getStep' )
+    def getStep( self, key, default=None ):
+
+        """ Return the IImportPlugin registered for 'key'.
+
+        o Return 'default' if no such step is registered.
+        """
+        marker = object()
+        info = self._registered.get( key, marker )
+
+        if info is marker:
+            return default
+
+        return _resolveDottedName( info[ 'handler' ] )
+
+    security.declarePrivate( 'registerStep' )
+    def registerStep( self
+                    , id
+                    , version
+                    , handler
+                    , dependencies=()
+                    , title=None
+                    , description=None
+                    ):
+        """ Register a setup step.
+
+        o 'id' is a unique name for this step,
+
+        o 'version' is a string for comparing versions, it is preferred to
+          be a yyyy/mm/dd-ii formatted string (date plus two-digit
+          ordinal).  when comparing two version strings, the version with
+          the lower sort order is considered the older version.
+
+          - Newer versions of a step supplant older ones.
+
+          - Attempting to register an older one after a newer one results
+            in a KeyError.
+
+        o 'handler' should implement IImportPlugin.
+
+        o 'dependencies' is a tuple of step ids which have to run before
+          this step in order to be able to run at all. Registration of
+          steps that have unmet dependencies are deferred until the
+          dependencies have been registered.
+
+        o 'title' is a one-line UI description for this step.
+          If None, the first line of the documentation string of the handler
+          is used, or the id if no docstring can be found.
+
+        o 'description' is a one-line UI description for this step.
+          If None, the remaining line of the documentation string of
+          the handler is used, or default to ''.
+        """
+        already = self.getStepMetadata( id )
+
+        if already and already[ 'version' ] > version:
+            raise KeyError( 'Existing registration for step %s, version %s'
+                          % ( id, already[ 'version' ] ) )
+
+        if title is None or description is None:
+
+            t, d = _extractDocstring( handler, id, '' )
+
+            title = title or t
+            description = description or d
+
+        info = { 'id'           : id
+               , 'version'      : version
+               , 'handler'      : _getDottedName( handler )
+               , 'dependencies' : dependencies
+               , 'title'        : title
+               , 'description'  : description
+               }
+
+        self._registered[ id ] = info
+
+    security.declarePrivate( 'parseXML' )
+    def parseXML( self, text, encoding=None ):
+
+        """ Parse 'text'.
+        """
+        reader = getattr( text, 'read', None )
+
+        if reader is not None:
+            text = reader()
+
+        parser = _ImportStepRegistryParser( encoding )
+        parseString( text, parser )
+
+        return parser._parsed
+
+    security.declarePrivate( 'clear' )
+    def clear( self ):
+
+        self._registered = {}
+
+    #
+    #   Helper methods
+    #
+    security.declarePrivate( '_computeTopologicalSort' )
+    def _computeTopologicalSort( self ):
+
+        result = []
+
+        graph = [ ( x[ 'id' ], x[ 'dependencies' ] )
+                    for x in self._registered.values() ]
+
+        for node, edges in graph:
+
+            after = -1
+
+            for edge in edges:
+
+                if edge in result:
+                    after = max( after, result.index( edge ) )
+
+            result.insert( after + 1, node )
+
+        return result
+
+    security.declarePrivate( '_exportTemplate' )
+    _exportTemplate = PageTemplateFile( 'isrExport.xml', _xmldir )
+
+InitializeClass( ImportStepRegistry )
+
+
+class ExportStepRegistry( Implicit ):
+
+    """ Registry of known site-configuration export steps.
+
+    o Each step is registered with a unique id.
+
+    o When called, with the portal object passed in as an argument,
+      the step must return a sequence of three-tuples,
+      ( 'data', 'content_type', 'filename' ), one for each file exported
+      by the step.
+
+      - 'data' is a string containing the file data;
+
+      - 'content_type' is the MIME type of the data;
+
+      - 'filename' is a suggested filename for use when downloading.
+
+    """
+    implements(IExportStepRegistry)
+
+    security = ClassSecurityInfo()
+
+    def __init__( self ):
+
+        self.clear()
+
+    security.declareProtected( ManagePortal, 'listSteps' )
+    def listSteps( self ):
+
+        """ Return a list of registered step IDs.
+        """
+        return self._registered.keys()
+
+    security.declareProtected( ManagePortal, 'getStepMetadata' )
+    def getStepMetadata( self, key, default=None ):
+
+        """ Return a mapping of metadata for the step identified by 'key'.
+
+        o Return 'default' if no such step is registered.
+
+        o The 'handler' metadata is available via 'getStep'.
+        """
+        info = self._registered.get( key )
+
+        if info is None:
+            return default
+
+        return info.copy()
+
+    security.declareProtected( ManagePortal, 'listStepMetadata' )
+    def listStepMetadata( self ):
+
+        """ Return a sequence of mappings describing registered steps.
+
+        o Steps will be alphabetical by ID.
+        """
+        step_ids = self.listSteps()
+        step_ids.sort()
+        return [ self.getStepMetadata( x ) for x in step_ids ]
+
+    security.declareProtected( ManagePortal, 'generateXML' )
+    def generateXML( self ):
+
+        """ Return a round-trippable XML representation of the registry.
+
+        o 'handler' values are serialized using their dotted names.
+        """
+        return self._exportTemplate()
+
+    security.declarePrivate( 'getStep' )
+    def getStep( self, key, default=None ):
+
+        """ Return the IExportPlugin registered for 'key'.
+
+        o Return 'default' if no such step is registered.
+        """
+        marker = object()
+        info = self._registered.get( key, marker )
+
+        if info is marker:
+            return default
+
+        return _resolveDottedName( info[ 'handler' ] )
+
+    security.declarePrivate( 'registerStep' )
+    def registerStep( self, id, handler, title=None, description=None ):
+
+        """ Register an export step.
+
+        o 'id' is the unique identifier for this step
+
+        o 'step' should implement IExportPlugin.
+
+        o 'title' is a one-line UI description for this step.
+          If None, the first line of the documentation string of the step
+          is used, or the id if no docstring can be found.
+
+        o 'description' is a one-line UI description for this step.
+          If None, the remaining line of the documentation string of
+          the step is used, or default to ''.
+        """
+        if title is None or description is None:
+
+            t, d = _extractDocstring( handler, id, '' )
+
+            title = title or t
+            description = description or d
+
+        info = { 'id'           : id
+               , 'handler'      : _getDottedName( handler )
+               , 'title'        : title
+               , 'description'  : description
+               }
+
+        self._registered[ id ] = info
+
+    security.declarePrivate( 'parseXML' )
+    def parseXML( self, text, encoding=None ):
+
+        """ Parse 'text'.
+        """
+        reader = getattr( text, 'read', None )
+
+        if reader is not None:
+            text = reader()
+
+        parser = _ExportStepRegistryParser( encoding )
+        parseString( text, parser )
+
+        return parser._parsed
+
+    security.declarePrivate( 'clear' )
+    def clear( self ):
+
+        self._registered = {}
+
+    #
+    #   Helper methods
+    #
+    security.declarePrivate( '_exportTemplate' )
+    _exportTemplate = PageTemplateFile( 'esrExport.xml', _xmldir )
+
+InitializeClass( ExportStepRegistry )
+
+class ToolsetRegistry( Implicit ):
+
+    """ Track required / forbidden tools.
+    """
+    implements(IToolsetRegistry)
+
+    security = ClassSecurityInfo()
+    security.setDefaultAccess( 'allow' )
+
+    def __init__( self ):
+
+        self.clear()
+
+    #
+    #   Toolset API
+    #
+    security.declareProtected( ManagePortal, 'listForbiddenTools' )
+    def listForbiddenTools( self ):
+
+        """ See IToolsetRegistry.
+        """
+        result = list( self._forbidden )
+        result.sort()
+        return result
+
+    security.declareProtected( ManagePortal, 'addForbiddenTool' )
+    def addForbiddenTool( self, tool_id ):
+
+        """ See IToolsetRegistry.
+        """
+        if tool_id in self._forbidden:
+            return
+
+        if self._required.get( tool_id ) is not None:
+            raise ValueError, 'Tool %s is required!' % tool_id
+
+        self._forbidden.append( tool_id )
+
+    security.declareProtected( ManagePortal, 'listRequiredTools' )
+    def listRequiredTools( self ):
+
+        """ See IToolsetRegistry.
+        """
+        result = list( self._required.keys() )
+        result.sort()
+        return result
+
+    security.declareProtected( ManagePortal, 'getRequiredToolInfo' )
+    def getRequiredToolInfo( self, tool_id ):
+
+        """ See IToolsetRegistry.
+        """
+        return self._required[ tool_id ]
+
+    security.declareProtected( ManagePortal, 'listRequiredToolInfo' )
+    def listRequiredToolInfo( self ):
+
+        """ See IToolsetRegistry.
+        """
+        return [ self.getRequiredToolInfo( x )
+                        for x in self.listRequiredTools() ]
+
+    security.declareProtected( ManagePortal, 'addRequiredTool' )
+    def addRequiredTool( self, tool_id, dotted_name ):
+
+        """ See IToolsetRegistry.
+        """
+        if tool_id in self._forbidden:
+            raise ValueError, "Forbidden tool ID: %s" % tool_id
+
+        self._required[ tool_id ] = { 'id' : tool_id
+                                    , 'class' : dotted_name
+                                    }
+
+    security.declareProtected( ManagePortal, 'generateXML' )
+    def generateXML( self ):
+
+        """ Pseudo API.
+        """
+        return self._toolsetConfig()
+
+    security.declareProtected( ManagePortal, 'parseXML' )
+    def parseXML( self, text, encoding=None ):
+
+        """ Pseudo-API
+        """
+        reader = getattr( text, 'read', None )
+
+        if reader is not None:
+            text = reader()
+
+        parser = _ToolsetParser( encoding )
+        parseString( text, parser )
+
+        for tool_id in parser._forbidden:
+            self.addForbiddenTool( tool_id )
+
+        for tool_id, dotted_name in parser._required.items():
+            self.addRequiredTool( tool_id, dotted_name )
+
+    security.declarePrivate( 'clear' )
+    def clear( self ):
+
+        self._forbidden = []
+        self._required = {}
+
+    #
+    #   Helper methods.
+    #
+    security.declarePrivate( '_toolsetConfig' )
+    _toolsetConfig = PageTemplateFile( 'tscExport.xml'
+                                     , _xmldir
+                                     , __name__='toolsetConfig'
+                                     )
+
+InitializeClass( ToolsetRegistry )
+
+class ProfileRegistry( Implicit ):
+
+    """ Track registered profiles.
+    """
+    implements(IProfileRegistry)
+
+    security = ClassSecurityInfo()
+    security.setDefaultAccess( 'allow' )
+
+    def __init__( self ):
+
+        self.clear()
+
+    security.declareProtected( ManagePortal, '' )
+    def getProfileInfo( self, profile_id ):
+
+        """ See IProfileRegistry.
+        """
+        result = self._profile_info[ profile_id ]
+        return result.copy()
+
+    security.declareProtected( ManagePortal, 'listProfiles' )
+    def listProfiles( self ):
+
+        """ See IProfileRegistry.
+        """
+        return tuple( self._profile_ids )
+
+    security.declareProtected( ManagePortal, 'listProfileInfo' )
+    def listProfileInfo( self ):
+
+        """ See IProfileRegistry.
+        """
+        return [ self.getProfileInfo( id ) for id in self.listProfiles() ]
+
+    security.declareProtected( ManagePortal, 'registerProfile' )
+    def registerProfile( self
+                       , name
+                       , title
+                       , description
+                       , path
+                       , product=None
+                       , profile_type=BASE
+                       ):
+        """ See IProfileRegistry.
+        """
+        profile_id = '%s:%s' % (product or 'other', name)
+        if self._profile_info.get( profile_id ) is not None:
+            raise KeyError, 'Duplicate profile ID: %s' % profile_id
+
+        self._profile_ids.append( profile_id )
+
+        info = { 'id' : profile_id
+               , 'title' : title
+               , 'description' : description
+               , 'path' : path
+               , 'product' : product
+               , 'type': profile_type
+               }
+
+        self._profile_info[ profile_id ] = info
+
+    security.declarePrivate( 'clear' )
+    def clear( self ):
+
+        self._profile_info = {}
+        self._profile_ids = []
+
+InitializeClass( ProfileRegistry )
+
+_profile_registry = ProfileRegistry()
+
+class _ImportStepRegistryParser( HandlerBase ):
+
+    security = ClassSecurityInfo()
+    security.declareObjectPrivate()
+    security.setDefaultAccess( 'deny' )
+
+    def __init__( self, encoding ):
+
+        self._encoding = encoding
+        self._started = False
+        self._pending = None
+        self._parsed = []
+
+    def startElement( self, name, attrs ):
+
+        if name == 'import-steps':
+
+            if self._started:
+                raise ValueError, 'Duplicated setup-steps element: %s' % name
+
+            self._started = True
+
+        elif name == 'import-step':
+
+            if self._pending is not None:
+                raise ValueError, 'Cannot nest setup-step elements'
+
+            self._pending = dict( [ ( k, self._extract( attrs, k ) )
+                                    for k in attrs.keys() ] )
+
+            self._pending[ 'dependencies' ] = []
+
+        elif name == 'dependency':
+
+            if not self._pending:
+                raise ValueError, 'Dependency outside of step'
+
+            depended = self._extract( attrs, 'step' )
+            self._pending[ 'dependencies' ].append( depended )
+
+        else:
+            raise ValueError, 'Unknown element %s' % name
+
+    def characters( self, content ):
+
+        if self._pending is not None:
+            content = self._encode( content )
+            self._pending.setdefault( 'description', [] ).append( content )
+
+    def endElement(self, name):
+
+        if name == 'import-steps':
+            pass
+
+        elif name == 'import-step':
+
+            if self._pending is None:
+                raise ValueError, 'No pending step!'
+
+            deps = tuple( self._pending[ 'dependencies' ] )
+            self._pending[ 'dependencies' ] = deps
+
+            desc = ''.join( self._pending[ 'description' ] )
+            self._pending[ 'description' ] = desc
+
+            self._parsed.append( self._pending )
+            self._pending = None
+
+InitializeClass( _ImportStepRegistryParser )
+
+class _ExportStepRegistryParser( HandlerBase ):
+
+    security = ClassSecurityInfo()
+    security.declareObjectPrivate()
+    security.setDefaultAccess( 'deny' )
+
+    def __init__( self, encoding ):
+
+        self._encoding = encoding
+        self._started = False
+        self._pending = None
+        self._parsed = []
+
+    def startElement( self, name, attrs ):
+
+        if name == 'export-steps':
+
+            if self._started:
+                raise ValueError, 'Duplicated export-steps element: %s' % name
+
+            self._started = True
+
+        elif name == 'export-step':
+
+            if self._pending is not None:
+                raise ValueError, 'Cannot nest export-step elements'
+
+            self._pending = dict( [ ( k, self._extract( attrs, k ) )
+                                    for k in attrs.keys() ] )
+
+        else:
+            raise ValueError, 'Unknown element %s' % name
+
+    def characters( self, content ):
+
+        if self._pending is not None:
+            content = self._encode( content )
+            self._pending.setdefault( 'description', [] ).append( content )
+
+    def endElement(self, name):
+
+        if name == 'export-steps':
+            pass
+
+        elif name == 'export-step':
+
+            if self._pending is None:
+                raise ValueError, 'No pending step!'
+
+            desc = ''.join( self._pending[ 'description' ] )
+            self._pending[ 'description' ] = desc
+
+            self._parsed.append( self._pending )
+            self._pending = None
+
+InitializeClass( _ExportStepRegistryParser )
+
+
+class _ToolsetParser( HandlerBase ):
+
+    security = ClassSecurityInfo()
+    security.declareObjectPrivate()
+    security.setDefaultAccess( 'deny' )
+
+    def __init__( self, encoding ):
+
+        self._encoding = encoding
+        self._required = {}
+        self._forbidden = []
+
+    def startElement( self, name, attrs ):
+
+        if name == 'tool-setup':
+            pass
+
+        elif name == 'forbidden':
+
+            tool_id = self._extract( attrs, 'tool_id' )
+
+            if tool_id not in self._forbidden:
+                self._forbidden.append( tool_id )
+
+        elif name == 'required':
+
+            tool_id = self._extract( attrs, 'tool_id' )
+            dotted_name = self._extract( attrs, 'class' )
+            self._required[ tool_id ] = dotted_name
+
+        else:
+            raise ValueError, 'Unknown element %s' % name
+
+
+InitializeClass( _ToolsetParser )

Added: CMF/branches/goldegg-phase-1/GenericSetup/rolemap.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/rolemap.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/rolemap.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,212 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" GenericSetup:  Role-permission export / import
+
+$Id: rolemap.py,v 1.1.1.1 2005/08/08 19:38:37 tseaver Exp $
+"""
+
+from AccessControl import ClassSecurityInfo
+from AccessControl.Permission import Permission
+from Globals import InitializeClass
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+
+from permissions import ManagePortal
+from utils import _xmldir
+from utils import ConfiguratorBase
+from utils import CONVERTER, DEFAULT, KEY
+
+
+#
+#   Configurator entry points
+#
+_FILENAME = 'rolemap.xml'
+
+def importRolemap( context ):
+
+    """ Import roles / permission map from an XML file.
+
+    o 'context' must implement IImportContext.
+
+    o Register via Python:
+
+      registry = site.setup_tool.setup_steps
+      registry.registerStep( 'importRolemap'
+                           , '20040518-01'
+                           , Products.GenericSetup.rolemap.importRolemap
+                           , ()
+                           , 'Role / Permission import'
+                           , 'Import additional roles, and map '
+                           'roles to permissions'
+                           )
+
+    o Register via XML:
+
+      <setup-step id="importRolemap"
+                  version="20040518-01"
+                  handler="Products.GenericSetup.rolemap.importRolemap"
+                  title="Role / Permission import"
+      >Import additional roles, and map roles to permissions.</setup-step>
+
+    """
+    site = context.getSite()
+    encoding = context.getEncoding()
+
+    if context.shouldPurge():
+
+        items = site.__dict__.items()
+
+        for k, v in items: # XXX: WAAA
+
+            if k == '__ac_roles__':
+                delattr( site, k )
+
+            if k.startswith( '_' ) and k.endswith( '_Permission' ):
+                delattr( site, k )
+
+    text = context.readDataFile( _FILENAME )
+
+    if text is not None:
+
+        rc = RolemapConfigurator( site, encoding )
+        rolemap_info = rc.parseXML( text )
+
+        immediate_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
+        already = {}
+
+        for role in site.valid_roles():
+            already[ role ] = 1
+
+        for role in rolemap_info[ 'roles' ]:
+
+            if already.get( role ) is None:
+                immediate_roles.append( role )
+                already[ role ] = 1
+
+        immediate_roles.sort()
+        site.__ac_roles__ = tuple( immediate_roles )
+
+        for permission in rolemap_info[ 'permissions' ]:
+
+            site.manage_permission( permission[ 'name' ]
+                                  , permission[ 'roles' ]
+                                  , permission[ 'acquire' ]
+                                  )
+
+    return 'Role / permission map imported.'
+
+
+def exportRolemap( context ):
+
+    """ Export roles / permission map as an XML file
+
+    o 'context' must implement IExportContext.
+
+    o Register via Python:
+
+      registry = site.setup_tool.export_steps
+      registry.registerStep( 'exportRolemap'
+                           , Products.GenericSetup.rolemap.exportRolemap
+                           , 'Role / Permission export'
+                           , 'Export additional roles, and '
+                             'role / permission map '
+                           )
+
+    o Register via XML:
+
+      <export-script id="exportRolemap"
+                     version="20040518-01"
+                     handler="Products.GenericSetup.rolemap.exportRolemap"
+                     title="Role / Permission export"
+      >Export additional roles, and role / permission map.</export-script>
+
+    """
+    site = context.getSite()
+    rc = RolemapConfigurator( site ).__of__( site )
+    text = rc.generateXML()
+
+    context.writeDataFile( _FILENAME, text, 'text/xml' )
+
+    return 'Role / permission map exported.'
+
+
+class RolemapConfigurator(ConfiguratorBase):
+    """ Synthesize XML description of sitewide role-permission settings.
+    """
+    security = ClassSecurityInfo()
+
+    security.declareProtected( ManagePortal, 'listRoles' )
+    def listRoles( self ):
+
+        """ List the valid role IDs for our site.
+        """
+        return self._site.valid_roles()
+
+    security.declareProtected( ManagePortal, 'listPermissions' )
+    def listPermissions( self ):
+
+        """ List permissions for export.
+
+        o Returns a sqeuence of mappings describing locally-modified
+          permission / role settings.  Keys include:
+
+          'permission' -- the name of the permission
+
+          'acquire' -- a flag indicating whether to acquire roles from the
+              site's container
+
+          'roles' -- the list of roles which have the permission.
+
+        o Do not include permissions which both acquire and which define
+          no local changes to the acquired policy.
+        """
+        permissions = []
+        valid_roles = self.listRoles()
+
+        for perm in self._site.ac_inherited_permissions( 1 ):
+
+            name = perm[ 0 ]
+            p = Permission( name, perm[ 1 ], self._site )
+            roles = p.getRoles( default=[] )
+            acquire = isinstance( roles, list )  # tuple means don't acquire
+            roles = [ r for r in roles if r in valid_roles ]
+
+            if roles or not acquire:
+                permissions.append( { 'name'    : name
+                                    , 'acquire' : acquire
+                                    , 'roles'   : roles
+                                    } )
+
+        return permissions
+
+    def _getExportTemplate(self):
+
+        return PageTemplateFile('rmeExport.xml', _xmldir)
+
+    def _getImportMapping(self):
+
+        return {
+          'rolemap':
+            { 'roles':       {CONVERTER: self._convertToUnique},
+              'permissions': {CONVERTER: self._convertToUnique} },
+          'roles':
+            { 'role':        {KEY: None} },
+          'role':
+            { 'name':        {KEY: None} },
+          'permissions':
+            { 'permission':  {KEY: None, DEFAULT: ()} },
+          'permission':
+            { 'name':        {},
+              'role':        {KEY: 'roles'},
+              'acquire':     {CONVERTER: self._convertToBoolean} } }
+
+InitializeClass(RolemapConfigurator)

Added: CMF/branches/goldegg-phase-1/GenericSetup/tests/__init__.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/tests/__init__.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/tests/__init__.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,16 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" GenericSetup product unit tests.
+
+$Id: __init__.py,v 1.1.1.1 2005/08/08 19:38:37 tseaver Exp $
+"""

Added: CMF/branches/goldegg-phase-1/GenericSetup/tests/common.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/tests/common.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/tests/common.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,218 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" GenericSetup product:  unit test utilities.
+
+$Id: common.py,v 1.1.1.1 2005/08/08 19:38:37 tseaver Exp $
+"""
+
+import os
+import shutil
+from tarfile import TarFile
+
+from Acquisition import Implicit
+from Testing.ZopeTestCase import ZopeTestCase
+
+class OmnipotentUser(Implicit):
+    """ Omnipotent User for unit testing purposes.
+    """
+    def getId(self):
+        return 'all_powerful_Oz'
+
+    getUserName = getId
+
+    def getRoles(self):
+        return ('Manager',)
+
+    def allowed(self, object, object_roles=None):
+        return 1
+
+    def getRolesInContext(self, object):
+        return ('Manager',)
+
+class SecurityRequestTest(ZopeTestCase):
+    def setUp(self):
+        from AccessControl.SecurityManagement import newSecurityManager
+        ZopeTestCase.setUp(self)
+        self.root = self.app
+        newSecurityManager(None, OmnipotentUser().__of__(self.app.acl_users))
+
+    def tearDown(self):
+        from AccessControl.SecurityManagement import noSecurityManager
+        noSecurityManager()
+        ZopeTestCase.tearDown(self)
+
+class DOMComparator:
+
+    def _compareDOM( self, found_text, expected_text, debug=False ):
+
+        found_lines = [ x.strip() for x in found_text.splitlines() ]
+        found_text = '\n'.join( filter( None, found_lines ) )
+
+        expected_lines = [ x.strip() for x in expected_text.splitlines() ]
+        expected_text = '\n'.join( filter( None, expected_lines ) )
+
+        from xml.dom.minidom import parseString
+        found = parseString( found_text )
+        expected = parseString( expected_text )
+        fxml = found.toxml()
+        exml = expected.toxml()
+
+        if fxml != exml:
+
+            if debug:
+                zipped = zip( fxml, exml )
+                diff = [ ( i, zipped[i][0], zipped[i][1] )
+                        for i in range( len( zipped ) )
+                        if zipped[i][0] != zipped[i][1]
+                    ]
+                import pdb; pdb.set_trace()
+
+            print 'Found:'
+            print fxml
+            print
+            print 'Expected:'
+            print exml
+            print
+
+        self.assertEqual( found.toxml(), expected.toxml() )
+
+class BaseRegistryTests( SecurityRequestTest, DOMComparator ):
+
+    def _makeOne( self, *args, **kw ):
+
+        # Derived classes must implement _getTargetClass
+        return self._getTargetClass()( *args, **kw )
+
+def _clearTestDirectory( root_path ):
+
+    if os.path.exists( root_path ):
+        shutil.rmtree( root_path )
+
+def _makeTestFile( filename, root_path, contents ):
+
+    path, filename = os.path.split( filename )
+
+    subdir = os.path.join( root_path, path )
+
+    if not os.path.exists( subdir ):
+        os.makedirs( subdir )
+
+    fqpath = os.path.join( subdir, filename )
+
+    file = open( fqpath, 'wb' )
+    file.write( contents )
+    file.close()
+    return fqpath
+
+class FilesystemTestBase( SecurityRequestTest ):
+
+    def _makeOne( self, *args, **kw ):
+
+        return self._getTargetClass()( *args, **kw )
+
+    def setUp( self ):
+
+        SecurityRequestTest.setUp( self )
+        self._clearTempDir()
+
+    def tearDown( self ):
+
+        self._clearTempDir()
+        SecurityRequestTest.tearDown( self )
+
+    def _clearTempDir( self ):
+
+        _clearTestDirectory( self._PROFILE_PATH )
+
+    def _makeFile( self, filename, contents ):
+
+        return _makeTestFile( filename, self._PROFILE_PATH, contents )
+
+
+class TarballTester( DOMComparator ):
+
+    def _verifyTarballContents( self, fileish, toc_list, when=None ):
+
+        fileish.seek( 0L )
+        tarfile = TarFile.open( 'foo.tar.gz', fileobj=fileish, mode='r:gz' )
+        items = tarfile.getnames()
+        items.sort()
+        toc_list.sort()
+
+        self.assertEqual( len( items ), len( toc_list ) )
+        for i in range( len( items ) ):
+            self.assertEqual( items[ i ], toc_list[ i ] )
+
+        if when is not None:
+            for tarinfo in tarfile:
+                self.failIf( tarinfo.mtime < when )
+
+    def _verifyTarballEntry( self, fileish, entry_name, data ):
+
+        fileish.seek( 0L )
+        tarfile = TarFile.open( 'foo.tar.gz', fileobj=fileish, mode='r:gz' )
+        extract = tarfile.extractfile( entry_name )
+        found = extract.read()
+        self.assertEqual( found, data )
+
+    def _verifyTarballEntryXML( self, fileish, entry_name, data ):
+
+        fileish.seek( 0L )
+        tarfile = TarFile.open( 'foo.tar.gz', fileobj=fileish, mode='r:gz' )
+        extract = tarfile.extractfile( entry_name )
+        found = extract.read()
+        self._compareDOM( found, data )
+
+
+class DummyExportContext:
+
+    def __init__( self, site ):
+        self._site = site
+        self._wrote = []
+
+    def getSite( self ):
+        return self._site
+
+    def writeDataFile( self, filename, text, content_type, subdir=None ):
+        if subdir is not None:
+            filename = '%s/%s' % ( subdir, filename )
+        self._wrote.append( ( filename, text, content_type ) )
+
+class DummyImportContext:
+
+    def __init__( self, site, purge=True, encoding=None ):
+        self._site = site
+        self._purge = purge
+        self._encoding = encoding
+        self._files = {}
+
+    def getSite( self ):
+        return self._site
+
+    def getEncoding( self ):
+        return self._encoding
+
+    def readDataFile( self, filename, subdir=None ):
+
+        if subdir is not None:
+            filename = '/'.join( (subdir, filename) )
+
+        return self._files.get( filename )
+
+    def shouldPurge( self ):
+
+        return self._purge
+
+def dummy_handler( context ):
+
+    pass

Added: CMF/branches/goldegg-phase-1/GenericSetup/tests/conformance.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/tests/conformance.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/tests/conformance.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,100 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" Base classes for testing interface conformance.
+
+Derived testcase classes should define '_getTargetClass()', which must
+return the class being tested for conformance.
+
+$Id: conformance.py,v 1.2 2005/08/09 18:49:48 tseaver Exp $
+"""
+
+class ConformsToISetupContext:
+
+    def test_ISetupContext_conformance( self ):
+
+        from Products.GenericSetup.interfaces import ISetupContext
+        from zope.interface.verify import verifyClass
+
+        verifyClass( ISetupContext, self._getTargetClass() )
+
+class ConformsToIImportContext:
+
+    def test_IImportContext_conformance( self ):
+
+        from Products.GenericSetup.interfaces import IImportContext
+        from zope.interface.verify import verifyClass
+
+        verifyClass( IImportContext, self._getTargetClass() )
+
+class ConformsToIExportContext:
+
+    def test_IExportContext_conformance( self ):
+
+        from Products.GenericSetup.interfaces import IExportContext
+        from zope.interface.verify import verifyClass
+
+        verifyClass( IExportContext, self._getTargetClass() )
+
+class ConformsToIStepRegistry:
+
+    def test_IStepRegistry_conformance( self ):
+
+        from Products.GenericSetup.interfaces import IStepRegistry
+        from zope.interface.verify import verifyClass
+
+        verifyClass( IStepRegistry, self._getTargetClass() )
+
+class ConformsToIImportStepRegistry:
+
+    def test_IImportStepRegistry_conformance( self ):
+
+        from Products.GenericSetup.interfaces import IImportStepRegistry
+        from zope.interface.verify import verifyClass
+
+        verifyClass( IImportStepRegistry, self._getTargetClass() )
+
+class ConformsToIExportStepRegistry:
+
+    def test_IExportStepRegistry_conformance( self ):
+
+        from Products.GenericSetup.interfaces import IExportStepRegistry
+        from zope.interface.verify import verifyClass
+
+        verifyClass( IExportStepRegistry, self._getTargetClass() )
+
+class ConformsToIToolsetRegistry:
+
+    def test_IToolsetRegistry_conformance( self ):
+
+        from Products.GenericSetup.interfaces import IToolsetRegistry
+        from zope.interface.verify import verifyClass
+
+        verifyClass( IToolsetRegistry, self._getTargetClass() )
+
+class ConformsToIProfileRegistry:
+
+    def test_IProfileRegistry_conformance( self ):
+
+        from Products.GenericSetup.interfaces import IProfileRegistry
+        from zope.interface.verify import verifyClass
+
+        verifyClass( IProfileRegistry, self._getTargetClass() )
+
+class ConformsToISetupTool:
+
+    def test_ISetupTool_conformance( self ):
+
+        from Products.GenericSetup.interfaces import ISetupTool
+        from zope.interface.verify import verifyClass
+
+        verifyClass( ISetupTool, self._getTargetClass() )

Added: CMF/branches/goldegg-phase-1/GenericSetup/tests/default_profile/export_steps.xml
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/tests/default_profile/export_steps.xml	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/tests/default_profile/export_steps.xml	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<export-steps>
+ <export-step id="one"
+                handler="Products.GenericSetup.tests.common.dummy_handler"
+                title="One Step">
+  One small step
+ </export-step>
+</export-steps>

Added: CMF/branches/goldegg-phase-1/GenericSetup/tests/default_profile/import_steps.xml
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/tests/default_profile/import_steps.xml	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/tests/default_profile/import_steps.xml	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<import-steps>
+ <import-step id="one"
+             version="1"
+             handler="Products.GenericSetup.tests.common.dummy_handler"
+             title="One Step">
+  One small step
+ </import-step>
+</import-steps>

Added: CMF/branches/goldegg-phase-1/GenericSetup/tests/default_profile/profile.ini
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/tests/default_profile/profile.ini	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/tests/default_profile/profile.ini	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,2 @@
+[Metadata]
+Title=Unit Test Profile Data

Added: CMF/branches/goldegg-phase-1/GenericSetup/tests/default_profile/toolset.xml
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/tests/default_profile/toolset.xml	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/tests/default_profile/toolset.xml	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<tool-setup>
+ <forbidden tool_id="doomed" />
+ <required tool_id="mandatory" class="path.to.one" />
+ <required tool_id="obligatory" class="path.to.another" />
+</tool-setup>

Added: CMF/branches/goldegg-phase-1/GenericSetup/tests/four/placeholder.txt
===================================================================

Added: CMF/branches/goldegg-phase-1/GenericSetup/tests/one/placeholder.txt
===================================================================

Added: CMF/branches/goldegg-phase-1/GenericSetup/tests/simple.png
===================================================================
(Binary files differ)


Property changes on: CMF/branches/goldegg-phase-1/GenericSetup/tests/simple.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: CMF/branches/goldegg-phase-1/GenericSetup/tests/test_context.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/tests/test_context.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/tests/test_context.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,1289 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" Unit tests for import / export contexts.
+
+$Id: test_context.py,v 1.2 2005/08/11 21:41:36 tseaver Exp $
+"""
+
+import unittest
+import Testing
+try:
+    import Zope2
+except ImportError: # BBB: for Zope 2.7
+    import Zope as Zope2
+Zope2.startup()
+
+import os
+import time
+from StringIO import StringIO
+from tarfile import TarFile
+from tarfile import TarInfo
+
+from DateTime.DateTime import DateTime
+from OFS.Folder import Folder
+from OFS.Image import File
+
+#from Products.CMFCore.tests.base.testcase import SecurityRequestTest
+from Testing.ZopeTestCase import ZopeTestCase
+
+from common import FilesystemTestBase
+from common import SecurityRequestTest
+from common import TarballTester
+from common import _makeTestFile
+from conformance import ConformsToISetupContext
+from conformance import ConformsToIImportContext
+from conformance import ConformsToIExportContext
+
+
+class DummySite( Folder ):
+
+    pass
+
+class DummyTool( Folder ):
+
+    pass
+
+class DirectoryImportContextTests( FilesystemTestBase
+                                 , ConformsToISetupContext
+                                 , ConformsToIImportContext
+                                 ):
+
+    _PROFILE_PATH = '/tmp/ICTTexts'
+
+    def _getTargetClass( self ):
+
+        from Products.GenericSetup.context import DirectoryImportContext
+        return DirectoryImportContext
+
+    def test_readDataFile_nonesuch( self ):
+
+        FILENAME = 'nonesuch.txt'
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        self.assertEqual( ctx.readDataFile( FILENAME ), None )
+
+    def test_readDataFile_simple( self ):
+
+        from string import printable
+
+        FILENAME = 'simple.txt'
+        self._makeFile( FILENAME, printable )
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        self.assertEqual( ctx.readDataFile( FILENAME ), printable )
+
+    def test_readDataFile_subdir( self ):
+
+        from string import printable
+
+        FILENAME = 'subdir/nested.txt'
+        self._makeFile( FILENAME, printable )
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        self.assertEqual( ctx.readDataFile( FILENAME ), printable )
+
+    def test_getLastModified_nonesuch( self ):
+
+        FILENAME = 'nonesuch.txt'
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        self.assertEqual( ctx.getLastModified( FILENAME ), None )
+
+    def test_getLastModified_simple( self ):
+
+        from string import printable
+
+        FILENAME = 'simple.txt'
+        fqpath = self._makeFile( FILENAME, printable )
+        timestamp = os.path.getmtime( fqpath )
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        lm = ctx.getLastModified( FILENAME )
+        self.failUnless( isinstance( lm, DateTime ) )
+        self.assertEqual( lm, timestamp )
+
+    def test_getLastModified_subdir( self ):
+
+        from string import printable
+
+        SUBDIR = 'subdir'
+        FILENAME = os.path.join( SUBDIR, 'nested.txt' )
+        fqpath = self._makeFile( FILENAME, printable )
+        timestamp = os.path.getmtime( fqpath )
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        lm = ctx.getLastModified( FILENAME )
+        self.failUnless( isinstance( lm, DateTime ) )
+        self.assertEqual( lm, timestamp )
+
+    def test_getLastModified_directory( self ):
+
+        from string import printable
+
+        SUBDIR = 'subdir'
+        FILENAME = os.path.join( SUBDIR, 'nested.txt' )
+        fqpath = self._makeFile( FILENAME, printable )
+        path, file = os.path.split( fqpath )
+        timestamp = os.path.getmtime( path )
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        lm = ctx.getLastModified( SUBDIR )
+        self.failUnless( isinstance( lm, DateTime ) )
+        self.assertEqual( lm, timestamp )
+
+    def test_isDirectory_nonesuch( self ):
+
+        FILENAME = 'nonesuch.txt'
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        self.assertEqual( ctx.isDirectory( FILENAME ), None )
+
+    def test_isDirectory_simple( self ):
+
+        from string import printable
+
+        FILENAME = 'simple.txt'
+        fqpath = self._makeFile( FILENAME, printable )
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        self.assertEqual( ctx.isDirectory( FILENAME ), False )
+
+    def test_isDirectory_nested( self ):
+
+        from string import printable
+
+        SUBDIR = 'subdir'
+        FILENAME = os.path.join( SUBDIR, 'nested.txt' )
+        fqpath = self._makeFile( FILENAME, printable )
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        self.assertEqual( ctx.isDirectory( FILENAME ), False )
+
+    def test_isDirectory_directory( self ):
+
+        from string import printable
+
+        SUBDIR = 'subdir'
+        FILENAME = os.path.join( SUBDIR, 'nested.txt' )
+        fqpath = self._makeFile( FILENAME, printable )
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        self.assertEqual( ctx.isDirectory( SUBDIR ), True )
+
+    def test_listDirectory_nonesuch( self ):
+
+        FILENAME = 'nonesuch.txt'
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        self.assertEqual( ctx.listDirectory( FILENAME ), None )
+
+    def test_listDirectory_root( self ):
+
+        from string import printable
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        FILENAME = 'simple.txt'
+        self._makeFile( FILENAME, printable )
+
+        self.assertEqual( len( ctx.listDirectory( None ) ), 1 )
+        self.failUnless( FILENAME in ctx.listDirectory( None ) )
+
+    def test_listDirectory_simple( self ):
+
+        from string import printable
+
+        FILENAME = 'simple.txt'
+        self._makeFile( FILENAME, printable )
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        self.assertEqual( ctx.listDirectory( FILENAME ), None )
+
+    def test_listDirectory_nested( self ):
+
+        from string import printable
+
+        SUBDIR = 'subdir'
+        FILENAME = os.path.join( SUBDIR, 'nested.txt' )
+        self._makeFile( FILENAME, printable )
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        self.assertEqual( ctx.listDirectory( FILENAME ), None )
+
+    def test_listDirectory_single( self ):
+
+        from string import printable
+
+        SUBDIR = 'subdir'
+        FILENAME = os.path.join( SUBDIR, 'nested.txt' )
+        self._makeFile( FILENAME, printable )
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        names = ctx.listDirectory( SUBDIR )
+        self.assertEqual( len( names ), 1 )
+        self.failUnless( 'nested.txt' in names )
+
+    def test_listDirectory_multiple( self ):
+
+        from string import printable
+        SUBDIR = 'subdir'
+        FILENAME = os.path.join( SUBDIR, 'nested.txt' )
+        self._makeFile( FILENAME, printable )
+        self._makeFile( os.path.join( SUBDIR, 'another.txt' ), 'ABC' )
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        names = ctx.listDirectory( SUBDIR )
+        self.assertEqual( len( names ), 2 )
+        self.failUnless( 'nested.txt' in names )
+        self.failUnless( 'another.txt' in names )
+
+    def test_listDirectory_skip_implicit( self ):
+
+        from string import printable
+        SUBDIR = 'subdir'
+        FILENAME = os.path.join( SUBDIR, 'nested.txt' )
+        self._makeFile( FILENAME, printable )
+        self._makeFile( os.path.join( SUBDIR, 'another.txt' ), 'ABC' )
+        self._makeFile( os.path.join( SUBDIR, 'CVS/skip.txt' ), 'DEF' )
+        self._makeFile( os.path.join( SUBDIR, '.svn/skip.txt' ), 'GHI' )
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        names = ctx.listDirectory( SUBDIR )
+        self.assertEqual( len( names ), 2 )
+        self.failUnless( 'nested.txt' in names )
+        self.failUnless( 'another.txt' in names )
+        self.failIf( 'CVS' in names )
+        self.failIf( '.svn' in names )
+
+    def test_listDirectory_skip_explicit( self ):
+
+        from string import printable
+        SUBDIR = 'subdir'
+        FILENAME = os.path.join( SUBDIR, 'nested.txt' )
+        self._makeFile( FILENAME, printable )
+        self._makeFile( os.path.join( SUBDIR, 'another.txt' ), 'ABC' )
+        self._makeFile( os.path.join( SUBDIR, 'CVS/skip.txt' ), 'DEF' )
+        self._makeFile( os.path.join( SUBDIR, '.svn/skip.txt' ), 'GHI' )
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        names = ctx.listDirectory( SUBDIR, ( 'nested.txt', ) )
+        self.assertEqual( len( names ), 3 )
+        self.failIf( 'nested.txt' in names )
+        self.failUnless( 'another.txt' in names )
+        self.failUnless( 'CVS' in names )
+        self.failUnless( '.svn' in names )
+
+class DirectoryExportContextTests( FilesystemTestBase
+                                 , ConformsToISetupContext
+                                 , ConformsToIExportContext
+                                 ):
+
+    _PROFILE_PATH = '/tmp/ECTTexts'
+
+    def _getTargetClass( self ):
+
+        from Products.GenericSetup.context import DirectoryExportContext
+        return DirectoryExportContext
+
+    def test_writeDataFile_simple( self ):
+
+        from string import printable, digits
+        FILENAME = 'simple.txt'
+        fqname = self._makeFile( FILENAME, printable )
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        ctx.writeDataFile( FILENAME, digits, 'text/plain' )
+
+        self.assertEqual( open( fqname, 'rb' ).read(), digits )
+
+    def test_writeDataFile_new_subdir( self ):
+
+        from string import printable, digits
+        SUBDIR = 'subdir'
+        FILENAME = 'nested.txt'
+        fqname = os.path.join( self._PROFILE_PATH, SUBDIR, FILENAME )
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        ctx.writeDataFile( FILENAME, digits, 'text/plain', SUBDIR )
+
+        self.assertEqual( open( fqname, 'rb' ).read(), digits )
+
+    def test_writeDataFile_overwrite( self ):
+
+        from string import printable, digits
+        SUBDIR = 'subdir'
+        FILENAME = 'nested.txt'
+        fqname = self._makeFile( os.path.join( SUBDIR, FILENAME )
+                               , printable )
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        ctx.writeDataFile( FILENAME, digits, 'text/plain', SUBDIR )
+
+        self.assertEqual( open( fqname, 'rb' ).read(), digits )
+
+    def test_writeDataFile_existing_subdir( self ):
+
+        from string import printable, digits
+        SUBDIR = 'subdir'
+        FILENAME = 'nested.txt'
+        self._makeFile( os.path.join( SUBDIR, 'another.txt' ), printable )
+        fqname = os.path.join( self._PROFILE_PATH, SUBDIR, FILENAME )
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._makeOne( site, self._PROFILE_PATH )
+
+        ctx.writeDataFile( FILENAME, digits, 'text/plain', SUBDIR )
+
+        self.assertEqual( open( fqname, 'rb' ).read(), digits )
+
+
+class TarballImportContextTests( SecurityRequestTest
+                               , ConformsToISetupContext
+                               , ConformsToIImportContext
+                               ):
+
+    def _getTargetClass( self ):
+
+        from Products.GenericSetup.context import TarballImportContext
+        return TarballImportContext
+
+    def _makeOne( self, file_dict={}, mod_time=None, *args, **kw ):
+
+        archive_stream = StringIO()
+        archive = TarFile.open('test.tar.gz', 'w:gz', archive_stream)
+
+        def _addOneMember(path, data, modtime):
+            stream = StringIO(v)
+            info = TarInfo(k)
+            info.size = len(v)
+            info.mtime = mod_time
+            archive.addfile(info, stream)
+
+        def _addMember(path, data, modtime):
+            from tarfile import DIRTYPE
+            elements = path.split('/')
+            parents = filter(None, [elements[x] for x in range(len(elements))])
+            for parent in parents:
+                info = TarInfo()
+                info.name = parent
+                info.size = 0
+                info.mtime = mod_time
+                info.type = DIRTYPE
+                archive.addfile(info, StringIO())
+            _addOneMember(path, data, modtime)
+
+        file_items = file_dict.items() or [('dummy', '')] # empty archive barfs
+
+        if mod_time is None:
+            mod_time = time.time()
+
+        for k, v in file_items:
+            _addMember(k, v, mod_time)
+
+        archive.close()
+        bits = archive_stream.getvalue()
+
+        site = DummySite( 'site' ).__of__( self.root )
+        site._setObject( 'setup_tool', Folder( 'setup_tool' ) )
+        tool = site._getOb( 'setup_tool' )
+
+        ctx = self._getTargetClass()( tool, bits, *args, **kw )
+
+        return site, tool, ctx.__of__( tool )
+
+    def test_ctorparms( self ):
+
+        ENCODING = 'latin-1'
+        site, tool, ctx = self._makeOne( encoding=ENCODING
+                                       , should_purge=True
+                                       )
+
+        self.assertEqual( ctx.getEncoding(), ENCODING )
+        self.assertEqual( ctx.shouldPurge(), True )
+
+    def test_empty( self ):
+
+        site, tool, ctx = self._makeOne()
+
+        self.assertEqual( ctx.getSite(), site )
+        self.assertEqual( ctx.getEncoding(), None )
+        self.assertEqual( ctx.shouldPurge(), False )
+
+        # These methods are all specified to return 'None' for non-existing
+        # paths / entities
+        self.assertEqual( ctx.isDirectory( 'nonesuch/path' ), None )
+        self.assertEqual( ctx.listDirectory( 'nonesuch/path' ), None )
+
+    def test_readDataFile_nonesuch( self ):
+
+        FILENAME = 'nonesuch.txt'
+
+        site, tool, ctx = self._makeOne()
+
+        self.assertEqual( ctx.readDataFile( FILENAME ), None )
+        self.assertEqual( ctx.readDataFile( FILENAME, 'subdir' ), None )
+
+    def test_readDataFile_simple( self ):
+
+        from string import printable
+
+        FILENAME = 'simple.txt'
+
+        site, tool, ctx = self._makeOne( { FILENAME: printable } )
+
+        self.assertEqual( ctx.readDataFile( FILENAME ), printable )
+
+    def test_readDataFile_subdir( self ):
+
+        from string import printable
+
+        FILENAME = 'subdir.txt'
+        SUBDIR = 'subdir'
+
+        site, tool, ctx = self._makeOne( { '%s/%s' % (SUBDIR, FILENAME):
+                                            printable } )
+
+        self.assertEqual( ctx.readDataFile( FILENAME, SUBDIR ), printable )
+
+    def test_getLastModified_nonesuch( self ):
+
+        FILENAME = 'nonesuch.txt'
+
+        site, tool, ctx = self._makeOne()
+
+        self.assertEqual( ctx.getLastModified( FILENAME ), None )
+
+    def test_getLastModified_simple( self ):
+
+        from string import printable
+
+        FILENAME = 'simple.txt'
+        WHEN = DateTime( '2004-01-01T00:00:00Z' )
+
+        site, tool, ctx = self._makeOne( { FILENAME : printable }
+                                       , mod_time=WHEN )
+
+        self.assertEqual( ctx.getLastModified( FILENAME ), WHEN )
+
+    def test_getLastModified_subdir( self ):
+
+        from string import printable
+
+        FILENAME = 'subdir.txt'
+        SUBDIR = 'subdir'
+        PATH = '%s/%s' % ( SUBDIR, FILENAME )
+        WHEN = DateTime( '2004-01-01T00:00:00Z' )
+
+        site, tool, ctx = self._makeOne( { PATH: printable }
+                                       , mod_time=WHEN )
+
+        self.assertEqual( ctx.getLastModified( PATH ), WHEN )
+
+    def test_getLastModified_directory( self ):
+
+        from string import printable
+
+        FILENAME = 'subdir.txt'
+        SUBDIR = 'subdir'
+        PATH = '%s/%s' % ( SUBDIR, FILENAME )
+        WHEN = DateTime( '2004-01-01T00:00:00Z' )
+
+        site, tool, ctx = self._makeOne( { PATH: printable }
+                                       , mod_time=WHEN
+                                       )
+
+        self.assertEqual( ctx.getLastModified( SUBDIR ), WHEN )
+
+    def test_isDirectory_nonesuch( self ):
+
+        FILENAME = 'nonesuch.txt'
+
+        site, tool, ctx = self._makeOne()
+
+        self.assertEqual( ctx.isDirectory( FILENAME ), None )
+
+    def test_isDirectory_simple( self ):
+
+        from string import printable
+
+        FILENAME = 'simple.txt'
+
+        site, tool, ctx = self._makeOne( { FILENAME: printable } )
+
+        self.assertEqual( ctx.isDirectory( FILENAME ), False )
+
+    def test_isDirectory_nested( self ):
+
+        from string import printable
+
+        SUBDIR = 'subdir'
+        FILENAME = 'nested.txt'
+        PATH = '%s/%s' % ( SUBDIR, FILENAME )
+
+        site, tool, ctx = self._makeOne( { PATH: printable } )
+
+        self.assertEqual( ctx.isDirectory( PATH ), False )
+
+    def test_isDirectory_subdir( self ):
+
+        from string import printable
+
+        SUBDIR = 'subdir'
+        FILENAME = 'nested.txt'
+        PATH = '%s/%s' % ( SUBDIR, FILENAME )
+
+        site, tool, ctx = self._makeOne( { PATH: printable } )
+
+        self.assertEqual( ctx.isDirectory( SUBDIR ), True )
+
+    def test_listDirectory_nonesuch( self ):
+
+        SUBDIR = 'nonesuch/path'
+
+        site, tool, ctx = self._makeOne()
+
+        self.assertEqual( ctx.listDirectory( SUBDIR ), None )
+
+    def test_listDirectory_root( self ):
+
+        from string import printable
+
+        FILENAME = 'simple.txt'
+
+        site, tool, ctx = self._makeOne( { FILENAME: printable } )
+
+        self.assertEqual( len( ctx.listDirectory( None ) ), 1 )
+        self.failUnless( FILENAME in ctx.listDirectory( None ) )
+
+    def test_listDirectory_simple( self ):
+
+        from string import printable
+
+        FILENAME = 'simple.txt'
+
+        site, tool, ctx = self._makeOne( { FILENAME: printable } )
+
+        self.assertEqual( ctx.listDirectory( FILENAME ), None )
+
+    def test_listDirectory_nested( self ):
+
+        from string import printable
+
+        SUBDIR = 'subdir'
+        FILENAME = 'nested.txt'
+        PATH = '%s/%s' % ( SUBDIR, FILENAME )
+
+        site, tool, ctx = self._makeOne( { PATH: printable } )
+
+        self.assertEqual( ctx.listDirectory( PATH ), None )
+
+    def test_listDirectory_single( self ):
+
+        from string import printable
+
+        SUBDIR = 'subdir'
+        FILENAME = 'nested.txt'
+        PATH = '%s/%s' % ( SUBDIR, FILENAME )
+
+        site, tool, ctx = self._makeOne( { PATH: printable } )
+
+        names = ctx.listDirectory( SUBDIR )
+        self.assertEqual( len( names ), 1 )
+        self.failUnless( FILENAME in names )
+
+    def test_listDirectory_multiple( self ):
+
+        from string import printable, uppercase
+
+        SUBDIR = 'subdir'
+        FILENAME1 = 'nested.txt'
+        PATH1 = '%s/%s' % ( SUBDIR, FILENAME1 )
+        FILENAME2 = 'another.txt'
+        PATH2 = '%s/%s' % ( SUBDIR, FILENAME2 )
+
+        site, tool, ctx = self._makeOne( { PATH1: printable
+                                         , PATH2: uppercase
+                                         } )
+                                             
+        names = ctx.listDirectory( SUBDIR )
+        self.assertEqual( len( names ), 2 )
+        self.failUnless( FILENAME1 in names )
+        self.failUnless( FILENAME2 in names )
+
+    def test_listDirectory_skip( self ):
+
+        from string import printable, uppercase
+
+        SUBDIR = 'subdir'
+        FILENAME1 = 'nested.txt'
+        PATH1 = '%s/%s' % ( SUBDIR, FILENAME1 )
+        FILENAME2 = 'another.txt'
+        PATH2 = '%s/%s' % ( SUBDIR, FILENAME2 )
+
+        site, tool, ctx = self._makeOne( { PATH1: printable
+                                         , PATH2: uppercase
+                                         } )
+
+        names = ctx.listDirectory( SUBDIR, skip=( FILENAME1, ) )
+        self.assertEqual( len( names ), 1 )
+        self.failIf( FILENAME1 in names )
+        self.failUnless( FILENAME2 in names )
+
+
+class TarballExportContextTests( FilesystemTestBase
+                               , TarballTester
+                               , ConformsToISetupContext
+                               , ConformsToIExportContext
+                               ):
+
+    _PROFILE_PATH = '/tmp/TECT_tests'
+
+    def _getTargetClass( self ):
+
+        from Products.GenericSetup.context import TarballExportContext
+        return TarballExportContext
+
+    def test_writeDataFile_simple( self ):
+
+        from string import printable
+        now = long( time.time() )
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._getTargetClass()( site )
+
+        ctx.writeDataFile( 'foo.txt', printable, 'text/plain' )
+
+        fileish = StringIO( ctx.getArchive() )
+
+        self._verifyTarballContents( fileish, [ 'foo.txt' ], now )
+        self._verifyTarballEntry( fileish, 'foo.txt', printable )
+
+    def test_writeDataFile_multiple( self ):
+
+        from string import printable
+        from string import digits
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._getTargetClass()( site )
+
+        ctx.writeDataFile( 'foo.txt', printable, 'text/plain' )
+        ctx.writeDataFile( 'bar.txt', digits, 'text/plain' )
+
+        fileish = StringIO( ctx.getArchive() )
+
+        self._verifyTarballContents( fileish, [ 'foo.txt', 'bar.txt' ] )
+        self._verifyTarballEntry( fileish, 'foo.txt', printable )
+        self._verifyTarballEntry( fileish, 'bar.txt', digits )
+
+    def test_writeDataFile_subdir( self ):
+
+        from string import printable
+        from string import digits
+
+        site = DummySite( 'site' ).__of__( self.root )
+        ctx = self._getTargetClass()( site )
+
+        ctx.writeDataFile( 'foo.txt', printable, 'text/plain' )
+        ctx.writeDataFile( 'bar/baz.txt', digits, 'text/plain' )
+
+        fileish = StringIO( ctx.getArchive() )
+
+        self._verifyTarballContents( fileish, [ 'foo.txt', 'bar/baz.txt' ] )
+        self._verifyTarballEntry( fileish, 'foo.txt', printable )
+        self._verifyTarballEntry( fileish, 'bar/baz.txt', digits )
+
+
+class SnapshotExportContextTests( SecurityRequestTest
+                                , ConformsToISetupContext
+                                , ConformsToIExportContext
+                                ):
+
+    def _getTargetClass( self ):
+
+        from Products.GenericSetup.context import SnapshotExportContext
+        return SnapshotExportContext
+
+    def _makeOne( self, *args, **kw ):
+
+        return self._getTargetClass()( *args, **kw )
+
+    def test_writeDataFile_simple_image( self ):
+
+        from OFS.Image import Image
+        FILENAME = 'simple.txt'
+        _CONTENT_TYPE = 'image/png'
+        png_filename = os.path.join( os.path.split( __file__ )[0]
+                                   , 'simple.png' )
+        png_file = open( png_filename, 'rb' )
+        png_data = png_file.read()
+        png_file.close()
+
+        site = DummySite( 'site' ).__of__( self.root )
+        site.setup_tool = DummyTool( 'setup_tool' )
+        tool = site.setup_tool
+        ctx = self._makeOne( tool, 'simple' )
+
+        ctx.writeDataFile( FILENAME, png_data, _CONTENT_TYPE )
+
+        snapshot = tool.snapshots._getOb( 'simple' )
+
+        self.assertEqual( len( snapshot.objectIds() ), 1 )
+        self.failUnless( FILENAME in snapshot.objectIds() )
+
+        fileobj = snapshot._getOb( FILENAME )
+
+        self.assertEqual( fileobj.getId(), FILENAME )
+        self.assertEqual( fileobj.meta_type, Image.meta_type )
+        self.assertEqual( fileobj.getContentType(), _CONTENT_TYPE )
+        self.assertEqual( fileobj.data, png_data )
+
+    def test_writeDataFile_simple_plain_text( self ):
+
+        from string import digits
+        from OFS.Image import File
+        FILENAME = 'simple.txt'
+        _CONTENT_TYPE = 'text/plain'
+
+        site = DummySite( 'site' ).__of__( self.root )
+        site.setup_tool = DummyTool( 'setup_tool' )
+        tool = site.setup_tool
+        ctx = self._makeOne( tool, 'simple' )
+
+        ctx.writeDataFile( FILENAME, digits, _CONTENT_TYPE )
+
+        snapshot = tool.snapshots._getOb( 'simple' )
+
+        self.assertEqual( len( snapshot.objectIds() ), 1 )
+        self.failUnless( FILENAME in snapshot.objectIds() )
+
+        fileobj = snapshot._getOb( FILENAME )
+
+        self.assertEqual( fileobj.getId(), FILENAME )
+        self.assertEqual( fileobj.meta_type, File.meta_type )
+        self.assertEqual( fileobj.getContentType(), _CONTENT_TYPE )
+        self.assertEqual( str( fileobj ), digits )
+
+    def test_writeDataFile_simple_xml( self ):
+
+        from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
+        FILENAME = 'simple.xml'
+        _CONTENT_TYPE = 'text/xml'
+        _XML = """<?xml version="1.0"?><simple />"""
+
+        site = DummySite( 'site' ).__of__( self.root )
+        site.setup_tool = DummyTool( 'setup_tool' )
+        tool = site.setup_tool
+        ctx = self._makeOne( tool, 'simple' )
+
+        ctx.writeDataFile( FILENAME, _XML, _CONTENT_TYPE )
+
+        snapshot = tool.snapshots._getOb( 'simple' )
+
+        self.assertEqual( len( snapshot.objectIds() ), 1 )
+        self.failUnless( FILENAME in snapshot.objectIds() )
+
+        template = snapshot._getOb( FILENAME )
+
+        self.assertEqual( template.getId(), FILENAME )
+        self.assertEqual( template.meta_type, ZopePageTemplate.meta_type )
+        self.assertEqual( template.read(), _XML )
+        self.failIf( template.html() )
+
+    def test_writeDataFile_unicode_xml( self ):
+
+        from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
+        FILENAME = 'simple.xml'
+        _CONTENT_TYPE = 'text/xml'
+        _XML = u"""<?xml version="1.0"?><simple />"""
+
+        site = DummySite( 'site' ).__of__( self.root )
+        site.setup_tool = DummyTool( 'setup_tool' )
+        tool = site.setup_tool
+        ctx = self._makeOne( tool, 'simple' )
+
+        ctx.writeDataFile( FILENAME, _XML, _CONTENT_TYPE )
+
+        snapshot = tool.snapshots._getOb( 'simple' )
+
+        self.assertEqual( len( snapshot.objectIds() ), 1 )
+        self.failUnless( FILENAME in snapshot.objectIds() )
+
+        template = snapshot._getOb( FILENAME )
+
+        self.assertEqual( template.getId(), FILENAME )
+        self.assertEqual( template.meta_type, ZopePageTemplate.meta_type )
+        self.assertEqual( template.read(), _XML )
+        self.failIf( template.html() )
+
+    def test_writeDataFile_subdir_dtml( self ):
+
+        from OFS.DTMLDocument import DTMLDocument
+        FILENAME = 'simple.dtml'
+        _CONTENT_TYPE = 'text/html'
+        _HTML = """<html><body><h1>HTML</h1></body></html>"""
+
+        site = DummySite( 'site' ).__of__( self.root )
+        site.setup_tool = DummyTool( 'setup_tool' )
+        tool = site.setup_tool
+        ctx = self._makeOne( tool, 'simple' )
+
+        ctx.writeDataFile( FILENAME, _HTML, _CONTENT_TYPE, 'sub1' )
+
+        snapshot = tool.snapshots._getOb( 'simple' )
+        sub1 = snapshot._getOb( 'sub1' )
+
+        self.assertEqual( len( sub1.objectIds() ), 1 )
+        self.failUnless( FILENAME in sub1.objectIds() )
+
+        template = sub1._getOb( FILENAME )
+
+        self.assertEqual( template.getId(), FILENAME )
+        self.assertEqual( template.meta_type, DTMLDocument.meta_type )
+        self.assertEqual( template.read(), _HTML )
+
+    def test_writeDataFile_nested_subdirs_html( self ):
+
+        from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
+        FILENAME = 'simple.html'
+        _CONTENT_TYPE = 'text/html'
+        _HTML = """<html><body><h1>HTML</h1></body></html>"""
+
+        site = DummySite( 'site' ).__of__( self.root )
+        site.setup_tool = DummyTool( 'setup_tool' )
+        tool = site.setup_tool
+        ctx = self._makeOne( tool, 'simple' )
+
+        ctx.writeDataFile( FILENAME, _HTML, _CONTENT_TYPE, 'sub1/sub2' )
+
+        snapshot = tool.snapshots._getOb( 'simple' )
+        sub1 = snapshot._getOb( 'sub1' )
+        sub2 = sub1._getOb( 'sub2' )
+
+        self.assertEqual( len( sub2.objectIds() ), 1 )
+        self.failUnless( FILENAME in sub2.objectIds() )
+
+        template = sub2._getOb( FILENAME )
+
+        self.assertEqual( template.getId(), FILENAME )
+        self.assertEqual( template.meta_type, ZopePageTemplate.meta_type )
+        self.assertEqual( template.read(), _HTML )
+        self.failUnless( template.html() )
+
+    def test_writeDataFile_multiple( self ):
+
+        from string import printable
+        from string import digits
+
+        site = DummySite( 'site' ).__of__( self.root )
+        site.setup_tool = DummyTool( 'setup_tool' )
+        tool = site.setup_tool
+        ctx = self._makeOne( tool, 'multiple' )
+
+        ctx.writeDataFile( 'foo.txt', printable, 'text/plain' )
+        ctx.writeDataFile( 'bar.txt', digits, 'text/plain' )
+
+        snapshot = tool.snapshots._getOb( 'multiple' )
+
+        self.assertEqual( len( snapshot.objectIds() ), 2 )
+
+        for id in [ 'foo.txt', 'bar.txt' ]:
+            self.failUnless( id in snapshot.objectIds() )
+
+
+class SnapshotImportContextTests( SecurityRequestTest
+                                , ConformsToISetupContext
+                                , ConformsToIImportContext
+                                ):
+
+    def _getTargetClass( self ):
+
+        from Products.GenericSetup.context import SnapshotImportContext
+        return SnapshotImportContext
+
+    def _makeOne( self, context_id, *args, **kw ):
+
+        site = DummySite( 'site' ).__of__( self.root )
+        site._setObject( 'setup_tool', Folder( 'setup_tool' ) )
+        tool = site._getOb( 'setup_tool' )
+
+        tool._setObject( 'snapshots', Folder( 'snapshots' ) )
+        tool.snapshots._setObject( context_id, Folder( context_id ) )
+
+        ctx = self._getTargetClass()( tool, context_id, *args, **kw )
+
+        return site, tool, ctx.__of__( tool )
+
+    def _makeFile( self
+                 , tool
+                 , snapshot_id
+                 , filename
+                 , contents
+                 , content_type='text/plain'
+                 , mod_time=None
+                 , subdir=None
+                 ):
+
+        snapshots = tool._getOb( 'snapshots' )
+        folder = snapshot = snapshots._getOb( snapshot_id )
+
+        if subdir is not None:
+
+            for element in subdir.split( '/' ):
+
+                try:
+                    folder = folder._getOb( element )
+                except AttributeError:
+                    folder._setObject( element, Folder( element ) )
+                    folder = folder._getOb( element )
+
+        file = File( filename, '', contents, content_type )
+        folder._setObject( filename, file )
+
+        if mod_time is not None:
+
+            def __faux_mod_time():
+                return mod_time
+
+            folder.bobobase_modification_time = \
+            file.bobobase_modification_time = __faux_mod_time
+
+        return folder._getOb( filename )
+
+    def test_ctorparms( self ):
+
+        SNAPSHOT_ID = 'ctorparms'
+        ENCODING = 'latin-1'
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID
+                                       , encoding=ENCODING
+                                       , should_purge=True
+                                       )
+
+        self.assertEqual( ctx.getEncoding(), ENCODING )
+        self.assertEqual( ctx.shouldPurge(), True )
+
+    def test_empty( self ):
+
+        SNAPSHOT_ID = 'empty'
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
+
+        self.assertEqual( ctx.getSite(), site )
+        self.assertEqual( ctx.getEncoding(), None )
+        self.assertEqual( ctx.shouldPurge(), False )
+
+        # These methods are all specified to return 'None' for non-existing
+        # paths / entities
+        self.assertEqual( ctx.isDirectory( 'nonesuch/path' ), None )
+        self.assertEqual( ctx.listDirectory( 'nonesuch/path' ), None )
+
+    def test_readDataFile_nonesuch( self ):
+
+        SNAPSHOT_ID = 'readDataFile_nonesuch'
+        FILENAME = 'nonesuch.txt'
+
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
+
+        self.assertEqual( ctx.readDataFile( FILENAME ), None )
+        self.assertEqual( ctx.readDataFile( FILENAME, 'subdir' ), None )
+
+    def test_readDataFile_simple( self ):
+
+        from string import printable
+
+        SNAPSHOT_ID = 'readDataFile_simple'
+        FILENAME = 'simple.txt'
+
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
+        self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable )
+
+        self.assertEqual( ctx.readDataFile( FILENAME ), printable )
+
+    def test_readDataFile_subdir( self ):
+
+        from string import printable
+
+        SNAPSHOT_ID = 'readDataFile_subdir'
+        FILENAME = 'subdir.txt'
+        SUBDIR = 'subdir'
+
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
+        self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable
+                      , subdir=SUBDIR )
+
+        self.assertEqual( ctx.readDataFile( FILENAME, SUBDIR ), printable )
+
+    def test_getLastModified_nonesuch( self ):
+
+        SNAPSHOT_ID = 'getLastModified_nonesuch'
+        FILENAME = 'nonesuch.txt'
+
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
+
+        self.assertEqual( ctx.getLastModified( FILENAME ), None )
+
+    def test_getLastModified_simple( self ):
+
+        from string import printable
+
+        SNAPSHOT_ID = 'getLastModified_simple'
+        FILENAME = 'simple.txt'
+        WHEN = DateTime( '2004-01-01T00:00:00Z' )
+
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
+        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable
+                             , mod_time=WHEN )
+
+        self.assertEqual( ctx.getLastModified( FILENAME ), WHEN )
+
+    def test_getLastModified_subdir( self ):
+
+        from string import printable
+
+        SNAPSHOT_ID = 'getLastModified_subdir'
+        FILENAME = 'subdir.txt'
+        SUBDIR = 'subdir'
+        PATH = '%s/%s' % ( SUBDIR, FILENAME )
+        WHEN = DateTime( '2004-01-01T00:00:00Z' )
+
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
+        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable
+                             , mod_time=WHEN, subdir=SUBDIR )
+
+        self.assertEqual( ctx.getLastModified( PATH ), WHEN )
+
+    def test_getLastModified_directory( self ):
+
+        from string import printable
+
+        SNAPSHOT_ID = 'readDataFile_subdir'
+        FILENAME = 'subdir.txt'
+        SUBDIR = 'subdir'
+        WHEN = DateTime( '2004-01-01T00:00:00Z' )
+
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
+        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable
+                             , mod_time=WHEN, subdir=SUBDIR )
+
+        self.assertEqual( ctx.getLastModified( SUBDIR ), WHEN )
+
+    def test_isDirectory_nonesuch( self ):
+
+        SNAPSHOT_ID = 'isDirectory_nonesuch'
+        FILENAME = 'nonesuch.txt'
+
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
+
+        self.assertEqual( ctx.isDirectory( FILENAME ), None )
+
+    def test_isDirectory_simple( self ):
+
+        from string import printable
+
+        SNAPSHOT_ID = 'isDirectory_simple'
+        FILENAME = 'simple.txt'
+
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
+        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable )
+
+        self.assertEqual( ctx.isDirectory( FILENAME ), False )
+
+    def test_isDirectory_nested( self ):
+
+        from string import printable
+
+        SNAPSHOT_ID = 'isDirectory_nested'
+        SUBDIR = 'subdir'
+        FILENAME = 'nested.txt'
+        PATH = '%s/%s' % ( SUBDIR, FILENAME )
+
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
+        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable
+                             , subdir=SUBDIR )
+
+        self.assertEqual( ctx.isDirectory( PATH ), False )
+
+    def test_isDirectory_subdir( self ):
+
+        from string import printable
+
+        SNAPSHOT_ID = 'isDirectory_subdir'
+        SUBDIR = 'subdir'
+        FILENAME = 'nested.txt'
+        PATH = '%s/%s' % ( SUBDIR, FILENAME )
+
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
+        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable
+                             , subdir=SUBDIR )
+
+        self.assertEqual( ctx.isDirectory( SUBDIR ), True )
+
+    def test_listDirectory_nonesuch( self ):
+
+        SNAPSHOT_ID = 'listDirectory_nonesuch'
+        SUBDIR = 'nonesuch/path'
+
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
+
+        self.assertEqual( ctx.listDirectory( SUBDIR ), None )
+
+    def test_listDirectory_root( self ):
+
+        from string import printable
+
+        SNAPSHOT_ID = 'listDirectory_root'
+        FILENAME = 'simple.txt'
+
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
+        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable )
+
+        self.assertEqual( len( ctx.listDirectory( None ) ), 1 )
+        self.failUnless( FILENAME in ctx.listDirectory( None ) )
+
+    def test_listDirectory_simple( self ):
+
+        from string import printable
+
+        SNAPSHOT_ID = 'listDirectory_simple'
+        FILENAME = 'simple.txt'
+
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
+        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable )
+
+        self.assertEqual( ctx.listDirectory( FILENAME ), None )
+
+    def test_listDirectory_nested( self ):
+
+        from string import printable
+
+        SNAPSHOT_ID = 'listDirectory_nested'
+        SUBDIR = 'subdir'
+        FILENAME = 'nested.txt'
+        PATH = '%s/%s' % ( SUBDIR, FILENAME )
+
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
+        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable
+                             , subdir=SUBDIR )
+
+        self.assertEqual( ctx.listDirectory( PATH ), None )
+
+    def test_listDirectory_single( self ):
+
+        from string import printable
+
+        SNAPSHOT_ID = 'listDirectory_nested'
+        SUBDIR = 'subdir'
+        FILENAME = 'nested.txt'
+
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
+        file = self._makeFile( tool, SNAPSHOT_ID, FILENAME, printable
+                             , subdir=SUBDIR )
+
+        names = ctx.listDirectory( SUBDIR )
+        self.assertEqual( len( names ), 1 )
+        self.failUnless( FILENAME in names )
+
+    def test_listDirectory_multiple( self ):
+
+        from string import printable, uppercase
+
+        SNAPSHOT_ID = 'listDirectory_nested'
+        SUBDIR = 'subdir'
+        FILENAME1 = 'nested.txt'
+        FILENAME2 = 'another.txt'
+
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
+        file1 = self._makeFile( tool, SNAPSHOT_ID, FILENAME1, printable
+                              , subdir=SUBDIR )
+        file2 = self._makeFile( tool, SNAPSHOT_ID, FILENAME2, uppercase
+                              , subdir=SUBDIR )
+
+        names = ctx.listDirectory( SUBDIR )
+        self.assertEqual( len( names ), 2 )
+        self.failUnless( FILENAME1 in names )
+        self.failUnless( FILENAME2 in names )
+
+    def test_listDirectory_skip( self ):
+
+        from string import printable, uppercase
+
+        SNAPSHOT_ID = 'listDirectory_nested'
+        SUBDIR = 'subdir'
+        FILENAME1 = 'nested.txt'
+        FILENAME2 = 'another.txt'
+
+        site, tool, ctx = self._makeOne( SNAPSHOT_ID )
+        file1 = self._makeFile( tool, SNAPSHOT_ID, FILENAME1, printable
+                              , subdir=SUBDIR )
+        file2 = self._makeFile( tool, SNAPSHOT_ID, FILENAME2, uppercase
+                              , subdir=SUBDIR )
+
+        names = ctx.listDirectory( SUBDIR, skip=( FILENAME1, ) )
+        self.assertEqual( len( names ), 1 )
+        self.failIf( FILENAME1 in names )
+        self.failUnless( FILENAME2 in names )
+
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite( DirectoryImportContextTests ),
+        unittest.makeSuite( DirectoryExportContextTests ),
+        unittest.makeSuite( TarballImportContextTests ),
+        unittest.makeSuite( TarballExportContextTests ),
+        unittest.makeSuite( SnapshotExportContextTests ),
+        unittest.makeSuite( SnapshotImportContextTests ),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: CMF/branches/goldegg-phase-1/GenericSetup/tests/test_differ.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/tests/test_differ.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/tests/test_differ.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,412 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" Unit tests for differ module.
+
+$Id: test_differ.py,v 1.1.1.1 2005/08/08 19:38:37 tseaver Exp $
+"""
+
+import unittest
+import Testing
+try:
+    import Zope2
+except ImportError: # BBB: for Zope 2.7
+    import Zope as Zope2
+Zope2.startup()
+
+from OFS.Folder import Folder
+from OFS.Image import File
+
+from DateTime.DateTime import DateTime
+
+#from Products.CMFCore.tests.base.testcase import SecurityRequestTest
+from Testing.ZopeTestCase import ZopeTestCase
+from common import SecurityRequestTest
+
+
+class DummySite( Folder ):
+
+    pass
+
+
+class Test_unidiff( unittest.TestCase ):
+
+    def test_unidiff_both_text( self ):
+
+        from Products.GenericSetup.differ import unidiff
+
+        diff_lines = unidiff( ONE_FOUR, ZERO_FOUR )
+        diff_text = '\n'.join( diff_lines )
+        self.assertEqual( diff_text, DIFF_TEXT )
+
+    def test_unidiff_both_lines( self ):
+
+        from Products.GenericSetup.differ import unidiff
+
+        diff_lines = unidiff( ONE_FOUR.splitlines(), ZERO_FOUR.splitlines() )
+        diff_text = '\n'.join( diff_lines )
+        self.assertEqual( diff_text, DIFF_TEXT )
+
+    def test_unidiff_mixed( self ):
+
+        from Products.GenericSetup.differ import unidiff
+
+        diff_lines = unidiff( ONE_FOUR, ZERO_FOUR.splitlines() )
+        diff_text = '\n'.join( diff_lines )
+        self.assertEqual( diff_text, DIFF_TEXT )
+
+    def test_unidiff_ignore_blanks( self ):
+
+        from Products.GenericSetup.differ import unidiff
+
+        double_spaced = ONE_FOUR.replace( '\n', '\n\n' )
+        diff_lines = unidiff( double_spaced
+                            , ZERO_FOUR.splitlines()
+                            , ignore_blanks=True
+                            )
+
+        diff_text = '\n'.join( diff_lines )
+        self.assertEqual( diff_text, DIFF_TEXT )
+
+ZERO_FOUR = """\
+zero
+one
+tree
+four
+"""
+
+ONE_FOUR = """\
+one
+two
+three
+four
+"""
+
+DIFF_TEXT = """\
+--- original None
++++ modified None
+@@ -1,4 +1,4 @@
++zero
+ one
+-two
+-three
++tree
+ four\
+"""
+
+class ConfigDiffTests( SecurityRequestTest ):
+
+    site = None
+    tool = None
+
+    def _getTargetClass( self ):
+
+        from Products.GenericSetup.differ import ConfigDiff
+        return ConfigDiff
+
+    def _makeOne( self, lhs, rhs, *args, **kw ):
+
+        return self._getTargetClass()( lhs, rhs, *args, **kw )
+
+    def _makeSite( self ):
+
+        if self.site is not None:
+            return
+
+        site = self.site = DummySite( 'site' ).__of__( self.root )
+        site._setObject( 'setup_tool', Folder( 'setup_tool' ) )
+        self.tool = tool = site._getOb( 'setup_tool' )
+
+        tool._setObject( 'snapshots', Folder( 'snapshots' ) )
+
+    def _makeContext( self, context_id ):
+
+        from Products.GenericSetup.context import SnapshotImportContext
+
+        self._makeSite()
+
+        if context_id not in self.tool.snapshots.objectIds():
+            self.tool.snapshots._setObject( context_id, Folder( context_id ) )
+
+        ctx = SnapshotImportContext( self.tool, context_id )
+
+        return ctx.__of__( self.tool )
+
+    def _makeDirectory( self, snapshot_id, subdir ):
+
+        self._makeSite()
+        folder = self.tool.snapshots._getOb( snapshot_id )
+
+        for element in subdir.split( '/' ):
+
+            try:
+                folder = folder._getOb( element )
+            except AttributeError:
+                folder._setObject( element, Folder( element ) )
+                folder = folder._getOb( element )
+
+        return folder
+
+    def _makeFile( self
+                 , snapshot_id
+                 , filename
+                 , contents
+                 , content_type='text/plain'
+                 , mod_time=None
+                 , subdir=None
+                 ):
+
+        self._makeSite()
+        snapshots = self.tool.snapshots
+        snapshot = snapshots._getOb( snapshot_id )
+
+        if subdir is not None:
+            folder = self._makeDirectory( snapshot_id, subdir )
+        else:
+            folder = snapshot
+
+        file = File( filename, '', contents, content_type )
+        folder._setObject( filename, file )
+
+        if mod_time is not None:
+
+            def __faux_mod_time():
+                return mod_time
+
+            folder.bobobase_modification_time = \
+            file.bobobase_modification_time = __faux_mod_time
+
+        return folder._getOb( filename )
+
+    def test_compare_empties( self ):
+
+        lhs = self._makeContext( 'lhs' )
+        rhs = self._makeContext( 'rhs' )
+
+        cd = self._makeOne( lhs, rhs )
+
+        diffs = cd.compare()
+
+        self.assertEqual( diffs, '' )
+
+    def test_compare_identical( self ):
+
+        lhs = self._makeContext( 'lhs' )
+        rhs = self._makeContext( 'rhs' )
+
+        self._makeFile( 'lhs', 'test.txt', 'ABCDEF' )
+        self._makeFile( 'lhs', 'again.txt', 'GHIJKL', subdir='sub' )
+        self._makeFile( 'rhs', 'test.txt', 'ABCDEF' )
+        self._makeFile( 'rhs', 'again.txt', 'GHIJKL', subdir='sub' )
+
+        cd = self._makeOne( lhs, rhs )
+
+        diffs = cd.compare()
+
+        self.assertEqual( diffs, '' )
+
+    def test_compare_changed_file( self ):
+
+        BEFORE = DateTime( '2004-01-01T00:00:00Z' )
+        AFTER = DateTime( '2004-02-29T23:59:59Z' )
+
+        lhs = self._makeContext( 'lhs' )
+        rhs = self._makeContext( 'rhs' )
+
+        self._makeFile( 'lhs', 'test.txt', 'ABCDEF\nWXYZ', mod_time=BEFORE )
+        self._makeFile( 'lhs', 'again.txt', 'GHIJKL', subdir='sub' )
+        self._makeFile( 'rhs', 'test.txt', 'ABCDEF\nQRST', mod_time=AFTER )
+        self._makeFile( 'rhs', 'again.txt', 'GHIJKL', subdir='sub' )
+
+        cd = self._makeOne( lhs, rhs )
+
+        diffs = cd.compare()
+
+        self.assertEqual( diffs, TEST_TXT_DIFFS % ( BEFORE, AFTER ) )
+
+    def test_compare_changed_file_ignore_blanks( self ):
+
+        BEFORE = DateTime( '2004-01-01T00:00:00Z' )
+        AFTER = DateTime( '2004-02-29T23:59:59Z' )
+
+        lhs = self._makeContext( 'lhs' )
+        rhs = self._makeContext( 'rhs' )
+
+        self._makeFile( 'lhs', 'test.txt', 'ABCDEF\nWXYZ', mod_time=BEFORE )
+        self._makeFile( 'rhs', 'test.txt', 'ABCDEF\n\n\nWXYZ', mod_time=AFTER )
+
+        cd = self._makeOne( lhs, rhs, ignore_blanks=True )
+
+        diffs = cd.compare()
+
+        self.assertEqual( diffs, '' )
+
+    def test_compare_changed_file_explicit_skip( self ):
+
+        BEFORE = DateTime( '2004-01-01T00:00:00Z' )
+        AFTER = DateTime( '2004-02-29T23:59:59Z' )
+
+        lhs = self._makeContext( 'lhs' )
+        rhs = self._makeContext( 'rhs' )
+
+        self._makeFile( 'lhs', 'test.txt', 'ABCDEF\nWXYZ', subdir='skipme'
+                      , mod_time=BEFORE )
+        self._makeFile( 'lhs', 'again.txt', 'GHIJKL', subdir='sub' )
+        self._makeFile( 'rhs', 'test.txt', 'ABCDEF\nQRST', subdir='skipme'
+                      , mod_time=AFTER )
+        self._makeFile( 'rhs', 'again.txt', 'GHIJKL', subdir='sub' )
+
+        cd = self._makeOne( lhs, rhs, skip=[ 'skipme' ] )
+
+        diffs = cd.compare()
+
+        self.assertEqual( diffs, '' )
+
+    def test_compare_changed_file_implicit_skip( self ):
+
+        BEFORE = DateTime( '2004-01-01T00:00:00Z' )
+        AFTER = DateTime( '2004-02-29T23:59:59Z' )
+
+        lhs = self._makeContext( 'lhs' )
+        rhs = self._makeContext( 'rhs' )
+
+        self._makeFile( 'lhs', 'test.txt', 'ABCDEF\nWXYZ', subdir='CVS'
+                      , mod_time=BEFORE )
+        self._makeFile( 'lhs', 'again.txt', 'GHIJKL', subdir='.svn'
+                      , mod_time=BEFORE )
+
+        self._makeFile( 'rhs', 'test.txt', 'ABCDEF\nQRST', subdir='CVS'
+                      , mod_time=AFTER )
+        self._makeFile( 'rhs', 'again.txt', 'MNOPQR', subdir='.svn'
+                      , mod_time=AFTER )
+
+        cd = self._makeOne( lhs, rhs )
+
+        diffs = cd.compare()
+
+        self.assertEqual( diffs, '' )
+
+    def test_compare_added_file_no_missing_as_empty( self ):
+
+        lhs = self._makeContext( 'lhs' )
+        rhs = self._makeContext( 'rhs' )
+
+        self._makeFile( 'lhs', 'test.txt', 'ABCDEF\nWXYZ' )
+        self._makeDirectory( 'lhs', subdir='sub' )
+        self._makeFile( 'rhs', 'test.txt', 'ABCDEF\nWXYZ' )
+        self._makeFile( 'rhs', 'again.txt', 'GHIJKL', subdir='sub' )
+
+        cd = self._makeOne( lhs, rhs )
+
+        diffs = cd.compare()
+
+        self.assertEqual( diffs, ADDED_FILE_DIFFS_NO_MAE )
+
+    def test_compare_added_file_missing_as_empty( self ):
+
+        AFTER = DateTime( '2004-02-29T23:59:59Z' )
+        lhs = self._makeContext( 'lhs' )
+        rhs = self._makeContext( 'rhs' )
+
+        self._makeFile( 'lhs', 'test.txt', 'ABCDEF\nWXYZ' )
+        self._makeDirectory( 'lhs', subdir='sub' )
+        self._makeFile( 'rhs', 'test.txt', 'ABCDEF\nWXYZ' )
+        self._makeFile( 'rhs', 'again.txt', 'GHIJKL', subdir='sub'
+                      , mod_time=AFTER )
+
+        cd = self._makeOne( lhs, rhs, missing_as_empty=True )
+
+        diffs = cd.compare()
+
+        self.assertEqual( diffs, ADDED_FILE_DIFFS_MAE % AFTER )
+
+    def test_compare_removed_file_no_missing_as_empty( self ):
+
+        lhs = self._makeContext( 'lhs' )
+        rhs = self._makeContext( 'rhs' )
+
+        self._makeFile( 'lhs', 'test.txt', 'ABCDEF\nWXYZ' )
+        self._makeFile( 'lhs', 'again.txt', 'GHIJKL', subdir='sub' )
+        self._makeFile( 'rhs', 'test.txt', 'ABCDEF\nWXYZ' )
+        self._makeDirectory( 'rhs', subdir='sub' )
+
+        cd = self._makeOne( lhs, rhs )
+
+        diffs = cd.compare()
+
+        self.assertEqual( diffs, REMOVED_FILE_DIFFS_NO_MAE )
+
+    def test_compare_removed_file_missing_as_empty( self ):
+
+        BEFORE = DateTime( '2004-01-01T00:00:00Z' )
+        lhs = self._makeContext( 'lhs' )
+        rhs = self._makeContext( 'rhs' )
+
+        self._makeFile( 'lhs', 'test.txt', 'ABCDEF\nWXYZ' )
+        self._makeFile( 'lhs', 'again.txt', 'GHIJKL', subdir='sub'
+                      , mod_time=BEFORE )
+        self._makeFile( 'rhs', 'test.txt', 'ABCDEF\nWXYZ' )
+        self._makeDirectory( 'rhs', subdir='sub' )
+
+        cd = self._makeOne( lhs, rhs, missing_as_empty=True )
+
+        diffs = cd.compare()
+
+        self.assertEqual( diffs, REMOVED_FILE_DIFFS_MAE % BEFORE )
+
+
+TEST_TXT_DIFFS = """\
+Index: test.txt
+===================================================================
+--- test.txt %s
++++ test.txt %s
+@@ -1,2 +1,2 @@
+ ABCDEF
+-WXYZ
++QRST\
+"""
+
+ADDED_FILE_DIFFS_NO_MAE = """\
+** File sub/again.txt added
+"""
+
+ADDED_FILE_DIFFS_MAE = """\
+Index: sub/again.txt
+===================================================================
+--- sub/again.txt 0
++++ sub/again.txt %s
+@@ -1,0 +1,1 @@
++GHIJKL\
+"""
+
+REMOVED_FILE_DIFFS_NO_MAE = """\
+** File sub/again.txt removed
+"""
+
+REMOVED_FILE_DIFFS_MAE = """\
+Index: sub/again.txt
+===================================================================
+--- sub/again.txt %s
++++ sub/again.txt 0
+@@ -1,1 +1,0 @@
+-GHIJKL\
+"""
+
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite( Test_unidiff ),
+        unittest.makeSuite( ConfigDiffTests ),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: CMF/branches/goldegg-phase-1/GenericSetup/tests/test_properties.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/tests/test_properties.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/tests/test_properties.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,291 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" Site properties export / import unit tests.
+
+$Id: test_properties.py,v 1.1.1.1 2005/08/08 19:38:37 tseaver Exp $
+"""
+
+import unittest
+import Testing
+try:
+    import Zope2
+except ImportError: # BBB: for Zope 2.7
+    import Zope as Zope2
+Zope2.startup()
+
+from OFS.Folder import Folder
+
+from common import BaseRegistryTests
+from common import DummyExportContext
+from common import DummyImportContext
+
+
+_EMPTY_EXPORT = """\
+<?xml version="1.0"?>
+<site>
+</site>
+"""
+
+_NORMAL_EXPORT = """\
+<?xml version="1.0"?>
+<site>
+  <property name="foo" type="string">Foo</property>
+  <property name="bar" type="tokens">
+   <element value="Bar"/></property>
+  <property name="moo" type="tokens">
+   <element value="Moo"/></property>
+</site>
+"""
+
+
+class DummySite(Folder):
+
+    _properties = ()
+
+
+class _SitePropertiesSetup(BaseRegistryTests):
+
+    def _initSite(self, foo=2, bar=2):
+
+        self.root.site = DummySite()
+        site = self.root.site
+
+        if foo > 0:
+            site._setProperty('foo', '', 'string')
+        if foo > 1:
+            site._updateProperty('foo', 'Foo')
+
+        if bar > 0:
+            site._setProperty( 'bar', (), 'tokens' )
+            site._setProperty( 'moo', (), 'tokens' )
+        if bar > 1:
+            site._updateProperty( 'bar', ('Bar',) )
+            site.moo = ['Moo']
+
+        return site
+
+
+class SitePropertiesConfiguratorTests(_SitePropertiesSetup):
+
+    def _getTargetClass(self):
+
+        from Products.GenericSetup.properties import SitePropertiesConfigurator
+        return SitePropertiesConfigurator
+
+    def test_listSiteInfos_normal(self):
+
+        site = self._initSite()
+
+        EXPECTED = [ { 'id': 'foo',
+                       'value': 'Foo',
+                       'elements': (),
+                       'type': 'string',
+                       'select_variable': None },
+                     { 'id': 'bar',
+                       'value': '',
+                       'elements': ('Bar',),
+                       'type': 'tokens',
+                       'select_variable': None },
+                     { 'id': 'moo',
+                       'value': '',
+                       'elements': ('Moo',),
+                       'type': 'tokens',
+                       'select_variable': None } ]
+
+        configurator = self._makeOne(site)
+
+        site_info = configurator.listSiteInfos()
+        self.assertEqual( len(site_info), len(EXPECTED) )
+
+        for found, expected in zip(site_info, EXPECTED):
+            self.assertEqual(found, expected)
+
+    def test_generateXML_empty(self):
+
+        site = self._initSite(0, 0)
+        configurator = self._makeOne(site).__of__(site)
+
+        self._compareDOM(configurator.generateXML(), _EMPTY_EXPORT)
+
+    def test_generateXML_normal(self):
+
+        site = self._initSite()
+        configurator = self._makeOne(site).__of__(site)
+
+        self._compareDOM( configurator.generateXML(), _NORMAL_EXPORT )
+
+    def test_parseXML_empty(self):
+
+        site = self._initSite(0, 0)
+        configurator = self._makeOne(site)
+        site_info = configurator.parseXML(_EMPTY_EXPORT)
+
+        self.assertEqual( len( site_info['properties'] ), 0 )
+
+    def test_parseXML_normal(self):
+
+        site = self._initSite()
+        configurator = self._makeOne(site)
+        site_info = configurator.parseXML(_NORMAL_EXPORT)
+
+        self.assertEqual( len( site_info['properties'] ), 3 )
+
+        info = site_info['properties'][0]
+        self.assertEqual( info['id'], 'foo' )
+        self.assertEqual( info['value'], 'Foo' )
+        self.assertEqual( len( info['elements'] ), 0 )
+
+        info = site_info['properties'][1]
+        self.assertEqual( info['id'], 'bar' )
+        self.assertEqual( info['value'], '' )
+        self.assertEqual( len( info['elements'] ), 1 )
+        self.assertEqual( info['elements'][0], 'Bar' )
+
+
+class Test_exportSiteProperties(_SitePropertiesSetup):
+
+    def test_empty(self):
+
+        site = self._initSite(0, 0)
+        context = DummyExportContext(site)
+
+        from Products.GenericSetup.properties import exportSiteProperties
+        exportSiteProperties(context)
+
+        self.assertEqual( len(context._wrote), 1 )
+        filename, text, content_type = context._wrote[0]
+        self.assertEqual(filename, 'properties.xml')
+        self._compareDOM(text, _EMPTY_EXPORT)
+        self.assertEqual(content_type, 'text/xml')
+
+    def test_normal(self):
+
+        site = self._initSite()
+        context = DummyExportContext( site )
+
+        from Products.GenericSetup.properties import exportSiteProperties
+        exportSiteProperties(context)
+
+        self.assertEqual( len(context._wrote), 1 )
+        filename, text, content_type = context._wrote[0]
+        self.assertEqual(filename, 'properties.xml')
+        self._compareDOM(text, _NORMAL_EXPORT)
+        self.assertEqual(content_type, 'text/xml')
+
+
+class Test_importSiteProperties(_SitePropertiesSetup):
+
+    def test_empty_default_purge(self):
+
+        site = self._initSite()
+
+        self.assertEqual( len( site.propertyIds() ), 3 )
+        self.failUnless( 'foo' in site.propertyIds() )
+        self.assertEqual( site.getProperty('foo'), 'Foo' )
+        self.failUnless( 'bar' in site.propertyIds() )
+        self.assertEqual( site.getProperty('bar'), ('Bar',) )
+
+        context = DummyImportContext(site)
+        context._files['properties.xml'] = _EMPTY_EXPORT
+
+        from Products.GenericSetup.properties import importSiteProperties
+        importSiteProperties(context)
+
+        self.assertEqual( len( site.propertyIds() ), 0 )
+
+    def test_empty_explicit_purge(self):
+
+        site = self._initSite()
+
+        self.assertEqual( len( site.propertyIds() ), 3 )
+        self.failUnless( 'foo' in site.propertyIds() )
+        self.assertEqual( site.getProperty('foo'), 'Foo' )
+        self.failUnless( 'bar' in site.propertyIds() )
+        self.assertEqual( site.getProperty('bar'), ('Bar',) )
+
+        context = DummyImportContext(site, True)
+        context._files['properties.xml'] = _EMPTY_EXPORT
+
+        from Products.GenericSetup.properties import importSiteProperties
+        importSiteProperties(context)
+
+        self.assertEqual( len( site.propertyIds() ), 0 )
+
+    def test_empty_skip_purge(self):
+
+        site = self._initSite()
+
+        self.assertEqual( len( site.propertyIds() ), 3 )
+        self.failUnless( 'foo' in site.propertyIds() )
+        self.assertEqual( site.getProperty('foo'), 'Foo' )
+        self.failUnless( 'bar' in site.propertyIds() )
+        self.assertEqual( site.getProperty('bar'), ('Bar',) )
+
+        context = DummyImportContext(site, False)
+        context._files['properties.xml'] = _EMPTY_EXPORT
+
+        from Products.GenericSetup.properties import importSiteProperties
+        importSiteProperties(context)
+
+        self.assertEqual( len( site.propertyIds() ), 3 )
+        self.failUnless( 'foo' in site.propertyIds() )
+        self.assertEqual( site.getProperty('foo'), 'Foo' )
+        self.failUnless( 'bar' in site.propertyIds() )
+        self.assertEqual( site.getProperty('bar'), ('Bar',) )
+
+    def test_normal(self):
+
+        site = self._initSite(0,0)
+
+        self.assertEqual( len( site.propertyIds() ), 0 )
+
+        context = DummyImportContext(site)
+        context._files['properties.xml'] = _NORMAL_EXPORT
+
+        from Products.GenericSetup.properties import importSiteProperties
+        importSiteProperties(context)
+
+        self.assertEqual( len( site.propertyIds() ), 3 )
+        self.failUnless( 'foo' in site.propertyIds() )
+        self.assertEqual( site.getProperty('foo'), 'Foo' )
+        self.failUnless( 'bar' in site.propertyIds() )
+        self.assertEqual( site.getProperty('bar'), ('Bar',) )
+
+    def test_normal_encode_as_ascii(self):
+
+        site = self._initSite(0,0)
+
+        self.assertEqual( len( site.propertyIds() ), 0 )
+
+        context = DummyImportContext(site, encoding='ascii')
+        context._files['properties.xml'] = _NORMAL_EXPORT
+
+        from Products.GenericSetup.properties import importSiteProperties
+        importSiteProperties(context)
+
+        self.assertEqual( len( site.propertyIds() ), 3 )
+        self.failUnless( 'foo' in site.propertyIds() )
+        self.assertEqual( site.getProperty('foo'), 'Foo' )
+        self.failUnless( 'bar' in site.propertyIds() )
+        self.assertEqual( site.getProperty('bar'), ('Bar',) )
+
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(SitePropertiesConfiguratorTests),
+        unittest.makeSuite(Test_exportSiteProperties),
+        unittest.makeSuite(Test_importSiteProperties),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: CMF/branches/goldegg-phase-1/GenericSetup/tests/test_registry.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/tests/test_registry.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/tests/test_registry.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,1101 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" Registry unit tests.
+
+$Id: test_registry.py,v 1.1.1.1 2005/08/08 19:38:37 tseaver Exp $
+"""
+
+import unittest
+import Testing
+try:
+    import Zope2
+except ImportError: # BBB: for Zope 2.7
+    import Zope as Zope2
+Zope2.startup()
+
+from OFS.Folder import Folder
+from Products.GenericSetup.tests.common import BaseRegistryTests
+from Products.GenericSetup import EXTENSION
+
+from conformance import ConformsToIStepRegistry
+from conformance import ConformsToIImportStepRegistry
+from conformance import ConformsToIExportStepRegistry
+from conformance import ConformsToIToolsetRegistry
+from conformance import ConformsToIProfileRegistry
+
+
+#==============================================================================
+#   Dummy handlers
+#==============================================================================
+def ONE_FUNC( context ): pass
+def TWO_FUNC( context ): pass
+def THREE_FUNC( context ): pass
+def FOUR_FUNC( context ): pass
+
+ONE_FUNC_NAME = '%s.%s' % ( __name__, ONE_FUNC.__name__ )
+TWO_FUNC_NAME = '%s.%s' % ( __name__, TWO_FUNC.__name__ )
+THREE_FUNC_NAME = '%s.%s' % ( __name__, THREE_FUNC.__name__ )
+FOUR_FUNC_NAME = '%s.%s' % ( __name__, FOUR_FUNC.__name__ )
+
+
+#==============================================================================
+#   SSR tests
+#==============================================================================
+class ImportStepRegistryTests( BaseRegistryTests
+                             , ConformsToIStepRegistry
+                             , ConformsToIImportStepRegistry
+                             ):
+
+    def _getTargetClass( self ):
+
+        from Products.GenericSetup.registry import ImportStepRegistry
+        return ImportStepRegistry
+
+    def test_empty( self ):
+
+        registry = self._makeOne()
+
+        self.assertEqual( len( registry.listSteps() ), 0 )
+        self.assertEqual( len( registry.listStepMetadata() ), 0 )
+        self.assertEqual( len( registry.sortSteps() ), 0 )
+
+    def test_getStep_nonesuch( self ):
+
+        registry = self._makeOne()
+
+        self.assertEqual( registry.getStep( 'nonesuch' ), None )
+        self.assertEqual( registry.getStep( 'nonesuch' ), None )
+        default = object()
+        self.failUnless( registry.getStepMetadata( 'nonesuch'
+                                                 , default ) is default )
+        self.failUnless( registry.getStep( 'nonesuch', default ) is default )
+        self.failUnless( registry.getStepMetadata( 'nonesuch'
+                                                 , default ) is default )
+
+    def test_getStep_defaulted( self ):
+
+        registry = self._makeOne()
+        default = object()
+
+        self.failUnless( registry.getStep( 'nonesuch', default ) is default )
+        self.assertEqual( registry.getStepMetadata( 'nonesuch', {} ), {} )
+
+    def test_registerStep_docstring( self ):
+
+        def func_with_doc( site ):
+            """This is the first line.
+
+            This is the second line.
+            """
+        FUNC_NAME = '%s.%s' % ( __name__, func_with_doc.__name__ )
+
+        registry = self._makeOne()
+
+        registry.registerStep( id='docstring'
+                             , version='1'
+                             , handler=func_with_doc
+                             , dependencies=()
+                             )
+
+        info = registry.getStepMetadata( 'docstring' )
+        self.assertEqual( info[ 'id' ], 'docstring' )
+        self.assertEqual( info[ 'handler' ], FUNC_NAME )
+        self.assertEqual( info[ 'dependencies' ], () )
+        self.assertEqual( info[ 'title' ], 'This is the first line.' )
+        self.assertEqual( info[ 'description' ] , 'This is the second line.' )
+
+    def test_registerStep_docstring_override( self ):
+
+        def func_with_doc( site ):
+            """This is the first line.
+
+            This is the second line.
+            """
+        FUNC_NAME = '%s.%s' % ( __name__, func_with_doc.__name__ )
+
+        registry = self._makeOne()
+
+        registry.registerStep( id='docstring'
+                             , version='1'
+                             , handler=func_with_doc
+                             , dependencies=()
+                             , title='Title'
+                             )
+
+        info = registry.getStepMetadata( 'docstring' )
+        self.assertEqual( info[ 'id' ], 'docstring' )
+        self.assertEqual( info[ 'handler' ], FUNC_NAME )
+        self.assertEqual( info[ 'dependencies' ], () )
+        self.assertEqual( info[ 'title' ], 'Title' )
+        self.assertEqual( info[ 'description' ] , 'This is the second line.' )
+
+    def test_registerStep_single( self ):
+
+        registry = self._makeOne()
+
+        registry.registerStep( id='one'
+                             , version='1'
+                             , handler=ONE_FUNC
+                             , dependencies=( 'two', 'three' )
+                             , title='One Step'
+                             , description='One small step'
+                             )
+
+        steps = registry.listSteps()
+        self.assertEqual( len( steps ), 1 )
+        self.failUnless( 'one' in steps )
+
+        sorted = registry.sortSteps()
+        self.assertEqual( len( sorted ), 1 )
+        self.assertEqual( sorted[ 0 ], 'one' )
+
+        self.assertEqual( registry.getStep( 'one' ), ONE_FUNC )
+
+        info = registry.getStepMetadata( 'one' )
+        self.assertEqual( info[ 'id' ], 'one' )
+        self.assertEqual( info[ 'version' ], '1' )
+        self.assertEqual( info[ 'handler' ], ONE_FUNC_NAME )
+        self.assertEqual( info[ 'dependencies' ], ( 'two', 'three' ) )
+        self.assertEqual( info[ 'title' ], 'One Step' )
+        self.assertEqual( info[ 'description' ], 'One small step' )
+
+        info_list = registry.listStepMetadata()
+        self.assertEqual( len( info_list ), 1 )
+        self.assertEqual( info, info_list[ 0 ] )
+
+    def test_registerStep_conflict( self ):
+
+        registry = self._makeOne()
+
+        registry.registerStep( id='one', version='1', handler=ONE_FUNC )
+
+        self.assertRaises( KeyError
+                         , registry.registerStep
+                         , id='one'
+                         , version='0'
+                         , handler=ONE_FUNC
+                         )
+
+        registry.registerStep( id='one', version='1', handler=ONE_FUNC )
+
+        info_list = registry.listStepMetadata()
+        self.assertEqual( len( info_list ), 1 )
+
+    def test_registerStep_replacement( self ):
+
+        registry = self._makeOne()
+
+        registry.registerStep( id='one'
+                             , version='1'
+                             , handler=ONE_FUNC
+                             , dependencies=( 'two', 'three' )
+                             , title='One Step'
+                             , description='One small step'
+                             )
+
+        registry.registerStep( id='one'
+                             , version='1.1'
+                             , handler=ONE_FUNC
+                             , dependencies=()
+                             , title='Leads to Another'
+                             , description='Another small step'
+                             )
+
+        info = registry.getStepMetadata( 'one' )
+        self.assertEqual( info[ 'id' ], 'one' )
+        self.assertEqual( info[ 'version' ], '1.1' )
+        self.assertEqual( info[ 'dependencies' ], () )
+        self.assertEqual( info[ 'title' ], 'Leads to Another' )
+        self.assertEqual( info[ 'description' ], 'Another small step' )
+
+    def test_registerStep_multiple( self ):
+
+        registry = self._makeOne()
+
+        registry.registerStep( id='one'
+                             , version='1'
+                             , handler=ONE_FUNC
+                             , dependencies=()
+                             )
+
+        registry.registerStep( id='two'
+                             , version='2'
+                             , handler=TWO_FUNC
+                             , dependencies=()
+                             )
+
+        registry.registerStep( id='three'
+                             , version='3'
+                             , handler=THREE_FUNC
+                             , dependencies=()
+                             )
+
+        steps = registry.listSteps()
+        self.assertEqual( len( steps ), 3 )
+        self.failUnless( 'one' in steps )
+        self.failUnless( 'two' in steps )
+        self.failUnless( 'three' in steps )
+
+    def test_sortStep_simple( self ):
+
+        registry = self._makeOne()
+
+        registry.registerStep( id='one'
+                             , version='1'
+                             , handler=ONE_FUNC
+                             , dependencies=( 'two', )
+                             )
+
+        registry.registerStep( id='two'
+                             , version='2'
+                             , handler=TWO_FUNC
+                             , dependencies=()
+                             )
+
+        steps = registry.sortSteps()
+        self.assertEqual( len( steps ), 2 )
+        one = steps.index( 'one' )
+        two = steps.index( 'two' )
+
+        self.failUnless( 0 <= two < one )
+
+    def test_sortStep_chained( self ):
+
+        registry = self._makeOne()
+
+        registry.registerStep( id='one'
+                             , version='1'
+                             , handler=ONE_FUNC
+                             , dependencies=( 'two', )
+                             , title='One small step'
+                             )
+
+        registry.registerStep( id='two'
+                             , version='2'
+                             , handler=TWO_FUNC
+                             , dependencies=( 'three', )
+                             , title='Texas two step'
+                             )
+
+        registry.registerStep( id='three'
+                             , version='3'
+                             , handler=THREE_FUNC
+                             , dependencies=()
+                             , title='Gimme three steps'
+                             )
+
+        steps = registry.sortSteps()
+        self.assertEqual( len( steps ), 3 )
+        one = steps.index( 'one' )
+        two = steps.index( 'two' )
+        three = steps.index( 'three' )
+
+        self.failUnless( 0 <= three < two < one )
+
+    def test_sortStep_complex( self ):
+
+        registry = self._makeOne()
+
+        registry.registerStep( id='one'
+                             , version='1'
+                             , handler=ONE_FUNC
+                             , dependencies=( 'two', )
+                             , title='One small step'
+                             )
+
+        registry.registerStep( id='two'
+                             , version='2'
+                             , handler=TWO_FUNC
+                             , dependencies=( 'four', )
+                             , title='Texas two step'
+                             )
+
+        registry.registerStep( id='three'
+                             , version='3'
+                             , handler=THREE_FUNC
+                             , dependencies=( 'four', )
+                             , title='Gimme three steps'
+                             )
+
+        registry.registerStep( id='four'
+                             , version='4'
+                             , handler=FOUR_FUNC
+                             , dependencies=()
+                             , title='Four step program'
+                             )
+
+        steps = registry.sortSteps()
+        self.assertEqual( len( steps ), 4 )
+        one = steps.index( 'one' )
+        two = steps.index( 'two' )
+        three = steps.index( 'three' )
+        four = steps.index( 'four' )
+
+        self.failUnless( 0 <= four < two < one )
+        self.failUnless( 0 <= four < three )
+
+    def test_sortStep_equivalence( self ):
+
+        registry = self._makeOne()
+
+        registry.registerStep( id='one'
+                             , version='1'
+                             , handler=ONE_FUNC
+                             , dependencies=( 'two', 'three' )
+                             , title='One small step'
+                             )
+
+        registry.registerStep( id='two'
+                             , version='2'
+                             , handler=TWO_FUNC
+                             , dependencies=( 'four', )
+                             , title='Texas two step'
+                             )
+
+        registry.registerStep( id='three'
+                             , version='3'
+                             , handler=THREE_FUNC
+                             , dependencies=( 'four', )
+                             , title='Gimme three steps'
+                             )
+
+        registry.registerStep( id='four'
+                             , version='4'
+                             , handler=FOUR_FUNC
+                             , dependencies=()
+                             , title='Four step program'
+                             )
+
+        steps = registry.sortSteps()
+        self.assertEqual( len( steps ), 4 )
+        one = steps.index( 'one' )
+        two = steps.index( 'two' )
+        three = steps.index( 'three' )
+        four = steps.index( 'four' )
+
+        self.failUnless( 0 <= four < two < one )
+        self.failUnless( 0 <= four < three < one )
+
+    def test_checkComplete_simple( self ):
+
+        registry = self._makeOne()
+
+        registry.registerStep( id='one'
+                             , version='1'
+                             , handler=ONE_FUNC
+                             , dependencies=( 'two', )
+                             )
+
+        incomplete = registry.checkComplete()
+        self.assertEqual( len( incomplete ), 1 )
+        self.failUnless( ( 'one', 'two' ) in incomplete )
+
+        registry.registerStep( id='two'
+                             , version='2'
+                             , handler=TWO_FUNC
+                             , dependencies=()
+                             )
+
+        self.assertEqual( len( registry.checkComplete() ), 0 )
+
+    def test_checkComplete_double( self ):
+
+        registry = self._makeOne()
+
+        registry.registerStep( id='one'
+                             , version='1'
+                             , handler=ONE_FUNC
+                             , dependencies=( 'two', 'three' )
+                             )
+
+        incomplete = registry.checkComplete()
+        self.assertEqual( len( incomplete ), 2 )
+        self.failUnless( ( 'one', 'two' ) in incomplete )
+        self.failUnless( ( 'one', 'three' ) in incomplete )
+
+        registry.registerStep( id='two'
+                             , version='2'
+                             , handler=TWO_FUNC
+                             , dependencies=()
+                             )
+
+        incomplete = registry.checkComplete()
+        self.assertEqual( len( incomplete ), 1 )
+        self.failUnless( ( 'one', 'three' ) in incomplete )
+
+        registry.registerStep( id='three'
+                             , version='3'
+                             , handler=THREE_FUNC
+                             , dependencies=()
+                             )
+
+        self.assertEqual( len( registry.checkComplete() ), 0 )
+
+        registry.registerStep( id='two'
+                             , version='2.1'
+                             , handler=TWO_FUNC
+                             , dependencies=( 'four', )
+                             )
+
+        incomplete = registry.checkComplete()
+        self.assertEqual( len( incomplete ), 1 )
+        self.failUnless( ( 'two', 'four' ) in incomplete )
+
+    def test_generateXML_empty( self ):
+
+        registry = self._makeOne().__of__( self.root )
+
+        xml = registry.generateXML()
+
+        self._compareDOM( registry.generateXML(), _EMPTY_IMPORT_XML )
+
+    def test_generateXML_single( self ):
+
+        registry = self._makeOne().__of__( self.root )
+
+        registry.registerStep( id='one'
+                             , version='1'
+                             , handler=ONE_FUNC
+                             , dependencies=()
+                             , title='One Step'
+                             , description='One small step'
+                             )
+
+        self._compareDOM( registry.generateXML(), _SINGLE_IMPORT_XML )
+
+    def test_generateXML_ordered( self ):
+
+        registry = self._makeOne().__of__( self.root )
+
+        registry.registerStep( id='one'
+                             , version='1'
+                             , handler=ONE_FUNC
+                             , dependencies=( 'two', )
+                             , title='One Step'
+                             , description='One small step'
+                             )
+
+        registry.registerStep( id='two'
+                             , version='2'
+                             , handler=TWO_FUNC
+                             , dependencies=( 'three', )
+                             , title='Two Steps'
+                             , description='Texas two step'
+                             )
+
+        registry.registerStep( id='three'
+                             , version='3'
+                             , handler=THREE_FUNC
+                             , dependencies=()
+                             , title='Three Steps'
+                             , description='Gimme three steps'
+                             )
+
+        self._compareDOM( registry.generateXML(), _ORDERED_IMPORT_XML )
+
+    def test_parseXML_empty( self ):
+
+        registry = self._makeOne().__of__( self.root )
+
+        registry.registerStep( id='one'
+                             , version='1'
+                             , handler=ONE_FUNC
+                             , dependencies=()
+                             , description='One small step'
+                             )
+
+        info_list = registry.parseXML( _EMPTY_IMPORT_XML )
+
+        self.assertEqual( len( info_list ), 0 )
+
+    def test_parseXML_single( self ):
+
+        registry = self._makeOne().__of__( self.root )
+
+        registry.registerStep( id='two'
+                             , version='2'
+                             , handler=TWO_FUNC
+                             , dependencies=()
+                             , title='Two Steps'
+                             , description='Texas two step'
+                             )
+
+        info_list = registry.parseXML( _SINGLE_IMPORT_XML )
+
+        self.assertEqual( len( info_list ), 1 )
+
+        info = info_list[ 0 ]
+        self.assertEqual( info[ 'id' ], 'one' )
+        self.assertEqual( info[ 'version' ], '1' )
+        self.assertEqual( info[ 'handler' ], ONE_FUNC_NAME )
+        self.assertEqual( info[ 'dependencies' ], () )
+        self.assertEqual( info[ 'title' ], 'One Step' )
+        self.failUnless( 'One small step' in info[ 'description' ] )
+
+
+_EMPTY_IMPORT_XML = """\
+<?xml version="1.0"?>
+<import-steps>
+</import-steps>
+"""
+
+_SINGLE_IMPORT_XML = """\
+<?xml version="1.0"?>
+<import-steps>
+ <import-step id="one"
+             version="1"
+             handler="%s"
+             title="One Step">
+  One small step
+ </import-step>
+</import-steps>
+""" % ( ONE_FUNC_NAME, )
+
+_ORDERED_IMPORT_XML = """\
+<?xml version="1.0"?>
+<import-steps>
+ <import-step id="one"
+             version="1"
+             handler="%s"
+             title="One Step">
+  <dependency step="two" />
+  One small step
+ </import-step>
+ <import-step id="three"
+             version="3"
+             handler="%s"
+             title="Three Steps">
+  Gimme three steps
+ </import-step>
+ <import-step id="two"
+             version="2"
+             handler="%s"
+             title="Two Steps">
+  <dependency step="three" />
+  Texas two step
+ </import-step>
+</import-steps>
+""" % ( ONE_FUNC_NAME, THREE_FUNC_NAME, TWO_FUNC_NAME )
+
+
+#==============================================================================
+#   ESR tests
+#==============================================================================
+class ExportStepRegistryTests( BaseRegistryTests
+                             , ConformsToIStepRegistry
+                             , ConformsToIExportStepRegistry
+                             ):
+
+    def _getTargetClass( self ):
+
+        from Products.GenericSetup.registry import ExportStepRegistry
+        return ExportStepRegistry
+
+    def _makeOne( self, *args, **kw ):
+
+        return self._getTargetClass()( *args, **kw )
+
+    def test_empty( self ):
+
+        registry = self._makeOne()
+        self.assertEqual( len( registry.listSteps() ), 0 )
+        self.assertEqual( len( registry.listStepMetadata() ), 0 )
+
+    def test_getStep_nonesuch( self ):
+
+        registry = self._makeOne()
+        self.assertEqual( registry.getStep( 'nonesuch' ), None )
+
+    def test_getStep_defaulted( self ):
+
+        registry = self._makeOne()
+        default = lambda x: false
+        self.assertEqual( registry.getStep( 'nonesuch', default ), default )
+
+    def test_getStepMetadata_nonesuch( self ):
+
+        registry = self._makeOne()
+        self.assertEqual( registry.getStepMetadata( 'nonesuch' ), None )
+
+    def test_getStepMetadata_defaulted( self ):
+
+        registry = self._makeOne()
+        self.assertEqual( registry.getStepMetadata( 'nonesuch', {} ), {} )
+
+    def test_registerStep_simple( self ):
+
+        registry = self._makeOne()
+        registry.registerStep( 'one', ONE_FUNC )
+        info = registry.getStepMetadata( 'one', {} )
+
+        self.assertEqual( info[ 'id' ], 'one' )
+        self.assertEqual( info[ 'handler' ], ONE_FUNC_NAME )
+        self.assertEqual( info[ 'title' ], 'one' )
+        self.assertEqual( info[ 'description' ], '' )
+
+    def test_registerStep_docstring( self ):
+
+        def func_with_doc( site ):
+            """This is the first line.
+
+            This is the second line.
+            """
+        FUNC_NAME = '%s.%s' % ( __name__, func_with_doc.__name__ )
+
+        registry = self._makeOne()
+        registry.registerStep( 'one', func_with_doc )
+        info = registry.getStepMetadata( 'one', {} )
+
+        self.assertEqual( info[ 'id' ], 'one' )
+        self.assertEqual( info[ 'handler' ], FUNC_NAME )
+        self.assertEqual( info[ 'title' ], 'This is the first line.' )
+        self.assertEqual( info[ 'description' ] , 'This is the second line.' )
+
+    def test_registerStep_docstring_with_override( self ):
+
+        def func_with_doc( site ):
+            """This is the first line.
+
+            This is the second line.
+            """
+        FUNC_NAME = '%s.%s' % ( __name__, func_with_doc.__name__ )
+
+        registry = self._makeOne()
+        registry.registerStep( 'one', func_with_doc
+                               , description='Description' )
+        info = registry.getStepMetadata( 'one', {} )
+
+        self.assertEqual( info[ 'id' ], 'one' )
+        self.assertEqual( info[ 'handler' ], FUNC_NAME )
+        self.assertEqual( info[ 'title' ], 'This is the first line.' )
+        self.assertEqual( info[ 'description' ], 'Description' )
+
+    def test_registerStep_collision( self ):
+
+        registry = self._makeOne()
+        registry.registerStep( 'one', ONE_FUNC )
+        registry.registerStep( 'one', TWO_FUNC )
+        info = registry.getStepMetadata( 'one', {} )
+
+        self.assertEqual( info[ 'id' ], 'one' )
+        self.assertEqual( info[ 'handler' ], TWO_FUNC_NAME )
+        self.assertEqual( info[ 'title' ], 'one' )
+        self.assertEqual( info[ 'description' ], '' )
+
+    def test_generateXML_empty( self ):
+
+        registry = self._makeOne().__of__( self.root )
+
+        xml = registry.generateXML()
+
+        self._compareDOM( registry.generateXML(), _EMPTY_EXPORT_XML )
+
+    def test_generateXML_single( self ):
+
+        registry = self._makeOne().__of__( self.root )
+
+        registry.registerStep( id='one'
+                             , handler=ONE_FUNC
+                             , title='One Step'
+                             , description='One small step'
+                             )
+
+        self._compareDOM( registry.generateXML(), _SINGLE_EXPORT_XML )
+
+    def test_generateXML_ordered( self ):
+
+        registry = self._makeOne().__of__( self.root )
+
+        registry.registerStep( id='one'
+                             , handler=ONE_FUNC
+                             , title='One Step'
+                             , description='One small step'
+                             )
+
+        registry.registerStep( id='two'
+                             , handler=TWO_FUNC
+                             , title='Two Steps'
+                             , description='Texas two step'
+                             )
+
+        registry.registerStep( id='three'
+                             , handler=THREE_FUNC
+                             , title='Three Steps'
+                             , description='Gimme three steps'
+                             )
+
+        self._compareDOM( registry.generateXML(), _ORDERED_EXPORT_XML )
+
+    def test_parseXML_empty( self ):
+
+        registry = self._makeOne().__of__( self.root )
+
+        registry.registerStep( id='one'
+                             , handler=ONE_FUNC
+                             , description='One small step'
+                             )
+
+        info_list = registry.parseXML( _EMPTY_EXPORT_XML )
+
+        self.assertEqual( len( info_list ), 0 )
+
+    def test_parseXML_single( self ):
+
+        registry = self._makeOne().__of__( self.root )
+
+        registry.registerStep( id='two'
+                             , handler=TWO_FUNC
+                             , title='Two Steps'
+                             , description='Texas two step'
+                             )
+
+        info_list = registry.parseXML( _SINGLE_EXPORT_XML )
+
+        self.assertEqual( len( info_list ), 1 )
+
+        info = info_list[ 0 ]
+        self.assertEqual( info[ 'id' ], 'one' )
+        self.assertEqual( info[ 'handler' ], ONE_FUNC_NAME )
+        self.assertEqual( info[ 'title' ], 'One Step' )
+        self.failUnless( 'One small step' in info[ 'description' ] )
+
+    def test_parseXML_single_as_ascii( self ):
+
+        registry = self._makeOne().__of__( self.root )
+
+        registry.registerStep( id='two'
+                             , handler=TWO_FUNC
+                             , title='Two Steps'
+                             , description='Texas two step'
+                             )
+
+        info_list = registry.parseXML( _SINGLE_EXPORT_XML, encoding='ascii' )
+
+        self.assertEqual( len( info_list ), 1 )
+
+        info = info_list[ 0 ]
+        self.assertEqual( info[ 'id' ], 'one' )
+        self.assertEqual( info[ 'handler' ], ONE_FUNC_NAME )
+        self.assertEqual( info[ 'title' ], 'One Step' )
+        self.failUnless( 'One small step' in info[ 'description' ] )
+
+
+_EMPTY_EXPORT_XML = """\
+<?xml version="1.0"?>
+<export-steps>
+</export-steps>
+"""
+
+_SINGLE_EXPORT_XML = """\
+<?xml version="1.0"?>
+<export-steps>
+ <export-step id="one"
+                handler="%s"
+                title="One Step">
+  One small step
+ </export-step>
+</export-steps>
+""" % ( ONE_FUNC_NAME, )
+
+_ORDERED_EXPORT_XML = """\
+<?xml version="1.0"?>
+<export-steps>
+ <export-step id="one"
+                handler="%s"
+                title="One Step">
+  One small step
+ </export-step>
+ <export-step id="three"
+                handler="%s"
+                title="Three Steps">
+  Gimme three steps
+ </export-step>
+ <export-step id="two"
+                handler="%s"
+                title="Two Steps">
+  Texas two step
+ </export-step>
+</export-steps>
+""" % ( ONE_FUNC_NAME, THREE_FUNC_NAME, TWO_FUNC_NAME )
+
+#==============================================================================
+#   ToolsetRegistry tests
+#==============================================================================
+class ToolsetRegistryTests( BaseRegistryTests
+                          , ConformsToIToolsetRegistry
+                          ):
+
+    def _getTargetClass( self ):
+
+        from Products.GenericSetup.registry import ToolsetRegistry
+        return ToolsetRegistry
+
+    def _initSite( self ):
+
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+
+        return site
+
+    def test_empty( self ):
+
+        site = self._initSite()
+        configurator = self._makeOne().__of__( site )
+
+        self.assertEqual( len( configurator.listForbiddenTools() ), 0 )
+        self.assertEqual( len( configurator.listRequiredTools() ), 0 )
+        self.assertEqual( len( configurator.listRequiredToolInfo() ), 0 )
+
+        self.assertRaises( KeyError
+                         , configurator.getRequiredToolInfo, 'nonesuch' )
+
+    def test_addForbiddenTool_multiple( self ):
+
+        VERBOTTEN = ( 'foo', 'bar', 'bam' )
+
+        site = self._initSite()
+        configurator = self._makeOne().__of__( site )
+
+        for verbotten in VERBOTTEN:
+            configurator.addForbiddenTool( verbotten )
+
+        self.assertEqual( len( configurator.listForbiddenTools() )
+                        , len( VERBOTTEN ) )
+
+        for verbotten in configurator.listForbiddenTools():
+            self.failUnless( verbotten in VERBOTTEN )
+
+    def test_addForbiddenTool_duplicate( self ):
+
+        site = self._initSite()
+        configurator = self._makeOne().__of__( site )
+
+        configurator.addForbiddenTool( 'once' )
+        configurator.addForbiddenTool( 'once' )
+
+        self.assertEqual( len( configurator.listForbiddenTools() ), 1 )
+
+    def test_addForbiddenTool_but_required( self ):
+
+        site = self._initSite()
+        configurator = self._makeOne().__of__( site )
+
+        configurator.addRequiredTool( 'required', 'some.dotted.name' )
+
+        self.assertRaises( ValueError
+                         , configurator.addForbiddenTool, 'required' )
+
+    def test_addRequiredTool_multiple( self ):
+
+        REQUIRED = ( ( 'one', 'path.to.one' )
+                   , ( 'two', 'path.to.two' )
+                   , ( 'three', 'path.to.three' )
+                   )
+
+        site = self._initSite()
+        configurator = self._makeOne().__of__( site )
+
+        for tool_id, dotted_name in REQUIRED:
+            configurator.addRequiredTool( tool_id, dotted_name )
+
+        self.assertEqual( len( configurator.listRequiredTools() )
+                        , len( REQUIRED ) )
+
+        for id in [ x[0] for x in REQUIRED ]:
+            self.failUnless( id in configurator.listRequiredTools() )
+
+        self.assertEqual( len( configurator.listRequiredToolInfo() )
+                        , len( REQUIRED ) )
+
+        for tool_id, dotted_name in REQUIRED:
+            info = configurator.getRequiredToolInfo( tool_id )
+            self.assertEqual( info[ 'id' ], tool_id )
+            self.assertEqual( info[ 'class' ], dotted_name )
+
+    def test_addRequiredTool_duplicate( self ):
+
+        site = self._initSite()
+        configurator = self._makeOne().__of__( site )
+
+        configurator.addRequiredTool( 'required', 'some.dotted.name' )
+        configurator.addRequiredTool( 'required', 'another.name' )
+
+        info = configurator.getRequiredToolInfo( 'required' )
+        self.assertEqual( info[ 'id' ], 'required' )
+        self.assertEqual( info[ 'class' ], 'another.name' )
+
+    def test_addRequiredTool_but_forbidden( self ):
+
+        site = self._initSite()
+        configurator = self._makeOne().__of__( site )
+
+        configurator.addForbiddenTool( 'forbidden' )
+
+        self.assertRaises( ValueError
+                         , configurator.addRequiredTool
+                         , 'forbidden'
+                         , 'a.name'
+                         )
+
+    def test_generateXML_empty( self ):
+
+        site = self._initSite()
+        configurator = self._makeOne().__of__( site )
+
+        self._compareDOM( configurator.generateXML(), _EMPTY_TOOLSET_XML )
+
+    def test_generateXML_normal( self ):
+
+        site = self._initSite()
+        configurator = self._makeOne().__of__( site )
+
+        configurator.addForbiddenTool( 'doomed' )
+        configurator.addRequiredTool( 'mandatory', 'path.to.one' )
+        configurator.addRequiredTool( 'obligatory', 'path.to.another' )
+
+        configurator.parseXML( _NORMAL_TOOLSET_XML )
+
+    def test_parseXML_empty( self ):
+
+        site = self._initSite()
+        configurator = self._makeOne().__of__( site )
+
+        configurator.parseXML( _EMPTY_TOOLSET_XML )
+
+        self.assertEqual( len( configurator.listForbiddenTools() ), 0 )
+        self.assertEqual( len( configurator.listRequiredTools() ), 0 )
+
+    def test_parseXML_normal( self ):
+
+        site = self._initSite()
+        configurator = self._makeOne().__of__( site )
+
+        configurator.parseXML( _NORMAL_TOOLSET_XML )
+
+        self.assertEqual( len( configurator.listForbiddenTools() ), 1 )
+        self.failUnless( 'doomed' in configurator.listForbiddenTools() )
+
+        self.assertEqual( len( configurator.listRequiredTools() ), 2 )
+
+        self.failUnless( 'mandatory' in configurator.listRequiredTools() )
+        info = configurator.getRequiredToolInfo( 'mandatory' )
+        self.assertEqual( info[ 'class' ], 'path.to.one' )
+
+        self.failUnless( 'obligatory' in configurator.listRequiredTools() )
+        info = configurator.getRequiredToolInfo( 'obligatory' )
+        self.assertEqual( info[ 'class' ], 'path.to.another' )
+
+    def test_parseXML_confused( self ):
+
+        site = self._initSite()
+        configurator = self._makeOne().__of__( site )
+
+        self.assertRaises( ValueError
+                         , configurator.parseXML, _CONFUSED_TOOLSET_XML )
+
+
+_EMPTY_TOOLSET_XML = """\
+<?xml version="1.0"?>
+<tool-setup>
+</tool-setup>
+"""
+
+_NORMAL_TOOLSET_XML = """\
+<?xml version="1.0"?>
+<tool-setup>
+ <forbidden tool_id="doomed" />
+ <required tool_id="mandatory" class="path.to.one" />
+ <required tool_id="obligatory" class="path.to.another" />
+</tool-setup>
+"""
+
+_CONFUSED_TOOLSET_XML = """\
+<?xml version="1.0"?>
+<tool-setup>
+ <forbidden tool_id="confused" />
+ <required tool_id="confused" class="path.to.one" />
+</tool-setup>
+"""
+
+class ProfileRegistryTests( BaseRegistryTests
+                          , ConformsToIProfileRegistry
+                          ):
+
+    def _getTargetClass( self ):
+
+        from Products.GenericSetup.registry import ProfileRegistry
+        return ProfileRegistry
+
+    def test_empty( self ):
+
+        registry = self._makeOne()
+
+        self.assertEqual( len( registry.listProfiles() ), 0 )
+        self.assertEqual( len( registry.listProfiles() ), 0 )
+        self.assertRaises( KeyError, registry.getProfileInfo, 'nonesuch' )
+
+    def test_registerProfile_normal( self ):
+
+        NAME = 'one'
+        TITLE = 'One'
+        DESCRIPTION = 'One profile'
+        PATH = '/path/to/one'
+        PRODUCT = 'TestProduct'
+        PROFILE_TYPE = EXTENSION
+        PROFILE_ID = 'TestProduct:one'
+
+        registry = self._makeOne()
+        registry.registerProfile( NAME
+                                , TITLE
+                                , DESCRIPTION
+                                , PATH
+                                , PRODUCT
+                                , PROFILE_TYPE
+                                )
+
+        self.assertEqual( len( registry.listProfiles() ), 1 )
+        self.assertEqual( len( registry.listProfileInfo() ), 1 )
+
+        info = registry.getProfileInfo( PROFILE_ID )
+
+        self.assertEqual( info[ 'id' ], PROFILE_ID )
+        self.assertEqual( info[ 'title' ], TITLE )
+        self.assertEqual( info[ 'description' ], DESCRIPTION )
+        self.assertEqual( info[ 'path' ], PATH )
+        self.assertEqual( info[ 'product' ], PRODUCT )
+        self.assertEqual( info[ 'type' ], PROFILE_TYPE )
+
+    def test_registerProfile_duplicate( self ):
+
+        PROFILE_ID = 'one'
+        TITLE = 'One'
+        DESCRIPTION = 'One profile'
+        PATH = '/path/to/one'
+
+        registry = self._makeOne()
+        registry.registerProfile( PROFILE_ID, TITLE, DESCRIPTION, PATH )
+        self.assertRaises( KeyError
+                         , registry.registerProfile
+                         , PROFILE_ID, TITLE, DESCRIPTION, PATH )
+
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite( ImportStepRegistryTests ),
+        unittest.makeSuite( ExportStepRegistryTests ),
+        unittest.makeSuite( ToolsetRegistryTests ),
+        unittest.makeSuite( ProfileRegistryTests ),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: CMF/branches/goldegg-phase-1/GenericSetup/tests/test_rolemap.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/tests/test_rolemap.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/tests/test_rolemap.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,787 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" GenericSetup rolemap export / import unit tests
+
+$Id: test_rolemap.py,v 1.1.1.1 2005/08/08 19:38:37 tseaver Exp $
+"""
+
+import unittest
+import Testing
+try:
+    import Zope2
+except ImportError: # BBB: for Zope 2.7
+    import Zope as Zope2
+Zope2.startup()
+
+from OFS.Folder import Folder
+
+from common import BaseRegistryTests
+from common import DummyExportContext
+from common import DummyImportContext
+
+class RolemapConfiguratorTests( BaseRegistryTests ):
+
+    def _getTargetClass( self ):
+
+        from Products.GenericSetup.rolemap import RolemapConfigurator
+        return RolemapConfigurator
+
+    def test_listRoles_normal( self ):
+
+        EXPECTED = [ 'Anonymous', 'Authenticated', 'Manager', 'Owner' ]
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        configurator = self._makeOne( site )
+
+        roles = list( configurator.listRoles() )
+        self.assertEqual( len( roles ), len( EXPECTED ) )
+
+        roles.sort()
+
+        for found, expected in zip( roles, EXPECTED ):
+            self.assertEqual( found, expected )
+
+    def test_listRoles_added( self ):
+
+        EXPECTED = [ 'Anonymous', 'Authenticated', 'Manager', 'Owner', 'ZZZ' ]
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        existing_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
+        existing_roles.append( 'ZZZ' )
+        site.__ac_roles__ = existing_roles
+
+        configurator = self._makeOne( site )
+
+        roles = list( configurator.listRoles() )
+        self.assertEqual( len( roles ), len( EXPECTED ) )
+
+        roles.sort()
+
+        for found, expected in zip( roles, EXPECTED ):
+            self.assertEqual( found, expected )
+
+    def test_listPermissions_nooverrides( self ):
+
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        configurator = self._makeOne( site )
+
+        self.assertEqual( len( configurator.listPermissions() ), 0 )
+
+    def test_listPermissions_nooverrides( self ):
+
+        site = Folder( id='site' ).__of__( self.root )
+        configurator = self._makeOne( site )
+
+        self.assertEqual( len( configurator.listPermissions() ), 0 )
+
+    def test_listPermissions_acquire( self ):
+
+        ACI = 'Access contents information'
+        ROLES = [ 'Manager', 'Owner' ]
+
+        site = Folder( id='site' ).__of__( self.root )
+        site.manage_permission( ACI, ROLES, acquire=1 )
+        configurator = self._makeOne( site )
+
+        self.assertEqual( len( configurator.listPermissions() ), 1 )
+        info = configurator.listPermissions()[ 0 ]
+        self.assertEqual( info[ 'name' ], ACI )
+        self.assertEqual( info[ 'roles' ], ROLES )
+        self.failUnless( info[ 'acquire' ] )
+
+    def test_listPermissions_no_acquire( self ):
+
+        ACI = 'Access contents information'
+        ROLES = [ 'Manager', 'Owner' ]
+
+        site = Folder( id='site' ).__of__( self.root )
+        site.manage_permission( ACI, ROLES )
+        configurator = self._makeOne( site )
+
+        self.assertEqual( len( configurator.listPermissions() ), 1 )
+        info = configurator.listPermissions()[ 0 ]
+        self.assertEqual( info[ 'name' ], ACI )
+        self.assertEqual( info[ 'roles' ], ROLES )
+        self.failIf( info[ 'acquire' ] )
+
+    def test_generateXML_empty( self ):
+
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        configurator = self._makeOne( site ).__of__( site )
+
+        self._compareDOM( configurator.generateXML(), _EMPTY_EXPORT )
+
+    def test_generateXML_added_role( self ):
+
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        existing_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
+        existing_roles.append( 'ZZZ' )
+        site.__ac_roles__ = existing_roles
+        configurator = self._makeOne( site ).__of__( site )
+
+        self._compareDOM( configurator.generateXML(), _ADDED_ROLE_EXPORT )
+
+    def test_generateEXML_acquired_perm( self ):
+
+        ACI = 'Access contents information'
+        ROLES = [ 'Manager', 'Owner' ]
+
+        site = Folder( id='site' ).__of__( self.root )
+        site.manage_permission( ACI, ROLES, acquire=1 )
+        configurator = self._makeOne( site ).__of__( site )
+
+        self._compareDOM( configurator.generateXML(), _ACQUIRED_EXPORT )
+
+    def test_generateEXML_unacquired_perm( self ):
+
+        ACI = 'Access contents information'
+        ROLES = [ 'Manager', 'Owner', 'ZZZ' ]
+
+        site = Folder( id='site' ).__of__( self.root )
+        existing_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
+        existing_roles.append( 'ZZZ' )
+        site.__ac_roles__ = existing_roles
+        site.manage_permission( ACI, ROLES )
+        configurator = self._makeOne( site ).__of__( site )
+
+        self._compareDOM( configurator.generateXML(), _COMBINED_EXPORT )
+
+    def test_generateEXML_unacquired_perm_added_role( self ):
+
+        ACI = 'Access contents information'
+        ROLES = [ 'Manager', 'Owner' ]
+
+        site = Folder( id='site' ).__of__( self.root )
+        site.manage_permission( ACI, ROLES )
+        configurator = self._makeOne( site ).__of__( site )
+
+        self._compareDOM( configurator.generateXML(), _UNACQUIRED_EXPORT )
+
+    def test_parseXML_empty( self ):
+
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        existing_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
+        configurator = self._makeOne( site )
+
+        rolemap_info = configurator.parseXML( _EMPTY_EXPORT )
+
+        self.assertEqual( len( rolemap_info[ 'roles' ] ), 4 )
+        self.assertEqual( len( rolemap_info[ 'permissions' ] ), 0 )
+
+    def test_parseXML_added_role( self ):
+
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        configurator = self._makeOne( site )
+
+        rolemap_info = configurator.parseXML( _ADDED_ROLE_EXPORT )
+        roles = rolemap_info[ 'roles' ]
+
+        self.assertEqual( len( roles ), 5 )
+        self.failUnless( 'Anonymous' in roles )
+        self.failUnless( 'Authenticated' in roles )
+        self.failUnless( 'Manager' in roles )
+        self.failUnless( 'Owner' in roles )
+        self.failUnless( 'ZZZ' in roles )
+
+    def test_parseXML_acquired_permission( self ):
+
+        ACI = 'Access contents information'
+
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        configurator = self._makeOne( site )
+
+        rolemap_info = configurator.parseXML( _ACQUIRED_EXPORT )
+
+        self.assertEqual( len( rolemap_info[ 'permissions' ] ), 1 )
+        permission = rolemap_info[ 'permissions' ][ 0 ]
+
+        self.assertEqual( permission[ 'name' ], ACI )
+        self.failUnless( permission[ 'acquire' ] )
+
+        p_roles = permission[ 'roles' ]
+        self.assertEqual( len( p_roles ), 2 )
+        self.failUnless( 'Manager' in p_roles )
+        self.failUnless( 'Owner' in p_roles )
+
+    def test_parseXML_unacquired_permission( self ):
+
+        ACI = 'Access contents information'
+
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        configurator = self._makeOne( site )
+
+        rolemap_info = configurator.parseXML( _UNACQUIRED_EXPORT )
+
+        self.assertEqual( len( rolemap_info[ 'permissions' ] ), 1 )
+        permission = rolemap_info[ 'permissions' ][ 0 ]
+
+        self.assertEqual( permission[ 'name' ], ACI )
+        self.failIf( permission[ 'acquire' ] )
+
+        p_roles = permission[ 'roles' ]
+        self.assertEqual( len( p_roles ), 2 )
+        self.failUnless( 'Manager' in p_roles )
+        self.failUnless( 'Owner' in p_roles )
+
+    def test_parseXML_unacquired_permission_added_role( self ):
+
+        ACI = 'Access contents information'
+
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        configurator = self._makeOne( site )
+
+        rolemap_info = configurator.parseXML( _COMBINED_EXPORT )
+        roles = rolemap_info[ 'roles' ]
+
+        self.assertEqual( len( roles ), 5 )
+        self.failUnless( 'Anonymous' in roles )
+        self.failUnless( 'Authenticated' in roles )
+        self.failUnless( 'Manager' in roles )
+        self.failUnless( 'Owner' in roles )
+        self.failUnless( 'ZZZ' in roles )
+
+        self.assertEqual( len( rolemap_info[ 'permissions' ] ), 1 )
+        permission = rolemap_info[ 'permissions' ][ 0 ]
+
+        self.assertEqual( permission[ 'name' ], ACI )
+        self.failIf( permission[ 'acquire' ] )
+
+        p_roles = permission[ 'roles' ]
+        self.assertEqual( len( p_roles ), 3 )
+        self.failUnless( 'Manager' in p_roles )
+        self.failUnless( 'Owner' in p_roles )
+        self.failUnless( 'ZZZ' in p_roles )
+
+
+
+_EMPTY_EXPORT = """\
+<?xml version="1.0"?>
+<rolemap>
+  <roles>
+    <role name="Anonymous"/>
+    <role name="Authenticated"/>
+    <role name="Manager"/>
+    <role name="Owner"/>
+  </roles>
+  <permissions>
+  </permissions>
+</rolemap>
+"""
+
+_ADDED_ROLE_EXPORT = """\
+<?xml version="1.0"?>
+<rolemap>
+  <roles>
+    <role name="Anonymous"/>
+    <role name="Authenticated"/>
+    <role name="Manager"/>
+    <role name="Owner"/>
+    <role name="ZZZ"/>
+  </roles>
+  <permissions>
+  </permissions>
+</rolemap>
+"""
+
+_ACQUIRED_EXPORT = """\
+<?xml version="1.0"?>
+<rolemap>
+  <roles>
+    <role name="Anonymous"/>
+    <role name="Authenticated"/>
+    <role name="Manager"/>
+    <role name="Owner"/>
+  </roles>
+  <permissions>
+    <permission name="Access contents information"
+                acquire="True">
+      <role name="Manager"/>
+      <role name="Owner"/>
+    </permission>
+  </permissions>
+</rolemap>
+"""
+
+_UNACQUIRED_EXPORT = """\
+<?xml version="1.0"?>
+<rolemap>
+  <roles>
+    <role name="Anonymous"/>
+    <role name="Authenticated"/>
+    <role name="Manager"/>
+    <role name="Owner"/>
+  </roles>
+  <permissions>
+    <permission name="Access contents information"
+                acquire="False">
+      <role name="Manager"/>
+      <role name="Owner"/>
+    </permission>
+  </permissions>
+</rolemap>
+"""
+
+_COMBINED_EXPORT = """\
+<?xml version="1.0"?>
+<rolemap>
+  <roles>
+    <role name="Anonymous"/>
+    <role name="Authenticated"/>
+    <role name="Manager"/>
+    <role name="Owner"/>
+    <role name="ZZZ"/>
+  </roles>
+  <permissions>
+    <permission name="Access contents information"
+                acquire="False">
+      <role name="Manager"/>
+      <role name="Owner"/>
+      <role name="ZZZ"/>
+    </permission>
+  </permissions>
+</rolemap>
+"""
+
+class Test_exportRolemap( BaseRegistryTests ):
+
+    def test_unchanged( self ):
+
+        self.root.site = Folder( 'site' )
+        site = self.root.site
+
+        context = DummyExportContext( site )
+
+        from Products.GenericSetup.rolemap import exportRolemap
+        exportRolemap( context )
+
+        self.assertEqual( len( context._wrote ), 1 )
+        filename, text, content_type = context._wrote[ 0 ]
+        self.assertEqual( filename, 'rolemap.xml' )
+        self._compareDOM( text, _EMPTY_EXPORT )
+        self.assertEqual( content_type, 'text/xml' )
+
+    def test_added_role( self ):
+
+        self.root.site = Folder( 'site' )
+        site = self.root.site
+        existing_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
+        existing_roles.append( 'ZZZ' )
+        site.__ac_roles__ = existing_roles
+
+        context = DummyExportContext( site )
+
+        from Products.GenericSetup.rolemap import exportRolemap
+        exportRolemap( context )
+
+        self.assertEqual( len( context._wrote ), 1 )
+        filename, text, content_type = context._wrote[ 0 ]
+        self.assertEqual( filename, 'rolemap.xml' )
+        self._compareDOM( text, _ADDED_ROLE_EXPORT )
+        self.assertEqual( content_type, 'text/xml' )
+
+
+    def test_acquired_perm( self ):
+
+        ACI = 'Access contents information'
+        ROLES = [ 'Manager', 'Owner' ]
+
+        self.root.site = Folder( 'site' )
+        site = self.root.site
+        site.manage_permission( ACI, ROLES, acquire=1 )
+
+        context = DummyExportContext( site )
+
+        from Products.GenericSetup.rolemap import exportRolemap
+        exportRolemap( context )
+
+        self.assertEqual( len( context._wrote ), 1 )
+        filename, text, content_type = context._wrote[ 0 ]
+        self.assertEqual( filename, 'rolemap.xml' )
+        self._compareDOM( text, _ACQUIRED_EXPORT )
+        self.assertEqual( content_type, 'text/xml' )
+
+    def test_unacquired_perm( self ):
+
+        ACI = 'Access contents information'
+        ROLES = [ 'Manager', 'Owner', 'ZZZ' ]
+
+        self.root.site = Folder( 'site' )
+        site = self.root.site
+        existing_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
+        existing_roles.append( 'ZZZ' )
+        site.__ac_roles__ = existing_roles
+        site.manage_permission( ACI, ROLES )
+
+        context = DummyExportContext( site )
+
+        from Products.GenericSetup.rolemap import exportRolemap
+        exportRolemap( context )
+
+        self.assertEqual( len( context._wrote ), 1 )
+        filename, text, content_type = context._wrote[ 0 ]
+        self.assertEqual( filename, 'rolemap.xml' )
+        self._compareDOM( text, _COMBINED_EXPORT )
+        self.assertEqual( content_type, 'text/xml' )
+
+    def test_unacquired_perm_added_role( self ):
+
+        ACI = 'Access contents information'
+        ROLES = [ 'Manager', 'Owner' ]
+
+        self.root.site = Folder( 'site' )
+        site = self.root.site
+        site.manage_permission( ACI, ROLES )
+
+        context = DummyExportContext( site )
+
+        from Products.GenericSetup.rolemap import exportRolemap
+        exportRolemap( context )
+
+        self.assertEqual( len( context._wrote ), 1 )
+        filename, text, content_type = context._wrote[ 0 ]
+        self.assertEqual( filename, 'rolemap.xml' )
+        self._compareDOM( text, _UNACQUIRED_EXPORT )
+        self.assertEqual( content_type, 'text/xml' )
+
+class Test_importRolemap( BaseRegistryTests ):
+
+    def test_empty_default_purge( self ):
+
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        original_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
+        modified_roles = original_roles[:]
+        modified_roles.append( 'ZZZ' )
+        site.__ac_roles__ = modified_roles
+
+        context = DummyImportContext( site )
+        context._files[ 'rolemap.xml' ] = _EMPTY_EXPORT
+
+        from Products.GenericSetup.rolemap import importRolemap
+        importRolemap( context )
+
+        new_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
+
+        original_roles.sort()
+        new_roles.sort()
+
+        self.assertEqual( original_roles, new_roles )
+
+    def test_empty_explicit_purge( self ):
+
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        original_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
+        modified_roles = original_roles[:]
+        modified_roles.append( 'ZZZ' )
+        site.__ac_roles__ = modified_roles
+
+        context = DummyImportContext( site, True )
+        context._files[ 'rolemap.xml' ] = _EMPTY_EXPORT
+
+        from Products.GenericSetup.rolemap import importRolemap
+        importRolemap( context )
+
+        new_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
+
+        original_roles.sort()
+        new_roles.sort()
+
+        self.assertEqual( original_roles, new_roles )
+
+    def test_empty_skip_purge( self ):
+
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        original_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
+        modified_roles = original_roles[:]
+        modified_roles.append( 'ZZZ' )
+        site.__ac_roles__ = modified_roles
+
+        context = DummyImportContext( site, False )
+        context._files[ 'rolemap.xml' ] = _EMPTY_EXPORT
+
+        from Products.GenericSetup.rolemap import importRolemap
+        importRolemap( context )
+
+        new_roles = list( getattr( site, '__ac_roles__', [] ) )[:]
+
+        modified_roles.sort()
+        new_roles.sort()
+
+        self.assertEqual( modified_roles, new_roles )
+
+    def test_acquired_permission_explicit_purge( self ):
+
+        ACI = 'Access contents information'
+        VIEW = 'View'
+
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        site.manage_permission( ACI, () )
+        site.manage_permission( VIEW, () )
+
+        existing_allowed = [ x[ 'name' ]
+                                for x in site.rolesOfPermission( ACI )
+                                if x[ 'selected' ] ]
+
+        self.assertEqual( existing_allowed, [] )
+
+        self.failIf( site.acquiredRolesAreUsedBy( ACI ) )
+        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
+
+        context = DummyImportContext( site, True )
+        context._files[ 'rolemap.xml' ] = _ACQUIRED_EXPORT
+
+        from Products.GenericSetup.rolemap import importRolemap
+        importRolemap( context )
+
+        new_allowed = [ x[ 'name' ]
+                           for x in site.rolesOfPermission( ACI )
+                           if x[ 'selected' ] ]
+
+        self.assertEqual( new_allowed, [ 'Manager', 'Owner' ] )
+
+        # ACI is overwritten by XML, but VIEW was purged
+        self.failUnless( site.acquiredRolesAreUsedBy( ACI ) )
+        self.failUnless( site.acquiredRolesAreUsedBy( VIEW ) )
+
+    def test_acquired_permission_no_purge( self ):
+
+        ACI = 'Access contents information'
+        VIEW = 'View'
+
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        site.manage_permission( ACI, () )
+        site.manage_permission( VIEW, () )
+
+        existing_allowed = [ x[ 'name' ]
+                                for x in site.rolesOfPermission( ACI )
+                                if x[ 'selected' ] ]
+
+        self.assertEqual( existing_allowed, [] )
+
+        self.failIf( site.acquiredRolesAreUsedBy( ACI ) )
+
+        context = DummyImportContext( site, False )
+        context._files[ 'rolemap.xml' ] = _ACQUIRED_EXPORT
+
+        from Products.GenericSetup.rolemap import importRolemap
+        importRolemap( context )
+
+        new_allowed = [ x[ 'name' ]
+                           for x in site.rolesOfPermission( ACI )
+                           if x[ 'selected' ] ]
+
+        self.assertEqual( new_allowed, [ 'Manager', 'Owner' ] )
+
+        # ACI is overwritten by XML, but VIEW is not
+        self.failUnless( site.acquiredRolesAreUsedBy( ACI ) )
+        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
+
+    def test_unacquired_permission_explicit_purge( self ):
+
+        ACI = 'Access contents information'
+        VIEW = 'View'
+
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        site.manage_permission( VIEW, () )
+
+        existing_allowed = [ x[ 'name' ]
+                                for x in site.rolesOfPermission( ACI )
+                                if x[ 'selected' ] ]
+
+        self.assertEqual( existing_allowed, [ 'Manager' ] )
+
+        self.failUnless( site.acquiredRolesAreUsedBy( ACI ) )
+        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
+
+        context = DummyImportContext( site, True )
+        context._files[ 'rolemap.xml' ] = _UNACQUIRED_EXPORT
+
+        from Products.GenericSetup.rolemap import importRolemap
+        importRolemap( context )
+
+        new_allowed = [ x[ 'name' ]
+                           for x in site.rolesOfPermission( ACI )
+                           if x[ 'selected' ] ]
+
+        self.assertEqual( new_allowed, [ 'Manager', 'Owner' ] )
+
+        self.failIf( site.acquiredRolesAreUsedBy( ACI ) )
+        self.failUnless( site.acquiredRolesAreUsedBy( VIEW ) )
+
+    def test_unacquired_permission_skip_purge( self ):
+
+        ACI = 'Access contents information'
+        VIEW = 'View'
+
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        site.manage_permission( VIEW, () )
+
+        existing_allowed = [ x[ 'name' ]
+                                for x in site.rolesOfPermission( ACI )
+                                if x[ 'selected' ] ]
+
+        self.assertEqual( existing_allowed, [ 'Manager' ] )
+
+        self.failUnless( site.acquiredRolesAreUsedBy( ACI ) )
+        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
+
+        context = DummyImportContext( site, False )
+        context._files[ 'rolemap.xml' ] = _UNACQUIRED_EXPORT
+
+        from Products.GenericSetup.rolemap import importRolemap
+        importRolemap( context )
+
+        new_allowed = [ x[ 'name' ]
+                           for x in site.rolesOfPermission( ACI )
+                           if x[ 'selected' ] ]
+
+        self.assertEqual( new_allowed, [ 'Manager', 'Owner' ] )
+
+        self.failIf( site.acquiredRolesAreUsedBy( ACI ) )
+        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
+
+    def test_unacquired_permission_added_role_explicit_purge( self ):
+
+        ACI = 'Access contents information'
+        VIEW = 'View'
+
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        site.manage_permission( VIEW, () )
+
+        existing_allowed = [ x[ 'name' ]
+                                for x in site.rolesOfPermission( ACI )
+                                if x[ 'selected' ] ]
+
+        self.assertEqual( existing_allowed, [ 'Manager' ] )
+
+        self.failUnless( site.acquiredRolesAreUsedBy( ACI ) )
+        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
+
+        self.failIf( site._has_user_defined_role( 'ZZZ' ) )
+
+        context = DummyImportContext( site, True )
+        context._files[ 'rolemap.xml' ] = _COMBINED_EXPORT
+
+        from Products.GenericSetup.rolemap import importRolemap
+        importRolemap( context )
+
+        self.failUnless( site._has_user_defined_role( 'ZZZ' ) )
+
+        new_allowed = [ x[ 'name' ]
+                           for x in site.rolesOfPermission( ACI )
+                           if x[ 'selected' ] ]
+
+        self.assertEqual( new_allowed, [ 'Manager', 'Owner', 'ZZZ' ] )
+
+        self.failIf( site.acquiredRolesAreUsedBy( ACI ) )
+        self.failUnless( site.acquiredRolesAreUsedBy( VIEW ) )
+
+    def test_unacquired_permission_added_role_skip_purge( self ):
+
+        ACI = 'Access contents information'
+        VIEW = 'View'
+
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        site.manage_permission( VIEW, () )
+
+        existing_allowed = [ x[ 'name' ]
+                                for x in site.rolesOfPermission( ACI )
+                                if x[ 'selected' ] ]
+
+        self.assertEqual( existing_allowed, [ 'Manager' ] )
+
+        self.failUnless( site.acquiredRolesAreUsedBy( ACI ) )
+        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
+
+        self.failIf( site._has_user_defined_role( 'ZZZ' ) )
+
+        context = DummyImportContext( site, False )
+        context._files[ 'rolemap.xml' ] = _COMBINED_EXPORT
+
+        from Products.GenericSetup.rolemap import importRolemap
+        importRolemap( context )
+
+        self.failUnless( site._has_user_defined_role( 'ZZZ' ) )
+
+        new_allowed = [ x[ 'name' ]
+                           for x in site.rolesOfPermission( ACI )
+                           if x[ 'selected' ] ]
+
+        self.assertEqual( new_allowed, [ 'Manager', 'Owner', 'ZZZ' ] )
+
+        self.failIf( site.acquiredRolesAreUsedBy( ACI ) )
+        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
+
+    def test_unacquired_permission_added_role_skip_purge_encode_ascii( self ):
+
+        ACI = 'Access contents information'
+        VIEW = 'View'
+
+        self.root.site = Folder( id='site' )
+        site = self.root.site
+        site.manage_permission( VIEW, () )
+
+        existing_allowed = [ x[ 'name' ]
+                                for x in site.rolesOfPermission( ACI )
+                                if x[ 'selected' ] ]
+
+        self.assertEqual( existing_allowed, [ 'Manager' ] )
+
+        self.failUnless( site.acquiredRolesAreUsedBy( ACI ) )
+        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
+
+        self.failIf( site._has_user_defined_role( 'ZZZ' ) )
+
+        context = DummyImportContext( site, False, encoding='ascii' )
+        context._files[ 'rolemap.xml' ] = _COMBINED_EXPORT
+
+        from Products.GenericSetup.rolemap import importRolemap
+        importRolemap( context )
+
+        self.failUnless( site._has_user_defined_role( 'ZZZ' ) )
+
+        new_allowed = [ x[ 'name' ]
+                           for x in site.rolesOfPermission( ACI )
+                           if x[ 'selected' ] ]
+
+        self.assertEqual( new_allowed, [ 'Manager', 'Owner', 'ZZZ' ] )
+
+        self.failIf( site.acquiredRolesAreUsedBy( ACI ) )
+        self.failIf( site.acquiredRolesAreUsedBy( VIEW ) )
+
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite( RolemapConfiguratorTests ),
+        unittest.makeSuite( Test_exportRolemap ),
+        unittest.makeSuite( Test_importRolemap ),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: CMF/branches/goldegg-phase-1/GenericSetup/tests/test_tool.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/tests/test_tool.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/tests/test_tool.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,921 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" Unit tests for GenericSetup tool.
+
+$Id: test_tool.py,v 1.1.1.1 2005/08/08 19:38:37 tseaver Exp $
+"""
+
+import unittest
+import Testing
+try:
+    import Zope2
+except ImportError: # BBB: for Zope 2.7
+    import Zope as Zope2
+Zope2.startup()
+
+from StringIO import StringIO
+
+from Acquisition import aq_base
+from OFS.Folder import Folder
+
+from Products.GenericSetup import profile_registry
+
+from common import DOMComparator
+from common import DummyExportContext
+from common import DummyImportContext
+from common import FilesystemTestBase
+
+#from Products.CMFCore.tests.base.testcase import SecurityRequestTest
+from Testing.ZopeTestCase import ZopeTestCase
+from common import SecurityRequestTest
+from common import TarballTester
+from conformance import ConformsToISetupTool
+
+
+class SetupToolTests( FilesystemTestBase
+                    , TarballTester
+                    , ConformsToISetupTool
+                    ):
+
+    _PROFILE_PATH = '/tmp/STT_test'
+
+    def setUp( self ):
+
+        FilesystemTestBase.setUp( self )
+        self._profile_registry_info = profile_registry._profile_info
+        self._profile_registry_ids = profile_registry._profile_ids
+        profile_registry.clear()
+
+    def tearDown( self ):
+
+        profile_registry._profile_info = self._profile_registry_info
+        profile_registry._profile_ids = self._profile_registry_ids
+        FilesystemTestBase.tearDown( self )
+
+    def _getTargetClass( self ):
+
+        from Products.GenericSetup.tool import SetupTool
+        return SetupTool
+
+    def _makeOne( self, *args, **kw ):
+
+        return self._getTargetClass()( *args, **kw )
+
+    def _makeSite( self, title="Don't care" ):
+
+        site = Folder()
+        site._setId( 'site' )
+        site.title = title
+
+        self.root._setObject( 'site', site )
+        return self.root._getOb( 'site' )
+
+    def test_empty( self ):
+
+        tool = self._makeOne()
+
+        self.assertEqual( tool.getImportContextID(), '' )
+
+        import_registry = tool.getImportStepRegistry()
+        self.assertEqual( len( import_registry.listSteps() ), 0 )
+
+        export_registry = tool.getExportStepRegistry()
+        export_steps = export_registry.listSteps()
+        self.assertEqual( len( export_steps ), 1 )
+        self.assertEqual( export_steps[ 0 ], 'step_registries' )
+
+        toolset_registry = tool.getToolsetRegistry()
+        self.assertEqual( len( toolset_registry.listForbiddenTools() ), 0 )
+        self.assertEqual( len( toolset_registry.listRequiredTools() ), 0 )
+
+    def test_getImportContextID( self ):
+
+        from Products.GenericSetup.tool import IMPORT_STEPS_XML
+        from Products.GenericSetup.tool import EXPORT_STEPS_XML
+        from Products.GenericSetup.tool import TOOLSET_XML
+        from test_registry import _EMPTY_IMPORT_XML
+        from test_registry import _EMPTY_EXPORT_XML
+        from test_registry import _EMPTY_TOOLSET_XML
+        from common import _makeTestFile
+
+        tool = self._makeOne()
+
+        _makeTestFile( IMPORT_STEPS_XML
+                     , self._PROFILE_PATH
+                     , _EMPTY_IMPORT_XML
+                     )
+
+        _makeTestFile( EXPORT_STEPS_XML
+                     , self._PROFILE_PATH
+                     , _EMPTY_EXPORT_XML
+                     )
+
+        _makeTestFile( TOOLSET_XML
+                     , self._PROFILE_PATH
+                     , _EMPTY_TOOLSET_XML
+                     )
+
+        profile_registry.registerProfile('foo', 'Foo', '', self._PROFILE_PATH)
+        tool.setImportContext('profile-other:foo')
+
+        self.assertEqual( tool.getImportContextID(), 'profile-other:foo' )
+
+    def test_setImportContext_invalid( self ):
+
+        tool = self._makeOne()
+
+        self.assertRaises( KeyError
+                         , tool.setImportContext
+                         , 'profile-foo'
+                         )
+
+    def test_setImportContext( self ):
+
+        from Products.GenericSetup.tool import IMPORT_STEPS_XML
+        from Products.GenericSetup.tool import EXPORT_STEPS_XML
+        from Products.GenericSetup.tool import TOOLSET_XML
+        from test_registry import _SINGLE_IMPORT_XML
+        from test_registry import _SINGLE_EXPORT_XML
+        from test_registry import _NORMAL_TOOLSET_XML
+        from test_registry import ONE_FUNC
+        from common import _makeTestFile
+
+        tool = self._makeOne()
+        tool.getExportStepRegistry().clear()
+
+        _makeTestFile( IMPORT_STEPS_XML
+                     , self._PROFILE_PATH
+                     , _SINGLE_IMPORT_XML
+                     )
+
+        _makeTestFile( EXPORT_STEPS_XML
+                     , self._PROFILE_PATH
+                     , _SINGLE_EXPORT_XML
+                     )
+
+        _makeTestFile( TOOLSET_XML
+                     , self._PROFILE_PATH
+                     , _NORMAL_TOOLSET_XML
+                     )
+
+        profile_registry.registerProfile('foo', 'Foo', '', self._PROFILE_PATH)
+        tool.setImportContext('profile-other:foo')
+
+        self.assertEqual( tool.getImportContextID(), 'profile-other:foo' )
+
+        import_registry = tool.getImportStepRegistry()
+        self.assertEqual( len( import_registry.listSteps() ), 1 )
+        self.failUnless( 'one' in import_registry.listSteps() )
+        info = import_registry.getStepMetadata( 'one' )
+        self.assertEqual( info[ 'id' ], 'one' )
+        self.assertEqual( info[ 'title' ], 'One Step' )
+        self.assertEqual( info[ 'version' ], '1' )
+        self.failUnless( 'One small step' in info[ 'description' ] )
+        self.assertEqual( info[ 'handler' ]
+                        , 'Products.GenericSetup.tests.test_registry.ONE_FUNC' )
+
+        self.assertEqual( import_registry.getStep( 'one' ), ONE_FUNC )
+
+        export_registry = tool.getExportStepRegistry()
+        self.assertEqual( len( export_registry.listSteps() ), 1 )
+        self.failUnless( 'one' in import_registry.listSteps() )
+        info = export_registry.getStepMetadata( 'one' )
+        self.assertEqual( info[ 'id' ], 'one' )
+        self.assertEqual( info[ 'title' ], 'One Step' )
+        self.failUnless( 'One small step' in info[ 'description' ] )
+        self.assertEqual( info[ 'handler' ]
+                        , 'Products.GenericSetup.tests.test_registry.ONE_FUNC' )
+
+        self.assertEqual( export_registry.getStep( 'one' ), ONE_FUNC )
+
+        toolset = tool.getToolsetRegistry()
+        self.assertEqual( len( toolset.listForbiddenTools() ), 1 )
+        self.failUnless( 'doomed' in toolset.listForbiddenTools() )
+        self.assertEqual( len( toolset.listRequiredTools() ), 2 )
+        self.failUnless( 'mandatory' in toolset.listRequiredTools() )
+        info = toolset.getRequiredToolInfo( 'mandatory' )
+        self.assertEqual( info[ 'class' ], 'path.to.one' )
+        self.failUnless( 'obligatory' in toolset.listRequiredTools() )
+        info = toolset.getRequiredToolInfo( 'obligatory' )
+        self.assertEqual( info[ 'class' ], 'path.to.another' )
+
+    def test_runImportStep_nonesuch( self ):
+
+        site = self._makeSite()
+
+        tool = self._makeOne().__of__( site )
+
+        self.assertRaises( ValueError, tool.runImportStep, 'nonesuch' )
+
+    def test_runImportStep_simple( self ):
+
+        TITLE = 'original title'
+        site = self._makeSite( TITLE )
+
+        tool = self._makeOne().__of__( site )
+
+        registry = tool.getImportStepRegistry()
+        registry.registerStep( 'simple', '1', _uppercaseSiteTitle )
+
+        result = tool.runImportStep( 'simple' )
+
+        self.assertEqual( len( result[ 'steps' ] ), 1 )
+
+        self.assertEqual( result[ 'steps' ][ 0 ], 'simple' )
+        self.assertEqual( result[ 'messages' ][ 'simple' ]
+                        , 'Uppercased title' )
+
+        self.assertEqual( site.title, TITLE.upper() )
+
+    def test_runImportStep_dependencies( self ):
+
+        TITLE = 'original title'
+        site = self._makeSite( TITLE )
+
+        tool = self._makeOne().__of__( site )
+
+        registry = tool.getImportStepRegistry()
+        registry.registerStep( 'dependable', '1', _underscoreSiteTitle )
+        registry.registerStep( 'dependent', '1'
+                             , _uppercaseSiteTitle, ( 'dependable', ) )
+
+        result = tool.runImportStep( 'dependent' )
+
+        self.assertEqual( len( result[ 'steps' ] ), 2 )
+
+        self.assertEqual( result[ 'steps' ][ 0 ], 'dependable' )
+        self.assertEqual( result[ 'messages' ][ 'dependable' ]
+                        , 'Underscored title' )
+
+        self.assertEqual( result[ 'steps' ][ 1 ], 'dependent' )
+        self.assertEqual( result[ 'messages' ][ 'dependent' ]
+                        , 'Uppercased title' )
+        self.assertEqual( site.title, TITLE.replace( ' ', '_' ).upper() )
+
+    def test_runImportStep_skip_dependencies( self ):
+
+        TITLE = 'original title'
+        site = self._makeSite( TITLE )
+
+        tool = self._makeOne().__of__( site )
+
+        registry = tool.getImportStepRegistry()
+        registry.registerStep( 'dependable', '1', _underscoreSiteTitle )
+        registry.registerStep( 'dependent', '1'
+                             , _uppercaseSiteTitle, ( 'dependable', ) )
+
+        result = tool.runImportStep( 'dependent', run_dependencies=False )
+
+        self.assertEqual( len( result[ 'steps' ] ), 1 )
+
+        self.assertEqual( result[ 'steps' ][ 0 ], 'dependent' )
+        self.assertEqual( result[ 'messages' ][ 'dependent' ]
+                        , 'Uppercased title' )
+
+        self.assertEqual( site.title, TITLE.upper() )
+
+    def test_runImportStep_default_purge( self ):
+
+        site = self._makeSite()
+
+        tool = self._makeOne().__of__( site )
+        registry = tool.getImportStepRegistry()
+        registry.registerStep( 'purging', '1', _purgeIfRequired )
+
+        result = tool.runImportStep( 'purging' )
+
+        self.assertEqual( len( result[ 'steps' ] ), 1 )
+        self.assertEqual( result[ 'steps' ][ 0 ], 'purging' )
+        self.assertEqual( result[ 'messages' ][ 'purging' ], 'Purged' )
+        self.failUnless( site.purged )
+
+    def test_runImportStep_explicit_purge( self ):
+
+        site = self._makeSite()
+
+        tool = self._makeOne().__of__( site )
+        registry = tool.getImportStepRegistry()
+        registry.registerStep( 'purging', '1', _purgeIfRequired )
+
+        result = tool.runImportStep( 'purging', purge_old=True )
+
+        self.assertEqual( len( result[ 'steps' ] ), 1 )
+        self.assertEqual( result[ 'steps' ][ 0 ], 'purging' )
+        self.assertEqual( result[ 'messages' ][ 'purging' ], 'Purged' )
+        self.failUnless( site.purged )
+
+    def test_runImportStep_skip_purge( self ):
+
+        site = self._makeSite()
+
+        tool = self._makeOne().__of__( site )
+        registry = tool.getImportStepRegistry()
+        registry.registerStep( 'purging', '1', _purgeIfRequired )
+
+        result = tool.runImportStep( 'purging', purge_old=False )
+
+        self.assertEqual( len( result[ 'steps' ] ), 1 )
+        self.assertEqual( result[ 'steps' ][ 0 ], 'purging' )
+        self.assertEqual( result[ 'messages' ][ 'purging' ], 'Unpurged' )
+        self.failIf( site.purged )
+
+    def test_runImportStep_consistent_context( self ):
+
+        site = self._makeSite()
+
+        tool = self._makeOne().__of__( site )
+
+        registry = tool.getImportStepRegistry()
+        registry.registerStep( 'purging', '1', _purgeIfRequired )
+        registry.registerStep( 'dependent', '1'
+                             , _uppercaseSiteTitle, ( 'purging', ) )
+
+        result = tool.runImportStep( 'dependent', purge_old=False )
+        self.failIf( site.purged )
+
+    def test_runAllImportSteps_empty( self ):
+
+        site = self._makeSite()
+        tool = self._makeOne().__of__( site )
+
+        result = tool.runAllImportSteps()
+
+        self.assertEqual( len( result[ 'steps' ] ), 0 )
+
+    def test_runAllImportSteps_sorted_default_purge( self ):
+
+        TITLE = 'original title'
+        site = self._makeSite( TITLE )
+        tool = self._makeOne().__of__( site )
+
+        registry = tool.getImportStepRegistry()
+        registry.registerStep( 'dependable', '1'
+                             , _underscoreSiteTitle, ( 'purging', ) )
+        registry.registerStep( 'dependent', '1'
+                             , _uppercaseSiteTitle, ( 'dependable', ) )
+        registry.registerStep( 'purging', '1'
+                             , _purgeIfRequired )
+
+        result = tool.runAllImportSteps()
+
+        self.assertEqual( len( result[ 'steps' ] ), 3 )
+
+        self.assertEqual( result[ 'steps' ][ 0 ], 'purging' )
+        self.assertEqual( result[ 'messages' ][ 'purging' ]
+                        , 'Purged' )
+
+        self.assertEqual( result[ 'steps' ][ 1 ], 'dependable' )
+        self.assertEqual( result[ 'messages' ][ 'dependable' ]
+                        , 'Underscored title' )
+
+        self.assertEqual( result[ 'steps' ][ 2 ], 'dependent' )
+        self.assertEqual( result[ 'messages' ][ 'dependent' ]
+                        , 'Uppercased title' )
+
+        self.assertEqual( site.title, TITLE.replace( ' ', '_' ).upper() )
+        self.failUnless( site.purged )
+
+    def test_runAllImportSteps_sorted_explicit_purge( self ):
+
+        site = self._makeSite()
+        tool = self._makeOne().__of__( site )
+
+        registry = tool.getImportStepRegistry()
+        registry.registerStep( 'dependable', '1'
+                             , _underscoreSiteTitle, ( 'purging', ) )
+        registry.registerStep( 'dependent', '1'
+                             , _uppercaseSiteTitle, ( 'dependable', ) )
+        registry.registerStep( 'purging', '1'
+                             , _purgeIfRequired )
+
+        result = tool.runAllImportSteps( purge_old=True )
+
+        self.assertEqual( len( result[ 'steps' ] ), 3 )
+
+        self.assertEqual( result[ 'steps' ][ 0 ], 'purging' )
+        self.assertEqual( result[ 'messages' ][ 'purging' ]
+                        , 'Purged' )
+
+        self.assertEqual( result[ 'steps' ][ 1 ], 'dependable' )
+        self.assertEqual( result[ 'steps' ][ 2 ], 'dependent' )
+        self.failUnless( site.purged )
+
+    def test_runAllImportSteps_sorted_skip_purge( self ):
+
+        site = self._makeSite()
+        tool = self._makeOne().__of__( site )
+
+        registry = tool.getImportStepRegistry()
+        registry.registerStep( 'dependable', '1'
+                             , _underscoreSiteTitle, ( 'purging', ) )
+        registry.registerStep( 'dependent', '1'
+                             , _uppercaseSiteTitle, ( 'dependable', ) )
+        registry.registerStep( 'purging', '1'
+                             , _purgeIfRequired )
+
+        result = tool.runAllImportSteps( purge_old=False )
+
+        self.assertEqual( len( result[ 'steps' ] ), 3 )
+
+        self.assertEqual( result[ 'steps' ][ 0 ], 'purging' )
+        self.assertEqual( result[ 'messages' ][ 'purging' ]
+                        , 'Unpurged' )
+
+        self.assertEqual( result[ 'steps' ][ 1 ], 'dependable' )
+        self.assertEqual( result[ 'steps' ][ 2 ], 'dependent' )
+        self.failIf( site.purged )
+
+    def test_runExportStep_nonesuch( self ):
+
+        site = self._makeSite()
+        tool = self._makeOne().__of__( site )
+
+        self.assertRaises( ValueError, tool.runExportStep, 'nonesuch' )
+
+    def test_runExportStep_step_registry( self ):
+
+        from test_registry import _EMPTY_IMPORT_XML
+
+        site = self._makeSite()
+        site.setup_tool = self._makeOne()
+        tool = site.setup_tool
+
+        result = tool.runExportStep( 'step_registries' )
+
+        self.assertEqual( len( result[ 'steps' ] ), 1 )
+        self.assertEqual( result[ 'steps' ][ 0 ], 'step_registries' )
+        self.assertEqual( result[ 'messages' ][ 'step_registries' ]
+                        , 'Step registries exported'
+                        )
+        fileish = StringIO( result[ 'tarball' ] )
+
+        self._verifyTarballContents( fileish, [ 'import_steps.xml'
+                                              , 'export_steps.xml'
+                                              ] )
+        self._verifyTarballEntryXML( fileish, 'import_steps.xml'
+                                   , _EMPTY_IMPORT_XML )
+        self._verifyTarballEntryXML( fileish, 'export_steps.xml'
+                                   , _DEFAULT_STEP_REGISTRIES_EXPORT_XML )
+
+    def test_runAllExportSteps_default( self ):
+
+        from test_registry import _EMPTY_IMPORT_XML
+
+        site = self._makeSite()
+        site.setup_tool = self._makeOne()
+        tool = site.setup_tool
+
+        result = tool.runAllExportSteps()
+
+        self.assertEqual( len( result[ 'steps' ] ), 1 )
+        self.assertEqual( result[ 'steps' ][ 0 ], 'step_registries' )
+        self.assertEqual( result[ 'messages' ][ 'step_registries' ]
+                        , 'Step registries exported'
+                        )
+        fileish = StringIO( result[ 'tarball' ] )
+
+        self._verifyTarballContents( fileish, [ 'import_steps.xml'
+                                              , 'export_steps.xml'
+                                              ] )
+        self._verifyTarballEntryXML( fileish, 'import_steps.xml'
+                                   , _EMPTY_IMPORT_XML )
+        self._verifyTarballEntryXML( fileish, 'export_steps.xml'
+                                   , _DEFAULT_STEP_REGISTRIES_EXPORT_XML )
+
+    def test_runAllExportSteps_extras( self ):
+
+        from test_registry import _EMPTY_IMPORT_XML
+
+        site = self._makeSite()
+        site.setup_tool = self._makeOne()
+        tool = site.setup_tool
+
+        import_reg = tool.getImportStepRegistry()
+        import_reg.registerStep( 'dependable', '1'
+                               , _underscoreSiteTitle, ( 'purging', ) )
+        import_reg.registerStep( 'dependent', '1'
+                               , _uppercaseSiteTitle, ( 'dependable', ) )
+        import_reg.registerStep( 'purging', '1'
+                               , _purgeIfRequired )
+
+        export_reg = tool.getExportStepRegistry()
+        export_reg.registerStep( 'properties'
+                               , _exportPropertiesINI )
+
+        result = tool.runAllExportSteps()
+
+        self.assertEqual( len( result[ 'steps' ] ), 2 )
+
+        self.failUnless( 'properties' in result[ 'steps' ] )
+        self.assertEqual( result[ 'messages' ][ 'properties' ]
+                        , 'Exported properties'
+                        )
+
+        self.failUnless( 'step_registries' in result[ 'steps' ] )
+        self.assertEqual( result[ 'messages' ][ 'step_registries' ]
+                        , 'Step registries exported'
+                        )
+
+        fileish = StringIO( result[ 'tarball' ] )
+
+        self._verifyTarballContents( fileish, [ 'import_steps.xml'
+                                              , 'export_steps.xml'
+                                              , 'properties.ini'
+                                              ] )
+        self._verifyTarballEntryXML( fileish, 'import_steps.xml'
+                                   , _EXTRAS_STEP_REGISTRIES_IMPORT_XML )
+        self._verifyTarballEntryXML( fileish, 'export_steps.xml'
+                                   , _EXTRAS_STEP_REGISTRIES_EXPORT_XML )
+        self._verifyTarballEntry( fileish, 'properties.ini'
+                                , _PROPERTIES_INI % site.title  )
+
+    def test_createSnapshot_default( self ):
+
+        from test_registry import _EMPTY_IMPORT_XML
+
+        _EXPECTED = [ ( 'import_steps.xml', _EMPTY_IMPORT_XML )
+                    , ( 'export_steps.xml'
+                      , _DEFAULT_STEP_REGISTRIES_EXPORT_XML
+                      )
+                    ]
+
+        site = self._makeSite()
+        site.setup_tool = self._makeOne()
+        tool = site.setup_tool
+
+        self.assertEqual( len( tool.listSnapshotInfo() ), 0 )
+
+        result = tool.createSnapshot( 'default' )
+
+        self.assertEqual( len( result[ 'steps' ] ), 1 )
+        self.assertEqual( result[ 'steps' ][ 0 ], 'step_registries' )
+        self.assertEqual( result[ 'messages' ][ 'step_registries' ]
+                        , 'Step registries exported'
+                        )
+
+        snapshot = result[ 'snapshot' ]
+
+        self.assertEqual( len( snapshot.objectIds() ), len( _EXPECTED ) )
+
+        for id in [ x[0] for x in _EXPECTED ]:
+            self.failUnless( id in snapshot.objectIds() )
+
+        def normalize_xml(xml):
+            # using this might mask a real problem on windows, but so far the
+            # different newlines just caused problems in this test
+            lines = [ line for line in xml.splitlines() if line ]
+            return '\n'.join(lines) + '\n'
+
+        fileobj = snapshot._getOb( 'import_steps.xml' )
+        self.assertEqual( normalize_xml( fileobj.read() ),
+                          _EMPTY_IMPORT_XML )
+
+        fileobj = snapshot._getOb( 'export_steps.xml' )
+
+        self.assertEqual( normalize_xml( fileobj.read() ),
+                          _DEFAULT_STEP_REGISTRIES_EXPORT_XML )
+
+        self.assertEqual( len( tool.listSnapshotInfo() ), 1 )
+
+        info = tool.listSnapshotInfo()[ 0 ]
+
+        self.assertEqual( info[ 'id' ], 'default' )
+        self.assertEqual( info[ 'title' ], 'default' )
+
+
+_DEFAULT_STEP_REGISTRIES_EXPORT_XML = """\
+<?xml version="1.0"?>
+<export-steps>
+ <export-step id="step_registries"
+              handler="Products.GenericSetup.tool.exportStepRegistries"
+              title="Export import / export steps.">
+  
+ </export-step>
+</export-steps>
+"""
+
+_EXTRAS_STEP_REGISTRIES_EXPORT_XML = """\
+<?xml version="1.0"?>
+<export-steps>
+ <export-step
+    id="properties"
+    handler="Products.GenericSetup.tests.test_tool._exportPropertiesINI"
+    title="properties">
+
+ </export-step>
+ <export-step
+    id="step_registries"
+    handler="Products.GenericSetup.tool.exportStepRegistries"
+    title="Export import / export steps.">
+
+ </export-step>
+</export-steps>
+"""
+
+_EXTRAS_STEP_REGISTRIES_IMPORT_XML = """\
+<?xml version="1.0"?>
+<import-steps>
+ <import-step
+    id="dependable"
+    version="1"
+    handler="Products.GenericSetup.tests.test_tool._underscoreSiteTitle"
+    title="dependable">
+  <dependency step="purging" />
+
+ </import-step>
+ <import-step
+    id="dependent"
+    version="1"
+    handler="Products.GenericSetup.tests.test_tool._uppercaseSiteTitle"
+    title="dependent">
+  <dependency step="dependable" />
+
+ </import-step>
+ <import-step
+    id="purging"
+    version="1"
+    handler="Products.GenericSetup.tests.test_tool._purgeIfRequired"
+    title="purging">
+
+ </import-step>
+</import-steps>
+"""
+
+_PROPERTIES_INI = """\
+[Default]
+Title=%s
+"""
+
+def _underscoreSiteTitle( context ):
+
+    site = context.getSite()
+    site.title = site.title.replace( ' ', '_' )
+    return 'Underscored title'
+
+def _uppercaseSiteTitle( context ):
+
+    site = context.getSite()
+    site.title = site.title.upper()
+    return 'Uppercased title'
+
+def _purgeIfRequired( context ):
+
+    site = context.getSite()
+    purged = site.purged = context.shouldPurge()
+    return purged and 'Purged' or 'Unpurged'
+
+def _exportPropertiesINI( context ):
+
+    site = context.getSite()
+    text = _PROPERTIES_INI % site.title
+
+    context.writeDataFile( 'properties.ini', text, 'text/plain' )
+
+    return 'Exported properties'
+
+class _ToolsetSetup( SecurityRequestTest ):
+
+    def _initSite( self ):
+
+        from Products.GenericSetup.tool import SetupTool
+        site = Folder()
+        site._setId( 'site' )
+        self.root._setObject( 'site', site )
+        site = self.root._getOb( 'site' )
+        site._setObject( 'setup_tool', SetupTool() )
+        return site
+
+class Test_exportToolset( _ToolsetSetup
+                        , DOMComparator
+                        ):
+
+    def test_empty( self ):
+
+        from Products.GenericSetup.tool import TOOLSET_XML
+        from Products.GenericSetup.tool import exportToolset
+
+        site = self._initSite()
+        context = DummyExportContext( site )
+
+        exportToolset( context )
+
+        self.assertEqual( len( context._wrote ), 1 )
+        filename, text, content_type = context._wrote[ 0 ]
+        self.assertEqual( filename, TOOLSET_XML )
+        self._compareDOM( text, _EMPTY_TOOLSET_XML )
+        self.assertEqual( content_type, 'text/xml' )
+
+    def test_normal( self ):
+
+        from Products.GenericSetup.tool import TOOLSET_XML
+        from Products.GenericSetup.tool import exportToolset
+
+        site = self._initSite()
+        toolset = site.setup_tool.getToolsetRegistry()
+        toolset.addForbiddenTool( 'doomed' )
+        toolset.addRequiredTool( 'mandatory', 'path.to.one' )
+        toolset.addRequiredTool( 'obligatory', 'path.to.another' )
+
+        context = DummyExportContext( site )
+
+        exportToolset( context )
+
+        self.assertEqual( len( context._wrote ), 1 )
+        filename, text, content_type = context._wrote[ 0 ]
+        self.assertEqual( filename, TOOLSET_XML )
+        self._compareDOM( text, _NORMAL_TOOLSET_XML )
+        self.assertEqual( content_type, 'text/xml' )
+
+class Test_importToolset( _ToolsetSetup ):
+
+    def test_tool_ids( self ):
+        # The tool import mechanism used to rely on the fact that all tools
+        # have unique IDs set at the class level and that you can call their
+        # constructor with no arguments. However, there might be tools
+        # that need IDs set.
+        from Products.GenericSetup.tool import TOOLSET_XML
+        from Products.GenericSetup.tool import importToolset
+
+        site = self._initSite()
+        context = DummyImportContext( site )
+        context._files[ TOOLSET_XML ] = _REQUIRED_TOOLSET_XML
+
+        importToolset( context )
+
+        for tool_id in ( 'mandatory', 'obligatory' ):
+            tool = getattr( site, tool_id )
+            self.assertEqual( tool.getId(), tool_id )
+
+    def test_forbidden_tools( self ):
+
+        from Products.GenericSetup.tool import TOOLSET_XML
+        from Products.GenericSetup.tool import importToolset
+        TOOL_IDS = ( 'doomed', 'blasted', 'saved' )
+
+        site = self._initSite()
+
+        for tool_id in TOOL_IDS:
+            pseudo = Folder()
+            pseudo._setId( tool_id )
+            site._setObject( tool_id, pseudo )
+
+        self.assertEqual( len( site.objectIds() ), len( TOOL_IDS ) + 1 )
+
+        for tool_id in TOOL_IDS:
+            self.failUnless( tool_id in site.objectIds() )
+
+        context = DummyImportContext( site )
+        context._files[ TOOLSET_XML ] = _FORBIDDEN_TOOLSET_XML
+
+        importToolset( context )
+
+        self.assertEqual( len( site.objectIds() ), 2 )
+        self.failUnless( 'setup_tool' in site.objectIds() )
+        self.failUnless( 'saved' in site.objectIds() )
+
+    def test_required_tools_missing( self ):
+
+        from Products.GenericSetup.tool import TOOLSET_XML
+        from Products.GenericSetup.tool import importToolset
+
+        site = self._initSite()
+        self.assertEqual( len( site.objectIds() ), 1 )
+
+        context = DummyImportContext( site )
+        context._files[ TOOLSET_XML ] = _REQUIRED_TOOLSET_XML
+
+        importToolset( context )
+
+        self.assertEqual( len( site.objectIds() ), 3 )
+        self.failUnless( isinstance( aq_base( site._getOb( 'mandatory' ) )
+                                   , DummyTool ) )
+        self.failUnless( isinstance( aq_base( site._getOb( 'obligatory' ) )
+                                   , DummyTool ) )
+
+    def test_required_tools_no_replacement( self ):
+
+        from Products.GenericSetup.tool import TOOLSET_XML
+        from Products.GenericSetup.tool import importToolset
+
+        site = self._initSite()
+
+        mandatory = DummyTool()
+        mandatory._setId( 'mandatory' )
+        site._setObject( 'mandatory', mandatory )
+
+        obligatory = DummyTool()
+        obligatory._setId( 'obligatory' )
+        site._setObject( 'obligatory', obligatory )
+
+        self.assertEqual( len( site.objectIds() ), 3 )
+
+        context = DummyImportContext( site )
+        context._files[ TOOLSET_XML ] = _REQUIRED_TOOLSET_XML
+
+        importToolset( context )
+
+        self.assertEqual( len( site.objectIds() ), 3 )
+        self.failUnless( aq_base( site._getOb( 'mandatory' ) ) is mandatory )
+        self.failUnless( aq_base( site._getOb( 'obligatory' ) ) is obligatory )
+
+    def test_required_tools_with_replacement( self ):
+
+        from Products.GenericSetup.tool import TOOLSET_XML
+        from Products.GenericSetup.tool import importToolset
+
+        site = self._initSite()
+
+        mandatory = AnotherDummyTool()
+        mandatory._setId( 'mandatory' )
+        site._setObject( 'mandatory', mandatory )
+
+        obligatory = AnotherDummyTool()
+        obligatory._setId( 'obligatory' )
+        site._setObject( 'obligatory', obligatory )
+
+        self.assertEqual( len( site.objectIds() ), 3 )
+
+        context = DummyImportContext( site )
+        context._files[ TOOLSET_XML ] = _REQUIRED_TOOLSET_XML
+
+        importToolset( context )
+
+        self.assertEqual( len( site.objectIds() ), 3 )
+
+        self.failIf( aq_base( site._getOb( 'mandatory' ) ) is mandatory )
+        self.failUnless( isinstance( aq_base( site._getOb( 'mandatory' ) )
+                                   , DummyTool ) )
+
+        self.failIf( aq_base( site._getOb( 'obligatory' ) ) is obligatory )
+        self.failUnless( isinstance( aq_base( site._getOb( 'obligatory' ) )
+                                   , DummyTool ) )
+
+
+class DummyTool( Folder ):
+
+    pass
+
+class AnotherDummyTool( Folder ):
+
+    pass
+
+_EMPTY_TOOLSET_XML = """\
+<?xml version="1.0"?>
+<tool-setup>
+</tool-setup>
+"""
+
+_NORMAL_TOOLSET_XML = """\
+<?xml version="1.0" ?>
+<tool-setup>
+<forbidden tool_id="doomed"/>
+<required class="path.to.one" tool_id="mandatory"/>
+<required class="path.to.another" tool_id="obligatory"/>
+</tool-setup>
+"""
+
+_FORBIDDEN_TOOLSET_XML = """\
+<?xml version="1.0"?>
+<tool-setup>
+ <forbidden tool_id="doomed" />
+ <forbidden tool_id="damned" />
+ <forbidden tool_id="blasted" />
+</tool-setup>
+"""
+
+_REQUIRED_TOOLSET_XML = """\
+<?xml version="1.0"?>
+<tool-setup>
+ <required
+    tool_id="mandatory"
+    class="Products.GenericSetup.tests.test_tool.DummyTool" />
+ <required
+    tool_id="obligatory"
+    class="Products.GenericSetup.tests.test_tool.DummyTool" />
+</tool-setup>
+"""
+
+def test_suite():
+    # reimport to make sure tests are run from Products
+    from Products.GenericSetup.tests.test_tool import SetupToolTests
+    from Products.GenericSetup.tests.test_tool import Test_exportToolset
+    from Products.GenericSetup.tests.test_tool import Test_importToolset
+
+    return unittest.TestSuite((
+        unittest.makeSuite( SetupToolTests ),
+        unittest.makeSuite( Test_exportToolset ),
+        unittest.makeSuite( Test_importToolset ),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: CMF/branches/goldegg-phase-1/GenericSetup/tests/test_utils.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/tests/test_utils.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/tests/test_utils.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,526 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" GenericSetup.utils unit tests
+
+$Id: test_utils.py,v 1.1.1.1 2005/08/08 19:38:37 tseaver Exp $
+"""
+
+import unittest
+import Testing
+try:
+    import Zope2
+except ImportError: # BBB: for Zope 2.7
+    import Zope as Zope2
+Zope2.startup()
+
+from DateTime.DateTime import DateTime
+from OFS.Folder import Folder
+
+from common import BaseRegistryTests
+
+
+_NORMAL_PROPERTY_NODES = """\
+  <property name="foo_boolean" type="boolean">True</property>
+  <property name="foo_date" type="date">2000/01/01</property>
+  <property name="foo_float" type="float">1.1</property>
+  <property name="foo_int" type="int">1</property>
+  <property name="foo_lines" type="lines">
+   <element value="Foo"/>
+   <element value="Lines"/></property>
+  <property name="foo_long" type="long">1</property>
+  <property name="foo_string" type="string">Foo String</property>
+  <property name="foo_text" type="text">Foo
+Text</property>
+  <property name="foo_tokens" type="tokens">
+   <element value="Foo"/>
+   <element value="Tokens"/></property>
+  <property name="foo_selection" type="selection"
+            select_variable="foobarbaz">Foo</property>
+  <property name="foo_mselection" type="multiple selection"
+            select_variable="foobarbaz">
+   <element value="Foo"/>
+   <element value="Baz"/></property>
+  <property name="foo_boolean0" type="boolean">0</property>
+"""
+
+_FIXED_PROPERTY_NODES = """\
+  <property name="foo_boolean">True</property>
+  <property name="foo_date">2000/01/01</property>
+  <property name="foo_float">1.1</property>
+  <property name="foo_int">1</property>
+  <property name="foo_lines">
+   <element value="Foo"/>
+   <element value="Lines"/></property>
+  <property name="foo_long">1</property>
+  <property name="foo_string">Foo String</property>
+  <property name="foo_text">Foo
+Text</property>
+  <property name="foo_tokens">
+   <element value="Foo"/>
+   <element value="Tokens"/></property>
+  <property name="foo_selection" type="selection"
+            select_variable="foobarbaz">Foo</property>
+  <property name="foo_mselection">
+   <element value="Foo"/>
+   <element value="Baz"/></property>
+  <property name="foo_boolean0">0</property>
+"""
+
+_NORMAL_PROPERTY_INFO = ( { 'id': 'foo_boolean',
+                            'value': True,
+                            'elements': (),
+                            'type': 'boolean',
+                            'select_variable': None },
+                          { 'id': 'foo_date',
+                            'value': DateTime('2000/01/01'),
+                            'elements': (),
+                            'type': 'date',
+                            'select_variable': None },
+                          { 'id': 'foo_float',
+                            'value': 1.1,
+                            'elements': (),
+                            'type': 'float',
+                            'select_variable': None },
+                          { 'id': 'foo_int',
+                            'value': 1,
+                            'elements': (),
+                            'type': 'int',
+                            'select_variable': None },
+                          { 'id': 'foo_lines',
+                            'value': '',
+                            'elements': ('Foo', 'Lines'),
+                            'type': 'lines',
+                            'select_variable': None },
+                          { 'id': 'foo_long',
+                            'value': 1,
+                            'elements': (),
+                            'type': 'long',
+                            'select_variable': None },
+                          { 'id': 'foo_string',
+                            'value': 'Foo String',
+                            'elements': (),
+                            'type': 'string',
+                            'select_variable': None },
+                          { 'id': 'foo_text',
+                            'value': 'Foo\nText',
+                            'elements': (),
+                            'type': 'text',
+                            'select_variable': None },
+                          { 'id': 'foo_tokens',
+                            'value': '',
+                            'elements': ('Foo', 'Tokens'),
+                            'type': 'tokens',
+                            'select_variable': None },
+                          { 'id': 'foo_selection',
+                            'value': 'Foo',
+                            'elements': (),
+                            'type': 'selection',
+                            'select_variable': 'foobarbaz' },
+                          { 'id': 'foo_mselection',
+                            'value': '',
+                            'elements': ('Foo', 'Baz'),
+                            'type': 'multiple selection',
+                            'select_variable': 'foobarbaz' },
+                          { 'id': 'foo_boolean0',
+                            'value': False, # 0 imports as False
+                            'elements': (),
+                            'type': 'boolean',
+                            'select_variable': None },
+                          )
+
+_NORMAL_PROPERTY_EXPORT = """\
+<?xml version="1.0"?>
+<dummy>
+%s
+</dummy>
+""" % _NORMAL_PROPERTY_NODES
+
+_FIXED_PROPERTY_EXPORT = """\
+<?xml version="1.0"?>
+<dummy>
+%s
+</dummy>
+""" % _FIXED_PROPERTY_NODES
+
+_NORMAL_OBJECT_EXPORT = """\
+<?xml version="1.0"?>
+<dummy>
+ <object meta_type="Dummy Type" name="dummy">
+%s
+ </object>
+</dummy>
+""" % _NORMAL_PROPERTY_NODES
+
+_SPECIAL_IMPORT = """\
+<?xml version="1.0"?>
+<dummy>
+ <!-- ignore comment, allow empty description -->
+ <description></description>
+</dummy>
+"""
+
+def _testFunc( *args, **kw ):
+
+    """ This is a test.
+
+    This is only a test.
+    """
+
+_TEST_FUNC_NAME = 'Products.GenericSetup.tests.test_utils._testFunc'
+
+class Whatever:
+    pass
+
+_WHATEVER_NAME = 'Products.GenericSetup.tests.test_utils.Whatever'
+
+whatever_inst = Whatever()
+whatever_inst.__name__ = 'whatever_inst'
+
+_WHATEVER_INST_NAME = 'Products.GenericSetup.tests.test_utils.whatever_inst'
+
+class UtilsTests( unittest.TestCase ):
+
+    def test__getDottedName_simple( self ):
+
+        from Products.GenericSetup.utils import _getDottedName
+
+        self.assertEqual( _getDottedName( _testFunc ), _TEST_FUNC_NAME )
+
+    def test__getDottedName_string( self ):
+
+        from Products.GenericSetup.utils import _getDottedName
+
+        self.assertEqual( _getDottedName( _TEST_FUNC_NAME ), _TEST_FUNC_NAME )
+
+    def test__getDottedName_unicode( self ):
+
+        from Products.GenericSetup.utils import _getDottedName
+
+        dotted = u'%s' % _TEST_FUNC_NAME
+        self.assertEqual( _getDottedName( dotted ), _TEST_FUNC_NAME )
+        self.assertEqual( type( _getDottedName( dotted ) ), str )
+
+    def test__getDottedName_class( self ):
+
+        from Products.GenericSetup.utils import _getDottedName
+
+        self.assertEqual( _getDottedName( Whatever ), _WHATEVER_NAME )
+
+    def test__getDottedName_inst( self ):
+
+        from Products.GenericSetup.utils import _getDottedName
+
+        self.assertEqual( _getDottedName( whatever_inst )
+                        , _WHATEVER_INST_NAME )
+
+    def test__getDottedName_noname( self ):
+
+        from Products.GenericSetup.utils import _getDottedName
+
+        class Doh:
+            pass
+
+        doh = Doh()
+        self.assertRaises( ValueError, _getDottedName, doh )
+
+
+class DummyObject(Folder):
+
+    meta_type = 'Dummy Type'
+    _properties = ()
+
+
+class _ConfiguratorBaseTests(BaseRegistryTests):
+
+    def _initSite(self, foo=2):
+
+        self.root.site = Folder(id='site')
+        site = self.root.site
+
+        site.dummy = DummyObject(id='dummy')
+        site.dummy.foobarbaz = ('Foo', 'Bar', 'Baz')
+
+        if foo > 0:
+            site.dummy._setProperty('foo_boolean', '', 'boolean')
+            site.dummy._setProperty('foo_date', '', 'date')
+            site.dummy._setProperty('foo_float', '', 'float')
+            site.dummy._setProperty('foo_int', '', 'int')
+            site.dummy._setProperty('foo_lines', '', 'lines')
+            site.dummy._setProperty('foo_long', '', 'long')
+            site.dummy._setProperty('foo_string', '', 'string')
+            site.dummy._setProperty('foo_text', '', 'text')
+            site.dummy._setProperty('foo_tokens', (), 'tokens')
+            site.dummy._setProperty('foo_selection', 'foobarbaz', 'selection')
+            site.dummy._setProperty('foo_mselection', 'foobarbaz',
+                                    'multiple selection')
+            site.dummy._setProperty('foo_boolean0', '', 'boolean')
+
+        if foo > 1:
+            site.dummy._updateProperty('foo_boolean', 'True')
+            site.dummy._updateProperty('foo_date', '2000/01/01')
+            site.dummy._updateProperty('foo_float', '1.1')
+            site.dummy._updateProperty('foo_int', '1')
+            site.dummy._updateProperty('foo_lines', 'Foo\nLines')
+            site.dummy._updateProperty('foo_long', '1')
+            site.dummy._updateProperty('foo_string', 'Foo String')
+            site.dummy._updateProperty('foo_text', 'Foo\nText')
+            site.dummy._updateProperty( 'foo_tokens', ('Foo', 'Tokens') )
+            site.dummy._updateProperty('foo_selection', 'Foo')
+            site.dummy._updateProperty( 'foo_mselection', ('Foo', 'Baz') )
+            site.dummy.foo_boolean0 = 0
+
+        return site
+
+
+class ExportConfiguratorBaseTests(_ConfiguratorBaseTests):
+
+    def _getTargetClass(self):
+
+        from Products.GenericSetup.utils import ExportConfiguratorBase
+
+        class Configurator(ExportConfiguratorBase):
+            def _getExportTemplate(self):
+                return None
+
+        return Configurator
+
+    def test__extractProperty_normal(self):
+
+        site = self._initSite()
+
+        EXPECTED = _NORMAL_PROPERTY_INFO
+
+        configurator = self._makeOne(site)
+        prop_infos = [ configurator._extractProperty(site.dummy, prop_def)
+                       for prop_def in site.dummy._propertyMap() ]
+
+        self.assertEqual( len(prop_infos), len(EXPECTED) )
+
+        for found, expected in zip(prop_infos, EXPECTED):
+            self.assertEqual(found, expected)
+
+    def test__extractObject_normal(self):
+
+        site = self._initSite()
+
+        EXPECTED = { 'id': 'dummy',
+                     'meta_type': 'Dummy Type',
+                     'properties': _NORMAL_PROPERTY_INFO,
+                     'subobjects': (),
+                     'i18n_domain' : None,
+                   }
+
+        configurator = self._makeOne(site)
+        obj_info = configurator._extractObject(site.dummy)
+
+        self.assertEqual( len(obj_info), len(EXPECTED) )
+        self.assertEqual(obj_info, EXPECTED)
+
+    def test_generatePropertyNodes_normal(self):
+
+        site = self._initSite()
+        configurator = self._makeOne(site).__of__(site)
+        prop_infos = [ configurator._extractProperty(site.dummy, prop_def)
+                       for prop_def in site.dummy._propertyMap() ]
+        nodes = configurator.generatePropertyNodes(prop_infos)
+        xml = '<?xml version="1.0"?><dummy>%s\n</dummy>' % nodes
+
+        self._compareDOM(xml, _NORMAL_PROPERTY_EXPORT)
+
+    def test_generateObjectNodes_normal(self):
+
+        site = self._initSite()
+        configurator = self._makeOne(site).__of__(site)
+        obj_infos = ( configurator._extractObject(site.dummy), )
+        nodes = configurator.generateObjectNodes(obj_infos)
+        xml = '<?xml version="1.0"?><dummy>%s\n</dummy>' % nodes
+
+        self._compareDOM(xml, _NORMAL_OBJECT_EXPORT)
+
+
+class ImportConfiguratorBaseTests(_ConfiguratorBaseTests):
+
+    def _getTargetClass(self):
+
+        from Products.GenericSetup.utils import ImportConfiguratorBase
+        from Products.GenericSetup.utils import CONVERTER, DEFAULT, KEY
+
+        class Configurator(ImportConfiguratorBase):
+            def _getImportMapping(self):
+                return {
+                  'dummy':
+                    { 'property':    {KEY: 'properties', DEFAULT: ()},
+                      'description': {CONVERTER: self._convertToUnique} } }
+
+        return Configurator
+
+
+    def test_parseXML_normal(self):
+
+        site = self._initSite()
+        configurator = self._makeOne(site)
+        site_info = configurator.parseXML(_NORMAL_PROPERTY_EXPORT)
+
+        self.assertEqual( len( site_info['properties'] ), 12 )
+
+        info = site_info['properties'][0]
+        self.assertEqual( info['id'], 'foo_boolean' )
+        self.assertEqual( info['value'], 'True' )
+        self.assertEqual( len( info['elements'] ), 0 )
+        self.assertEqual( info['type'], 'boolean' )
+
+        info = site_info['properties'][1]
+        self.assertEqual( info['id'], 'foo_date' )
+        self.assertEqual( info['value'], '2000/01/01' )
+        self.assertEqual( len( info['elements'] ), 0 )
+        self.assertEqual( info['type'], 'date' )
+
+        info = site_info['properties'][2]
+        self.assertEqual( info['id'], 'foo_float' )
+        self.assertEqual( info['value'], '1.1' )
+        self.assertEqual( len( info['elements'] ), 0 )
+        self.assertEqual( info['type'], 'float' )
+
+        info = site_info['properties'][3]
+        self.assertEqual( info['id'], 'foo_int' )
+        self.assertEqual( info['value'], '1' )
+        self.assertEqual( len( info['elements'] ), 0 )
+        self.assertEqual( info['type'], 'int' )
+
+        info = site_info['properties'][4]
+        self.assertEqual( info['id'], 'foo_lines' )
+        self.assertEqual( info['value'], '' )
+        self.assertEqual( len( info['elements'] ), 2 )
+        self.assertEqual( info['elements'][0], 'Foo' )
+        self.assertEqual( info['elements'][1], 'Lines' )
+        self.assertEqual( info['type'], 'lines' )
+
+        info = site_info['properties'][5]
+        self.assertEqual( info['id'], 'foo_long' )
+        self.assertEqual( info['value'], '1' )
+        self.assertEqual( len( info['elements'] ), 0 )
+        self.assertEqual( info['type'], 'long' )
+
+        info = site_info['properties'][6]
+        self.assertEqual( info['id'], 'foo_string' )
+        self.assertEqual( info['value'], 'Foo String' )
+        self.assertEqual( len( info['elements'] ), 0 )
+        self.assertEqual( info['type'], 'string' )
+
+        info = site_info['properties'][7]
+        self.assertEqual( info['id'], 'foo_text' )
+        self.assertEqual( info['value'], 'Foo\nText' )
+        self.assertEqual( len( info['elements'] ), 0 )
+        self.assertEqual( info['type'], 'text' )
+
+        info = site_info['properties'][8]
+        self.assertEqual( info['id'], 'foo_tokens' )
+        self.assertEqual( info['value'], '' )
+        self.assertEqual( len( info['elements'] ), 2 )
+        self.assertEqual( info['elements'][0], 'Foo' )
+        self.assertEqual( info['elements'][1], 'Tokens' )
+        self.assertEqual( info['type'], 'tokens' )
+
+        info = site_info['properties'][9]
+        self.assertEqual( info['id'], 'foo_selection' )
+        self.assertEqual( info['value'], 'Foo' )
+        self.assertEqual( len( info['elements'] ), 0 )
+        self.assertEqual( info['type'], 'selection' )
+        self.assertEqual( info['select_variable'], 'foobarbaz' )
+
+        info = site_info['properties'][10]
+        self.assertEqual( info['id'], 'foo_mselection' )
+        self.assertEqual( info['value'], '' )
+        self.assertEqual( len( info['elements'] ), 2 )
+        self.assertEqual( info['elements'][0], 'Foo' )
+        self.assertEqual( info['elements'][1], 'Baz' )
+        self.assertEqual( info['type'], 'multiple selection' )
+        self.assertEqual( info['select_variable'], 'foobarbaz' )
+
+        info = site_info['properties'][11]
+        self.assertEqual( info['id'], 'foo_boolean0' )
+        self.assertEqual( info['value'], '0' )
+        self.assertEqual( len( info['elements'] ), 0 )
+        self.assertEqual( info['type'], 'boolean' )
+
+    def test_parseXML_special(self):
+
+        site = self._initSite()
+        configurator = self._makeOne(site)
+        try:
+            site_info = configurator.parseXML(_SPECIAL_IMPORT)
+        except KeyError:
+            self.fail('CMF Collector issue #352 (comment or empty '
+                      'description bug): KeyError raised')
+
+        self.assertEqual( len(site_info), 2 )
+        self.assertEqual( site_info['description'], '' )
+        self.assertEqual( len(site_info['properties']), 0 )
+
+    def test_initProperty_normal(self):
+
+        EXPECTED = _NORMAL_PROPERTY_INFO
+
+        site = self._initSite(0)
+        dummy = site.dummy
+        configurator = self._makeOne(site)
+        site_info = configurator.parseXML(_NORMAL_PROPERTY_EXPORT)
+
+        self.assertEqual( len( dummy.propertyIds() ), 0 )
+
+        for prop_info in site_info['properties']:
+            configurator.initProperty(dummy, prop_info)
+
+        self.assertEqual( len( dummy.propertyIds() ), len(EXPECTED) )
+
+        for exp_info in EXPECTED:
+            exp_id = exp_info['id']
+            exp_value = exp_info['elements'] or exp_info['value']
+            self.failUnless( exp_id in dummy.propertyIds() )
+            self.assertEqual( dummy.getProperty(exp_id), exp_value )
+
+    def test_initProperty_fixed(self):
+
+        EXPECTED = _NORMAL_PROPERTY_INFO
+
+        site = self._initSite(1)
+        dummy = site.dummy
+        configurator = self._makeOne(site)
+        site_info = configurator.parseXML(_FIXED_PROPERTY_EXPORT)
+
+        self.assertEqual( len( dummy.propertyIds() ), 12 )
+
+        for prop_info in site_info['properties']:
+            configurator.initProperty(dummy, prop_info)
+
+        self.assertEqual( len( dummy.propertyIds() ), len(EXPECTED) )
+
+        for exp_info in EXPECTED:
+            exp_id = exp_info['id']
+            exp_value = exp_info['elements'] or exp_info['value']
+            self.failUnless( exp_id in dummy.propertyIds() )
+            self.assertEqual( dummy.getProperty(exp_id), exp_value )
+
+
+def test_suite():
+    # reimport to make sure tests are run from Products
+    from Products.GenericSetup.tests.test_utils import UtilsTests
+
+    return unittest.TestSuite((
+        unittest.makeSuite( UtilsTests ),
+        unittest.makeSuite( ImportConfiguratorBaseTests ),
+        unittest.makeSuite( ExportConfiguratorBaseTests ),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: CMF/branches/goldegg-phase-1/GenericSetup/tests/three/placeholder.txt
===================================================================

Added: CMF/branches/goldegg-phase-1/GenericSetup/tests/two/placeholder.txt
===================================================================

Added: CMF/branches/goldegg-phase-1/GenericSetup/tool.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/tool.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/tool.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,733 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" Classes:  SetupTool
+
+$Id: tool.py,v 1.1.1.1 2005/08/08 19:38:37 tseaver Exp $
+"""
+
+import os
+import time
+from cgi import escape
+
+from AccessControl import ClassSecurityInfo
+from Acquisition import aq_base
+from Globals import InitializeClass
+from OFS.Folder import Folder
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+from zope.interface import implements
+from zope.interface import implementedBy
+
+from interfaces import EXTENSION
+from interfaces import ISetupTool
+from permissions import ManagePortal
+from context import DirectoryImportContext
+from context import SnapshotImportContext
+from context import TarballExportContext
+from context import SnapshotExportContext
+from differ import ConfigDiff
+from registry import ImportStepRegistry
+from registry import ExportStepRegistry
+from registry import ToolsetRegistry
+from registry import _profile_registry
+
+from utils import _resolveDottedName
+from utils import _wwwdir
+
+IMPORT_STEPS_XML = 'import_steps.xml'
+EXPORT_STEPS_XML = 'export_steps.xml'
+TOOLSET_XML = 'toolset.xml'
+
+def exportStepRegistries(context):
+
+    """ Built-in handler for exporting import / export step registries.
+    """
+    site = context.getSite()
+    setup_tool = getattr(site, 'setup_tool')
+
+    import_steps_xml = setup_tool.getImportStepRegistry().generateXML()
+    context.writeDataFile('import_steps.xml', import_steps_xml, 'text/xml')
+
+    export_steps_xml = setup_tool.getExportStepRegistry().generateXML()
+    context.writeDataFile('export_steps.xml', export_steps_xml, 'text/xml')
+
+    return 'Step registries exported'
+
+def importToolset(context):
+
+    """ Import required / forbidden tools from XML file.
+    """
+    site = context.getSite()
+    encoding = context.getEncoding()
+
+    xml = context.readDataFile(TOOLSET_XML)
+    if xml is None:
+        return 'Toolset: Nothing to import.'
+
+    setup_tool = getattr(site, 'setup_tool')
+    toolset = setup_tool.getToolsetRegistry()
+
+    toolset.parseXML(xml, encoding)
+
+    existing_ids = site.objectIds()
+    existing_values = site.objectValues()
+
+    for tool_id in toolset.listForbiddenTools():
+
+        if tool_id in existing_ids:
+            site._delObject(tool_id)
+
+    for info in toolset.listRequiredToolInfo():
+
+        tool_id = str(info['id'])
+        tool_class = _resolveDottedName(info['class'])
+
+        existing = getattr(site, tool_id, None)
+        new_tool = tool_class()
+
+        try:
+            new_tool._setId(tool_id)
+        except: # XXX:  ImmutableId raises result of calling MessageDialog
+            pass
+
+        if existing is None:
+            site._setObject(tool_id, new_tool)
+
+        else:
+            unwrapped = aq_base(existing)
+            if not isinstance(unwrapped, tool_class):
+                site._delObject(tool_id)
+                site._setObject(tool_id, tool_class())
+
+    return 'Toolset imported.'
+
+def exportToolset(context):
+
+    """ Export required / forbidden tools to XML file.
+    """
+    site = context.getSite()
+    setup_tool = getattr(site, 'setup_tool')
+    toolset = setup_tool.getToolsetRegistry()
+
+    xml = toolset.generateXML()
+    context.writeDataFile(TOOLSET_XML, xml, 'text/xml')
+
+    return 'Toolset exported.'
+
+
+class SetupTool(Folder):
+
+    """ Profile-based site configuration manager.
+    """
+    implements(ISetupTool, implementedBy(Folder))
+
+    id = 'setup_tool'
+    meta_type = 'Generic Setup Tool'
+
+    _import_context_id = ''
+
+    security = ClassSecurityInfo()
+
+    def __init__(self):
+
+        self._import_registry = ImportStepRegistry()
+        self._export_registry = ExportStepRegistry()
+        self._export_registry.registerStep('step_registries',
+                                           exportStepRegistries,
+                                           'Export import / export steps.',
+                                          )
+        self._toolset_registry = ToolsetRegistry()
+
+    #
+    #   ISetupTool API
+    #
+    security.declareProtected(ManagePortal, 'getEncoding')
+    def getEncoding(self):
+
+        """ See ISetupTool.
+        """
+        return 'ascii'
+
+    security.declareProtected(ManagePortal, 'getImportContextId')
+    def getImportContextID(self):
+
+        """ See ISetupTool.
+        """
+        return self._import_context_id
+
+    security.declareProtected(ManagePortal, 'setImportContext')
+    def setImportContext(self, context_id, encoding=None):
+
+        """ See ISetupTool.
+        """
+        self._import_context_id = context_id
+
+        self._updateImportStepsRegistry(encoding)
+        self._updateExportStepsRegistry(encoding)
+        self._updateToolsetRegistry(encoding)
+
+    security.declareProtected(ManagePortal, 'getImportStepRegistry')
+    def getImportStepRegistry(self):
+
+        """ See ISetupTool.
+        """
+        return self._import_registry
+
+    security.declareProtected(ManagePortal, 'getImportStepRegistry')
+    def getExportStepRegistry(self):
+
+        """ See ISetupTool.
+        """
+        return self._export_registry
+
+    security.declareProtected(ManagePortal, 'getToolsetRegistry')
+    def getToolsetRegistry(self):
+
+        """ See ISetupTool.
+        """
+        return self._toolset_registry
+
+    security.declareProtected(ManagePortal, 'executeStep')
+    def runImportStep(self, step_id, run_dependencies=True, purge_old=None):
+
+        """ See ISetupTool.
+        """
+        context = self._getImportContext(self._import_context_id, purge_old)
+
+        info = self._import_registry.getStepMetadata(step_id)
+
+        if info is None:
+            raise ValueError, 'No such import step: %s' % step_id
+
+        dependencies = info.get('dependencies', ())
+
+        messages = {}
+        steps = []
+        if run_dependencies:
+            for dependency in dependencies:
+
+                if dependency not in steps:
+                    message = self._doRunImportStep(dependency, context)
+                    messages[dependency] = message
+                    steps.append(dependency)
+
+        message = self._doRunImportStep(step_id, context)
+        messages[step_id] = message
+        steps.append(step_id)
+
+        return { 'steps' : steps, 'messages' : messages }
+
+    security.declareProtected(ManagePortal, 'runAllSetupSteps')
+    def runAllImportSteps(self, purge_old=None):
+
+        """ See ISetupTool.
+        """
+        context = self._getImportContext(self._import_context_id, purge_old)
+
+        steps = self._import_registry.sortSteps()
+        messages = {}
+
+        for step in steps:
+            message = self._doRunImportStep(step, context)
+            messages[step] = message
+
+        return { 'steps' : steps, 'messages' : messages }
+
+    security.declareProtected(ManagePortal, 'runExportStep')
+    def runExportStep(self, step_id):
+
+        """ See ISetupTool.
+        """
+        return self._doRunExportSteps([step_id])
+
+    security.declareProtected(ManagePortal, 'runAllExportSteps')
+    def runAllExportSteps(self):
+
+        """ See ISetupTool.
+        """
+        return self._doRunExportSteps(self._export_registry.listSteps())
+
+    security.declareProtected(ManagePortal, 'createSnapshot')
+    def createSnapshot(self, snapshot_id):
+
+        """ See ISetupTool.
+        """
+        context = SnapshotExportContext(self, snapshot_id)
+        messages = {}
+        steps = self._export_registry.listSteps()
+
+        for step_id in steps:
+
+            handler = self._export_registry.getStep(step_id)
+
+            if handler is None:
+                raise ValueError('Invalid export step: %s' % step_id)
+
+            messages[step_id] = handler(context)
+
+
+        return { 'steps' : steps
+               , 'messages' : messages
+               , 'url' : context.getSnapshotURL()
+               , 'snapshot' : context.getSnapshotFolder()
+               }
+
+    security.declareProtected(ManagePortal, 'compareConfigurations')
+    def compareConfigurations(self,
+                              lhs_context,
+                              rhs_context,
+                              missing_as_empty=False,
+                              ignore_blanks=False,
+                              skip=('CVS', '.svn'),
+                             ):
+        """ See ISetupTool.
+        """
+        differ = ConfigDiff(lhs_context,
+                            rhs_context,
+                            missing_as_empty,
+                            ignore_blanks,
+                            skip,
+                           )
+
+        return differ.compare()
+
+    security.declareProtected(ManagePortal, 'markupComparison')
+    def markupComparison(self, lines):
+
+        """ See ISetupTool.
+        """
+        result = []
+
+        for line in lines.splitlines():
+
+            if line.startswith('** '):
+
+                if line.find('File') > -1:
+                    if line.find('replaced') > -1:
+                        result.append(('file-to-dir', line))
+                    elif line.find('added') > -1:
+                        result.append(('file-added', line))
+                    else:
+                        result.append(('file-removed', line))
+                else:
+                    if line.find('replaced') > -1:
+                        result.append(('dir-to-file', line))
+                    elif line.find('added') > -1:
+                        result.append(('dir-added', line))
+                    else:
+                        result.append(('dir-removed', line))
+
+            elif line.startswith('@@'):
+                result.append(('diff-range', line))
+
+            elif line.startswith(' '):
+                result.append(('diff-context', line))
+
+            elif line.startswith('+'):
+                result.append(('diff-added', line))
+
+            elif line.startswith('-'):
+                result.append(('diff-removed', line))
+
+            elif line == '\ No newline at end of file':
+                result.append(('diff-context', line))
+
+            else:
+                result.append(('diff-header', line))
+
+        return '<pre>\n%s\n</pre>' % (
+            '\n'.join([('<span class="%s">%s</span>' % (cl, escape(l)))
+                                  for cl, l in result]))
+
+    #
+    #   ZMI
+    #
+    manage_options = (Folder.manage_options[:1]
+                    + ({'label' : 'Properties',
+                        'action' : 'manage_tool'
+                       },
+                       {'label' : 'Import',
+                        'action' : 'manage_importSteps'
+                       },
+                       {'label' : 'Export',
+                        'action' : 'manage_exportSteps'
+                       },
+                       {'label' : 'Snapshots',
+                        'action' : 'manage_snapshots'
+                       },
+                       {'label' : 'Comparison',
+                        'action' : 'manage_showDiff'
+                       },
+                      )
+                    + Folder.manage_options[3:] # skip "View", "Properties"
+                     )
+
+    security.declareProtected(ManagePortal, 'manage_tool')
+    manage_tool = PageTemplateFile('sutProperties', _wwwdir)
+
+    security.declareProtected(ManagePortal, 'manage_updateToolProperties')
+    def manage_updateToolProperties(self, context_id, RESPONSE):
+        """ Update the tool's settings.
+        """
+        self.setImportContext(context_id)
+
+        RESPONSE.redirect('%s/manage_tool?manage_tabs_message=%s'
+                         % (self.absolute_url(), 'Properties+updated.'))
+
+    security.declareProtected(ManagePortal, 'manage_importSteps')
+    manage_importSteps = PageTemplateFile('sutImportSteps', _wwwdir)
+
+    security.declareProtected(ManagePortal, 'manage_importSelectedSteps')
+    def manage_importSelectedSteps(self,
+                                   ids,
+                                   run_dependencies,
+                                   RESPONSE,
+                                  ):
+        """ Import the steps selected by the user.
+        """
+        if not ids:
+            message = 'No+steps+selected.'
+
+        else:
+            steps_run = []
+            for step_id in ids:
+                result = self.runImportStep(step_id, run_dependencies)
+                steps_run.extend(result['steps'])
+
+            message = 'Steps+run:%s' % '+,'.join(steps_run)
+
+        RESPONSE.redirect('%s/manage_importSteps?manage_tabs_message=%s'
+                         % (self.absolute_url(), message))
+
+    security.declareProtected(ManagePortal, 'manage_importSelectedSteps')
+    def manage_importAllSteps(self, RESPONSE):
+
+        """ Import all steps.
+        """
+        result = self.runAllImportSteps()
+        message = 'Steps+run:%s' % '+,'.join(result['steps'])
+
+        RESPONSE.redirect('%s/manage_importSteps?manage_tabs_message=%s'
+                         % (self.absolute_url(), message))
+
+    security.declareProtected(ManagePortal, 'manage_exportSteps')
+    manage_exportSteps = PageTemplateFile('sutExportSteps', _wwwdir)
+
+    security.declareProtected(ManagePortal, 'manage_exportSelectedSteps')
+    def manage_exportSelectedSteps(self, ids, RESPONSE):
+
+        """ Export the steps selected by the user.
+        """
+        if not ids:
+            RESPONSE.redirect('%s/manage_exportSteps?manage_tabs_message=%s'
+                             % (self.absolute_url(), 'No+steps+selected.'))
+
+        result = self._doRunExportSteps(ids)
+        RESPONSE.setHeader('Content-type', 'application/x-gzip')
+        RESPONSE.setHeader('Content-disposition',
+                           'attachment; filename=%s' % result['filename'])
+        return result['tarball']
+
+    security.declareProtected(ManagePortal, 'manage_exportAllSteps')
+    def manage_exportAllSteps(self, RESPONSE):
+
+        """ Export all steps.
+        """
+        result = self.runAllExportSteps()
+        RESPONSE.setHeader('Content-type', 'application/x-gzip')
+        RESPONSE.setHeader('Content-disposition',
+                           'attachment; filename=%s' % result['filename'])
+        return result['tarball']
+
+    security.declareProtected(ManagePortal, 'manage_snapshots')
+    manage_snapshots = PageTemplateFile('sutSnapshots', _wwwdir)
+
+    security.declareProtected(ManagePortal, 'listSnapshotInfo')
+    def listSnapshotInfo(self):
+
+        """ Return a list of mappings describing available snapshots.
+
+        o Keys include:
+
+          'id' -- snapshot ID
+
+          'title' -- snapshot title or ID
+
+          'url' -- URL of the snapshot folder
+        """
+        result = []
+        snapshots = self._getOb('snapshots', None)
+
+        if snapshots:
+
+            for id, folder in snapshots.objectItems('Folder'):
+
+                result.append({ 'id' : id
+                               , 'title' : folder.title_or_id()
+                               , 'url' : folder.absolute_url()
+                               })
+        return result
+
+    security.declareProtected(ManagePortal, 'listProfileInfo')
+    def listProfileInfo(self):
+
+        """ Return a list of mappings describing registered profiles.
+
+        o Keys include:
+
+          'id' -- profile ID
+
+          'title' -- profile title or ID
+
+          'description' -- description of the profile
+
+          'path' -- path to the profile within its product
+
+          'product' -- name of the registering product
+        """
+        return _profile_registry.listProfileInfo()
+
+    security.declareProtected(ManagePortal, 'listContextInfos')
+    def listContextInfos(self):
+
+        """ List registered profiles and snapshots.
+        """
+
+        s_infos = [{ 'id': 'snapshot-%s' % info['id'],
+                      'title': info['title'] }
+                    for info in self.listSnapshotInfo()]
+        p_infos = [{ 'id': 'profile-%s' % info['id'],
+                      'title': info['title'] }
+                    for info in self.listProfileInfo()]
+
+        return tuple(s_infos + p_infos)
+
+    security.declareProtected(ManagePortal, 'manage_createSnapshot')
+    def manage_createSnapshot(self, RESPONSE, snapshot_id=None):
+
+        """ Create a snapshot with the given ID.
+
+        o If no ID is passed, generate one.
+        """
+        if snapshot_id is None:
+            timestamp = time.gmtime()
+            snapshot_id = 'snapshot-%4d%02d%02d%02d%02d%02d' % timestamp[:6]
+
+        self.createSnapshot(snapshot_id)
+
+        RESPONSE.redirect('%s/manage_snapshots?manage_tabs_message=%s'
+                         % (self.absolute_url(), 'Snapshot+created.'))
+
+    security.declareProtected(ManagePortal, 'manage_showDiff')
+    manage_showDiff = PageTemplateFile('sutCompare', _wwwdir)
+
+    def manage_downloadDiff(self,
+                            lhs,
+                            rhs,
+                            missing_as_empty,
+                            ignore_blanks,
+                            RESPONSE,
+                           ):
+        """ Crack request vars and call compareConfigurations.
+
+        o Return the result as a 'text/plain' stream, suitable for framing.
+        """
+        comparison = self.manage_compareConfigurations(lhs,
+                                                       rhs,
+                                                       missing_as_empty,
+                                                       ignore_blanks,
+                                                      )
+        RESPONSE.setHeader('Content-Type', 'text/plain')
+        return _PLAINTEXT_DIFF_HEADER % (lhs, rhs, comparison)
+
+    security.declareProtected(ManagePortal, 'manage_compareConfigurations')
+    def manage_compareConfigurations(self,
+                                     lhs,
+                                     rhs,
+                                     missing_as_empty,
+                                     ignore_blanks,
+                                    ):
+        """ Crack request vars and call compareConfigurations.
+        """
+        lhs_context = self._getImportContext(lhs)
+        rhs_context = self._getImportContext(rhs)
+
+        return self.compareConfigurations(lhs_context,
+                                          rhs_context,
+                                          missing_as_empty,
+                                          ignore_blanks,
+                                         )
+
+
+    #
+    #   Helper methods
+    #
+    security.declarePrivate('_getProductPath')
+    def _getProductPath(self, product_name):
+
+        """ Return the absolute path of the product's directory.
+        """
+        try:
+            product = __import__('Products.%s' % product_name
+                                , globals(), {}, ['initialize'])
+        except ImportError:
+            raise ValueError, 'Not a valid product name: %s' % product_name
+
+        return product.__path__[0]
+
+    security.declarePrivate('_getImportContext')
+    def _getImportContext(self, context_id, should_purge=None):
+
+        """ Crack ID and generate appropriate import context.
+        """
+        encoding = self.getEncoding()
+
+        if context_id.startswith('profile-'):
+
+            context_id = context_id[len('profile-'):]
+            info = _profile_registry.getProfileInfo(context_id)
+
+            if info.get('product'):
+                path = os.path.join(self._getProductPath(info['product'])
+                                   , info['path'])
+            else:
+                path = info['path']
+            if should_purge is None:
+                should_purge = (info.get('type') != EXTENSION)
+            return DirectoryImportContext(self, path, should_purge, encoding)
+
+        # else snapshot
+        context_id = context_id[len('snapshot-'):]
+        if should_purge is None:
+            should_purge = True
+        return SnapshotImportContext(self, context_id, should_purge, encoding)
+
+    security.declarePrivate('_updateImportStepsRegistry')
+    def _updateImportStepsRegistry(self, encoding):
+
+        """ Update our import steps registry from our profile.
+        """
+        context = self._getImportContext(self._import_context_id)
+        xml = context.readDataFile(IMPORT_STEPS_XML)
+        if xml is None:
+            return
+
+        info_list = self._import_registry.parseXML(xml, encoding)
+
+        for step_info in info_list:
+
+            id = step_info['id']
+            version = step_info['version']
+            handler = _resolveDottedName(step_info['handler'])
+
+            dependencies = tuple(step_info.get('dependencies', ()))
+            title = step_info.get('title', id)
+            description = ''.join(step_info.get('description', []))
+
+            self._import_registry.registerStep(id=id,
+                                               version=version,
+                                               handler=handler,
+                                               dependencies=dependencies,
+                                               title=title,
+                                               description=description,
+                                              )
+
+    security.declarePrivate('_updateExportStepsRegistry')
+    def _updateExportStepsRegistry(self, encoding):
+
+        """ Update our export steps registry from our profile.
+        """
+        context = self._getImportContext(self._import_context_id)
+        xml = context.readDataFile(EXPORT_STEPS_XML)
+        if xml is None:
+            return
+
+        info_list = self._export_registry.parseXML(xml, encoding)
+
+        for step_info in info_list:
+
+            id = step_info['id']
+            handler = _resolveDottedName(step_info['handler'])
+
+            title = step_info.get('title', id)
+            description = ''.join(step_info.get('description', []))
+
+            self._export_registry.registerStep(id=id,
+                                               handler=handler,
+                                               title=title,
+                                               description=description,
+                                              )
+
+    security.declarePrivate('_updateToolsetRegistry')
+    def _updateToolsetRegistry(self, encoding):
+
+        """ Update our toolset registry from our profile.
+        """
+        context = self._getImportContext(self._import_context_id)
+        xml = context.readDataFile(TOOLSET_XML)
+        if xml is None:
+            return
+
+        self._toolset_registry.parseXML(xml, encoding)
+
+    security.declarePrivate('_doRunImportStep')
+    def _doRunImportStep(self, step_id, context):
+
+        """ Run a single import step, using a pre-built context.
+        """
+        handler = self._import_registry.getStep(step_id)
+
+        if handler is None:
+            raise ValueError('Invalid import step: %s' % step_id)
+
+        return handler(context)
+
+    security.declarePrivate('_doRunExportSteps')
+    def _doRunExportSteps(self, steps):
+
+        """ See ISetupTool.
+        """
+        context = TarballExportContext(self)
+        messages = {}
+
+        for step_id in steps:
+
+            handler = self._export_registry.getStep(step_id)
+
+            if handler is None:
+                raise ValueError('Invalid export step: %s' % step_id)
+
+            messages[step_id] = handler(context)
+
+
+        return { 'steps' : steps
+               , 'messages' : messages
+               , 'tarball' : context.getArchive()
+               , 'filename' : context.getArchiveFilename()
+               }
+
+InitializeClass(SetupTool)
+
+_PLAINTEXT_DIFF_HEADER ="""\
+Comparing configurations: '%s' and '%s'
+
+%s"""
+
+addSetupToolForm = PageTemplateFile('toolAdd.zpt', _wwwdir)
+
+def addSetupTool(dispatcher, RESPONSE):
+    """
+    """
+    tool = SetupTool()
+    dispatcher._setObject(tool.id, tool)
+
+    RESPONSE.redirect('%s/manage_main' % dispatcher.absolute_url())

Added: CMF/branches/goldegg-phase-1/GenericSetup/utils.py
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/utils.py	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/utils.py	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,514 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+""" GenericSetup product utilities
+
+$Id: utils.py,v 1.1.1.1 2005/08/08 19:38:37 tseaver Exp $
+"""
+
+import os
+from inspect import getdoc
+from xml.dom.minidom import parseString as domParseString
+from xml.sax.handler import ContentHandler
+
+import Products
+from AccessControl import ClassSecurityInfo
+from Acquisition import aq_base
+from Acquisition import Implicit
+from Globals import InitializeClass
+from Globals import package_home
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+
+from exceptions import BadRequest
+from permissions import ManagePortal
+
+
+_pkgdir = package_home( globals() )
+_wwwdir = os.path.join( _pkgdir, 'www' )
+_datadir = os.path.join( _pkgdir, 'data' )
+_xmldir = os.path.join( _pkgdir, 'xml' )
+
+CONVERTER, DEFAULT, KEY = range(3)
+
+
+def _getDottedName( named ):
+
+    if isinstance( named, basestring ):
+        return str( named )
+
+    try:
+        return '%s.%s' % ( named.__module__, named.__name__ )
+    except AttributeError:
+        raise ValueError, 'Cannot compute dotted name: %s' % named
+
+def _resolveDottedName( dotted ):
+
+    parts = dotted.split( '.' )
+
+    if not parts:
+        raise ValueError, "incomplete dotted name: %s" % dotted
+
+    parts_copy = parts[:]
+
+    while parts_copy:
+        try:
+            module = __import__( '.'.join( parts_copy ) )
+            break
+
+        except ImportError:
+
+            del parts_copy[ -1 ]
+
+            if not parts_copy:
+                raise
+
+    parts = parts[ 1: ] # Funky semantics of __import__'s return value
+
+    obj = module
+
+    for part in parts:
+        obj = getattr( obj, part )
+
+    return obj
+
+def _extractDocstring( func, default_title, default_description ):
+
+    try:
+        doc = getdoc( func )
+        lines = doc.split( '\n' )
+
+    except AttributeError:
+
+        title = default_title
+        description = default_description
+
+    else:
+        title = lines[ 0 ]
+
+        if len( lines ) > 1 and lines[ 1 ].strip() == '':
+            del lines[ 1 ]
+
+        description = '\n'.join( lines[ 1: ] )
+
+    return title, description
+
+
+class HandlerBase( ContentHandler ):
+
+    _encoding = None
+    _MARKER = object()
+
+    def _extract( self, attrs, key, default=None ):
+
+        result = attrs.get( key, self._MARKER )
+
+        if result is self._MARKER:
+            return default
+
+        return self._encode( result )
+
+    def _extractBoolean( self, attrs, key, default ):
+
+        result = attrs.get( key, self._MARKER )
+
+        if result is self._MARKER:
+            return default
+
+        result = result.lower()
+        return result in ( '1', 'yes', 'true' )
+
+    def _encode( self, content ):
+
+        if self._encoding is None:
+            return content
+
+        return content.encode( self._encoding )
+
+
+class ImportConfiguratorBase(Implicit):
+    """ Synthesize data from XML description.
+    """
+    security = ClassSecurityInfo()
+    security.setDefaultAccess('allow')
+
+    def __init__(self, site, encoding=None):
+
+        self._site = site
+        self._encoding = encoding
+
+    security.declareProtected(ManagePortal, 'parseXML')
+    def parseXML(self, xml):
+        """ Pseudo API.
+        """
+        reader = getattr(xml, 'read', None)
+
+        if reader is not None:
+            xml = reader()
+
+        dom = domParseString(xml)
+        root = dom.documentElement
+
+        return self._extractNode(root)
+
+    def _extractNode(self, node):
+
+        nodes_map = self._getImportMapping()
+        if node.nodeName not in nodes_map:
+            nodes_map = self._getSharedImportMapping()
+            if node.nodeName not in nodes_map:
+                raise ValueError('Unknown node: %s' % node.nodeName)
+        node_map = nodes_map[node.nodeName]
+        info = {}
+
+        for name, val in node.attributes.items():
+            key = node_map[name].get( KEY, str(name) )
+            val = self._encoding and val.encode(self._encoding) or val
+            info[key] = val
+
+        for child in node.childNodes:
+            name = child.nodeName
+
+            if name == '#comment':
+                continue
+
+            if not name == '#text':
+                key = node_map[name].get(KEY, str(name) )
+                info[key] = info.setdefault( key, () ) + (
+                                                    self._extractNode(child),)
+
+            elif '#text' in node_map:
+                key = node_map['#text'].get(KEY, 'value')
+                val = child.nodeValue.lstrip()
+                val = self._encoding and val.encode(self._encoding) or val
+                info[key] = info.setdefault(key, '') + val
+
+        for k, v in node_map.items():
+            key = v.get(KEY, k)
+
+            if DEFAULT in v and not key in info:
+                if isinstance( v[DEFAULT], basestring ):
+                    info[key] = v[DEFAULT] % info
+                else:
+                    info[key] = v[DEFAULT]
+
+            elif CONVERTER in v and key in info:
+                info[key] = v[CONVERTER]( info[key] )
+
+            if key is None:
+                info = info[key]
+
+        return info
+
+    def _getSharedImportMapping(self):
+
+        return {
+          'object':
+            { 'i18n:domain':     {},
+              'name':            {KEY: 'id'},
+              'meta_type':       {},
+              'insert-before':   {},
+              'insert-after':    {},
+              'property':        {KEY: 'properties', DEFAULT: ()},
+              'object':          {KEY: 'objects', DEFAULT: ()},
+              'xmlns:i18n':      {} },
+          'property':
+            { 'name':            {KEY: 'id'},
+              '#text':           {KEY: 'value', DEFAULT: ''},
+              'element':         {KEY: 'elements', DEFAULT: ()},
+              'type':            {},
+              'select_variable': {},
+              'i18n:translate':  {} },
+          'element':
+            { 'value':           {KEY: None} },
+          'description':
+            { '#text':           {KEY: None, DEFAULT: ''} } }
+
+    def _convertToBoolean(self, val):
+
+        return val.lower() in ('true', 'yes', '1')
+
+    def _convertToUnique(self, val):
+
+        assert len(val) == 1
+        return val[0]
+
+    security.declareProtected(ManagePortal, 'initObject')
+    def initObject(self, parent, o_info):
+
+        obj_id = str(o_info['id'])
+        if obj_id not in parent.objectIds():
+            meta_type = o_info['meta_type']
+            for mt_info in Products.meta_types:
+                if mt_info['name'] == meta_type:
+                    parent._setObject( obj_id, mt_info['instance'](obj_id) )
+                    break
+            else:
+                raise ValueError('unknown meta_type \'%s\'' % obj_id)
+        obj = parent._getOb(obj_id)
+
+        if 'insert-before' in o_info:
+            if o_info['insert-before'] == '*':
+                parent.moveObjectsToTop(obj_id)
+            else:
+                try:
+                    position = parent.getObjectPosition(o_info['insert-before'])
+                    parent.moveObjectToPosition(obj_id, position)
+                except ValueError:
+                    pass
+        elif 'insert-after' in o_info:
+            if o_info['insert-after'] == '*':
+                parent.moveObjectsToBottom(obj_id)
+            else:
+                try:
+                    position = parent.getObjectPosition(o_info['insert-after'])
+                    parent.moveObjectToPosition(obj_id, position+1)
+                except ValueError:
+                    pass
+
+        [ self.initObject(obj, info) for info in o_info['objects'] ]
+
+        if 'i18n:domain' in o_info:
+            obj.i18n_domain = o_info['i18n:domain']
+
+        [ self.initProperty(obj, info) for info in o_info['properties'] ]
+
+    security.declareProtected(ManagePortal, 'initProperty')
+    def initProperty(self, obj, p_info):
+
+        prop_id = p_info['id']
+        prop_map = obj.propdict().get(prop_id, None)
+
+        if prop_map is None:
+            type = p_info.get('type', None)
+            if type:
+                val = p_info.get('select_variable', '')
+                obj._setProperty(prop_id, val, type)
+                prop_map = obj.propdict().get(prop_id, None)
+            else:
+                raise ValueError('undefined property \'%s\'' % prop_id)
+
+        if not 'w' in prop_map.get('mode', 'wd'):
+            raise BadRequest('%s cannot be changed' % prop_id)
+
+        if prop_map.get('type') == 'multiple selection':
+            prop_value = p_info['elements'] or ()
+        elif prop_map.get('type') == 'boolean':
+            # Make sure '0' is imported as False
+            prop_value = str(p_info['value'])
+            if prop_value == '0':
+                prop_value = ''
+        else:
+            # if we pass a *string* to _updateProperty, all other values
+            # are converted to the right type
+            prop_value = p_info['elements'] or str( p_info['value'] )
+
+        obj._updateProperty(prop_id, prop_value)
+
+InitializeClass(ImportConfiguratorBase)
+
+
+class ExportConfiguratorBase(Implicit):
+    """ Synthesize XML description.
+    """
+    security = ClassSecurityInfo()
+    security.setDefaultAccess('allow')
+
+    def __init__(self, site, encoding=None):
+
+        self._site = site
+        self._encoding = encoding
+        self._template = self._getExportTemplate()
+
+    security.declareProtected(ManagePortal, 'generateXML')
+    def generateXML(self, **kw):
+        """ Pseudo API.
+        """
+        return self._template(**kw)
+
+    #
+    #   generic object and property support
+    #
+    _ob_nodes = PageTemplateFile('object_nodes.xml', _xmldir)
+    _prop_nodes = PageTemplateFile('property_nodes.xml', _xmldir)
+
+    security.declareProtected(ManagePortal, 'generateObjectNodes')
+    def generateObjectNodes(self, obj_infos):
+        """ Pseudo API.
+        """
+        lines = self._ob_nodes(objects=obj_infos).splitlines()
+        return '\n'.join(lines)
+
+    security.declareProtected(ManagePortal, 'generatePropertyNodes')
+    def generatePropertyNodes(self, prop_infos):
+        """ Pseudo API.
+        """
+        lines = self._prop_nodes(properties=prop_infos).splitlines()
+        return '\n'.join(lines)
+
+    def _extractObject(self, obj):
+
+        properties = []
+        subobjects = []
+        i18n_domain = getattr(obj, 'i18n_domain', None)
+
+        if getattr( aq_base(obj), '_propertyMap' ):
+            for prop_map in obj._propertyMap():
+                prop_info = self._extractProperty(obj, prop_map)
+                if i18n_domain and prop_info['id'] in ('title', 'description'):
+                    prop_info['i18ned'] = ''
+                if prop_info['id'] != 'i18n_domain':
+                    properties.append(prop_info)
+
+        if getattr( aq_base(obj), 'objectValues' ):
+            for sub in obj.objectValues():
+                subobjects.append( self._extractObject(sub) )
+
+        return { 'id': obj.getId(),
+                 'meta_type': obj.meta_type,
+                 'i18n_domain': i18n_domain or None,
+                 'properties': tuple(properties),
+                 'subobjects': tuple(subobjects) }
+
+    def _extractProperty(self, obj, prop_map):
+
+        prop_id = prop_map['id']
+        prop = obj.getProperty(prop_id)
+
+        if isinstance(prop, tuple):
+            prop_value = ''
+            prop_elements = prop
+        elif isinstance(prop, list):
+            # Backward compat for old instances that stored
+            # properties as list.
+            prop_value = ''
+            prop_elements = tuple(prop)
+        else:
+            prop_value = prop
+            prop_elements = ()
+
+        if 'd' in prop_map.get('mode', 'wd') and not prop_id == 'title':
+            type = prop_map.get('type', 'string')
+            select_variable = prop_map.get('select_variable', None)
+        else:
+            type = None
+            select_variable = None
+
+        return { 'id': prop_id,
+                 'value': prop_value,
+                 'elements': prop_elements,
+                 'type': type,
+                 'select_variable': select_variable }
+
+InitializeClass(ExportConfiguratorBase)
+
+
+# BBB: old class mixing the two, will be removed in CMF 1.7
+class ConfiguratorBase(ImportConfiguratorBase, ExportConfiguratorBase):
+    """ Synthesize XML description.
+    """
+    security = ClassSecurityInfo()
+    security.setDefaultAccess('allow')
+
+    def __init__(self, site, encoding=None):
+        ImportConfiguratorBase.__init__(self, site, encoding)
+        ExportConfiguratorBase.__init__(self, site, encoding)
+
+InitializeClass(ConfiguratorBase)
+
+
+#
+#   deprecated DOM parsing utilities
+#
+_marker = object()
+
+def _queryNodeAttribute( node, attr_name, default, encoding=None ):
+
+    """ Extract a string-valued attribute from node.
+
+    o Return 'default' if the attribute is not present.
+    """
+    attr_node = node.attributes.get( attr_name, _marker )
+
+    if attr_node is _marker:
+        return default
+
+    value = attr_node.nodeValue
+
+    if encoding is not None:
+        value = value.encode( encoding )
+
+    return value
+
+def _getNodeAttribute( node, attr_name, encoding=None ):
+
+    """ Extract a string-valued attribute from node.
+    """
+    value = _queryNodeAttribute( node, attr_name, _marker, encoding )
+
+    if value is _marker:
+        raise ValueError, 'Invaid attribute: %s' % attr_name
+
+    return value
+
+def _queryNodeAttributeBoolean( node, attr_name, default ):
+
+    """ Extract a string-valued attribute from node.
+
+    o Return 'default' if the attribute is not present.
+    """
+    attr_node = node.attributes.get( attr_name, _marker )
+
+    if attr_node is _marker:
+        return default
+
+    value = node.attributes[ attr_name ].nodeValue.lower()
+
+    return value in ( 'true', 'yes', '1' )
+
+def _getNodeAttributeBoolean( node, attr_name ):
+
+    """ Extract a string-valued attribute from node.
+    """
+    value = node.attributes[ attr_name ].nodeValue.lower()
+
+    return value in ( 'true', 'yes', '1' )
+
+def _coalesceTextNodeChildren( node, encoding=None ):
+
+    """ Concatenate all childe text nodes into a single string.
+    """
+    from xml.dom import Node
+    fragments = []
+    node.normalize()
+    child = node.firstChild
+
+    while child is not None:
+
+        if child.nodeType == Node.TEXT_NODE:
+            fragments.append( child.nodeValue )
+
+        child = child.nextSibling
+
+    joined = ''.join( fragments )
+
+    if encoding is not None:
+        joined = joined.encode( encoding )
+
+    return ''.join( [ line.lstrip() for line in joined.splitlines(True) ] )
+
+def _extractDescriptionNode(parent, encoding=None):
+
+    d_nodes = parent.getElementsByTagName('description')
+    if d_nodes:
+        return _coalesceTextNodeChildren(d_nodes[0], encoding)
+    else:
+        return ''

Added: CMF/branches/goldegg-phase-1/GenericSetup/version.txt
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/version.txt	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/version.txt	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1 @@
+GenericSetup-1.0

Added: CMF/branches/goldegg-phase-1/GenericSetup/www/siteAddForm.zpt
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/www/siteAddForm.zpt	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/www/siteAddForm.zpt	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,51 @@
+<h1 tal:replace="structure context/manage_page_header">PAGE HEADER</h1>
+<h2 tal:define="form_title string:Add Configured CMF Site"
+    tal:replace="structure context/manage_form_title">FORM TITLE</h2>
+
+<p class="form-help">Please select ID and configuration for the new site.</p>
+
+<form action="addConfiguredSite" method="post">
+<table cellspacing="0" cellpadding="2" border="0">
+ <tr valign="top">
+  <td>
+   <div class="form-label">Site ID</div>
+  </td>
+  <td>
+   <input type="text" name="site_id" />
+  </td>
+ </tr>
+ <tr valign="top">
+  <td>
+   <div class="form-label">Setup profile</div>
+  </td>
+  <td>
+    <select name="profile_id">
+      <option value="PROFILE_ID"
+              tal:repeat="info options/base_profiles"
+              tal:attributes="value info/id"
+              tal:content="info/title">PROFILE TITLE</option>
+    </select>
+  </td>
+ </tr>
+ <tr valign="top">
+  <td>
+   <div class="form-label">Optional extensions</div>
+  </td>
+  <td><tal:span tal:repeat="info options/extension_profiles">
+   <input type="checkbox" name="extension_ids:list" value="PROFILE_ID"
+          tal:attributes="value info/id" />
+   <tal:span tal:content="info/title">PROFILE TITLE</tal:span><br /></tal:span>
+  </td>
+ </tr>
+ <tr>
+  <td>
+   &nbsp;
+  </td>
+  <td>
+   <input class="form-element" type="submit" name="submit" value="Add" /> 
+  </td>
+ </tr>
+</table>
+</form>
+
+<h1 tal:replace="structure context/manage_page_footer">PAGE FOOTER</h1>

Added: CMF/branches/goldegg-phase-1/GenericSetup/www/sutCompare.zpt
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/www/sutCompare.zpt	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/www/sutCompare.zpt	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,136 @@
+<h1 tal:replace="structure context/manage_page_header">PAGE HEADER</h1>
+<h2 tal:replace="structure context/manage_tabs">TABS</h2>
+
+<style type="text/css">
+.file-to-dir {
+    font-weight: bold;
+    background-color: rgb(255, 182, 193);
+}
+
+.file-added {
+    font-weight: bold;
+    color: rgb(0, 139, 139);
+}
+
+.file-removed {
+    font-weight: bold;
+    color: rgb(0, 0, 139);
+}
+
+.dir-to-file {
+    font-weight: bold;
+    background-color: rgb(255, 182, 193);
+}
+
+.dir-added {
+    font-weight: bold;
+    color: rgb(0, 139, 139);
+}
+
+.dir-removed {
+    font-weight: bold;
+    color: rgb(0, 0, 139);
+}
+
+.diff-range {
+    background-color: rgb(244, 244, 244);
+    color: rgb(165, 42, 42);
+    font-weight: bold;
+}
+
+.diff-context {
+    background-color: rgb(244, 244, 244);
+}
+
+.diff-added {
+    background-color: rgb(244, 244, 244);
+    color: rgb(0, 139, 139);
+}
+
+.diff-removed {
+    background-color: rgb(244, 244, 244);
+    color: rgb(0, 0, 139);
+}
+
+.diff-header {
+    font-weight: bold;
+}
+</style>
+
+<div tal:define="lhs request/lhs | string:;
+                 rhs request/rhs | string:;
+                 ">
+
+<h3> Setup Tool </h3>
+
+<p class="form-help">
+By selecting two snapshots (or a snapshot and a filesystem setup directory), a
+comparison can be made, highlighting the differences between the two
+configuration sets.
+</p>
+
+<p>Configurations to compare:</p>
+<form method="post" action="."
+      tal:attributes="action string:${context/absolute_url}">
+
+<select name="lhs">
+ <option value="context-CONTEXT_ID"
+    tal:repeat="context_info context/listContextInfos"
+    tal:attributes="selected python:lhs == context_info['id'];
+                    value context_info/id"
+    tal:content="context_info/title"
+ >CONTEXT_TITLE</option>
+</select>
+
+<select name="rhs">
+ <option value="context-CONTEXT_ID"
+    tal:repeat="context_info context/listContextInfos"
+    tal:attributes="selected python:rhs == context_info['id'];
+                    value context_info/id"
+    tal:content="context_info/title"
+ >CONTEXT_TITLE</option>
+</select>
+
+<br />
+
+<input type="hidden" name="missing_as_empty:int:default" value="0" />
+<input type="checkbox" name="missing_as_empty:boolean" value="1"
+       tal:attributes="checked request/missing_as_empty | nothing" />
+Treat missing files as empty 
+
+<br />
+
+<input type="hidden" name="ignore_blanks:int:default" value="0" />
+<input type="checkbox" name="ignore_blanks:boolean" value="1"
+       tal:attributes="checked request/ignore_blanks | nothing" />
+Ignore lines of whitespace
+
+<br />
+
+<input type="submit" name="manage_showDiff:method" value="Compare">
+<input type="submit" name="manage_downloadDiff:method" value="Download">
+
+</form>
+
+<hr />
+
+<div tal:condition="python: lhs and rhs" >
+    
+<div tal:define="mae request/missing_as_empty | nothing;
+                 ib request/ignore_blanks | nothing;
+                 mcc nocall: context/manage_compareConfigurations;
+                 comparison python:mcc( lhs, rhs, mae, ib )"
+>
+<p>
+Comparison of <span tal:replace="request/lhs">LHS</span>
+          and <span tal:replace="request/rhs">RHS</span>:</p>
+
+<span tal:replace="structure python: context.markupComparison( comparison )"
+>COMPARISON HERE</span>
+
+</div>
+</div>
+
+</div>
+
+<h1 tal:replace="structure context/manage_page_footer">PAGE FOOTER</h1>

Added: CMF/branches/goldegg-phase-1/GenericSetup/www/sutExportSteps.zpt
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/www/sutExportSteps.zpt	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/www/sutExportSteps.zpt	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,75 @@
+<h1 tal:replace="structure here/manage_page_header"> PAGE HEADER </h1>
+<h2 tal:replace="structure here/manage_tabs"> PAGE HEADER </h2>
+
+<h3> Site Configuration Export Steps </h3>
+
+
+<p class="form-help">
+Download selected export steps as tarball.
+</p>
+
+<h3>Available Export Steps</h3>
+
+<form action="." method="POST"
+      tal:attributes="action here/absolute_url" >
+<input type="hidden" name="ids:default:tokens" value="" />
+
+<table cellspacing="0" cellpadding="4">
+
+ <thead>
+  <tr class="list-header">
+   <td class="list-item">Sel.</td>
+   <td class="list-item">#</td>
+   <td class="list-item">Title / Description</td>
+   <td class="list-item">Handler</td>
+  </tr>
+ </thead>
+
+ <tbody tal:define="registry here/getExportStepRegistry;
+                    step_ids registry/listSteps;
+                   ">
+  <tal:loop tal:repeat="step_id step_ids">
+  <tr valign="top"
+      tal:define="info python: registry.getStepMetadata( step_id );"
+      tal:attributes="class python:
+                     repeat[ 'step_id' ].odd and 'row-normal' or 'row-hilite'" >
+   <td class="list-item" width="16">
+    <input type="checkbox" name="ids:list" value="STEP_ID"
+           tal:attributes="value step_id" />
+   </td>
+   <td align="right" class="list-item"
+       tal:content="repeat/step_id/number">1</td>
+   <td class="list-item">
+    <span tal:content="info/title">STEP TITLE</span><br />
+    <em tal:content="info/description">STEP DESCRIPTION</em>
+   </td>
+   <td class="list-item"
+       tal:content="info/handler">DOTTED.NAME</td>
+  </tr>
+  </tal:loop>
+
+  <tr valign="top" class="list-header">
+   <td colspan="4">&nbsp;</td>
+  </tr>
+
+  <tr valign="top">
+   <td />
+   <td colspan="3">
+
+    <input class="form-element" type="submit"
+           name="manage_exportSelectedSteps:method"
+           value=" Export selected steps " />
+      
+    <input class="form-element" type="submit"
+           name="manage_exportAllSteps:method"
+           value=" Export all steps " />
+      
+   </td>
+  </tr>
+ </tbody>
+
+</table>
+</form>
+
+
+<h1 tal:replace="structure here/manage_page_footer"> PAGE FOOTER </h1>

Added: CMF/branches/goldegg-phase-1/GenericSetup/www/sutImportSteps.zpt
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/www/sutImportSteps.zpt	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/www/sutImportSteps.zpt	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,80 @@
+<h1 tal:replace="structure here/manage_page_header"> PAGE HEADER </h1>
+<h2 tal:replace="structure here/manage_tabs"> PAGE HEADER </h2>
+
+<h2> Site Configuration Import Steps </h2>
+
+<p class="form-help">
+This tool allows one to re-run individual steps of the site setup
+procedure, in order to pick up changes since the site was created.
+</p>
+
+<h3>Available Import Steps</h3>
+
+<form action="." method="POST"
+      tal:attributes="action here/absolute_url" >
+<input type="hidden" name="ids:default:tokens" value="" />
+
+<table cellspacing="0" cellpadding="4">
+
+ <thead>
+  <tr class="list-header">
+   <td class="list-item">Sel.</td>
+   <td class="list-item">#</td>
+   <td class="list-item">Title / Description</td>
+   <td class="list-item">Handler</td>
+  </tr>
+ </thead>
+
+ <tbody tal:define="registry here/getImportStepRegistry;
+                    step_ids registry/sortSteps;
+                   ">
+  <tal:loop tal:repeat="step_id step_ids">
+  <tr valign="top"
+      tal:define="info python: registry.getStepMetadata( step_id );"
+      tal:attributes="class python:
+                     repeat[ 'step_id' ].odd and 'row-normal' or 'row-hilite'" >
+   <td class="list-item" width="16">
+    <input type="checkbox" name="ids:list" value="STEP_ID"
+           tal:attributes="value step_id" />
+   </td>
+   <td align="right" class="list-item"
+       tal:content="repeat/step_id/number">1</td>
+   <td class="list-item">
+    <span tal:content="info/title">STEP TITLE</span><br />
+    <em tal:content="info/description">STEP DESCRIPTION</em>
+   </td>
+   <td class="list-item"
+       tal:content="info/handler">DOTTED.NAME</td>
+  </tr>
+  </tal:loop>
+
+  <tr valign="top" class="list-header">
+   <td colspan="4">&nbsp;</td>
+  </tr>
+
+  <tr valign="top">
+   <td />
+   <td colspan="3">
+
+    <input type="hidden" name="run_dependencies:int:default" value="0" />
+    <input class="form-element" type="checkbox" id="run_dependencies"
+           name="run_dependencies:boolean" value="1" checked="checked" />
+    <label for="run_dependencies">Include dependencies?</label>
+    &nbsp; &nbsp;
+
+    <input class="form-element" type="submit"
+           name="manage_importSelectedSteps:method"
+           value=" Import selected steps " />
+      
+    <input class="form-element" type="submit"
+           name="manage_importAllSteps:method"
+           value=" Import all steps " />
+   </td>
+  </tr>
+ </tbody>
+
+</table>
+</form>
+
+
+<h1 tal:replace="structure here/manage_page_footer"> PAGE FOOTER </h1>

Added: CMF/branches/goldegg-phase-1/GenericSetup/www/sutProperties.zpt
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/www/sutProperties.zpt	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/www/sutProperties.zpt	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,37 @@
+<h1 tal:replace="structure context/manage_page_header">PAGE HEADER</h1>
+<h2 tal:replace="structure context/manage_tabs">TABS</h2>
+
+<h3> Setup Tool Properties </h3>
+
+<form method="post" action="manage_updateToolProperties">
+
+<table>
+
+ <tr valign="top">
+  <td>
+   <div class="form-label">Active site configuration:</div>
+  </td>
+  <td>
+   <select name="context_id"
+      tal:define="context_id context/getImportContextID">
+    <option value="context-CONTEXT_ID"
+       tal:repeat="context_info context/listContextInfos"
+       tal:attributes="selected python:context_id == context_info['id'];
+                       value context_info/id"
+       tal:content="context_info/title"
+    >CONTEXT_TITLE</option>
+   </select>
+  </td>
+ </tr>
+
+ <tr valign="top">
+  <td />
+  <td>
+   <input class="form-element" type="submit" value=" Update " />
+  </td>
+ </tr>
+
+</table>
+</form>
+
+<h1 tal:replace="structure context/manage_page_footer">PAGE FOOTER</h1>

Added: CMF/branches/goldegg-phase-1/GenericSetup/www/sutSnapshots.zpt
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/www/sutSnapshots.zpt	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/www/sutSnapshots.zpt	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,64 @@
+<h1 tal:replace="structure here/manage_page_header"> PAGE HEADER </h1>
+<h2 tal:replace="structure here/manage_tabs"> PAGE HEADER </h2>
+
+<h3> Site Configuration Snapshots </h3>
+
+
+<p class="form-help">
+Snapshots capture site configuration at a particular point in time.
+</p>
+
+<h3>Available Snapshots</h3>
+
+<form action="." method="POST"
+      tal:attributes="action here/absolute_url" >
+<input type="hidden" name="ids:default:tokens" value="" />
+
+<table cellspacing="0" cellpadding="4"
+       tal:define="snapshot_info here/listSnapshotInfo;"
+>
+
+ <thead tal:condition="not: snapshot_info">
+  <tr>
+   <td colspan="4">No snapshots exist.</td>
+  </tr>
+ </thead>
+
+ <thead tal:condition="snapshot_info">
+  <tr class="list-header">
+   <td class="list-item">Title</td>
+  </tr>
+ </thead>
+
+ <tbody >
+  <tal:loop tal:repeat="info snapshot_info">
+  <tr valign="top"
+      tal:attributes="class python:
+                       ( repeat[ 'info' ].odd and 'row-normal'
+                                               or 'row-hilite' )" >
+   <td class="list-item">
+    <a href="."
+       tal:attributes="href string:${info/url}/manage_main"
+       tal:content="info/title">STEP TITLE</a>
+   </td>
+  </tr>
+  </tal:loop>
+
+  <tr valign="top" class="list-header">
+   <td>&nbsp;</td>
+  </tr>
+
+  <tr valign="top">
+   <td>
+    <input class="form-element" type="submit"
+           name="manage_createSnapshot:method"
+           value=" Create a Snapshot " />
+   </td>
+  </tr>
+ </tbody>
+
+</table>
+</form>
+
+
+<h1 tal:replace="structure here/manage_page_footer"> PAGE FOOTER </h1>

Added: CMF/branches/goldegg-phase-1/GenericSetup/www/tool.png
===================================================================
(Binary files differ)


Property changes on: CMF/branches/goldegg-phase-1/GenericSetup/www/tool.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: CMF/branches/goldegg-phase-1/GenericSetup/www/toolAdd.zpt
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/www/toolAdd.zpt	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/www/toolAdd.zpt	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,6 @@
+<h1 tal:replace="structure context/manage_page_header">PAGE HEADER</h1>
+<h1 tal:replace="structure context/manage_tabs">TABS</h1>
+
+<h3> Add Setup Tool </h3>
+
+<h1 tal:replace="structure context/manage_page_footer">PAGE FOOTER</h1>

Added: CMF/branches/goldegg-phase-1/GenericSetup/xml/esrExport.xml
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/xml/esrExport.xml	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/xml/esrExport.xml	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<export-steps xmlns:tal="http://xml.zope.org/namespaces/tal">
+ <export-step id="STEP_ID"
+                handler="DOTTED.NAME"
+                title="TITLE"
+                tal:repeat="step here/listStepMetadata"
+                tal:attributes="id step/id;
+                                handler step/handler;
+                                title step/title
+                               ">
+  <span tal:replace="step/description">DESCRIPTION</span>
+ </export-step>
+</export-steps>

Added: CMF/branches/goldegg-phase-1/GenericSetup/xml/isrExport.xml
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/xml/isrExport.xml	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/xml/isrExport.xml	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<import-steps xmlns:tal="http://xml.zope.org/namespaces/tal">
+ <import-step id="STEP_ID"
+             version="STEP_VERSION"
+             handler="DOTTED.NAME"
+             title="TITLE"
+             tal:repeat="step here/listStepMetadata"
+             tal:attributes="id step/id;
+                             version step/version;
+                             handler step/handler;
+                             title step/title
+                            ">
+  <dependency step="DEPENDENCY"
+              tal:repeat="dep step/dependencies"
+              tal:attributes="step dep" />
+  <span tal:replace="step/description">DESCRIPTION</span>
+ </import-step>
+</import-steps>

Added: CMF/branches/goldegg-phase-1/GenericSetup/xml/object_nodes.xml
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/xml/object_nodes.xml	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/xml/object_nodes.xml	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,11 @@
+<tal:loop xmlns:tal="http://xml.zope.org/namespaces/tal"
+   tal:repeat="obj_info options/objects">
+ <object name="ID" meta_type="META TYPE"
+    tal:attributes="name obj_info/id;
+                    meta_type obj_info/meta_type"
+ ><tal:span tal:define="prop_infos obj_info/properties"
+     tal:replace="structure python: context.generatePropertyNodes(prop_infos)"
+/><tal:span tal:define="sub_infos obj_info/subobjects"
+     tal:condition="sub_infos"
+     tal:replace="structure python: context.generateObjectNodes(sub_infos)"/>
+ </object></tal:loop>

Added: CMF/branches/goldegg-phase-1/GenericSetup/xml/property_nodes.xml
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/xml/property_nodes.xml	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/xml/property_nodes.xml	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,10 @@
+<tal:loop xmlns:tal="http://xml.zope.org/namespaces/tal"
+   tal:repeat="prop_info options/properties">
+  <property name="ID"
+     tal:attributes="name prop_info/id;
+                     type prop_info/type;
+                     select_variable prop_info/select_variable"
+  ><tal:span tal:content="prop_info/value"
+ /><tal:loop tal:repeat="element prop_info/elements">
+   <element value="VALUE"
+      tal:attributes="value element"/></tal:loop></property></tal:loop>

Added: CMF/branches/goldegg-phase-1/GenericSetup/xml/rmeExport.xml
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/xml/rmeExport.xml	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/xml/rmeExport.xml	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<rolemap xmlns:tal="http://xml.zope.org/namespaces/tal">
+  <roles>
+    <role name="ROLENAME"
+          tal:repeat="role context/listRoles"
+          tal:attributes="name role"/>
+  </roles>
+  <permissions>
+    <permission name="PERMISSION NAME" acquire="True"
+                tal:repeat="info context/listPermissions"
+                tal:attributes="name info/name;
+                                acquire info/acquire;
+                               ">
+      <role name="ROLENAME"
+         tal:repeat="role info/roles"
+         tal:attributes="name role"/>
+    </permission>
+  </permissions>
+</rolemap>

Added: CMF/branches/goldegg-phase-1/GenericSetup/xml/spcExport.xml
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/xml/spcExport.xml	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/xml/spcExport.xml	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,5 @@
+<?xml version="1.0"?>
+<site xmlns:tal="http://xml.zope.org/namespaces/tal"
+><tal:span tal:define="prop_infos context/listSiteInfos"
+   tal:replace="structure python: context.generatePropertyNodes(prop_infos)"/>
+</site>

Added: CMF/branches/goldegg-phase-1/GenericSetup/xml/tscExport.xml
===================================================================
--- CMF/branches/goldegg-phase-1/GenericSetup/xml/tscExport.xml	2005-09-23 18:27:29 UTC (rev 38572)
+++ CMF/branches/goldegg-phase-1/GenericSetup/xml/tscExport.xml	2005-09-23 21:33:02 UTC (rev 38573)
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<tool-setup xmlns:tal="http://xml.zope.org/namespaces/tal">
+ <forbidden
+    tool_id="TOOL_ID"
+    tal:repeat="id here/listForbiddenTools"
+    tal:attributes="tool_id id" />
+ <required
+    tool_id="TOOL_ID"
+    class="dotted.name.of.tool.class"
+    tal:repeat="info here/listRequiredToolInfo"
+    tal:attributes="tool_id info/id;
+                    class info/class;
+                   " />
+</tool-setup>



More information about the CMF-checkins mailing list