r665 - in zope-externaleditor/trunk: . debian tests win32
Fabio Tranchitella
kobold at alioth.debian.org
Fri Feb 9 15:51:13 CET 2007
Author: kobold
Date: 2007-02-09 15:51:12 +0100 (Fri, 09 Feb 2007)
New Revision: 665
Modified:
zope-externaleditor/trunk/CHANGES.txt
zope-externaleditor/trunk/ExternalEditor.py
zope-externaleditor/trunk/debian/changelog
zope-externaleditor/trunk/manage_main.dtml
zope-externaleditor/trunk/tests/edit.txt
zope-externaleditor/trunk/tests/test_functional.py
zope-externaleditor/trunk/version.txt
zope-externaleditor/trunk/win32/buildexe.bat
zope-externaleditor/trunk/win32/setup.iss
zope-externaleditor/trunk/zopeedit.py
Log:
New upstream release.
Modified: zope-externaleditor/trunk/CHANGES.txt
===================================================================
--- zope-externaleditor/trunk/CHANGES.txt 2007-02-09 14:50:20 UTC (rev 664)
+++ zope-externaleditor/trunk/CHANGES.txt 2007-02-09 14:51:12 UTC (rev 665)
@@ -1,5 +1,12 @@
External Editor Change Log
+ 01/03/2007 - 0.9.3 Release
+
+ - Fixed issue with 'manage_FTPget' overriding the 'Content-Type'
+ header.
+
+ - Only run ExpandEnvironmentStrings on win32 systems.
+
9/14/2006 - 0.9.2 Release
- Added 'skip_data' option to make External Editor send out only
Modified: zope-externaleditor/trunk/ExternalEditor.py
===================================================================
--- zope-externaleditor/trunk/ExternalEditor.py 2007-02-09 14:50:20 UTC (rev 664)
+++ zope-externaleditor/trunk/ExternalEditor.py 2007-02-09 14:51:12 UTC (rev 665)
@@ -11,7 +11,7 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""$Id: ExternalEditor.py 69097 2006-07-11 19:51:48Z sidnei $
+"""$Id: ExternalEditor.py 71655 2006-12-26 22:12:16Z sidnei $
"""
# Zope External Editor Product by Casey Duncan
@@ -50,7 +50,7 @@
self.data = data
def __iter__(self):
- return iter(self)
+ return self
def next(self):
if self.data is None:
@@ -165,12 +165,6 @@
metadata = join(r, '\n')
metadata_len = len(metadata)
- # Using RESPONSE.setHeader('Pragma', 'no-cache') would be better, but
- # this chokes crappy most MSIE versions when downloads happen on SSL.
- # cf. http://support.microsoft.com/support/kb/articles/q316/4/31.asp
- RESPONSE.setHeader('Last-Modified', rfc1123_date())
- RESPONSE.setHeader('Content-Type', 'application/x-zope-edit')
-
# Check if we should send the file's data down the response.
if REQUEST.get('skip_data'):
# We've been requested to send only the metadata. The
@@ -180,10 +174,31 @@
ob_data = getattr(Acquisition.aq_base(ob), 'data', None)
if (ob_data is not None and isinstance(ob_data, Image.Pdata)):
- # We have a File instance with chunked data, lets stream it
+ # We have a File instance with chunked data, lets stream it.
+ #
+ # Note we are setting the content-length header here. This
+ # is a simplification. Read comment below.
+ #
+ # We assume that ob.get_size() will return the exact size
+ # of the PData chain. If that assumption is broken we
+ # might have problems. This is mainly an optimization. If
+ # we read the whole PData chain just to compute the
+ # correct size that could cause the whole file to be read
+ # into memory.
RESPONSE.setHeader('Content-Length', ob.get_size())
+ # It is safe to use this PDataStreamIterator here because
+ # it is consumed right below. This is only used to
+ # simplify the code below so it only has to deal with
+ # stream iterators or plain strings.
body = PDataStreamIterator(ob.data)
elif hasattr(ob, 'manage_FTPget'):
+ # Calling manage_FTPget *might* have side-effects. For
+ # example, in Archetypes it does set the 'content-type'
+ # response header, which would end up overriding our own
+ # content-type header because we've set it 'too
+ # early'. We've moved setting the content-type header to
+ # the '_write_metadata' method since, and any manipulation
+ # of response headers should happen there, if possible.
try:
body = ob.manage_FTPget()
except TypeError: # some need the R/R pair!
@@ -211,10 +226,26 @@
RESPONSE.write(data)
return ''
- # If we reached this point, body *must* be a string.
+ # If we reached this point, body *must* be a string. We *must*
+ # set the headers ourselves since _write_metadata won't get
+ # called.
+ self._set_headers(RESPONSE)
return join((metadata, body), '\n')
+ def _set_headers(self, RESPONSE):
+ # Using RESPONSE.setHeader('Pragma', 'no-cache') would be better, but
+ # this chokes crappy most MSIE versions when downloads happen on SSL.
+ # cf. http://support.microsoft.com/support/kb/articles/q316/4/31.asp
+ RESPONSE.setHeader('Last-Modified', rfc1123_date())
+ RESPONSE.setHeader('Content-Type', 'application/x-zope-edit')
+
def _write_metadata(self, RESPONSE, metadata, length):
+ # Set response content-type so that the browser gets hinted
+ # about what application should handle this.
+ self._set_headers(RESPONSE)
+
+ # Set response length and write our metadata. The '+1' on the
+ # content-length is the '\n' after the metadata.
RESPONSE.setHeader('Content-Length', length + 1)
RESPONSE.write(metadata)
RESPONSE.write('\n')
Modified: zope-externaleditor/trunk/debian/changelog
===================================================================
--- zope-externaleditor/trunk/debian/changelog 2007-02-09 14:50:20 UTC (rev 664)
+++ zope-externaleditor/trunk/debian/changelog 2007-02-09 14:51:12 UTC (rev 665)
@@ -1,3 +1,9 @@
+zope-externaleditor (0.9.3-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Fabio Tranchitella <kobold at debian.org> Fri, 9 Feb 2007 15:02:02 +0100
+
zope-externaleditor (0.9.2-2) unstable; urgency=low
* debian/zopeedit.README.Debian: added a note for epiphany users.
Modified: zope-externaleditor/trunk/manage_main.dtml
===================================================================
--- zope-externaleditor/trunk/manage_main.dtml 2007-02-09 14:50:20 UTC (rev 664)
+++ zope-externaleditor/trunk/manage_main.dtml 2007-02-09 14:51:12 UTC (rev 665)
@@ -40,13 +40,15 @@
onChange="location.href='&dtml-URL1;/'+this.options[this.selectedIndex].value">
<option value="manage_workspace" disabled>Select type to add...</option>
<dtml-in filtered_meta_types mapping sort=name>
- <option value="&dtml.url_quote-action;">&dtml-name;</option>
+ <dtml-if action>
+ <option value="&dtml.html_quote-action;">&dtml-name;</option>
+ </dtml-if>
</dtml-in>
</select>
<input class="form-element" type="submit" name="submit" value=" Add " />
<dtml-else>
<dtml-in filtered_meta_types mapping sort=name>
- <input type="hidden" name=":method" value="&dtml.url_quote-action;" />
+ <input type="hidden" name=":method" value="&dtml.html_quote-action;" />
<input class="form-element" type="submit" name="submit" value="Add &dtml-name;" />
</dtml-in>
</dtml-if>
@@ -99,7 +101,7 @@
<td width="19%" align="left"><div class="list-item"><a
href="./manage_main?skey=bobobase_modification_time<dtml-if
"skey == 'bobobase_modification_time' and not rkey"
- >&rkey=bobobase_modification_time</dtml-if>"
+ >&rkey=bobobase_modification_time</dtml-if>"
onMouseOver="window.status='Sort objects by modification time'; return true"
onMouseOut="window.status=''; return true"><dtml-if
"skey == 'bobobase_modification_time' or rkey == 'bobobase_modification_time'"
Modified: zope-externaleditor/trunk/tests/edit.txt
===================================================================
--- zope-externaleditor/trunk/tests/edit.txt 2007-02-09 14:50:20 UTC (rev 664)
+++ zope-externaleditor/trunk/tests/edit.txt 2007-02-09 14:51:12 UTC (rev 665)
@@ -145,6 +145,32 @@
>>> self.folder['some-file'].wl_clearLocks()
+
+Create a class that has a 'manage_FTPget' method with
+side-effects. Make sure that the content-type header is properly set
+to 'application/x-zope-edit':
+
+ >>> from Products.ExternalEditor.tests.test_functional import SideEffects
+ >>> _ = self.folder._setObject('another-file', SideEffects('another-file', 'some content'))
+
+ >>> print http(r"""
+ ... GET /test_folder_1_/externalEdit_/another-file HTTP/1.1
+ ... Authorization: Basic %s:%s
+ ... """ % (user_name, user_password))
+ HTTP/1.1 200 OK
+ Content-Length: 140
+ Content-Type: application/x-zope-edit; charset=iso-8859-15
+ Last-Modified:...
+ <BLANKLINE>
+ url:http://localhost/test_folder_1_/another-file
+ meta_type:Side Effects
+ title:
+ auth:...
+ cookie:
+ <BLANKLINE>
+ some content
+
+
Callback Registry
=================
Modified: zope-externaleditor/trunk/tests/test_functional.py
===================================================================
--- zope-externaleditor/trunk/tests/test_functional.py 2007-02-09 14:50:20 UTC (rev 664)
+++ zope-externaleditor/trunk/tests/test_functional.py 2007-02-09 14:51:12 UTC (rev 665)
@@ -20,6 +20,16 @@
# Install our product
ZopeTestCase.installProduct('ExternalEditor')
+from OFS.SimpleItem import SimpleItem
+class SideEffects(SimpleItem):
+ meta_type = 'Side Effects'
+ def __init__(self, id, content):
+ self.id = id
+ self.content = content
+ def manage_FTPget(self, REQUEST, RESPONSE):
+ RESPONSE.setHeader('Content-Type', 'text/plain')
+ return self.content
+
def test_suite():
import unittest
suite = unittest.TestSuite()
Modified: zope-externaleditor/trunk/version.txt
===================================================================
--- zope-externaleditor/trunk/version.txt 2007-02-09 14:50:20 UTC (rev 664)
+++ zope-externaleditor/trunk/version.txt 2007-02-09 14:51:12 UTC (rev 665)
@@ -1 +1 @@
-0.9.2
+0.9.3
Modified: zope-externaleditor/trunk/win32/buildexe.bat
===================================================================
--- zope-externaleditor/trunk/win32/buildexe.bat 2007-02-09 14:50:20 UTC (rev 664)
+++ zope-externaleditor/trunk/win32/buildexe.bat 2007-02-09 14:51:12 UTC (rev 665)
@@ -1,3 +1,4 @@
cd ..
-c:\python23\python setup.py py2exe -p Plugins -p win32com -e Tkinter -p encodings
+c:\python24\python setup.py py2exe -p Plugins -p win32com -e Tkinter -p encodings -p MFC71
+cd win32
pause
Modified: zope-externaleditor/trunk/win32/setup.iss
===================================================================
--- zope-externaleditor/trunk/win32/setup.iss 2007-02-09 14:50:20 UTC (rev 664)
+++ zope-externaleditor/trunk/win32/setup.iss 2007-02-09 14:51:12 UTC (rev 665)
@@ -3,10 +3,10 @@
[Setup]
DisableStartupPrompt=yes
AppName=Zope External Editor Helper Application
-AppVerName=Zope External Editor 0.9.2
+AppVerName=Zope External Editor 0.9.3
AppPublisher=Casey Duncan, Zope Corporation (maintained by Chris McDonough)
AppPublisherURL=http://plope.com/software/ExternalEditor
-AppVersion=0.9.2
+AppVersion=0.9.3
AppSupportURL=http://plope.com/software/ExternalEditor
AppUpdatesURL=http://plope.com/software/ExternalEditor
DefaultDirName={pf}\ZopeExternalEditor
@@ -14,7 +14,7 @@
AllowNoIcons=yes
LicenseFile=..\LICENSE.txt
ChangesAssociations=yes
-OutputBaseFilename=zopeedit-win32-0.9.2
+OutputBaseFilename=zopeedit-win32-0.9.3
[Registry]
; Register file type for use by helper app
Modified: zope-externaleditor/trunk/zopeedit.py
===================================================================
--- zope-externaleditor/trunk/zopeedit.py 2007-02-09 14:50:20 UTC (rev 664)
+++ zope-externaleditor/trunk/zopeedit.py 2007-02-09 14:51:12 UTC (rev 665)
@@ -14,15 +14,18 @@
##############################################################################
"""Zope External Editor Helper Application by Casey Duncan
-$Id: zopeedit.py 70187 2006-09-15 04:09:15Z chrism $"""
+$Id: zopeedit.py 71558 2006-12-15 11:19:41Z wichert $"""
-__version__ = '0.9.2'
+__version__ = '0.9.3'
import sys
win32 = sys.platform == 'win32'
if win32:
+ # import pywin32 stuff first so it never looks into system32
+ import pythoncom, pywintypes
+
# prevent warnings from being turned into errors by py2exe
import warnings
warnings.filterwarnings('ignore')
@@ -30,12 +33,18 @@
import os, re
import rfc822
import traceback
-from tempfile import mktemp
+import logging
+import urllib
+import shutil
+
+from tempfile import mktemp, NamedTemporaryFile
from ConfigParser import ConfigParser
from httplib import HTTPConnection, HTTPSConnection
from urlparse import urlparse
-import urllib
+logger = logging.getLogger('zopeedit')
+log_file = None
+
class Configuration:
def __init__(self, path):
@@ -95,22 +104,47 @@
did_lock = 0
def __init__(self, input_file):
+ global log_file
+ log_file = NamedTemporaryFile(suffix='-zopeedit-log.txt')
+
+ self.input_file = input_file
+
+ # Setup logging.
+ logging.basicConfig(stream=log_file,
+ level=logging.DEBUG)
+ logger.info('Opening %r.', input_file)
+
try:
# Read the configuration file
if win32:
# Check the home dir first and then the program dir
config_path = os.path.expanduser('~\\ZopeEdit.ini')
- global_config = os.path.join(sys.path[0] or '', 'ZopeEdit.ini')
- if not os.path.exists(config_path) \
- and os.path.exists(global_config):
+
+ # sys.path[0] might be library.zip!!!!
+ app_dir = sys.path[0]
+ if app_dir.lower().endswith('library.zip'):
+ app_dir = os.path.dirname(app_dir)
+ global_config = os.path.join(app_dir or '', 'ZopeEdit.ini')
+
+ if not os.path.exists(config_path):
+ logger.debug('Config file %r does not exist. '
+ 'Using global configuration file: %r.',
+ config_path, global_config)
+
+ # Don't check for the existence of the global
+ # config file. It will be created anyway.
config_path = global_config
+ else:
+ logger.debug('Using user configuration file: %r.',
+ config_path)
+
else:
config_path = os.path.expanduser('~/.zope-external-edit')
self.config = Configuration(config_path)
# Open the input file and read the metadata headers
- in_f = open(input_file, 'rU')
+ in_f = open(input_file, 'rb')
m = rfc822.Message(in_f)
self.metadata = metadata = m.dict.copy()
@@ -125,6 +159,9 @@
metadata.get('content_type',''),
self.host)
+ # Should we keep the log file?
+ self.keep_log = int(self.options.get('keep_log', 0))
+
# Write the body of the input file to a separate file
if int(self.options.get('long_file_name', 1)):
sep = self.options.get('file_name_separator', ',')
@@ -148,8 +185,10 @@
else:
content_file = mktemp(content_file)
+ logger.debug('Destination filename will be: %r.', content_file)
+
body_f = open(content_file, 'wb')
- body_f.write(in_f.read())
+ shutil.copyfileobj(in_f, body_f)
self.content_file = content_file
self.saved = 1
in_f.close()
@@ -157,8 +196,10 @@
self.clean_up = int(self.options.get('cleanup_files', 1))
if self.clean_up:
try:
+ logger.debug('Cleaning up %r.', input_file)
os.remove(input_file)
except OSError:
+ logger.exception('Failed to clean up %r.', input_file)
pass # Sometimes we aren't allowed to delete it
if self.ssl:
@@ -190,11 +231,29 @@
try:
os.remove(self.content_file)
except OSError:
+ logger.exception('Failed to clean up %r', self.content_file)
pass
if self.did_lock:
# Try not to leave dangling locks on the server
- self.unlock(interactive=0)
+ try:
+ self.unlock(interactive=0)
+ except:
+ logger.exception('Failure during unlock.')
+
+ if getattr(self, 'keep_log', 0):
+ if log_file is not None:
+ base = getattr(self, 'content_file', '')
+ if not base:
+ base = getattr(self, 'input_file', 'noname')
+ base = os.path.basename(base)
+ fname = mktemp(suffix='-zopeedit-log.txt',
+ prefix='%s-' % base)
+ bkp_f = open(fname, 'wb')
+
+ # Copy the log file to a backup file.
+ log_file.seek(0)
+ shutil.copyfileobj(log_file, bkp_f)
def getEditorCommand(self):
"""Return the editor command"""
@@ -203,11 +262,14 @@
if win32 and editor is None:
from _winreg import HKEY_CLASSES_ROOT, OpenKeyEx, \
QueryValueEx, EnumKey
- from win32api import FindExecutable
- import pywintypes
+ from win32api import FindExecutable, ExpandEnvironmentStrings
+
# Find editor application based on mime type and extension
content_type = self.metadata.get('content_type')
extension = self.options.get('extension')
+
+ logger.debug('Have content type: %r, extension: %r',
+ content_type, extension)
if content_type:
# Search registry for the extension by MIME type
@@ -215,6 +277,9 @@
key = 'MIME\\Database\\Content Type\\%s' % content_type
key = OpenKeyEx(HKEY_CLASSES_ROOT, key)
extension, nil = QueryValueEx(key, 'Extension')
+ logger.debug('Registry has extension %r for '
+ 'content type %r',
+ extension, content_type)
except EnvironmentError:
pass
@@ -225,71 +290,90 @@
if dot != -1 and dot > url.rfind('/'):
extension = url[dot:]
+ logger.debug('Extracted extension from url: %r',
+ extension)
+
+ classname = editor = None
if extension is not None:
try:
key = OpenKeyEx(HKEY_CLASSES_ROOT, extension)
classname, nil = QueryValueEx(key, None)
+ logger.debug('ClassName for extension %r is: %r',
+ extension, classname)
except EnvironmentError:
classname = None
- if classname is not None:
- try:
- # Look for Edit action in registry
- key = OpenKeyEx(HKEY_CLASSES_ROOT,
- classname+'\\Shell\\Edit\\Command')
- editor, nil = QueryValueEx(key, None)
- except EnvironmentError:
- pass
+ if classname is not None:
+ try:
+ # Look for Edit action in registry
+ key = OpenKeyEx(HKEY_CLASSES_ROOT,
+ classname+'\\Shell\\Edit\\Command')
+ editor, nil = QueryValueEx(key, None)
+ logger.debug('Edit action for %r is: %r',
+ classname, editor)
+ except EnvironmentError:
+ pass
- if editor is None:
- # Enumerate the actions looking for one
- # starting with 'Edit'
+ if classname is not None and editor is None:
+ logger.debug('Could not find Edit action for %r. '
+ 'Brute-force enumeration.', classname)
+ # Enumerate the actions looking for one
+ # starting with 'Edit'
+ try:
+ key = OpenKeyEx(HKEY_CLASSES_ROOT,
+ classname+'\\Shell')
+ index = 0
+ while 1:
try:
- key = OpenKeyEx(HKEY_CLASSES_ROOT,
- classname+'\\Shell')
- index = 0
- while 1:
- try:
- subkey = EnumKey(key, index)
- index += 1
- if str(subkey).lower().startswith('edit'):
- subkey = OpenKeyEx(key,
- subkey +
- '\\Command')
- editor, nil = QueryValueEx(subkey,
- None)
- else:
- continue
- except EnvironmentError:
- break
+ subkey = EnumKey(key, index)
+ index += 1
+ if str(subkey).lower().startswith('edit'):
+ subkey = OpenKeyEx(key, subkey + '\\Command')
+ editor, nil = QueryValueEx(subkey,
+ None)
+ if editor is None:
+ continue
+ logger.debug('Found action %r for %r. '
+ 'Command will be: %r',
+ subkey, classname, editor)
except EnvironmentError:
- pass
+ break
+ except EnvironmentError:
+ pass
- if editor is None:
- try:
- # Look for Open action in registry
- key = OpenKeyEx(HKEY_CLASSES_ROOT,
- classname+'\\Shell\\Open\\Command')
- editor, nil = QueryValueEx(key, None)
- except EnvironmentError:
- pass
+ if classname is not None and editor is None:
+ try:
+ # Look for Open action in registry
+ key = OpenKeyEx(HKEY_CLASSES_ROOT,
+ classname+'\\Shell\\Open\\Command')
+ editor, nil = QueryValueEx(key, None)
+ logger.debug('Open action for %r has command: %r. ',
+ classname, editor)
+ except EnvironmentError:
+ pass
- if editor is None:
- try:
- nil, editor = FindExecutable(self.content_file, '')
- except pywintypes.error:
- pass
+ if editor is None:
+ try:
+ nil, editor = FindExecutable(self.content_file, '')
+ logger.debug('Executable for %r is: %r. ',
+ self.content_file, editor)
+ except pywintypes.error:
+ pass
# Don't use IE as an "editor"
if editor is not None and editor.find('\\iexplore.exe') != -1:
+ logger.debug('Found iexplore.exe. Skipping.')
editor = None
- if editor is not None:
- return editor
- else:
+ if editor is not None:
+ return ExpandEnvironmentStrings(editor)
+
+ if editor is None:
fatalError('No editor was found for that object.\n'
'Specify an editor in the configuration file:\n'
'(%s)' % self.config.path)
+
+ return editor
def launch(self):
"""Launch external editor"""
@@ -322,6 +406,8 @@
else:
bin = command
+ logger.debug('Command %r, will use %r', command, bin)
+
if bin is not None:
# Try to load the plugin for this editor
try:
@@ -329,6 +415,8 @@
Plugin = __import__(module, globals(), locals(),
('EditorProcess',))
editor = Plugin.EditorProcess(self.content_file)
+ logger.debug('Launching Plugin %r with: %r',
+ Plugin, self.content_file)
except (ImportError, AttributeError):
bin = None
@@ -344,6 +432,7 @@
else:
command = '%s %s' % (command, self.content_file)
+ logger.debug('Launching EditorProcess with: %r', command)
editor = EditorProcess(command)
launch_success = editor.isAlive()
@@ -558,7 +647,6 @@
from win32con import MB_OK, MB_OKCANCEL, MB_YESNO, MB_RETRYCANCEL, \
MB_SYSTEMMODAL, MB_ICONERROR, MB_ICONQUESTION, \
MB_ICONEXCLAMATION
- import pywintypes
def errorDialog(message):
MessageBox(message, title, MB_OK + MB_ICONERROR + MB_SYSTEMMODAL)
@@ -577,6 +665,7 @@
def __init__(self, command):
"""Launch editor process"""
try:
+ logger.debug('CreateProcess: %r', command)
self.handle, nil, nil, nil = CreateProcess(None, command, None,
None, 1, 0, None,
None, STARTUPINFO())
@@ -660,10 +749,16 @@
def fatalError(message, exit=1):
"""Show error message and exit"""
+ global log_file
errorDialog('FATAL ERROR: %s' % message)
# Write out debug info to a temp file
debug_f = open(mktemp('-zopeedit-traceback.txt'), 'w')
try:
+ # Copy the log_file before it goes away on a fatalError.
+ if log_file is not None:
+ log_file.seek(0)
+ shutil.copyfileobj(log_file, debug_f)
+ print >> debug_f, '-' * 80
traceback.print_exc(file=debug_f)
finally:
debug_f.close()
More information about the pkg-zope-commits
mailing list