X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=roundup%2Fcgi_client.py;h=86177bb6b257e0978b35d5d4970cb982d5e404f7;hb=1b8dd00d357bfc90e4f1d57ac8c3dd36aa9a0ccd;hp=02560d9ac20609b040af4c4313467a9519c0cca0;hpb=0fb7b386ffbd9e79cef2917739fedd30202ef0cd;p=roundup.git diff --git a/roundup/cgi_client.py b/roundup/cgi_client.py index 02560d9..86177bb 100644 --- a/roundup/cgi_client.py +++ b/roundup/cgi_client.py @@ -15,14 +15,14 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: cgi_client.py,v 1.71 2001-11-30 20:28:10 rochecompaan Exp $ +# $Id: cgi_client.py,v 1.90 2002-01-08 03:56:55 richard Exp $ __doc__ = """ WWW request handler (also used in the stand-alone server). """ import os, cgi, pprint, StringIO, urlparse, re, traceback, mimetypes -import binascii, Cookie, time +import binascii, Cookie, time, random import roundupdb, htmltemplate, date, hyperdb, password from roundup.i18n import _ @@ -60,14 +60,17 @@ class Client: ANONYMOUS_ACCESS = 'deny' # one of 'deny', 'allow' ANONYMOUS_REGISTER = 'deny' # one of 'deny', 'allow' - def __init__(self, instance, request, env): + def __init__(self, instance, request, env, form=None): self.instance = instance self.request = request self.env = env self.path = env['PATH_INFO'] self.split_path = self.path.split('/') - self.form = cgi.FieldStorage(environ=env) + if form is None: + self.form = cgi.FieldStorage(environ=env) + else: + self.form = form self.headers_done = 0 try: self.debug = int(env.get("ROUNDUP_DEBUG", 0)) @@ -105,13 +108,14 @@ class Client: user_name = self.user or '' if self.user == 'admin': admin_links = _(' | Class List' \ - ' | User List') + ' | User List' \ + ' | Add User') else: admin_links = '' if self.user not in (None, 'anonymous'): userid = self.db.user.lookup(self.user) user_info = _(''' -My Issues | +My Issues | My Details | Logout ''')%locals() else: @@ -119,8 +123,7 @@ class Client: if self.user is not None: add_links = _(''' | Add -Issue, -User +Issue ''') else: add_links = '' @@ -135,9 +138,9 @@ class Client:
%s'%cgi.escape(s.getvalue()) @@ -362,84 +350,152 @@ class Client: showissue = shownode showmsg = shownode - def showuser(self, message=None): - '''Display a user page for editing. Make sure the user is allowed - to edit this node, and also check for password changes. + def _add_assignedto_to_nosy(self, props): + ''' add the assignedto value from the props to the nosy list ''' - if self.user == 'anonymous': - raise Unauthorised - - user = self.db.user - - # get the username of the node being edited - node_user = user.get(self.nodeid, 'username') - - if self.user not in ('admin', node_user): - raise Unauthorised - - # - # perform any editing - # - keys = self.form.keys() - num_re = re.compile('^\d+$') - if keys: - try: - props, changed = parsePropsFromForm(self.db, user, self.form, - self.nodeid) - set_cookie = 0 - if self.nodeid == self.getuid() and 'password' in changed: - password = self.form['password'].value.strip() - if password: - set_cookie = password - else: - del props['password'] - del changed[changed.index('password')] - user.set(self.nodeid, **props) - self._post_editnode(self.nodeid, changed) - # and some feedback for the user - message = _('%(changes)s edited ok')%{'changes': - ', '.join(changed)} - except: - s = StringIO.StringIO() - traceback.print_exc(None, s) - message = '
%s'%cgi.escape(s.getvalue()) + if not props.has_key('assignedto'): + return + assignedto_id = props['assignedto'] + if props.has_key('nosy') and assignedto_id not in props['nosy']: + props['nosy'].append(assignedto_id) else: - set_cookie = 0 + props['nosy'] = cl.get(self.nodeid, 'nosy') + props['nosy'].append(assignedto_id) - # fix the cookie if the password has changed - if set_cookie: - self.set_cookie(self.user, set_cookie) + def _changenode(self, props): + ''' change the node based on the contents of the form + ''' + cl = self.db.classes[self.classname] + # set status to chatting if 'unread' or 'resolved' + try: + # determine the id of 'unread','resolved' and 'chatting' + unread_id = self.db.status.lookup('unread') + resolved_id = self.db.status.lookup('resolved') + chatting_id = self.db.status.lookup('chatting') + current_status = cl.get(self.nodeid, 'status') + if props.has_key('status'): + new_status = props['status'] + else: + # apparently there's a chance that some browsers don't + # send status... + new_status = current_status + except KeyError: + pass + else: + if new_status == unread_id or (new_status == resolved_id + and current_status == resolved_id): + props['status'] = chatting_id - # - # now the display - # - self.pagehead(_('User: %(user)s')%{'user': node_user}, message) + self._add_assignedto_to_nosy(props) - # use the template to display the item - item = htmltemplate.ItemTemplate(self, self.TEMPLATES, 'user') - item.render(self.nodeid) - self.pagefoot() - - def showfile(self): - ''' display a file - ''' - nodeid = self.nodeid - cl = self.db.file - mime_type = cl.get(nodeid, 'type') - if mime_type == 'message/rfc822': - mime_type = 'text/plain' - self.header(headers={'Content-Type': mime_type}) - self.write(cl.get(nodeid, 'content')) + # create the message + message, files = self._handle_message() + if message: + props['messages'] = cl.get(self.nodeid, 'messages') + [message] + if files: + props['files'] = cl.get(self.nodeid, 'files') + files + # make the changes + cl.set(self.nodeid, **props) def _createnode(self): ''' create a node based on the contents of the form ''' cl = self.db.classes[self.classname] props, dummy = parsePropsFromForm(self.db, cl, self.form) + + # set status to 'unread' if not specified - a status of '- no + # selection -' doesn't make sense + if not props.has_key('status'): + try: + unread_id = self.db.status.lookup('unread') + except KeyError: + pass + else: + props['status'] = unread_id + + self._add_assignedto_to_nosy(props) + + # check for messages and files + message, files = self._handle_message() + if message: + props['messages'] = [message] + if files: + props['files'] = files + # create the node and return it's id return cl.create(**props) - def _post_editnode(self, nid, changes=None): - ''' do the linking and message sending part of the node creation + def _handle_message(self): + ''' generate and edit message + ''' + # handle file attachments + files = [] + if self.form.has_key('__file'): + file = self.form['__file'] + if file.filename: + filename = file.filename.split('\\')[-1] + mime_type = mimetypes.guess_type(filename)[0] + if not mime_type: + mime_type = "application/octet-stream" + # create the new file entry + files.append(self.db.file.create(type=mime_type, + name=filename, content=file.file.read())) + + # we don't want to do a message if none of the following is true... + cn = self.classname + cl = self.db.classes[self.classname] + props = cl.getprops() + note = None + # in a nutshell, don't do anything if there's no note or there's no + # NOSY + if self.form.has_key('__note'): + note = self.form['__note'].value + if not props.has_key('messages'): + return None, files + if not isinstance(props['messages'], hyperdb.Multilink): + return None, files + if not props['messages'].classname == 'msg': + return None, files + if not (self.form.has_key('nosy') or note): + return None, files + + # handle the note + if note: + if '\n' in note: + summary = re.split(r'\n\r?', note)[0] + else: + summary = note + m = ['%s\n'%note] + elif not files: + # don't generate a useless message + return None, files + + # handle the messageid + # TODO: handle inreplyto + messageid = "%s.%s.%s-%s"%(time.time(), random.random(), + self.classname, self.MAIL_DOMAIN) + + # now create the message, attaching the files + content = '\n'.join(m) + message_id = self.db.msg.create(author=self.getuid(), + recipients=[], date=date.Date('.'), summary=summary, + content=content, files=files, messageid=messageid) + + # update the messages property + return message_id, files + + def _post_editnode(self, nid): + '''Do the linking part of the node creation. + + If a form element has :link or :multilink appended to it, its + value specifies a node designator and the property on that node + to add _this_ node to as a link or multilink. + + This is typically used on, eg. the file upload page to indicated + which issue to link the file to. + + TODO: I suspect that this and newfile will go away now that + there's the ability to upload a file using the issue __file form + element! ''' cn = self.classname cl = self.db.classes[cn] @@ -465,52 +521,6 @@ class Client: link = self.db.classes[link] link.set(nodeid, **{property: nid}) - # handle file attachments - files = [] - if self.form.has_key('__file'): - file = self.form['__file'] - if file.filename: - mime_type = mimetypes.guess_type(file.filename)[0] - if not mime_type: - mime_type = "application/octet-stream" - # create the new file entry - files.append(self.db.file.create(type=mime_type, - name=file.filename, content=file.file.read())) - - # generate an edit message - # don't bother if there's no messages or nosy list - props = cl.getprops() - note = None - if self.form.has_key('__note'): - note = self.form['__note'] - note = note.value - send = len(cl.get(nid, 'nosy', [])) or note - if (send and props.has_key('messages') and - isinstance(props['messages'], hyperdb.Multilink) and - props['messages'].classname == 'msg'): - - # handle the note - if note: - if '\n' in note: - summary = re.split(r'\n\r?', note)[0] - else: - summary = note - m = ['%s\n'%note] - else: - summary = _('This %(classname)s has been edited through' - ' the web.\n')%{'classname': cn} - m = [summary] - - # now create the message - content = '\n'.join(m) - message_id = self.db.msg.create(author=self.getuid(), - recipients=[], date=date.Date('.'), summary=summary, - content=content, files=files) - messages = cl.get(nid, 'messages') - messages.append(message_id) - props = {'messages': messages, 'files': files} - return props - def newnode(self, message=None): ''' Add a new node to the database. @@ -542,16 +552,28 @@ class Client: props = {} try: nid = self._createnode() - props = self._post_editnode(nid) - cl.set(nid, **props) + # handle linked nodes + self._post_editnode(nid) # and some nice feedback for the user message = _('%(classname)s created ok')%{'classname': cn} + + # render the newly created issue + self.db.commit() + self.nodeid = nid + self.pagehead('%s: %s'%(self.classname.capitalize(), nid), + message) + item = htmltemplate.ItemTemplate(self, self.TEMPLATES, + self.classname) + item.render(nid) + self.pagefoot() + return except: + self.db.rollback() s = StringIO.StringIO() traceback.print_exc(None, s) message = '
%s'%cgi.escape(s.getvalue()) self.pagehead(_('New %(classname)s')%{'classname': - self.classname.capitalize()}, message) + self.classname.capitalize()}, message) # call the template newitem = htmltemplate.NewItemTemplate(self, self.TEMPLATES, @@ -560,7 +582,39 @@ class Client: self.pagefoot() newissue = newnode - newuser = newnode + + def newuser(self, message=None): + ''' Add a new user to the database. + + Don't do any of the message or file handling, just create the node. + ''' + cn = self.classname + cl = self.db.classes[cn] + + # possibly perform a create + keys = self.form.keys() + if [i for i in keys if i[0] != ':']: + try: + props, dummy = parsePropsFromForm(self.db, cl, self.form) + nid = cl.create(**props) + # handle linked nodes + self._post_editnode(nid) + # and some nice feedback for the user + message = _('%(classname)s created ok')%{'classname': cn} + except: + self.db.rollback() + s = StringIO.StringIO() + traceback.print_exc(None, s) + message = '
%s'%cgi.escape(s.getvalue()) + self.pagehead(_('New %(classname)s')%{'classname': + self.classname.capitalize()}, message) + + # call the template + newitem = htmltemplate.NewItemTemplate(self, self.TEMPLATES, + self.classname) + newitem.render(self.form) + + self.pagefoot() def newfile(self, message=None): ''' Add a new file to the database. @@ -579,11 +633,15 @@ class Client: mime_type = mimetypes.guess_type(file.filename)[0] if not mime_type: mime_type = "application/octet-stream" - self._post_editnode(cl.create(content=file.file.read(), - type=mime_type, name=file.filename)) + # save the file + nid = cl.create(content=file.file.read(), type=mime_type, + name=file.filename) + # handle linked nodes + self._post_editnode(nid) # and some nice feedback for the user message = _('%(classname)s created ok')%{'classname': cn} except: + self.db.rollback() s = StringIO.StringIO() traceback.print_exc(None, s) message = '
%s'%cgi.escape(s.getvalue()) @@ -595,6 +653,76 @@ class Client: newitem.render(self.form) self.pagefoot() + def showuser(self, message=None): + '''Display a user page for editing. Make sure the user is allowed + to edit this node, and also check for password changes. + ''' + if self.user == 'anonymous': + raise Unauthorised + + user = self.db.user + + # get the username of the node being edited + node_user = user.get(self.nodeid, 'username') + + if self.user not in ('admin', node_user): + raise Unauthorised + + # + # perform any editing + # + keys = self.form.keys() + num_re = re.compile('^\d+$') + if keys: + try: + props, changed = parsePropsFromForm(self.db, user, self.form, + self.nodeid) + set_cookie = 0 + if self.nodeid == self.getuid() and changed.has_key('password'): + password = self.form['password'].value.strip() + if password: + set_cookie = password + else: + # no password was supplied - don't change it + del props['password'] + del changed['password'] + user.set(self.nodeid, **props) + # and some feedback for the user + message = _('%(changes)s edited ok')%{'changes': + ', '.join(changed.keys())} + except: + self.db.rollback() + s = StringIO.StringIO() + traceback.print_exc(None, s) + message = '
%s'%cgi.escape(s.getvalue()) + else: + set_cookie = 0 + + # fix the cookie if the password has changed + if set_cookie: + self.set_cookie(self.user, set_cookie) + + # + # now the display + # + self.pagehead(_('User: %(user)s')%{'user': node_user}, message) + + # use the template to display the item + item = htmltemplate.ItemTemplate(self, self.TEMPLATES, 'user') + item.render(self.nodeid) + self.pagefoot() + + def showfile(self): + ''' display a file + ''' + nodeid = self.nodeid + cl = self.db.file + mime_type = cl.get(nodeid, 'type') + if mime_type == 'message/rfc822': + mime_type = 'text/plain' + self.header(headers={'Content-Type': mime_type}) + self.write(cl.get(nodeid, 'content')) + def classes(self, message=None): ''' display a list of all the classes in the database ''' @@ -617,6 +745,8 @@ class Client: raise Unauthorised def login(self, message=None, newuser_form=None, action='index'): + '''Display a login page. + ''' self.pagehead(_('Login to roundup'), message) self.write(_('''
New User Registration | |
marked items are optional... |