[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