diff --git a/roundup/cgi/actions.py b/roundup/cgi/actions.py
index 196ad5d1478ed44255eabc458cdbaa064582419d..33e7281399c16268aa20d34f75ae2ac60994828f 100755 (executable)
--- a/roundup/cgi/actions.py
+++ b/roundup/cgi/actions.py
from roundup import hyperdb, token, date, password, rcsv
from roundup.i18n import _
from roundup.cgi import templating
-from roundup.cgi.exceptions import Redirect, Unauthorised
+from roundup.cgi.exceptions import Redirect, Unauthorised, SeriousError
from roundup.mailgw import uidFromAddress
__all__ = ['Action', 'ShowAction', 'RetireAction', 'SearchAction',
# used by a couple of routines
chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
-class Action:
+class Action:
def __init__(self, client):
self.client = client
self.form = client.form
self.userid = client.userid
self.base = client.base
self.user = client.user
-
+
def execute(self):
"""Execute the action specified by this object."""
self.permission()
a simple permission, check whether the user has that permission.
Subclasses must also define the name attribute if they define
permissionType.
-
+
Despite having this permission, users may still be unauthorised to
- perform parts of actions. It is up to the subclasses to detect this.
+ perform parts of actions. It is up to the subclasses to detect this.
"""
if (self.permissionType and
- not self.hasPermission(self.permissionType)):
-
- raise Unauthorised, _('You do not have permission to %s the %s class.' %
- (self.name, self.classname))
+ not self.hasPermission(self.permissionType)):
+ info = {'action': self.name, 'classname': self.classname}
+ raise Unauthorised, _('You do not have permission to '
+ '%(action)s the %(classname)s class.')%info
def hasPermission(self, permission):
"""Check whether the user has 'permission' on the current class."""
return self.db.security.hasPermission(permission, self.client.userid,
- self.client.classname)
+ self.client.classname)
class ShowAction(Action):
def handle(self, typere=re.compile('[@:]type'),
elif numre.match(key):
n = self.form[key].value.strip()
if not t:
- raise ValueError, 'Invalid %s number'%t
+ raise ValueError, 'No type specified'
+ if not n:
+ raise SeriousError, _('No ID entered')
+ try:
+ int(n)
+ except ValueError:
+ d = {'input': n, 'classname': t}
+ raise SeriousError, _(
+ '"%(input)s" is not an ID (%(classname)s ID required)')%d
url = '%s%s%s'%(self.db.config.TRACKER_WEB, t, n)
raise Redirect, url
permissionType = 'Edit'
def handle(self):
- """Retire the context item."""
+ """Retire the context item."""
# if we want to view the index template now, then unset the nodeid
# context info (a special-case for retire actions on the index page)
nodeid = self.nodeid
class SearchAction(Action):
name = 'search'
permissionType = 'View'
-
+
def handle(self, wcre=re.compile(r'[\s,]+')):
"""Mangle some of the form variables.
"""
self.fakeFilterVars()
- queryname = self.getQueryName()
+ queryname = self.getQueryName()
# handle saving the query params
if queryname:
# and add it to the user's query multilink
queries = self.db.user.get(self.userid, 'queries')
- queries.append(qid)
- self.db.user.set(self.userid, queries=queries)
+ if qid not in queries:
+ queries.append(qid)
+ self.db.user.set(self.userid, queries=queries)
# commit the query change to the database
self.db.commit()
# replace the single value with the split list
for v in l:
self.form.value.append(cgi.MiniFieldStorage(key, v))
-
+
self.form.value.append(cgi.MiniFieldStorage('@filter', key))
FV_QUERYNAME = re.compile(r'[@:]queryname')
class EditCSVAction(Action):
name = 'edit'
permissionType = 'Edit'
-
+
def handle(self):
"""Performs an edit of all of a class' items in one go.
self.db.commit()
self.client.ok_message.append(_('Items edited OK'))
-
+
class _EditAction(Action):
def isEditingSelf(self):
"""Check whether a user is editing his/her own details."""
return (self.nodeid == self.userid
and self.db.user.get(self.nodeid, 'username') != 'anonymous')
-
+
def editItemPermission(self, props):
"""Determine whether the user has permission to edit this item.
def handleCollision(self):
self.client.template = 'collision'
-
+
def handle(self):
"""Perform an edit of an item in the database.
See parsePropsFromForm and _editnodes for special variables.
-
+
"""
if self.detectCollision(self.lastUserActivity(), self.lastNodeActivity()):
self.handleCollision()
req = templating.HTMLRequest(self)
url += '&' + req.indexargs_href('', {})[1:]
raise Redirect, url
-
+
class NewItemAction(_EditAction):
def handle(self):
''' Add a new item to the database.
'''
# parse the props from the form
try:
- props, links = self.client.parsePropsFromForm(create=True)
+ props, links = self.client.parsePropsFromForm(create=1)
except (ValueError, KeyError), message:
self.client.error_message.append(_('Error: ') + str(message))
return
raise Redirect, '%s%s%s?@ok_message=%s&@template=%s'%(self.base,
self.classname, self.nodeid, urllib.quote(messages),
urllib.quote(self.template))
-
+
class PassResetAction(Action):
def handle(self):
"""Handle password reset requests.
-
+
Presence of either "name" or "address" generates email. Presence of
"otk" performs the reset.
-
+
"""
if self.form.has_key('otk'):
# pull the rego information out of the otk database
otk = self.form['otk'].value
- uid = self.db.otks.get(otk, 'uid')
+ otks = self.db.getOTKManager()
+ uid = otks.get(otk, 'uid')
if uid is None:
self.client.error_message.append("""Invalid One Time Key!
(a Mozilla bug may cause this message to show up erroneously,
newpw = password.generatePassword()
cl = self.db.user
-# XXX we need to make the "default" page be able to display errors!
+ # 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)
+ otks.destroy(otk)
self.db.commit()
except (ValueError, KeyError), message:
self.client.error_message.append(str(message))
if not self.client.standard_message([address], subject, body):
return
- self.client.ok_message.append('Password reset and email sent to %s' %
- address)
+ self.client.ok_message.append(
+ 'Password reset and email sent to %s'%address)
return
# no OTK, so now figure the user
# generate the one-time-key and store the props for later
otk = ''.join([random.choice(chars) for x in range(32)])
- self.db.otks.set(otk, uid=uid, __time=time.time())
+ while otks.exists(otk):
+ otk = ''.join([random.choice(chars) for x in range(32)])
+ otks.set(otk, uid=uid)
+ self.db.commit()
# send the email
tracker_name = self.db.config.TRACKER_NAME
# pull the rego information out of the otk database
self.userid = self.db.confirm_registration(self.form['otk'].value)
except (ValueError, KeyError), message:
- # XXX: we need to make the "default" page be able to display errors!
self.client.error_message.append(str(message))
return
-
+
# log the new user in
self.client.user = self.db.user.get(self.userid, 'username')
# re-open the database for real, using the user
self.client.opendb(self.client.user)
- self.db = client.db
# if we have a session, update it
if hasattr(self, 'session'):
- self.db.sessions.set(self.session, user=self.user,
+ self.client.db.sessions.set(self.session, user=self.user,
last_use=time.time())
else:
# new session cookie
# nice message
message = _('You are now registered, welcome!')
+ url = '%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))
+ # redirect to the user's page (but not 302, as some email clients seem
+ # to want to reload the page, or something)
+ return '''<html><head><title>%s</title></head>
+ <body><p><a href="%s">%s</a></p>
+ <script type="text/javascript">
+ window.setTimeout('window.location = "%s"', 1000);
+ </script>'''%(message, url, message, url)
class RegisterAction(Action):
name = 'register'
permissionType = 'Web Registration'
-
+
def handle(self):
"""Attempt to create a new user based on the contents of the form
and then set the cookie.
Return 1 on successful login.
- """
- props = self.client.parsePropsFromForm()[0][('user', None)]
+ """
+ props = self.client.parsePropsFromForm(create=1)[0][('user', None)]
# registration isn't allowed to supply roles
if props.has_key('roles'):
- raise Unauthorised, _("It is not permitted to supply roles at registration.")
+ raise Unauthorised, _("It is not permitted to supply roles "
+ "at registration.")
+ username = props['username']
try:
- self.db.user.lookup(props['username'])
- self.client.error_message.append('Error: A user with the username "%s" '
- 'already exists'%props['username'])
+ self.db.user.lookup(username)
+ self.client.error_message.append(_('Error: A user with the '
+ 'username "%(username)s" already exists')%props)
return
except KeyError:
pass
# generate the one-time-key and store the props for later
- otk = ''.join([random.choice(chars) for x in range(32)])
for propname, proptype in self.db.user.getprops().items():
value = props.get(propname, None)
if value is None:
props[propname] = str(value)
elif isinstance(proptype, hyperdb.Password):
props[propname] = str(value)
- props['__time'] = time.time()
- self.db.otks.set(otk, **props)
+ otks = self.db.getOTKManager()
+ while otks.exists(otk):
+ otk = ''.join([random.choice(chars) for x in range(32)])
+ otks.set(otk, **props)
# send the email
tracker_name = self.db.config.TRACKER_NAME
tracker_email = self.db.config.TRACKER_EMAIL
- subject = 'Complete your registration to %s -- key %s' % (tracker_name,
+ subject = 'Complete your registration to %s -- key %s'%(tracker_name,
otk)
body = """To complete your registration of the user "%(name)s" with
%(tracker)s, please do one of the following: