[Zope-CVS] CVS: Products/ExternalEditor - CHANGES.txt:1.24 ExternalEditor.py:1.13 version.txt:1.6 zopeedit.py:1.33

Casey Duncan casey@zope.com
Thu, 18 Jul 2002 00:26:50 -0400


Update of /cvs-repository/Products/ExternalEditor
In directory cvs.zope.org:/tmp/cvs-serv19006

Modified Files:
	CHANGES.txt ExternalEditor.py version.txt zopeedit.py 
Log Message:
Refactored lock borrowing and locking code
Updated default config file for new option
Bug fix for editing in Squishdot


=== Products/ExternalEditor/CHANGES.txt 1.23 => 1.24 ===
 External Editor Change Log
 
+    - Fixed bug editing objects inside a Squishdot site. Thanks to Kevin Salt.
+
     - Added the capability to borrow exising DAV locks. This allows external
       editor to play well with other systems using locks, such as CMFStaging. A
       new configuration flag, always_borrow_locks can be set to suppress the


=== Products/ExternalEditor/ExternalEditor.py 1.12 => 1.13 ===
         security = getSecurityManager()
         parent = self.aq_parent
         try:
-            ob = parent[REQUEST['target']]
+            ob = parent[REQUEST['target']] # Try getitem
+        except KeyError:
+            ob = getattr(parent, REQUEST['target']) # Try getattr
         except AttributeError:
             # Handle objects that are methods in ZClasses
             ob = parent.propertysheets.methods[REQUEST['target']]


=== Products/ExternalEditor/version.txt 1.5 => 1.6 ===
-0.4.1
+0.5


=== Products/ExternalEditor/zopeedit.py 1.32 => 1.33 ===
 
 # Zope External Editor Helper Application by Casey Duncan
 
-__version__ = '0.4.2'
+__version__ = '0.5'
 
 import sys, os
 import traceback
@@ -29,10 +29,9 @@
 
 try:
     # See if ssl is available on this system
-    from socket import ssl, sslerror
+    from socket import ssl
 except ImportError:
     ssl_available = 0
-    sslerror = None # Used for catching sslserver protocol violations
 else:
     ssl_available = 1
     del ssl 
@@ -95,9 +94,6 @@
         
 class ExternalEditor:
     
-    saved = 1
-    did_lock = 0
-    
     def __init__(self, input_file):
         try:
             # Read the configuration file
@@ -157,6 +153,7 @@
             body_f = open(content_file, 'wb')
             body_f.write(in_f.read())
             self.content_file = content_file
+            self.saved = 1
             in_f.close()
             body_f.close()
             self.clean_up = int(self.options.get('cleanup_files', 1))
@@ -170,6 +167,8 @@
                 fatalError('SSL support is not available on this system. '
                            'Make sure openssl is installed '
                            'and reinstall Python.')
+            self.lock_token = None
+            self.did_lock = 0
         except:
             # for security, always delete the input file even if
             # a fatal error occurs, unless explicitly stated otherwise
@@ -188,7 +187,7 @@
             # for security we always delete the files by default
             os.remove(self.content_file)
 
-        if self.did_lock and hasattr(self, 'lock_token'):
+        if self.did_lock:
             # Try not to leave dangling locks on the server
             self.unlock(interactive=0)
             
@@ -298,8 +297,20 @@
         
     def launch(self):
         """Launch external editor"""
-        save_interval = float(self.options.get('save_interval'))
         use_locks = int(self.options.get('use_locks', 0))
+        if use_locks and self.metadata.get('lock-token'):
+            # A lock token came down with the data, so the object is
+            # already locked, see if we can borrow the lock
+            if int(self.options.get('always_borrow_locks', 0)) \
+               or askYesNo('This object is already locked by you in another'
+                           ' session.\n Do you want to borrow this lock'
+                           ' and continue?'):
+                self.lock_token = 'opaquelocktoken:%s' \
+                                  % self.metadata['lock-token']
+            else:
+                sys.exit()            
+        
+        save_interval = float(self.options.get('save_interval'))
         launch_success = 0
         last_mtime = os.path.getmtime(self.content_file)
         command = self.getEditorCommand()
