[CMF-checkins] SVN: CMF/trunk/C CMFCore.FSPythonScript: Customized versions now track the "original" source from which they were customized, and can present a diff between that version and their current source text.

Tres Seaver tseaver at palladion.com
Sun Jan 22 06:58:18 EST 2006


Log message for revision 41404:
  CMFCore.FSPythonScript:  Customized versions now track the "original" source from which they were customized, and can present a diff between that version and their current source text.
  
  

Changed:
  U   CMF/trunk/CHANGES.txt
  U   CMF/trunk/CMFCore/FSPythonScript.py
  U   CMF/trunk/CMFCore/tests/test_FSPythonScript.py
  A   CMF/trunk/CMFCore/www/cpsDiff.pt

-=-
Modified: CMF/trunk/CHANGES.txt
===================================================================
--- CMF/trunk/CHANGES.txt	2006-01-21 18:34:10 UTC (rev 41403)
+++ CMF/trunk/CHANGES.txt	2006-01-22 11:58:17 UTC (rev 41404)
@@ -2,6 +2,10 @@
 
   New Features
 
+    - CMFCore.FSPythonScript:  Customized versions now track the "original"
+      source from which they were customized, and can present a diff between
+      that version and their current source text.
+
     - CMFDefault and CMFCalendar: Added locales directories with .pot files.
       A modified i18nextract.py script from Zope 3 is used to extract
       translatable strings from .py, .pt, .html and .xml files.

Modified: CMF/trunk/CMFCore/FSPythonScript.py
===================================================================
--- CMF/trunk/CMFCore/FSPythonScript.py	2006-01-21 18:34:10 UTC (rev 41403)
+++ CMF/trunk/CMFCore/FSPythonScript.py	2006-01-22 11:58:17 UTC (rev 41404)
@@ -15,6 +15,7 @@
 $Id$
 """
 
+from difflib import unified_diff
 import new
 
 from AccessControl import ClassSecurityInfo
@@ -23,6 +24,7 @@
 from Globals import DTMLFile
 from Globals import InitializeClass
 from OFS.Cache import Cacheable
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
 from Products.PythonScripts.PythonScript import PythonScript
 from Shared.DC.Scripts.Script import Script
 
@@ -43,6 +45,40 @@
     co_argcount = 0
 
 
+class CustomizedPythonScript(PythonScript):
+    """ Subclass which captures the "source" version's text.
+    """
+    #meta_type = 'Customized Python Script'  #(need permissions here)
+    security = ClassSecurityInfo()
+
+    def __init__(self, id, text):
+        self._setId(id)
+        self.write(text)
+        self.original_source = text
+
+    security.declareProtected(ViewManagementScreens, 'getDiff')
+    def getDiff(self):
+        """ Return a diff of the current source with the original source.
+        """
+        return unified_diff( self.original_source.splitlines()
+                           , self.read().splitlines()
+                           , 'original'
+                           , 'modified'
+                           , ''
+                           , ''
+                           , lineterm=""
+                           )
+
+    security.declareProtected(ViewManagementScreens, 'manage_showDiff')
+    manage_showDiff = PageTemplateFile('www/cpsDiff.pt', globals())
+
+    manage_options = (PythonScript.manage_options[:1]
+                    + ({'label': 'Diff', 'action': 'manage_showDiff'},)
+                    + PythonScript.manage_options[1:]
+                     )
+
+InitializeClass(CustomizedPythonScript)
+
 class FSPythonScript (FSObject, Script):
     """FSPythonScripts act like Python Scripts but are not directly
     modifiable from the management interface."""
@@ -77,8 +113,7 @@
 
     def _createZODBClone(self):
         """Create a ZODB (editable) equivalent of this object."""
-        obj = PythonScript(self.getId())
-        obj.write(self.read())
+        obj = CustomizedPythonScript(self.getId(), self.read())
         return obj
 
     def _readFile(self, reparse):

Modified: CMF/trunk/CMFCore/tests/test_FSPythonScript.py
===================================================================
--- CMF/trunk/CMFCore/tests/test_FSPythonScript.py	2006-01-21 18:34:10 UTC (rev 41403)
+++ CMF/trunk/CMFCore/tests/test_FSPythonScript.py	2006-01-22 11:58:17 UTC (rev 41404)
@@ -14,7 +14,8 @@
 
 $Id$
 """
-from unittest import TestSuite, makeSuite, main
+import unittest
+
 import Testing
 import Zope2
 Zope2.startup()
@@ -44,12 +45,12 @@
 
 class FSPythonScriptTests(FSPSMaker):
 
-    def test_GetSize( self ):
+    def test_get_size( self ):
         # Test get_size returns correct value
         script = self._makeOne('test1', 'test1.py')
         self.assertEqual(len(script.read()),script.get_size())
 
