Code

fixed ZRoundup - mostly changes to classic template
[roundup.git] / frontends / ZRoundup / ZRoundup.py
1 # Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/)
2 # This module is free software, and you may redistribute it and/or modify
3 # under the same terms as Python, so long as this copyright message and
4 # disclaimer are retained in their original form.
5 #
6 # IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR
7 # DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
8 # OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE
9 # POSSIBILITY OF SUCH DAMAGE.
10 #
11 # BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
12 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
13 # FOR A PARTICULAR PURPOSE.  THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
14 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
15 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
16
17 # $Id: ZRoundup.py,v 1.17 2003-11-12 01:00:58 richard Exp $
18 #
19 ''' ZRoundup module - exposes the roundup web interface to Zope
21 This frontend works by providing a thin layer that sits between Zope and the
22 regular CGI interface of roundup, providing the web frontend with the minimum
23 of effort.
25 This means that the regular CGI interface does all authentication quite
26 independently of Zope. The roundup code is kept in memory though, and it
27 runs in the same server as all your other Zope stuff, so it does have _some_
28 advantages over regular CGI :)
29 '''
31 import urlparse
33 from Globals import InitializeClass, HTMLFile
34 from OFS.SimpleItem import Item
35 from OFS.PropertyManager import PropertyManager
36 from Acquisition import Explicit, Implicit
37 from Persistence import Persistent
38 from AccessControl import ClassSecurityInfo
39 from AccessControl import ModuleSecurityInfo
40 modulesecurity = ModuleSecurityInfo()
42 import roundup.instance
43 from roundup.cgi.client import NotFound
45 modulesecurity.declareProtected('View management screens',
46     'manage_addZRoundupForm')
47 manage_addZRoundupForm = HTMLFile('dtml/manage_addZRoundupForm', globals())
49 modulesecurity.declareProtected('Add Z Roundups', 'manage_addZRoundup')
50 def manage_addZRoundup(self, id, instance_home, REQUEST):
51     """Add a ZRoundup product """
52     # validate the instance_home
53     roundup.instance.open(instance_home)
54     self._setObject(id, ZRoundup(id, instance_home))
55     return self.manage_main(self, REQUEST)
57 class RequestWrapper:
58     '''Make the Zope RESPONSE look like a BaseHTTPServer
59     '''
60     def __init__(self, RESPONSE):
61         self.RESPONSE = RESPONSE
62         self.wfile = self.RESPONSE
63     def send_response(self, status):
64         self.RESPONSE.setStatus(status)
65     def send_header(self, header, value):
66         self.RESPONSE.addHeader(header, value)
67     def end_headers(self):
68         # not needed - the RESPONSE object handles this internally on write()
69         pass
71 class FormItem:
72     '''Make a Zope form item look like a cgi.py one
73     '''
74     def __init__(self, value):
75         self.value = value
76         if hasattr(self.value, 'filename'):
77             self.filename = self.value.filename
78             self.file = self.value
80 class FormWrapper:
81     '''Make a Zope form dict look like a cgi.py one
82     '''
83     def __init__(self, form):
84         self.form = form
85     def __getitem__(self, item):
86         return FormItem(self.form[item])
87     def has_key(self, item):
88         return self.form.has_key(item)
89     def keys(self):
90         return self.form.keys()
92 class ZRoundup(Item, PropertyManager, Implicit, Persistent):
93     '''An instance of this class provides an interface between Zope and
94        roundup for one roundup instance
95     '''
96     meta_type =  'Z Roundup'
97     security = ClassSecurityInfo()
99     def __init__(self, id, instance_home):
100         self.id = id
101         self.instance_home = instance_home
103     # define the properties that define this object
104     _properties = (
105         {'id':'id', 'type': 'string', 'mode': 'w'},
106         {'id':'instance_home', 'type': 'string', 'mode': 'w'},
107     )
108     property_extensible_schema__ = 0
110     # define the tabs for the management interface
111     manage_options= PropertyManager.manage_options + (
112         {'label': 'View', 'action':'index_html'},
113     ) + Item.manage_options
115     icon = "misc_/ZRoundup/icon"
117     security.declarePrivate('roundup_opendb')
118     def roundup_opendb(self):
119         '''Open the roundup instance database for a transaction.
120         '''
121         instance = roundup.instance.open(self.instance_home)
122         request = RequestWrapper(self.REQUEST['RESPONSE'])
123         env = self.REQUEST.environ
125         # figure out the path components to set
126         url = urlparse.urlparse( self.absolute_url() )
127         path = url[2]
128         path_components = path.split( '/' )
130         # special case when roundup is '/' in this virtual host,
131         if path == "/" :
132             env['SCRIPT_NAME'] = "/"
133             env['TRACKER_NAME'] = ''
134         else :
135             # all but the last element is the path
136             env['SCRIPT_NAME'] = '/'.join( path_components[:-1] )
137             # the last element is the name
138             env['TRACKER_NAME'] = path_components[-1]
140         form = FormWrapper(self.REQUEST.form)
141         return instance.Client(instance, request, env, form)
143     security.declareProtected('View', 'index_html')
144     def index_html(self):
145         '''Alias index_html to roundup's index
146         '''
147         # Redirect misdirected requests -- bugs 558867 , 565992
148         # PATH_INFO, as defined by the CGI spec, has the *real* request path
149         orig_path = self.REQUEST.environ['PATH_INFO']
150         if orig_path[-1] != '/' : 
151             url = urlparse.urlparse( self.absolute_url() )
152             url = list( url ) # make mutable
153             url[2] = url[2]+'/' # patch
154             url = urlparse.urlunparse( url ) # reassemble
155             RESPONSE = self.REQUEST.RESPONSE
156             RESPONSE.setStatus( "MovedPermanently" ) # 301
157             RESPONSE.setHeader( "Location" , url )
158             return RESPONSE
160         client = self.roundup_opendb()
161         # fake the path that roundup should use
162         client.split_path = ['index']
163         return client.main()
165     def __getitem__(self, item):
166         '''All other URL accesses are passed throuh to roundup
167         '''
168         return PathElement(self, item).__of__(self)
170 class PathElement(Item, Implicit):
171     def __init__(self, zr, path):
172         self.zr = zr
173         self.path = path
175     def __getitem__(self, item):
176         ''' Get a subitem.
177         '''
178         return PathElement(self.zr, self.path + '/' + item).__of__(self)
180     def index_html(self, REQUEST=None):
181         ''' Actually call through to roundup to handle the request.
182         '''
183         try:
184             client = self.zr.roundup_opendb()
185             # fake the path that roundup should use
186             client.path = self.path
187             # and call roundup to do something 
188             client.main()
189             return ''
190         except NotFound:
191             raise 'NotFound', REQUEST.URL
192             pass
193         except:
194             import traceback
195             traceback.print_exc()
196             # all other exceptions in roundup are valid
197             raise
199 InitializeClass(ZRoundup)
200 modulesecurity.apply(globals())
203 # vim: set filetype=python ts=4 sw=4 et si