Code

cgi fixes
[roundup.git] / roundup / cgi / client.py
index 8d425923416ef1e55e91e3c013e09a67aabe635d..86c1c83a3af78ef7d1ce05f36e911933688c34a8 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: client.py,v 1.102 2003-03-07 21:51:31 richard Exp $
+# $Id: client.py,v 1.110 2003-03-26 03:35:00 richard Exp $
 
 __doc__ = """
 WWW request handler (also used in the stand-alone server).
@@ -27,6 +27,11 @@ class  Redirect(HTTPException):
 class  NotModified(HTTPException):
        pass
 
+# set to indicate to roundup not to actually _send_ email
+# this var must contain a file to write the mail to
+SENDMAILDEBUG = os.environ.get('SENDMAILDEBUG', '')
+
+
 # XXX actually _use_ FormError
 class FormError(ValueError):
     ''' An "expected" exception occurred during form parsing.
@@ -224,7 +229,10 @@ class Client:
             # we don't want clients caching our dynamic pages
             self.additional_headers['Cache-Control'] = 'no-cache'
             self.additional_headers['Pragma'] = 'no-cache'
-            self.additional_headers['Expires'] = 'Thu, 1 Jan 1970 00:00:00 GMT'
+
+            # expire this page 5 seconds from now
+            date = rfc822.formatdate(time.time() + 5)
+            self.additional_headers['Expires'] = date
 
             # render the content
             self.write(self.renderContext())
@@ -280,9 +288,9 @@ class Client:
             # remove aged otks
             otks = self.db.otks
             for sessid in otks.list():
-                interval = now - okts.get(sessid, '__time')
+                interval = now - otks.get(sessid, '__time')
                 if interval > week:
-                    otk.destroy(sessid)
+                    otks.destroy(sessid)
             sessions.set('last_clean', last_use=time.time())
 
     def determine_user(self):
@@ -442,6 +450,7 @@ class Client:
         self.write(file.get(nodeid, 'content'))
 
     def serve_static_file(self, file):
+        ims = None
         # see if there's an if-modified-since...
         if hasattr(self.request, 'headers'):
             ims = self.request.headers.getheader('if-modified-since')
@@ -457,9 +466,13 @@ class Client:
                 raise NotModified
 
         # we just want to serve up the file named
-        mt = mimetypes.guess_type(str(file))[0]
+        file = str(file)
+        mt = mimetypes.guess_type(file)[0]
         if not mt:
-            mt = 'text/plain'
+            if file.endswith('.css'):
+                mt = 'text/css'
+            else:
+                mt = 'text/plain'
         self.additional_headers['Content-Type'] = mt
         self.additional_headers['Last-Modifed'] = rfc822.formatdate(lmt)
         self.write(open(filename, 'rb').read())
@@ -762,19 +775,26 @@ please visit the following URL:
         content = StringIO.StringIO(content)
         quopri.encode(content, body, 0)
 
-        # now try to send the message
-        try:
-            # send the message as admin so bounces are sent there
-            # instead of to roundup
-            smtp = smtplib.SMTP(self.db.config.MAILHOST)
-            smtp.sendmail(self.db.config.ADMIN_EMAIL, [to], message.getvalue())
-        except socket.error, value:
-            self.error_message.append("Error: couldn't send email: "
-                "mailhost %s"%value)
-            return 0
-        except smtplib.SMTPException, value:
-            self.error_message.append("Error: couldn't send email: %s"%value)
-            return 0
+        if SENDMAILDEBUG:
+            # don't send - just write to a file
+            open(SENDMAILDEBUG, 'a').write('FROM: %s\nTO: %s\n%s\n'%(
+                self.db.config.ADMIN_EMAIL,
+                ', '.join(to),message.getvalue()))
+        else:
+            # now try to send the message
+            try:
+                # send the message as admin so bounces are sent there
+                # instead of to roundup
+                smtp = smtplib.SMTP(self.db.config.MAILHOST)
+                smtp.sendmail(self.db.config.ADMIN_EMAIL, [to],
+                    message.getvalue())
+            except socket.error, value:
+                self.error_message.append("Error: couldn't send email: "
+                    "mailhost %s"%value)
+                return 0
+            except smtplib.SMTPException, msg:
+                self.error_message.append("Error: couldn't send email: %s"%msg)
+                return 0
         return 1
 
     def registerPermission(self, props):
@@ -814,17 +834,16 @@ please visit the following URL:
         # create the new user
         cl = self.db.user
 # XXX we need to make the "default" page be able to display errors!
-#        try:
-        if 1:
+        try:
             props['roles'] = self.instance.config.NEW_WEB_USER_ROLES
             del props['__time']
             self.userid = cl.create(**props)
             # clear the props from the otk database
             self.db.otks.destroy(otk)
             self.db.commit()
-#        except (ValueError, KeyError), message:
-#            self.error_message.append(str(message))
-#            return
+        except (ValueError, KeyError), message:
+            self.error_message.append(str(message))
+            return
 
         # log the new user in
         self.user = cl.get(self.userid, 'username')
@@ -842,9 +861,9 @@ please visit the following URL:
         # nice message
         message = _('You are now registered, welcome!')
 
-        # redirect to the item's edit page
-        raise Redirect, '%suser%s?@ok_message=%s'%(
-            self.base, self.userid,  urllib.quote(message))
+        # redirect to the user's page
+        raise Redirect, '%suser%s?@ok_message=%s'%(self.base,
+            self.userid, urllib.quote(message))
 
     def passResetAction(self):
         ''' Handle password reset requests.
@@ -856,6 +875,9 @@ please visit the following URL:
             # pull the rego information out of the otk database
             otk = self.form['otk'].value
             uid = self.db.otks.get(otk, 'uid')
+            if uid is None:
+                self.error_message.append('Invalid One Time Key!')
+                return
 
             # re-open the database as "admin"
             if self.user != 'admin':
@@ -865,17 +887,16 @@ please visit the following URL:
             newpw = ''.join([random.choice(self.chars) for x in range(8)])
 
             cl = self.db.user
-    # XXX we need to make the "default" page be able to display errors!
-    #        try:
-            if 1:
+# XXX we need to make the "default" page be able to display errors!
+            try:
                 # set the password
                 cl.set(uid, password=password.Password(newpw))
                 # clear the props from the otk database
                 self.db.otks.destroy(otk)
                 self.db.commit()
-    #        except (ValueError, KeyError), message:
-    #            self.error_message.append(str(message))
-    #            return
+            except (ValueError, KeyError), message:
+                self.error_message.append(str(message))
+                return
 
             # user info
             address = self.db.user.get(uid, 'address')
@@ -943,29 +964,26 @@ You should then receive another email with the new password.
            See parsePropsFromForm and _editnodes for special variables
         '''
         # parse the props from the form
-# XXX reinstate exception handling
-#        try:
-        if 1:
+        try:
             props, links = self.parsePropsFromForm()
-#        except (ValueError, KeyError), message:
-#            self.error_message.append(_('Error: ') + str(message))
-#            return
+        except (ValueError, KeyError), message:
+            self.error_message.append(_('Error: ') + str(message))
+            return
 
         # handle the props
-# XXX reinstate exception handling
-#        try:
-        if 1:
+        try:
             message = self._editnodes(props, links)
-#        except (ValueError, KeyError, IndexError), message:
-#            self.error_message.append(_('Error: ') + str(message))
-#            return
+        except (ValueError, KeyError, IndexError), message:
+            self.error_message.append(_('Error: ') + str(message))
+            return
 
         # commit now that all the tricky stuff is done
         self.db.commit()
 
         # redirect to the item's edit page
-        raise Redirect, '%s%s%s?@ok_message=%s'%(self.base, self.classname,
-            self.nodeid,  urllib.quote(message))
+        raise Redirect, '%s%s%s?@ok_message=%s&@template=%s'%(self.base,
+            self.classname, self.nodeid, urllib.quote(message),
+            urllib.quote(self.template))
 
     def editItemPermission(self, props):
         ''' Determine whether the user has permission to edit this item.
@@ -998,37 +1016,29 @@ You should then receive another email with the new password.
             special form values.
         '''
         # parse the props from the form
-# XXX reinstate exception handling
-#        try:
-        if 1:
+        try:
             props, links = self.parsePropsFromForm()
-#        except (ValueError, KeyError), message:
-#            self.error_message.append(_('Error: ') + str(message))
-#            return
+        except (ValueError, KeyError), message:
+            self.error_message.append(_('Error: ') + str(message))
+            return
 
         # handle the props - edit or create
-# XXX reinstate exception handling
-#        try:
-        if 1:
-            # create the context here
-#            cn = self.classname
-#            nid = self._createnode(cn, props[(cn, None)])
-#            del props[(cn, None)]
-
+        try:
             # when it hits the None element, it'll set self.nodeid
-            messages = self._editnodes(props, links) #, {(cn, None): nid})
+            messages = self._editnodes(props, links)
 
-#        except (ValueError, KeyError, IndexError), message:
-#            # these errors might just be indicative of user dumbness
-#            self.error_message.append(_('Error: ') + str(message))
-#            return
+        except (ValueError, KeyError, IndexError), message:
+            # these errors might just be indicative of user dumbness
+            self.error_message.append(_('Error: ') + str(message))
+            return
 
         # commit now that all the tricky stuff is done
         self.db.commit()
 
         # redirect to the new item's page
-        raise Redirect, '%s%s%s?@ok_message=%s'%(self.base, self.classname,
-            self.nodeid, urllib.quote(messages))
+        raise Redirect, '%s%s%s?@ok_message=%s&@template=%s'%(self.base,
+            self.classname, self.nodeid, urllib.quote(messages),
+            urllib.quote(self.template))
 
     def newItemPermission(self, props):
         ''' Determine whether the user has permission to create (edit) this
@@ -1148,7 +1158,7 @@ You should then receive another email with the new password.
         '''
         # check for permission
         if not self.editItemPermission(props):
-            raise PermissionError, 'You do not have permission to edit %s'%cn
+            raise Unauthorised, 'You do not have permission to edit %s'%cn
 
         # make the changes
         cl = self.db.classes[cn]
@@ -1159,7 +1169,7 @@ You should then receive another email with the new password.
         '''
         # check for permission
         if not self.newItemPermission(props):
-            raise PermissionError, 'You do not have permission to create %s'%cn
+            raise Unauthorised, 'You do not have permission to create %s'%cn
 
         # create the node and return its id
         cl = self.db.classes[cn]
@@ -1213,6 +1223,12 @@ You should then receive another email with the new password.
             nodeid, values = values[0], values[1:]
             found[nodeid] = 1
 
+            # see if the node exists
+            if cl.hasnode(nodeid):
+                exists = 1
+            else:
+                exists = 0
+
             # confirm correct weight
             if len(idlessprops) != len(values):
                 self.error_message.append(
@@ -1222,16 +1238,23 @@ You should then receive another email with the new password.
             # extract the new values
             d = {}
             for name, value in zip(idlessprops, values):
+                prop = cl.properties[name]
                 value = value.strip()
                 # only add the property if it has a value
                 if value:
                     # if it's a multilink, split it
-                    if isinstance(cl.properties[name], hyperdb.Multilink):
+                    if isinstance(prop, hyperdb.Multilink):
                         value = value.split(':')
                     d[name] = value
+                elif exists:
+                    # nuke the existing value
+                    if isinstance(prop, hyperdb.Multilink):
+                        d[name] = []
+                    else:
+                        d[name] = None
 
             # perform the edit
-            if cl.hasnode(nodeid):
+            if exists:
                 # edit existing
                 cl.set(nodeid, **d)
             else:
@@ -1292,7 +1315,8 @@ You should then receive another email with the new password.
                 else:
                     continue
             else:
-                if not self.form[key].value: continue
+                if not self.form[key].value:
+                    continue
             self.form.value.append(cgi.MiniFieldStorage('@filter', key))
 
         # handle saving the query params