Code

Fix issue2550493: hide 'hidden' files.
[roundup.git] / frontends / ZRoundup / ZRoundup.py
index 2b5795765eddca20457c7aa34035a6134530ad3c..e2a7d4b826475d92013bd71133bba8e63d007270 100644 (file)
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: ZRoundup.py,v 1.2 2001-12-12 23:33:58 richard Exp $
+# $Id: ZRoundup.py,v 1.23 2008-02-07 01:03:39 richard Exp $
 #
 ''' ZRoundup module - exposes the roundup web interface to Zope
 
-This frontend works by providing a thin layer that sits between Zope and the regular CGI
-interface of roundup, providing the web frontend with the minimum of effort.
+This frontend works by providing a thin layer that sits between Zope and the
+regular CGI interface of roundup, providing the web frontend with the minimum
+of effort.
 
-This means that the regular CGI interface does all authentication quite independently of
-Zope.
-
-It also means that any requests which specify :filter, :columns or :sort _must_ be done
-using a GET, so that this interface can re-parse the QUERY_STRING. Zope interprets the
-':' as a special character, and the special args are lost to it.
+This means that the regular CGI interface does all authentication quite
+independently of Zope. The roundup code is kept in memory though, and it
+runs in the same server as all your other Zope stuff, so it does have _some_
+advantages over regular CGI :)
 '''
+
+import urlparse
+
 from Globals import InitializeClass, HTMLFile
 from OFS.SimpleItem import Item
 from OFS.PropertyManager import PropertyManager
-from Acquisition import Implicit
+from Acquisition import Explicit, Implicit
 from Persistence import Persistent
 from AccessControl import ClassSecurityInfo
 from AccessControl import ModuleSecurityInfo
 modulesecurity = ModuleSecurityInfo()
 
 import roundup.instance
-from roundup import cgi_client
+from roundup.cgi import client
 
-modulesecurity.declareProtected('View management screens', 'manage_addZRoundupForm')
+modulesecurity.declareProtected('View management screens',
+    'manage_addZRoundupForm')
 manage_addZRoundupForm = HTMLFile('dtml/manage_addZRoundupForm', globals())
 
 modulesecurity.declareProtected('Add Z Roundups', 'manage_addZRoundup')
@@ -64,6 +67,11 @@ class RequestWrapper:
     def end_headers(self):
         # not needed - the RESPONSE object handles this internally on write()
         pass
+    def start_response(self, headers, response):
+        self.send_response(response)
+        for key, value in headers:
+            self.send_header(key, value)
+        self.end_headers()
 
 class FormItem:
     '''Make a Zope form item look like a cgi.py one
@@ -72,23 +80,38 @@ class FormItem:
         self.value = value
         if hasattr(self.value, 'filename'):
             self.filename = self.value.filename
-            self.file = self.value
+            self.value = self.value.read()
 
 class FormWrapper:
     '''Make a Zope form dict look like a cgi.py one
     '''
     def __init__(self, form):
-        self.form = form
+        self.__form = form
     def __getitem__(self, item):
-        return FormItem(self.form[item])
+        entry = self.__form[item]
+        if isinstance(entry, type([])):
+            entry = map(FormItem, entry)
+        else:
+            entry = FormItem(entry)
+        return entry
+    def __iter__(self):
+        return iter(self.__form)
+    def getvalue(self, key, default=None):
+        if self.__form.has_key(key):
+            return self.__form[key]
+        else:
+            return default
     def has_key(self, item):
-        return self.form.has_key(item)
+        return self.__form.has_key(item)
     def keys(self):
-        return self.form.keys()
+        return self.__form.keys()
+
+    def __repr__(self):
+        return '<ZRoundup.FormWrapper %r>'%self.__form
 
 class ZRoundup(Item, PropertyManager, Implicit, Persistent):
-    '''An instance of this class provides an interface between Zope and roundup for one
-       roundup instance
+    '''An instance of this class provides an interface between Zope and
+       roundup for one roundup instance
     '''
     meta_type =  'Z Roundup'
     security = ClassSecurityInfo()
@@ -111,27 +134,52 @@ class ZRoundup(Item, PropertyManager, Implicit, Persistent):
 
     icon = "misc_/ZRoundup/icon"
 
