diff --git a/roundup/cgi_client.py b/roundup/cgi_client.py
index f6178d419cae085d08e79b5aae7cb6aa7c4b73b7..7d18ef97b7ea64b6a2e9944fc082de646206569c 100644 (file)
--- a/roundup/cgi_client.py
+++ b/roundup/cgi_client.py
-# $Id: cgi_client.py,v 1.10 2001-07-30 02:37:34 richard Exp $
+# $Id: cgi_client.py,v 1.17 2001-08-02 06:38:17 richard Exp $
-import os, cgi, pprint, StringIO, urlparse, re, traceback
+import os, cgi, pprint, StringIO, urlparse, re, traceback, mimetypes
import roundupdb, htmltemplate, date
self.headers_done = 0
self.debug = 0
+ def getuid(self):
+ return self.db.user.lookup(self.user)
+
def header(self, headers={'Content-Type':'text/html'}):
if not headers.has_key('Content-Type'):
headers['Content-Type'] = 'text/html'
filter, columns, sort, group)
self.pagefoot()
- def showitem(self, message=None):
+ def shownode(self, message=None):
''' display an item
'''
cn = self.classname
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():
- # TODO: the None default is only here because we
- # don't have schema migration :(
- if prop.isMultilinkType:
- value = cl.get(nid, name, [])
- else:
- value = cl.get(nid, name, None)
- 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)
-
+ self._post_editnode(self.nodeid, changed)
# and some nice feedback for the user
message = '%s edited ok'%', '.join(changed)
except:
# use the template to display the item
htmltemplate.item(self, self.TEMPLATES, self.db, self.classname, nodeid)
self.pagefoot()
- showissue = showitem
- showmsg = showitem
+ showissue = shownode
+ showmsg = shownode
+
+ def showuser(self, message=None):
+ ''' display an item
+ '''
+ if self.user in ('admin', self.db.user.get(self.nodeid, 'username')):
+ self.shownode(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 newissue(self, message=None):
- ''' add an issue
+ def _createnode(self):
+ ''' create a node based on the contents of the form
'''
cn = self.classname
cl = self.db.classes[cn]
-
- # possibly perform a create
+ props = {}
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 = self.form[key].value.strip()
- elif proptype.isDateType:
- value = date.Date(self.form[key].value.strip())
- elif proptype.isIntervalType:
- value = date.Interval(self.form[key].value.strip())
- elif proptype.isLinkType:
- value = 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 value.value.split(',')]
+ for key in keys:
+ if not cl.properties.has_key(key):
+ continue
+ proptype = cl.properties[key]
+ if proptype.isStringType:
+ value = self.form[key].value.strip()
+ elif proptype.isDateType:
+ value = date.Date(self.form[key].value.strip())
+ elif proptype.isIntervalType:
+ value = date.Interval(self.form[key].value.strip())
+ elif proptype.isLinkType:
+ value = 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 value.value.split(',')]
+ else:
+ value = [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
+ return cl.create(**props)
+
+ def _post_editnode(self, nid, changes=None):
+ ''' do the linking and message sending part of the node creation
+ '''
+ cn = self.classname
+ cl = self.db.classes[cn]
+ # link if necessary
+ keys = self.form.keys()
+ for key in keys:
+ if key == ':multilink':
+ value = self.form[key].value
+ if type(value) != type([]): value = [value]
+ for value in value:
+ designator, property = value.split(':')
+ link, nodeid = roundupdb.splitDesignator(designator)
+ link = self.db.classes[link]
+ value = link.get(nodeid, property)
+ value.append(nid)
+ link.set(nodeid, **{property: value})
+ elif key == ':link':
+ value = self.form[key].value
+ if type(value) != type([]): value = [value]
+ for value in value:
+ designator, property = value.split(':')
+ link, nodeid = roundupdb.splitDesignator(designator)
+ link = self.db.classes[link]
+ link.set(nodeid, **{property: nid})
+
+ # see if we want to send a message to the nosy list...
+ props = cl.getprops()
+ # don't do the message thing if there's no nosy list, or the editor
+ # of the node is the only person on the nosy list - they're already
+ # aware of the change.
+ nosy = 0
+ if props.has_key('nosy'):
+ nosy = cl.get(nid, 'nosy')
+ uid = self.getuid()
+ if len(nosy) == 1 and uid in nosy:
+ nosy = 0
+ if (nosy and props.has_key('messages') and
+ props['messages'].isMultilinkType and
+ props['messages'].classname == 'msg'):
+
+ # handle the note
+ note = None
+ if self.form.has_key('__note'):
+ note = self.form['__note']
+ if note is not None and note.value:
+ note = note.value
+ if '\n' in note:
+ summary = re.split(r'\n\r?', note)[0]
+ else:
+ summary = note
+ m = ['%s\n'%note]
+ else:
+ summary = 'This %s has been edited through the web.\n'%cn
+ m = [summary]
+
+ # generate an edit message - nosyreactor will send it
+ first = 1
+ for name, prop in props.items():
+ if changes is not None and name not in changes: continue
+ if first:
+ m.append('\n-------')
+ first = 0
+ value = cl.get(nid, name, None)
+ if prop.isLinkType:
+ link = self.db.classes[prop.classname]
+ key = link.labelprop(default_to_id=1)
+ if value is not None and key:
+ value = link.get(value, key)
+ else:
+ value = '-'
+ elif prop.isMultilinkType:
+ if value is None: value = []
+ l = []
+ link = self.db.classes[prop.classname]
+ key = link.labelprop(default_to_id=1)
+ for entry in value:
+ if key:
+ l.append(link.get(entry, link.getkey()))
else:
- value = [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
- note = None
- if self.form.has_key('__note'):
- note = self.form['__note']
- if note and note.value:
- note = 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)
+ value = ', '.join(l)
+ m.append('%s: %s'%(name, value))
+
+ # 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)
+ messages = cl.get(nid, 'messages')
+ messages.append(message_id)
+ props = {'messages': messages}
+ cl.set(nid, **props)
+
+ def newnode(self, message=None):
+ ''' Add a new node to the database.
+
+ The form works in two modes: blank form and submission (that is,
+ the submission goes to the same URL). **Eventually this means that
+ the form will have previously entered information in it if
+ submission fails.
+
+ The new node will be created with the properties specified in the
+ form submission. For multilinks, multiple form entries are handled,
+ as are prop=value,value,value. You can't mix them though.
+
+ If the new node is to be referenced from somewhere else immediately
+ (ie. the new node is a file that is to be attached to a support
+ issue) then supply one of these arguments in addition to the usual
+ form entries:
+ :link=designator:property
+ :multilink=designator:property
+ ... which means that once the new node is created, the "property"
+ on the node given by "designator" should now reference the new
+ node's id. The node id will be appended to the multilink.
+ '''
+ 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] != ':']:
+ props = {}
+ try:
+ nid = self._createnode()
+ self._post_editnode(nid)
# and some nice feedback for the user
message = '%s created ok'%cn
except:
htmltemplate.newitem(self, self.TEMPLATES, self.db, self.classname,
self.form)
self.pagefoot()
- newuser = newissue
-
- def showuser(self, message=None):
- ''' display an item
+ newissue = newnode
+ newuser = newnode
+
+ def newfile(self, message=None):
+ ''' Add a new file to the database.
+
+ This form works very much the same way as newnode - it just has a
+ file upload.
'''
- if self.user in ('admin', self.db.user.get(self.nodeid, 'username')):
- self.showitem(message)
- else:
- raise Unauthorised
+ cn = self.classname
+ cl = self.db.classes[cn]
- 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'))
+ # possibly perform a create
+ keys = self.form.keys()
+ if [i for i in keys if i[0] != ':']:
+ try:
+ file = self.form['content']
+ self._post_editnode(cl.create(content=file.file.read(),
+ type=mimetypes.guess_type(file.filename)[0],
+ name=file.filename))
+ # 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 classes(self, message=None):
''' display a list of all the classes in the database
#
# $Log: not supported by cvs2svn $
+# Revision 1.16 2001/08/02 05:55:25 richard
+# Web edit messages aren't sent to the person who did the edit any more. No
+# message is generated if they are the only person on the nosy list.
+#
+# Revision 1.15 2001/08/02 00:34:10 richard
+# bleah syntax error
+#
+# Revision 1.14 2001/08/02 00:26:16 richard
+# Changed the order of the information in the message generated by web edits.
+#
+# Revision 1.13 2001/07/30 08:12:17 richard
+# Added time logging and file uploading to the templates.
+#
+# Revision 1.12 2001/07/30 06:26:31 richard
+# Added some documentation on how the newblah works.
+#
+# Revision 1.11 2001/07/30 06:17:45 richard
+# Features:
+# . Added ability for cgi newblah forms to indicate that the new node
+# should be linked somewhere.
+# Fixed:
+# . Fixed the agument handling for the roundup-admin find command.
+# . Fixed handling of summary when no note supplied for newblah. Again.
+# . Fixed detection of no form in htmltemplate Field display.
+#
+# Revision 1.10 2001/07/30 02:37:34 richard
+# Temporary measure until we have decent schema migration...
+#
# Revision 1.9 2001/07/30 01:25:07 richard
# Default implementation is now "classic" rather than "extended" as one would
# expect.