@@ -310,7 +321,7 @@
         editor = EditorProcess(command)
         
         if use_locks:
-            self.did_lock = self.lock()
+            self.lock()
             
         while 1:
             editor.wait(save_interval or 2)
@@ -333,8 +344,8 @@
                        'to editor process.\n'
                        '(%s)' % command)
         
-        if use_locks and self.did_lock:
-            self.did_lock = not self.unlock()
+        if use_locks:
+            self.unlock()
         
         if not self.saved \
            and askYesNo('File not saved to Zope.\nReopen local copy?'):
@@ -342,8 +353,7 @@
         
     def putChanges(self):
         """Save changes to the file back to Zope"""
-        if int(self.options.get('use_locks', 0)) and \
-           not hasattr(self, 'lock_token'):
+        if int(self.options.get('use_locks', 0)) and self.lock_token is None:
             # We failed to get a lock initially, so try again before saving
             if not self.lock():
                 # Confirm save without lock
@@ -357,7 +367,7 @@
         headers = {'Content-Type': 
                    self.metadata.get('content_type', 'text/plain')}
         
-        if hasattr(self, 'lock_token'):
+        if self.lock_token is not None:
             headers['If'] = '<%s> (<%s>)' % (self.path, self.lock_token)
         
         response = self.zopeRequest('PUT', headers, body)
@@ -375,18 +385,8 @@
     
     def lock(self):
         """Apply a webdav lock to the object in Zope"""
-        
-        # Check and see if we already have a lock token
-        # that came down with the data
-        if self.metadata.get('lock-token'):
-            if self.options.get('always_borrow_locks') \
-               or askYesNo('This object is already locked by you in another'
-                           ' session.\n Do you want to borrow this lock?'):
-                self.lock_token = 'opaquelocktoken:%s' \
-                                  % self.metadata['lock-token']
-                return 0
-            else:
-                sys.exit()            
+        if self.lock_token is not None:
+            return 0 # Already have a lock token
         
         headers = {'Content-Type':'text/xml; charset="utf-8"',
                    'Timeout':'infinite',
@@ -412,6 +412,7 @@
             token_end = reply.find('<', token_start)
             if token_start > 0 and token_end > 0:
                 self.lock_token = reply[token_start+1:token_end]
+                self.did_lock = 1
         else:
             # We can't lock her sir!
             if response.status == 423:
@@ -422,27 +423,29 @@
             if self.askRetryAfterError(response, 
                                        'Lock request failed', 
                                        message):
-                return self.lock()
+                self.lock()
             else:
-                return 0
-        return 1
+                self.did_lock = 0
+        return self.did_lock
                     
     def unlock(self, interactive=1):
         """Remove webdav lock from edited zope object"""
-        if not hasattr(self, 'lock_token'): 
-            return 0
+        if not self.did_lock or self.lock_token is None:
+            return 0 # nothing to do
             
         headers = {'Lock-Token':self.lock_token}
         response = self.zopeRequest('UNLOCK', headers)
         
         if interactive and response.status / 100 != 2:
             # Captain, she's still locked!
-            if self.askRetryAfterError(response, 
-                                       'Unlock request failed'):
-                return self.unlock(token)
+            if self.askRetryAfterError(response, 'Unlock request failed'):
+                self.unlock(token)
             else:
-                return 0
-        return 1
+                self.did_lock = 0
+        else:
+            self.did_lock = 1
+            self.lock_token = None
+        return self.did_lock
         
     def zopeRequest(self, method, headers={}, body=''):
         """Send a request back to Zope"""
@@ -648,6 +651,12 @@
 # different users. Disable for single user use or for
 # better performance
 use_locks = 1
+
+# To suppress warnings about borrowing locks on objects
+# locked by you before you began editing you can
+# set this flag. This is useful for applications that
+# use server-side locking, like CMFStaging
+always_borrow_locks = 0
 
 # Specific settings by content-type or meta-type. Specific
 # settings override general options above. Content-type settings