-    def testInitializationRaceCondition(self):
+    def test_initialization_race_condition(self):
         # Tries to exercise a former race condition where
         # FSObject._updateFromFS() set self._parsed before the
         # object was really parsed.
@@ -77,64 +78,87 @@
         FSPSMaker.setUp(self)
         SecurityTest.setUp( self )
 
-        self.root._setObject( 'portal_skins', Folder( 'portal_skins' ) )
-        self.skins = self.root.portal_skins
+    def tearDown(self):
+        SecurityTest.tearDown(self)
+        FSPSMaker.tearDown(self)
 
-        self.skins._setObject( 'custom', Folder( 'custom' ) )
-        self.custom = self.skins.custom
+    def _makeSkins(self):
 
-        self.skins._setObject( 'fsdir', Folder( 'fsdir' ) )
-        self.fsdir = self.skins.fsdir
+        root = self.root
+        root._setObject( 'portal_skins', Folder( 'portal_skins' ) )
+        tool = self.root.portal_skins
 
-        self.fsdir._setObject( 'test6'
-                             , self._makeOne( 'test6', 'test6.py' ) )
+        tool._setObject( 'custom', Folder( 'custom' ) )
+        custom = tool.custom
 
-        self.fsPS = self.fsdir.test6
+        tool._setObject( 'fsdir', Folder( 'fsdir' ) )
+        fsdir = tool.fsdir
 
+        fsdir._setObject( 'test6'
+                        , self._makeOne( 'test6', 'test6.py' ) )
+
+        fsPS = fsdir.test6
+
+        return root, tool, custom, fsdir, fsPS
+
     def test_customize( self ):
 
-        self.fsPS.manage_doCustomize( folder_path='custom' )
+        from Products.CMFCore.FSPythonScript import CustomizedPythonScript
 
-        self.assertEqual( len( self.custom.objectIds() ), 1 )
-        self.failUnless( 'test6' in self.custom.objectIds() )  
+        root, tool, custom, fsdir, fsPS = self._makeSkins()
 
+        fsPS.manage_doCustomize( folder_path='custom' )
+
+        self.assertEqual( len( custom.objectIds() ), 1 )
+        self.failUnless( 'test6' in custom.objectIds() )  
+
+        test6 = custom._getOb('test6')
+
+        self.failUnless(isinstance(test6, CustomizedPythonScript))
+        self.assertEqual(test6.original_source, fsPS.read())
+
     def test_customize_caching(self):
         # Test to ensure that cache manager associations survive customizing
+        root, tool, custom, fsdir, fsPS = self._makeSkins()
+
         cache_id = 'gofast'
