Mercurial > p > roundup > code
view roundup/cgi_client.py @ 54:b68bcb176d1a
disabled the reloading until it can be done properly
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Mon, 23 Jul 2001 10:31:45 +0000 |
| parents | 6eebdf2bf262 |
| children | 381016730332 |
line wrap: on
line source
# $Id: cgi_client.py,v 1.3 2001-07-23 03:56:30 richard Exp $ import os, cgi, pprint, StringIO, urlparse, re, traceback import roundupdb, htmltemplate, date class Unauthorised(ValueError): pass class Client: def __init__(self, out, db, env, user): self.out = out self.db = db self.env = env self.user = user self.path = env['PATH_INFO'] self.split_path = self.path.split('/') self.headers_done = 0 self.form = cgi.FieldStorage(environ=env) self.headers_done = 0 self.debug = 0 def header(self, headers={'Content-Type':'text/html'}): if not headers.has_key('Content-Type'): headers['Content-Type'] = 'text/html' for entry in headers.items(): self.out.write('%s: %s\n'%entry) self.out.write('\n') self.headers_done = 1 def pagehead(self, title, message=None): url = self.env['SCRIPT_NAME'] + '/' #self.env.get('PATH_INFO', '/') machine = self.env['SERVER_NAME'] port = self.env['SERVER_PORT'] if port != '80': machine = machine + ':' + port base = urlparse.urlunparse(('http', machine, url, None, None, None)) if message is not None: message = '<div class="system-msg">%s</div>'%message else: message = '' style = open(os.path.join(self.TEMPLATES, 'style.css')).read() userid = self.db.user.lookup(self.user) if self.user == 'admin': extras = ' | <a href="list_classes">Class List</a>' else: extras = '' self.write('''<html><head> <title>%s</title> <style type="text/css">%s</style> </head> <body bgcolor=#ffffff> %s <table width=100%% border=0 cellspacing=0 cellpadding=2> <tr class="location-bar"><td><big><strong>%s</strong></big></td> <td align=right valign=bottom>%s</td></tr> <tr class="location-bar"> <td align=left><a href="issue?status=unread,deferred,chatting,need-eg,in-progress,testing,done-cbb&:sort=activity&:columns=activity,status,title&:group=priority">All issues</a> | <a href="issue?priority=fatal-bug,bug">Bugs</a> | <a href="issue?priority=usability">Support</a> | <a href="issue?priority=feature">Wishlist</a> | <a href="newissue">New Issue</a> %s</td> <td align=right><a href="user%s">Your Details</a></td> </table> '''%(title, style, message, title, self.user, extras, userid)) def pagefoot(self): if self.debug: self.write('<hr><small><dl>') self.write('<dt><b>Path</b></dt>') self.write('<dd>%s</dd>'%(', '.join(map(repr, self.split_path)))) keys = self.form.keys() keys.sort() if keys: self.write('<dt><b>Form entries</b></dt>') for k in self.form.keys(): v = str(self.form[k].value) self.write('<dd><em>%s</em>:%s</dd>'%(k, cgi.escape(v))) keys = self.env.keys() keys.sort() self.write('<dt><b>CGI environment</b></dt>') for k in keys: v = self.env[k] self.write('<dd><em>%s</em>:%s</dd>'%(k, cgi.escape(v))) self.write('</dl></small>') self.write('</body></html>') def write(self, content): if not self.headers_done: self.header() self.out.write(content) def index_arg(self, arg): ''' handle the args to index - they might be a list from the form (ie. submitted from a form) or they might be a command-separated single string (ie. manually constructed GET args) ''' if self.form.has_key(arg): arg = self.form[arg] if type(arg) == type([]): return [arg.value for arg in arg] return arg.value.split(',') return [] def index_filterspec(self): ''' pull the index filter spec from the form ''' # all the other form args are filters filterspec = {} for key in self.form.keys(): if key[0] == ':': continue value = self.form[key] if type(value) == type([]): value = [arg.value for arg in value] else: value = value.value.split(',') l = filterspec.get(key, []) l = l + value filterspec[key] = l return filterspec def index(self): ''' put up an index ''' self.classname = 'issue' if self.form.has_key(':sort'): sort = self.index_arg(':sort') else: sort=['-activity'] if self.form.has_key(':group'): group = self.index_arg(':group') else: group=['priority'] if self.form.has_key(':filter'): filter = self.index_arg(':filter') else: filter = [] if self.form.has_key(':columns'): columns = self.index_arg(':columns') else: columns=['activity','status','title'] filterspec = self.index_filterspec() if not filterspec: filterspec['status'] = ['1', '2', '3', '4', '5', '6', '7'] return self.list(columns=columns, filter=filter, group=group, sort=sort, filterspec=filterspec) # XXX deviates from spec - loses the '+' (that's a reserved character # in URLS def list(self, sort=None, group=None, filter=None, columns=None, filterspec=None): ''' call the template index with the args :sort - sort by prop name, optionally preceeded with '-' to give descending or nothing for ascending sorting. :group - group by prop name, optionally preceeded with '-' or to sort in descending or nothing for ascending order. :filter - selects which props should be displayed in the filter section. Default is all. :columns - selects the columns that should be displayed. Default is all. ''' cn = self.classname self.pagehead('Index: %s'%cn) if sort is None: sort = self.index_arg(':sort') if group is None: group = self.index_arg(':group') if filter is None: filter = self.index_arg(':filter') if columns is None: columns = self.index_arg(':columns') if filterspec is None: filterspec = self.index_filterspec() htmltemplate.index(self, self.TEMPLATES, self.db, cn, filterspec, filter, columns, sort, group) self.pagefoot() def showitem(self, message=None): ''' display an item ''' cn = self.classname cl = self.db.classes[cn] # possibly perform an edit keys = self.form.keys() num_re = re.compile('^\d+$') if keys: changed = [] props = {} try: keys = self.form.keys() for key in keys: if not cl.properties.has_key(key): continue proptype = cl.properties[key] if proptype.isStringType: value = str(self.form[key].value).strip() elif proptype.isDateType: value = date.Date(str(self.form[key].value)) elif proptype.isIntervalType: value = date.Interval(str(self.form[key].value)) elif proptype.isLinkType: value = str(self.form[key].value).strip() # handle key values link = cl.properties[key].classname if not num_re.match(value): try: value = self.db.classes[link].lookup(value) except: raise ValueError, 'property "%s": %s not a %s'%( key, value, link) elif proptype.isMultilinkType: value = self.form[key] if type(value) != type([]): value = [i.strip() for i in str(value.value).split(',')] else: value = [str(i.value).strip() for i in value] link = cl.properties[key].classname l = [] for entry in map(str, value): if not num_re.match(entry): try: entry = self.db.classes[link].lookup(entry) except: raise ValueError, \ 'property "%s": %s not a %s'%(key, entry, link) l.append(entry) l.sort() value = l # if changed, set it if value != cl.get(self.nodeid, key): changed.append(key) props[key] = value cl.set(self.nodeid, **props) # if this item has messages, generate an edit message # TODO: don't send the edit message to the person who # performed the edit if (cl.getprops().has_key('messages') and cl.getprops()['messages'].isMultilinkType and cl.getprops()['messages'].classname == 'msg'): nid = self.nodeid m = [] for name, prop in cl.getprops().items(): value = cl.get(nid, name) if prop.isLinkType: link = self.db.classes[prop.classname] key = link.getkey() if value is not None and key: value = link.get(value, key) else: value = '-' elif prop.isMultilinkType: l = [] link = self.db.classes[prop.classname] for entry in value: key = link.getkey() if key: l.append(link.get(entry, link.getkey())) else: l.append(entry) value = ', '.join(l) if name in changed: chg = '*' else: chg = ' ' m.append('%s %s: %s'%(chg, name, value)) # handle the note if self.form.has_key('__note'): note = self.form['__note'].value if '\n' in note: summary = re.split(r'\n\r?', note)[0] else: summary = note m.insert(0, '%s\n\n'%note) else: if len(changed) > 1: plural = 's were' else: plural = ' was' summary = 'This %s has been edited through the web '\ 'and the %s value%s changed.'%(cn, ', '.join(changed), plural) m.insert(0, '%s\n\n'%summary) # now create the message content = '\n'.join(m) message_id = self.db.msg.create(author=1, recipients=[], date=date.Date('.'), summary=summary, content=content) messages = cl.get(nid, 'messages') messages.append(message_id) props = {'messages': messages} cl.set(nid, **props) # and some nice feedback for the user message = '%s edited ok'%', '.join(changed) except: s = StringIO.StringIO() traceback.print_exc(None, s) message = '<pre>%s</pre>'%cgi.escape(s.getvalue()) # now the display id = self.nodeid if cl.getkey(): id = cl.get(id, cl.getkey()) self.pagehead('%s: %s'%(self.classname.capitalize(), id), message) nodeid = self.nodeid # use the template to display the item htmltemplate.item(self, self.TEMPLATES, self.db, self.classname, nodeid) self.pagefoot() showissue = showitem showmsg = showitem def newissue(self, message=None): ''' add an issue ''' cn = self.classname cl = self.db.classes[cn] # possibly perform a create keys = self.form.keys() num_re = re.compile('^\d+$') if keys: props = {} try: keys = self.form.keys() for key in keys: if not cl.properties.has_key(key): continue proptype = cl.properties[key] if proptype.isStringType: value = str(self.form[key].value).strip() elif proptype.isDateType: value = date.Date(str(self.form[key].value)) elif proptype.isIntervalType: value = date.Interval(str(self.form[key].value)) elif proptype.isLinkType: value = str(self.form[key].value).strip() # handle key values link = cl.properties[key].classname if not num_re.match(value): try: value = self.db.classes[link].lookup(value) except: raise ValueError, 'property "%s": %s not a %s'%( key, value, link) elif proptype.isMultilinkType: value = self.form[key] if type(value) != type([]): value = [i.strip() for i in str(value.value).split(',')] else: value = [str(i.value).strip() for i in value] link = cl.properties[key].classname l = [] for entry in map(str, value): if not num_re.match(entry): try: entry = self.db.classes[link].lookup(entry) except: raise ValueError, \ 'property "%s": %s not a %s'%(key, entry, link) l.append(entry) l.sort() value = l props[key] = value nid = cl.create(**props) # if this item has messages, if (cl.getprops().has_key('messages') and cl.getprops()['messages'].isMultilinkType and cl.getprops()['messages'].classname == 'msg'): # generate an edit message - nosyreactor will send it m = [] for name, prop in cl.getprops().items(): value = cl.get(nid, name) if prop.isLinkType: link = self.db.classes[prop.classname] key = link.getkey() if value is not None and key: value = link.get(value, key) else: value = '-' elif prop.isMultilinkType: l = [] link = self.db.classes[prop.classname] for entry in value: key = link.getkey() if key: l.append(link.get(entry, link.getkey())) else: l.append(entry) value = ', '.join(l) m.append('%s: %s'%(name, value)) # handle the note if self.form.has_key('__note'): note = self.form['__note'].value if '\n' in note: summary = re.split(r'\n\r?', note)[0] else: summary = note m.append('\n%s\n'%note) else: m.append('\nThis %s has been created through ' 'the web.\n'%cn) # now create the message content = '\n'.join(m) message_id = self.db.msg.create(author=1, recipients=[], date=date.Date('.'), summary=summary, content=content) messages = cl.get(nid, 'messages') messages.append(message_id) props = {'messages': messages} cl.set(nid, **props) # and some nice feedback for the user message = '%s created ok'%cn except: s = StringIO.StringIO() traceback.print_exc(None, s) message = '<pre>%s</pre>'%cgi.escape(s.getvalue()) self.pagehead('New %s'%self.classname.capitalize(), message) htmltemplate.newitem(self, self.TEMPLATES, self.db, self.classname, self.form) self.pagefoot() def showuser(self, message=None): ''' display an item ''' if self.user in ('admin', self.db.user.get(self.nodeid, 'username')): self.showitem(message) else: raise Unauthorised def showfile(self): ''' display a file ''' nodeid = self.nodeid cl = self.db.file type = cl.get(nodeid, 'type') if type == 'message/rfc822': type = 'text/plain' self.header(headers={'Content-Type': type}) self.write(cl.get(nodeid, 'content')) def classes(self, message=None): ''' display a list of all the classes in the database ''' if self.user == 'admin': self.pagehead('Table of classes', message) classnames = self.db.classes.keys() classnames.sort() self.write('<table border=0 cellspacing=0 cellpadding=2>\n') for cn in classnames: cl = self.db.getclass(cn) self.write('<tr class="list-header"><th colspan=2 align=left>%s</th></tr>'%cn.capitalize()) for key, value in cl.properties.items(): if value is None: value = '' else: value = str(value) self.write('<tr><th align=left>%s</th><td>%s</td></tr>'%( key, cgi.escape(value))) self.write('</table>') self.pagefoot() else: raise Unauthorised def main(self, dre=re.compile(r'([^\d]+)(\d+)'), nre=re.compile(r'new(\w+)')): path = self.split_path if not path or path[0] in ('', 'index'): self.index() elif len(path) == 1: if path[0] == 'list_classes': self.classes() return m = dre.match(path[0]) if m: self.classname = m.group(1) self.nodeid = m.group(2) getattr(self, 'show%s'%self.classname)() return m = nre.match(path[0]) if m: self.classname = m.group(1) getattr(self, 'new%s'%self.classname)() return self.classname = path[0] self.list() else: raise 'ValueError', 'Path not understood' def __del__(self): self.db.close() # # $Log: not supported by cvs2svn $ # Revision 1.2 2001/07/22 12:09:32 richard # Final commit of Grande Splite # # Revision 1.1 2001/07/22 11:58:35 richard # More Grande Splite #
