Code

Added a Zope frontend for roundup.
[roundup.git] / roundup / cgi_client.py
index f7075eaad15e376e18d5721c6b5e819f2dce03c9..59b587b33aff973c225554a5cf5761a9796485bf 100644 (file)
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: cgi_client.py,v 1.74 2001-12-02 05:06:16 richard Exp $
+# $Id: cgi_client.py,v 1.80 2001-12-12 23:27:14 richard Exp $
 
 __doc__ = """
 WWW request handler (also used in the stand-alone server).
@@ -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))
@@ -313,7 +316,7 @@ class Client:
                     self.nodeid)
 
                 # set status to chatting if 'unread' or 'resolved'
-                if 'status' not in changed:
+                if not changed.has_key('status'):
                     try:
                         # determine the id of 'unread','resolved' and 'chatting'
                         unread_id = self.db.status.lookup('unread')
@@ -326,21 +329,27 @@ class Client:
                                 props['status'] == unread_id or
                                 props['status'] == resolved_id):
                             props['status'] = chatting_id
-                            changed.append('status')
+                            changed['status'] = chatting_id
+
+                # get the change note
+                change_note = cl.generateChangeNote(self.nodeid, changed)
 
                 # make the changes
                 cl.set(self.nodeid, **props)
 
                 # handle linked nodes and change message generation
-                self._post_editnode(self.nodeid, changed)
+                self._post_editnode(self.nodeid, change_note)
 
                 # and some nice feedback for the user
                 if changed:
                     message = _('%(changes)s edited ok')%{'changes':
-                        ', '.join(changed)}
+                        ', '.join(changed.keys())}
+                elif self.form.has_key('__note') and self.form['__note'].value:
+                    message = _('note added')
                 else:
                     message = _('nothing changed')
             except:
+                self.db.rollback()
                 s = StringIO.StringIO()
                 traceback.print_exc(None, s)
                 message = '<pre>%s</pre>'%cgi.escape(s.getvalue())
@@ -394,11 +403,12 @@ class Client:
                         del props['password']
                         del changed[changed.index('password')]
                 user.set(self.nodeid, **props)
-                self._post_editnode(self.nodeid, changed)
+                self._post_editnode(self.nodeid)
                 # and some feedback for the user
                 message = _('%(changes)s edited ok')%{'changes':
-                    ', '.join(changed)}
+                    ', '.join(changed.keys())}
             except:
+                self.db.rollback()
                 s = StringIO.StringIO()
                 traceback.print_exc(None, s)
                 message = '<pre>%s</pre>'%cgi.escape(s.getvalue())
@@ -435,9 +445,19 @@ class Client:
         '''
         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
         return cl.create(**props)
 
-    def _post_editnode(self, nid, changes=None):
+    def _post_editnode(self, nid, change_note=''):
         ''' do the linking and message sending part of the node creation
         '''
         cn = self.classname
@@ -465,7 +485,7 @@ class Client:
                     link.set(nodeid, **{property: nid})
 
         # handle file attachments
-        files = []
+        files = cl.get(nid, 'files')
         if self.form.has_key('__file'):
             file = self.form['__file']
             if file.filename:
@@ -477,8 +497,6 @@ class Client:
                     name=file.filename, content=file.file.read()))
                 # and save the reference
                 cl.set(nid, files=files)
-                if changes is not None and 'file' not in changes:
-                    changes.append('file')
 
         #
         # generate an edit message
@@ -488,8 +506,7 @@ class Client:
         props = cl.getprops()
         note = None
         if self.form.has_key('__note'):
-            note = self.form['__note']
-            note = note.value
+            note = self.form['__note'].value
         if not props.has_key('messages'):
             return
         if not isinstance(props['messages'], hyperdb.Multilink):
@@ -511,7 +528,9 @@ class Client:
                 ' the web.\n')%{'classname': cn}
             m = [summary]
 
-        # TODO: append the change note!
+        # append the change note
+        if change_note:
+            m.append(change_note)
 
         # now create the message
         content = '\n'.join(m)
@@ -560,6 +579,7 @@ class Client:
                 # 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 = '<pre>%s</pre>'%cgi.escape(s.getvalue())
@@ -600,6 +620,7 @@ class Client:
                 # 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 = '<pre>%s</pre>'%cgi.escape(s.getvalue())
@@ -999,7 +1020,7 @@ def parsePropsFromForm(db, cl, form, nodeid=0):
     '''Pull properties for the given class out of the form.
     '''
     props = {}
-    changed = []
+    changed = {}
     keys = form.keys()
     num_re = re.compile('^\d+$')
     for key in keys:
@@ -1039,6 +1060,7 @@ def parsePropsFromForm(db, cl, form, nodeid=0):
             link = cl.properties[key].classname
             l = []
             for entry in map(str, value):
+                if entry == '': continue
                 if not num_re.match(entry):
                     try:
                         entry = db.classes[link].lookup(entry)
@@ -1062,12 +1084,47 @@ def parsePropsFromForm(db, cl, form, nodeid=0):
 
         # if changed, set it
         if nodeid and value != existing:
-            changed.append(key)
+            changed[key] = value
             props[key] = value
     return props, changed
 
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.79  2001/12/10 22:20:01  richard
+# Enabled transaction support in the bsddb backend. It uses the anydbm code
+# where possible, only replacing methods where the db is opened (it uses the
+# btree opener specifically.)
+# Also cleaned up some change note generation.
+# Made the backends package work with pydoc too.
+#
+# Revision 1.78  2001/12/07 05:59:27  rochecompaan
+# Fixed small bug that prevented adding issues through the web.
+#
+# Revision 1.77  2001/12/06 22:48:29  richard
+# files multilink was being nuked in post_edit_node
+#
+# Revision 1.76  2001/12/05 14:26:44  rochecompaan
+# Removed generation of change note from "sendmessage" in roundupdb.py.
+# The change note is now generated when the message is created.
+#
+# Revision 1.75  2001/12/04 01:25:08  richard
+# Added some rollbacks where we were catching exceptions that would otherwise
+# have stopped committing.
+#
+# Revision 1.74  2001/12/02 05:06:16  richard
+# . We now use weakrefs in the Classes to keep the database reference, so
+#   the close() method on the database is no longer needed.
+#   I bumped the minimum python requirement up to 2.1 accordingly.
+# . #487480 ] roundup-server
+# . #487476 ] INSTALL.txt
+#
+# I also cleaned up the change message / post-edit stuff in the cgi client.
+# There's now a clearly marked "TODO: append the change note" where I believe
+# the change note should be added there. The "changes" list will obviously
+# have to be modified to be a dict of the changes, or somesuch.
+#
+# More testing needed.
+#
 # Revision 1.73  2001/12/01 07:17:50  richard
 # . We now have basic transaction support! Information is only written to
 #   the database when the commit() method is called. Only the anydbm