-        RAMCacheManager.manage_addRAMCacheManager( self.root
+        RAMCacheManager.manage_addRAMCacheManager( root
                                                  , cache_id
                                                  , REQUEST=None
                                                  )
-        self.fsPS.ZCacheable_setManagerId(cache_id, REQUEST=None)
+        fsPS.ZCacheable_setManagerId(cache_id, REQUEST=None)
 
-        self.assertEqual(self.fsPS.ZCacheable_getManagerId(), cache_id)
+        self.assertEqual(fsPS.ZCacheable_getManagerId(), cache_id)
 
-        self.fsPS.manage_doCustomize(folder_path='custom')
-        custom_ps = self.custom.test6
+        fsPS.manage_doCustomize(folder_path='custom')
+        custom_ps = custom.test6
 
         self.assertEqual(custom_ps.ZCacheable_getManagerId(), cache_id)
 
     def test_customize_proxyroles(self):
         # Test to ensure that proxy roles survive customizing
-        self.fsPS._proxy_roles = ('Manager', 'Anonymous')
-        self.failUnless(self.fsPS.manage_haveProxy('Anonymous'))
-        self.failUnless(self.fsPS.manage_haveProxy('Manager'))
+        root, tool, custom, fsdir, fsPS = self._makeSkins()
 
-        self.fsPS.manage_doCustomize(folder_path='custom')
-        custom_ps = self.custom.test6
+        fsPS._proxy_roles = ('Manager', 'Anonymous')
+        self.failUnless(fsPS.manage_haveProxy('Anonymous'))
+        self.failUnless(fsPS.manage_haveProxy('Manager'))
+
+        fsPS.manage_doCustomize(folder_path='custom')
+        custom_ps = custom.test6
         self.failUnless(custom_ps.manage_haveProxy('Anonymous'))
         self.failUnless(custom_ps.manage_haveProxy('Manager'))
 
     def test_customization_permissions(self):
         # Test to ensure that permission settings survive customizing
+        root, tool, custom, fsdir, fsPS = self._makeSkins()
         perm = 'View management screens'
 
         # First, set a permission to an odd role and verify
-        self.fsPS.manage_permission( perm
-                                   , roles=('Anonymous',)
-                                   , acquire=0
-                                   )
-        rop = self.fsPS.rolesOfPermission(perm)
+        fsPS.manage_permission( perm
+                              , roles=('Anonymous',)
+                              , acquire=0
+                              )
+        rop = fsPS.rolesOfPermission(perm)
         for rop_info in rop:
             if rop_info['name'] == 'Anonymous':
                 self.failIf(rop_info['selected'] == '')
@@ -142,8 +166,8 @@
                 self.failUnless(rop_info['selected'] == '')
 
         # Now customize and verify again
-        self.fsPS.manage_doCustomize(folder_path='custom')
-        custom_ps = self.custom.test6
+        fsPS.manage_doCustomize(folder_path='custom')
+        custom_ps = custom.test6
         rop = custom_ps.rolesOfPermission(perm)
         for rop_info in rop:
             if rop_info['name'] == 'Anonymous':
@@ -151,16 +175,73 @@
             else:
                 self.failUnless(rop_info['selected'] == '')
 
-    def tearDown(self):
-        SecurityTest.tearDown(self)
-        FSPSMaker.tearDown(self)
+_ORIGINAL_TEXT = """\
+## Script (Python) "cps"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=
+##title=
+##
+return 'cps'
+"""
 
+_REPLACEMENT_TEXT = """\
+## Script (Python) "cps"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=
+##title=
+##
+return 'cps -- replaced'
+"""
 
+_DIFF_TEXT = """\
+--- original 
++++ modified 
+@@ -7,4 +7,4 @@
+ ##parameters=
+ ##title=
+ ##
+-return 'cps'
++return 'cps -- replaced'
+"""
+
+class CustomizedPythonScriptTests(unittest.TestCase):
+
+    def _getTargetClass(self):
+        from Products.CMFCore.FSPythonScript import CustomizedPythonScript
+        return CustomizedPythonScript
+
+    def _makeOne(self, id, text):
+        return self._getTargetClass()(id, text)
+
+    def test_write_leaves_original_source(self):
+        cps = self._makeOne('cps', _ORIGINAL_TEXT)
+        self.assertEqual(cps.read(), _ORIGINAL_TEXT)
+        self.assertEqual(cps.original_source, _ORIGINAL_TEXT)
+        cps.write(_REPLACEMENT_TEXT)
+        self.assertEqual(cps.read(), _REPLACEMENT_TEXT)
+        self.assertEqual(cps.original_source, _ORIGINAL_TEXT)
+
+    def test_getDiff(self):
+        cps = self._makeOne('cps', _ORIGINAL_TEXT)
+        self.assertEqual(len(list(cps.getDiff())), 0)
+
+        cps.write(_REPLACEMENT_TEXT)
+        self.assertEqual(list(cps.getDiff()), _DIFF_TEXT.splitlines())
+
 def test_suite():
-    return TestSuite((
-        makeSuite(FSPythonScriptTests),
-        makeSuite(FSPythonScriptCustomizationTests),
+    return unittest.TestSuite((
+        unittest.makeSuite(FSPythonScriptTests),
+        unittest.makeSuite(FSPythonScriptCustomizationTests),
+        unittest.makeSuite(CustomizedPythonScriptTests),
         ))
 
 if __name__ == '__main__':
-    main(defaultTest='test_suite')
+    unittest.main(defaultTest='test_suite')

Added: CMF/trunk/CMFCore/www/cpsDiff.pt
===================================================================
--- CMF/trunk/CMFCore/www/cpsDiff.pt	2006-01-21 18:34:10 UTC (rev 41403)
+++ CMF/trunk/CMFCore/www/cpsDiff.pt	2006-01-22 11:58:17 UTC (rev 41404)
@@ -0,0 +1,19 @@
+<h1 tal:replace="structure context/manage_page_header">Header</h1>
+<h2 tal:define="manage_tabs_message options/manage_tabs_message | nothing"
+    tal:replace="structure context/manage_tabs">Tabs</h2>
+
+<h3>Diff of Customized vs. Original Source</h3>
+
+<div tal:define="diffLines python:list(context.getDiff());
+                ">
+<pre tal:condition="diffLines"
+     tal:content="python: '\n'.join(diffLines)">
+DIFF GOES HERE
+</pre>
+
+<p tal:condition="not: diffLines"><em> No changes. </em></p>
+
+</div>
+
+<h1 tal:replace="structure context/manage_page_footer">Footer</h1>
+


Property changes on: CMF/trunk/CMFCore/www/cpsDiff.pt
___________________________________________________________________
Name: svn:eol-style
   + native



More information about the CMF-checkins mailing list