Mercurial > p > roundup > code
comparison roundup/cgi/actions.py @ 2032:5a7ec0c63095
fixes to some unit tests, and a cleanup
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Fri, 20 Feb 2004 03:48:16 +0000 |
| parents | bcb21e5722b8 |
| children | d124af927369 |
comparison
equal
deleted
inserted
replaced
| 2031:bcb21e5722b8 | 2032:5a7ec0c63095 |
|---|---|
| 12 'NewItemAction'] | 12 'NewItemAction'] |
| 13 | 13 |
| 14 # used by a couple of routines | 14 # used by a couple of routines |
| 15 chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' | 15 chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' |
| 16 | 16 |
| 17 class Action: | 17 class Action: |
| 18 def __init__(self, client): | 18 def __init__(self, client): |
| 19 self.client = client | 19 self.client = client |
| 20 self.form = client.form | 20 self.form = client.form |
| 21 self.db = client.db | 21 self.db = client.db |
| 22 self.nodeid = client.nodeid | 22 self.nodeid = client.nodeid |
| 23 self.template = client.template | 23 self.template = client.template |
| 24 self.classname = client.classname | 24 self.classname = client.classname |
| 25 self.userid = client.userid | 25 self.userid = client.userid |
| 26 self.base = client.base | 26 self.base = client.base |
| 27 self.user = client.user | 27 self.user = client.user |
| 28 | 28 |
| 29 def execute(self): | 29 def execute(self): |
| 30 """Execute the action specified by this object.""" | 30 """Execute the action specified by this object.""" |
| 31 self.permission() | 31 self.permission() |
| 32 self.handle() | 32 self.handle() |
| 33 | 33 |
| 38 | 38 |
| 39 True by default. If the permissionType attribute is a string containing | 39 True by default. If the permissionType attribute is a string containing |
| 40 a simple permission, check whether the user has that permission. | 40 a simple permission, check whether the user has that permission. |
| 41 Subclasses must also define the name attribute if they define | 41 Subclasses must also define the name attribute if they define |
| 42 permissionType. | 42 permissionType. |
| 43 | 43 |
| 44 Despite having this permission, users may still be unauthorised to | 44 Despite having this permission, users may still be unauthorised to |
| 45 perform parts of actions. It is up to the subclasses to detect this. | 45 perform parts of actions. It is up to the subclasses to detect this. |
| 46 """ | 46 """ |
| 47 if (self.permissionType and | 47 if (self.permissionType and |
| 48 not self.hasPermission(self.permissionType)): | 48 not self.hasPermission(self.permissionType)): |
| 49 | 49 info = {'action': self.name, 'classname': self.classname} |
| 50 raise Unauthorised, _('You do not have permission to %s the %s class.' % | 50 raise Unauthorised, _('You do not have permission to ' |
| 51 (self.name, self.classname)) | 51 '%(action)s the %(classname)s class.')%info |
| 52 | 52 |
| 53 def hasPermission(self, permission): | 53 def hasPermission(self, permission): |
| 54 """Check whether the user has 'permission' on the current class.""" | 54 """Check whether the user has 'permission' on the current class.""" |
| 55 return self.db.security.hasPermission(permission, self.client.userid, | 55 return self.db.security.hasPermission(permission, self.client.userid, |
| 56 self.client.classname) | 56 self.client.classname) |
| 57 | 57 |
| 58 class ShowAction(Action): | 58 class ShowAction(Action): |
| 59 def handle(self, typere=re.compile('[@:]type'), | 59 def handle(self, typere=re.compile('[@:]type'), |
| 60 numre=re.compile('[@:]number')): | 60 numre=re.compile('[@:]number')): |
| 61 """Show a node of a particular class/id.""" | 61 """Show a node of a particular class/id.""" |
| 73 class RetireAction(Action): | 73 class RetireAction(Action): |
| 74 name = 'retire' | 74 name = 'retire' |
| 75 permissionType = 'Edit' | 75 permissionType = 'Edit' |
| 76 | 76 |
| 77 def handle(self): | 77 def handle(self): |
| 78 """Retire the context item.""" | 78 """Retire the context item.""" |
| 79 # if we want to view the index template now, then unset the nodeid | 79 # if we want to view the index template now, then unset the nodeid |
| 80 # context info (a special-case for retire actions on the index page) | 80 # context info (a special-case for retire actions on the index page) |
| 81 nodeid = self.nodeid | 81 nodeid = self.nodeid |
| 82 if self.template == 'index': | 82 if self.template == 'index': |
| 83 self.client.nodeid = None | 83 self.client.nodeid = None |
| 96 'classname': self.classname.capitalize(), 'itemid': nodeid}) | 96 'classname': self.classname.capitalize(), 'itemid': nodeid}) |
| 97 | 97 |
| 98 class SearchAction(Action): | 98 class SearchAction(Action): |
| 99 name = 'search' | 99 name = 'search' |
| 100 permissionType = 'View' | 100 permissionType = 'View' |
| 101 | 101 |
| 102 def handle(self, wcre=re.compile(r'[\s,]+')): | 102 def handle(self, wcre=re.compile(r'[\s,]+')): |
| 103 """Mangle some of the form variables. | 103 """Mangle some of the form variables. |
| 104 | 104 |
| 105 Set the form ":filter" variable based on the values of the filter | 105 Set the form ":filter" variable based on the values of the filter |
| 106 variables - if they're set to anything other than "dontcare" then add | 106 variables - if they're set to anything other than "dontcare" then add |
| 111 | 111 |
| 112 Split any String query values on whitespace and comma. | 112 Split any String query values on whitespace and comma. |
| 113 | 113 |
| 114 """ | 114 """ |
| 115 self.fakeFilterVars() | 115 self.fakeFilterVars() |
| 116 queryname = self.getQueryName() | 116 queryname = self.getQueryName() |
| 117 | 117 |
| 118 # handle saving the query params | 118 # handle saving the query params |
| 119 if queryname: | 119 if queryname: |
| 120 # parse the environment and figure what the query _is_ | 120 # parse the environment and figure what the query _is_ |
| 121 req = templating.HTMLRequest(self.client) | 121 req = templating.HTMLRequest(self.client) |
| 163 if len(l) > 1 or l[0] != v: | 163 if len(l) > 1 or l[0] != v: |
| 164 self.form.value.remove(self.form[key]) | 164 self.form.value.remove(self.form[key]) |
| 165 # replace the single value with the split list | 165 # replace the single value with the split list |
| 166 for v in l: | 166 for v in l: |
| 167 self.form.value.append(cgi.MiniFieldStorage(key, v)) | 167 self.form.value.append(cgi.MiniFieldStorage(key, v)) |
| 168 | 168 |
| 169 self.form.value.append(cgi.MiniFieldStorage('@filter', key)) | 169 self.form.value.append(cgi.MiniFieldStorage('@filter', key)) |
| 170 | 170 |
| 171 FV_QUERYNAME = re.compile(r'[@:]queryname') | 171 FV_QUERYNAME = re.compile(r'[@:]queryname') |
| 172 def getQueryName(self): | 172 def getQueryName(self): |
| 173 for key in self.form.keys(): | 173 for key in self.form.keys(): |
| 176 return '' | 176 return '' |
| 177 | 177 |
| 178 class EditCSVAction(Action): | 178 class EditCSVAction(Action): |
| 179 name = 'edit' | 179 name = 'edit' |
| 180 permissionType = 'Edit' | 180 permissionType = 'Edit' |
| 181 | 181 |
| 182 def handle(self): | 182 def handle(self): |
| 183 """Performs an edit of all of a class' items in one go. | 183 """Performs an edit of all of a class' items in one go. |
| 184 | 184 |
| 185 The "rows" CGI var defines the CSV-formatted entries for the class. New | 185 The "rows" CGI var defines the CSV-formatted entries for the class. New |
| 186 nodes are identified by the ID 'X' (or any other non-existent ID) and | 186 nodes are identified by the ID 'X' (or any other non-existent ID) and |
| 268 | 268 |
| 269 # all OK | 269 # all OK |
| 270 self.db.commit() | 270 self.db.commit() |
| 271 | 271 |
| 272 self.client.ok_message.append(_('Items edited OK')) | 272 self.client.ok_message.append(_('Items edited OK')) |
| 273 | 273 |
| 274 class _EditAction(Action): | 274 class _EditAction(Action): |
| 275 def isEditingSelf(self): | 275 def isEditingSelf(self): |
| 276 """Check whether a user is editing his/her own details.""" | 276 """Check whether a user is editing his/her own details.""" |
| 277 return (self.nodeid == self.userid | 277 return (self.nodeid == self.userid |
| 278 and self.db.user.get(self.nodeid, 'username') != 'anonymous') | 278 and self.db.user.get(self.nodeid, 'username') != 'anonymous') |
| 279 | 279 |
| 280 def editItemPermission(self, props): | 280 def editItemPermission(self, props): |
| 281 """Determine whether the user has permission to edit this item. | 281 """Determine whether the user has permission to edit this item. |
| 282 | 282 |
| 283 Base behaviour is to check the user can edit this class. If we're | 283 Base behaviour is to check the user can edit this class. If we're |
| 284 editing the "user" class, users are allowed to edit their own details. | 284 editing the "user" class, users are allowed to edit their own details. |
| 442 if userActivity: | 442 if userActivity: |
| 443 return userActivity < nodeActivity | 443 return userActivity < nodeActivity |
| 444 | 444 |
| 445 def handleCollision(self): | 445 def handleCollision(self): |
| 446 self.client.template = 'collision' | 446 self.client.template = 'collision' |
| 447 | 447 |
| 448 def handle(self): | 448 def handle(self): |
| 449 """Perform an edit of an item in the database. | 449 """Perform an edit of an item in the database. |
| 450 | 450 |
| 451 See parsePropsFromForm and _editnodes for special variables. | 451 See parsePropsFromForm and _editnodes for special variables. |
| 452 | 452 |
| 453 """ | 453 """ |
| 454 if self.detectCollision(self.lastUserActivity(), self.lastNodeActivity()): | 454 if self.detectCollision(self.lastUserActivity(), self.lastNodeActivity()): |
| 455 self.handleCollision() | 455 self.handleCollision() |
| 456 return | 456 return |
| 457 | 457 |
| 478 urllib.quote(self.template)) | 478 urllib.quote(self.template)) |
| 479 if self.nodeid is None: | 479 if self.nodeid is None: |
| 480 req = templating.HTMLRequest(self) | 480 req = templating.HTMLRequest(self) |
| 481 url += '&' + req.indexargs_href('', {})[1:] | 481 url += '&' + req.indexargs_href('', {})[1:] |
| 482 raise Redirect, url | 482 raise Redirect, url |
| 483 | 483 |
| 484 class NewItemAction(_EditAction): | 484 class NewItemAction(_EditAction): |
| 485 def handle(self): | 485 def handle(self): |
| 486 ''' Add a new item to the database. | 486 ''' Add a new item to the database. |
| 487 | 487 |
| 488 This follows the same form as the EditItemAction, with the same | 488 This follows the same form as the EditItemAction, with the same |
| 510 | 510 |
| 511 # redirect to the new item's page | 511 # redirect to the new item's page |
| 512 raise Redirect, '%s%s%s?@ok_message=%s&@template=%s'%(self.base, | 512 raise Redirect, '%s%s%s?@ok_message=%s&@template=%s'%(self.base, |
| 513 self.classname, self.nodeid, urllib.quote(messages), | 513 self.classname, self.nodeid, urllib.quote(messages), |
| 514 urllib.quote(self.template)) | 514 urllib.quote(self.template)) |
| 515 | 515 |
| 516 class PassResetAction(Action): | 516 class PassResetAction(Action): |
| 517 def handle(self): | 517 def handle(self): |
| 518 """Handle password reset requests. | 518 """Handle password reset requests. |
| 519 | 519 |
| 520 Presence of either "name" or "address" generates email. Presence of | 520 Presence of either "name" or "address" generates email. Presence of |
| 521 "otk" performs the reset. | 521 "otk" performs the reset. |
| 522 | 522 |
| 523 """ | 523 """ |
| 524 if self.form.has_key('otk'): | 524 if self.form.has_key('otk'): |
| 525 # pull the rego information out of the otk database | 525 # pull the rego information out of the otk database |
| 526 otk = self.form['otk'].value | 526 otk = self.form['otk'].value |
| 527 uid = self.db.otks.get(otk, 'uid') | 527 uid = self.db.otks.get(otk, 'uid') |
| 619 # pull the rego information out of the otk database | 619 # pull the rego information out of the otk database |
| 620 self.userid = self.db.confirm_registration(self.form['otk'].value) | 620 self.userid = self.db.confirm_registration(self.form['otk'].value) |
| 621 except (ValueError, KeyError), message: | 621 except (ValueError, KeyError), message: |
| 622 self.client.error_message.append(str(message)) | 622 self.client.error_message.append(str(message)) |
| 623 return | 623 return |
| 624 | 624 |
| 625 # log the new user in | 625 # log the new user in |
| 626 self.client.user = self.db.user.get(self.userid, 'username') | 626 self.client.user = self.db.user.get(self.userid, 'username') |
| 627 # re-open the database for real, using the user | 627 # re-open the database for real, using the user |
| 628 self.client.opendb(self.client.user) | 628 self.client.opendb(self.client.user) |
| 629 | 629 |
| 643 self.userid, urllib.quote(message)) | 643 self.userid, urllib.quote(message)) |
| 644 | 644 |
| 645 class RegisterAction(Action): | 645 class RegisterAction(Action): |
| 646 name = 'register' | 646 name = 'register' |
| 647 permissionType = 'Web Registration' | 647 permissionType = 'Web Registration' |
| 648 | 648 |
| 649 def handle(self): | 649 def handle(self): |
| 650 """Attempt to create a new user based on the contents of the form | 650 """Attempt to create a new user based on the contents of the form |
| 651 and then set the cookie. | 651 and then set the cookie. |
| 652 | 652 |
| 653 Return 1 on successful login. | 653 Return 1 on successful login. |
| 654 """ | 654 """ |
| 655 props = self.client.parsePropsFromForm(create=1)[0][('user', None)] | 655 props = self.client.parsePropsFromForm(create=1)[0][('user', None)] |
| 656 | 656 |
| 657 # registration isn't allowed to supply roles | 657 # registration isn't allowed to supply roles |
| 658 if props.has_key('roles'): | 658 if props.has_key('roles'): |
| 659 raise Unauthorised, _("It is not permitted to supply roles " | 659 raise Unauthorised, _("It is not permitted to supply roles " |
| 660 "at registration.") | 660 "at registration.") |
| 661 | 661 |
| 662 username = props['username'] | 662 username = props['username'] |
| 663 try: | 663 try: |
| 664 self.db.user.lookup(username) | 664 self.db.user.lookup(username) |
| 665 self.client.error_message.append(_('Error: A user with the ' | 665 self.client.error_message.append(_('Error: A user with the ' |
