[CMF-checkins] CVS: CMF/CMFSetup - context.py:1.11 interfaces.py:1.13 tool.py:1.16

Tres Seaver tseaver at zope.com
Tue Jul 20 12:14:23 EDT 2004


Update of /cvs-repository/CMF/CMFSetup
In directory cvs.zope.org:/tmp/cvs-serv27771

Modified Files:
	context.py interfaces.py tool.py 
Log Message:


  - interfaces.py:

    o Modify API for 'compareConfigurations', which should now be passed
      IimportContext implementations for lhs / rhs arguments.

  - context.py:

    o Rename ImportContext -> DirectoryImportContext.

    o Rename ExportContext -> ExrectoryImportContext.

    o Fix glitch in DirectoryImportContext which broke 'listDirectory'
      for the profile root.

    o Use 'read()' instead of 'manage_FTPget' to fetch content for objects
      in SnapshotImportContexts;  note that this may not be general enough
      for all cases (but it should work for PythonScripts, and *does* work
      for PageTemplates).

  - tool/py:

    o Add UI for displaying / downloading diffs of configurations (profiles
      and snapshots).


=== CMF/CMFSetup/context.py 1.10 => 1.11 ===
--- CMF/CMFSetup/context.py:1.10	Mon Jul 19 14:04:18 2004
+++ CMF/CMFSetup/context.py	Tue Jul 20 12:13:52 2004
@@ -1,4 +1,4 @@
-""" Classes:  ImportContext, ExportContext
+""" Various context implementations for export / import of configurations.
 
 Wrappers representing the state of an import / export operation.
 
@@ -26,7 +26,7 @@
 from interfaces import IImportContext
 from interfaces import IExportContext
 
-class ImportContext( Implicit ):
+class DirectoryImportContext( Implicit ):
 
     __implements__ = ( IImportContext, )
 
@@ -106,6 +106,9 @@
 
         """ 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 ):
@@ -122,9 +125,9 @@
         """
         return self._should_purge
 
-InitializeClass( ImportContext )
+InitializeClass( DirectoryImportContext )
 
-class ExportContext( Implicit ):
+class DirectoryExportContext( Implicit ):
 
     __implements__ = ( IExportContext, )
 
@@ -163,7 +166,7 @@
         file.write( text )
         file.close()
 
-InitializeClass( ExportContext )
+InitializeClass( DirectoryExportContext )
 
 class TarballExportContext( Implicit ):
 
@@ -366,7 +369,8 @@
         except ( AttributeError, KeyError ):
             return None
         else:
-            return object.manage_FTPget()
+            # XXX:  this API may not be general enough for all objects.
+            return object.read()
 
     security.declareProtected( ManagePortal, 'getLastModified' )
     def getLastModified( self, path ):


=== CMF/CMFSetup/interfaces.py 1.12 => 1.13 ===
--- CMF/CMFSetup/interfaces.py:1.12	Thu Jul  1 19:14:23 2004
+++ CMF/CMFSetup/interfaces.py	Tue Jul 20 12:13:52 2004
@@ -478,17 +478,14 @@
         o 'snapshot_id' is the ID of the new folder.
         """
 
-    def compareConfigurations( lhs_id
-                             , rhs_id
+    def compareConfigurations( lhs_context
+                             , rhs_context
                              , missing_as_empty=False
                              , ignore_whitespace=False
                              ):
         """ Compare two configurations.
 
-        o 'lhs_id' and 'rhs_id', if None, refer to the "default" filesystem
-          configuration.
-
-        o Otherwise, 'lhs_id' and 'rhs_id' refer to snapshots.
+        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.


=== CMF/CMFSetup/tool.py 1.15 => 1.16 ===
--- CMF/CMFSetup/tool.py:1.15	Wed Jun 30 14:58:51 2004
+++ CMF/CMFSetup/tool.py	Tue Jul 20 12:13:52 2004
@@ -4,6 +4,7 @@
 """
 import os
 import time
+from cgi import escape
 
 from AccessControl import ClassSecurityInfo
 from Acquisition import aq_base
@@ -16,12 +17,15 @@
 
 from interfaces import ISetupTool
 from permissions import ManagePortal
-from context import ImportContext
+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
@@ -149,13 +153,8 @@
         """ See ISetupTool.
         """
         if product_name is not None:
-            try:
-                product = __import__( 'Products.%s' % product_name
-                                    , globals(), {}, ['initialize' ] )
-            except ImportError:
-                raise ValueError, 'Not a valid product name: %s' % product_name
 
-            root = self._root_directory = product.__path__[0]
+            root = self._root_directory = self._getProductPath( product_name )
 
             if not os.path.exists( os.path.join( root, path ) ):
                 raise ValueError, 'Invalid path: %s' % path
@@ -200,7 +199,7 @@
         """ See ISetupTool.
         """
         profile_path = self._getFullyQualifiedProfileDirectory()
-        context = ImportContext( self, profile_path, purge_old )
+        context = DirectoryImportContext( self, profile_path, purge_old )
 
         info = self._import_registry.getStepMetadata( step_id )
 
@@ -231,7 +230,7 @@
         """ See ISetupTool.
         """
         profile_path = self._getFullyQualifiedProfileDirectory()
-        context = ImportContext( self, profile_path, purge_old )
+        context = DirectoryImportContext( self, profile_path, purge_old )
 
         steps = self._import_registry.sortSteps()
         messages = {}
@@ -283,21 +282,70 @@
 
     security.declareProtected(ManagePortal, 'compareConfigurations')
     def compareConfigurations( self   
-                             , source1
-                             , source2
+                             , lhs_context
+                             , rhs_context
                              , missing_as_empty=False
-                             , ignore_whitespace=False
+                             , ignore_blanks=False
+                             , skip=( 'CVS', '.svn' )
                              ):
         """ See ISetupTool.
         """
-        raise NotImplementedError
+        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.
         """
-        raise NotImplementedError
+        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
@@ -315,6 +363,9 @@
                        , { 'label' : 'Snapshots'
                          , 'action' : 'manage_snapshots'
                          }
+                       , { 'label' : 'Comparison'
+                         , 'action' : 'manage_showDiff'
+                         }
                        )
                      + Folder.manage_options[ 3: ] # skip "View", "Properties"
                      )
@@ -436,8 +487,6 @@
           'title' -- snapshot title or ID
 
           'url' -- URL of the snapshot folder
-
-        o ZMI support.
         """
         result = []
         snapshots = self._getOb( 'snapshots', None )
@@ -452,6 +501,25 @@
                                } )
         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, 'manage_createSnapshot' )
     def manage_createSnapshot( self, RESPONSE, snapshot_id=None ):
 
@@ -468,10 +536,85 @@
         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 ):
+
+        """ Crack ID and generate appropriate import context.
+        """
+        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' ]
+
+            return DirectoryImportContext( self, path )
+        
+        # else snapshot
+        context_id = context_id[ len( 'snapshot-' ): ]
+        return SnapshotImportContext( self, context_id )
+
     security.declarePrivate( '_getFullyQualifiedProfileDirectory' )
     def _getFullyQualifiedProfileDirectory( self ):
 
@@ -591,3 +734,8 @@
                }
 
 InitializeClass( SetupTool )
+
+_PLAINTEXT_DIFF_HEADER ="""\
+Comparing configurations: '%s' and '%s'
+
+%s"""



More information about the CMF-checkins mailing list