-    security.declarePrivate('_opendb')
-    def _opendb(self):
-        '''Open the roundup instnace database for a transaction
+    security.declarePrivate('roundup_opendb')
+    def roundup_opendb(self):
+        '''Open the roundup instance database for a transaction.
         '''
-        instance = roundup.instance.open(self.instance_home)
+        tracker = roundup.instance.open(self.instance_home)
         request = RequestWrapper(self.REQUEST['RESPONSE'])
         env = self.REQUEST.environ
-        env['INSTANCE_NAME'] = self.id
-        if env['REQUEST_METHOD'] == 'GET':
-            # force roundup to re-parse the request because Zope fiddles
-            # with it and we lose all the :filter, :columns, etc goodness
-            form = None
-        else:
-            form = FormWrapper(self.REQUEST.form)
-        return instance.Client(instance, request, env, form)
+
+        # figure out the path components to set
+        url = urlparse.urlparse( self.absolute_url() )
+        path = url[2]
+        path_components = path.split( '/' )
+
+        # special case when roundup is '/' in this virtual host,
+        if path == "/" :
+            env['SCRIPT_NAME'] = "/"
+            env['TRACKER_NAME'] = ''
+        else :
+            # all but the last element is the path
+            env['SCRIPT_NAME'] = '/'.join( path_components[:-1] )
+            # the last element is the name
+            env['TRACKER_NAME'] = path_components[-1]
+
+        form = FormWrapper(self.REQUEST.form)
+        if hasattr(tracker, 'Client'):
+            return tracker.Client(tracker, request, env, form)
+        return client.Client(tracker, request, env, form)
 
     security.declareProtected('View', 'index_html')
     def index_html(self):
         '''Alias index_html to roundup's index
         '''
-        client = self._opendb()
+        # Redirect misdirected requests -- bugs 558867 , 565992
+        # PATH_INFO, as defined by the CGI spec, has the *real* request path
+        orig_path = self.REQUEST.environ['PATH_INFO']
+        if orig_path[-1] != '/' : 
+            url = urlparse.urlparse( self.absolute_url() )
+            url = list( url ) # make mutable
+            url[2] = url[2]+'/' # patch
+            url = urlparse.urlunparse( url ) # reassemble
+            RESPONSE = self.REQUEST.RESPONSE
+            RESPONSE.setStatus( "MovedPermanently" ) # 301
+            RESPONSE.setHeader( "Location" , url )
+            return RESPONSE
+
+        client = self.roundup_opendb()
         # fake the path that roundup should use
         client.split_path = ['index']
         return client.main()
@@ -139,33 +187,39 @@ class ZRoundup(Item, PropertyManager, Implicit, Persistent):
     def __getitem__(self, item):
         '''All other URL accesses are passed throuh to roundup
         '''
+        return PathElement(self, item).__of__(self)
+
+class PathElement(Item, Implicit):
+    def __init__(self, zr, path):
+        self.zr = zr
+        self.path = path
+
+    def __getitem__(self, item):
+        ''' Get a subitem.
+        '''
+        return PathElement(self.zr, self.path + '/' + item).__of__(self)
+
+    def index_html(self, REQUEST=None):
+        ''' Actually call through to roundup to handle the request.
+        '''
         try:
-            client = self._opendb()
+            client = self.zr.roundup_opendb()
             # fake the path that roundup should use
-            client.split_path = [item]
+            client.path = self.path
             # and call roundup to do something 
             client.main()
             return ''
-        except cgi_client.NotFound:
-            raise 'NotFound', self.REQUEST.URL
+        except client.NotFound:
+            raise 'NotFound', REQUEST.URL
             pass
         except:
             import traceback
             traceback.print_exc()
             # all other exceptions in roundup are valid
             raise
-        raise KeyError, item
-
 
 InitializeClass(ZRoundup)
 modulesecurity.apply(globals())
 
 
-#
-# $Log: not supported by cvs2svn $
-# Revision 1.1  2001/12/12 23:27:13  richard
-# Added a Zope frontend for roundup.
-#
-#
-#
 # vim: set filetype=python ts=4 sw=4 et si