Mercurial > p > roundup > code
diff roundup/cgi/actions.py @ 6066:b011e5ac06d5
Flake8 whitespace; add translate; change use 'is None' not =
Changed out some strings like:
_("foo bar bas....")
into
_( "foo"
"bar bas")
to handle long lines. Verified both forms result in the same string
extraction using locale tools xgettext/xpot.
Added a couple of translation marks.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Sat, 18 Jan 2020 20:27:02 -0500 |
| parents | 8128ca0cb764 |
| children | f74d078cfd9a |
line wrap: on
line diff
--- a/roundup/cgi/actions.py Sat Jan 18 14:53:44 2020 -0500 +++ b/roundup/cgi/actions.py Sat Jan 18 20:27:02 2020 -0500 @@ -14,11 +14,11 @@ from roundup.anypy.html import html_escape -import time from datetime import timedelta # Also add action to client.py::Client.actions property -__all__ = ['Action', 'ShowAction', 'RetireAction', 'RestoreAction', 'SearchAction', +__all__ = ['Action', 'ShowAction', 'RetireAction', 'RestoreAction', + 'SearchAction', 'EditCSVAction', 'EditItemAction', 'PassResetAction', 'ConfRegoAction', 'RegisterAction', 'LoginAction', 'LogoutAction', 'NewItemAction', 'ExportCSVAction', 'ExportCSVWithIdAction'] @@ -26,6 +26,7 @@ # used by a couple of routines chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' + class Action: def __init__(self, client): self.client = client @@ -38,7 +39,8 @@ self.base = client.base self.user = client.user self.context = templating.context(client) - self.loginLimit = RateLimit(client.db.config.WEB_LOGIN_ATTEMPTS_MIN, timedelta(seconds=60)) + self.loginLimit = RateLimit(client.db.config.WEB_LOGIN_ATTEMPTS_MIN, + timedelta(seconds=60)) def handle(self): """Action handler procedure""" @@ -96,30 +98,35 @@ else: raise ValueError(self._("Base url not set. Check configuration.")) - info={ 'url': url, - 'base_url': self.base, - 'base_scheme': parsed_base_url_tuple.scheme, - 'base_netloc': parsed_base_url_tuple.netloc, - 'base_path': parsed_base_url_tuple.path, - 'url_scheme': parsed_url_tuple.scheme, - 'url_netloc': parsed_url_tuple.netloc, - 'url_path': parsed_url_tuple.path, - 'url_params': parsed_url_tuple.params, - 'url_query': parsed_url_tuple.query, - 'url_fragment': parsed_url_tuple.fragment } + info = {'url': url, + 'base_url': self.base, + 'base_scheme': parsed_base_url_tuple.scheme, + 'base_netloc': parsed_base_url_tuple.netloc, + 'base_path': parsed_base_url_tuple.path, + 'url_scheme': parsed_url_tuple.scheme, + 'url_netloc': parsed_url_tuple.netloc, + 'url_path': parsed_url_tuple.path, + 'url_params': parsed_url_tuple.params, + 'url_query': parsed_url_tuple.query, + 'url_fragment': parsed_url_tuple.fragment} if parsed_base_url_tuple.scheme == "https": if parsed_url_tuple.scheme != "https": - raise ValueError(self._("Base url %(base_url)s requires https. Redirect url %(url)s uses http.")%info) + raise ValueError(self._("Base url %(base_url)s requires https." + " Redirect url %(url)s uses http.") % + info) else: if parsed_url_tuple.scheme not in ('http', 'https'): - raise ValueError(self._("Unrecognized scheme in %(url)s")%info) + raise ValueError(self._("Unrecognized scheme in %(url)s") % + info) if parsed_url_tuple.netloc != parsed_base_url_tuple.netloc: - raise ValueError(self._("Net location in %(url)s does not match base: %(base_netloc)s")%info) + raise ValueError(self._("Net location in %(url)s does not match " + "base: %(base_netloc)s") % info) if parsed_url_tuple.path.find(parsed_base_url_tuple.path) != 0: - raise ValueError(self._("Base path %(base_path)s is not a prefix for url %(url)s")%info) + raise ValueError(self._("Base path %(base_path)s is not a " + "prefix for url %(url)s") % info) # I am not sure if this has to be language sensitive. # Do ranges depend on the LANG of the user?? @@ -132,21 +139,23 @@ allowed_pattern = re.compile(r'''^[A-Za-z0-9@:/?._~%!$&'()*+,;=-]*$''') if not allowed_pattern.match(parsed_url_tuple.path): - raise ValueError(self._("Path component (%(url_path)s) in %(url)s is not properly escaped")%info) + raise ValueError(self._("Path component (%(url_path)s) in %(url)s " + "is not properly escaped") % info) if not allowed_pattern.match(parsed_url_tuple.params): - raise ValueError(self._("Params component (%(url_params)s) in %(url)s is not properly escaped")%info) + raise ValueError(self._("Params component (%(url_params)s) in %(url)s is not properly escaped") % info) if not allowed_pattern.match(parsed_url_tuple.query): - raise ValueError(self._("Query component (%(url_query)s) in %(url)s is not properly escaped")%info) + raise ValueError(self._("Query component (%(url_query)s) in %(url)s is not properly escaped") % info) if not allowed_pattern.match(parsed_url_tuple.fragment): - raise ValueError(self._("Fragment component (%(url_fragment)s) in %(url)s is not properly escaped")%info) + raise ValueError(self._("Fragment component (%(url_fragment)s) in %(url)s is not properly escaped") % info) return(urllib_.urlunparse(parsed_url_tuple)) name = '' permissionType = None + def permission(self): """Check whether the user has permission to execute this action. @@ -163,15 +172,18 @@ info = {'action': self.name, 'classname': self.classname} raise exceptions.Unauthorised(self._( 'You do not have permission to ' - '%(action)s the %(classname)s class.')%info) + '%(action)s the %(classname)s class.') % info) _marker = [] - def hasPermission(self, permission, classname=_marker, itemid=None, property=None): + + def hasPermission(self, permission, classname=_marker, itemid=None, + property=None): """Check whether the user has 'permission' on the current class.""" if classname is self._marker: classname = self.client.classname return self.db.security.hasPermission(permission, self.client.userid, - classname=classname, itemid=itemid, property=property) + classname=classname, + itemid=itemid, property=property) def gettext(self, msgid): """Return the localized translation of msgid""" @@ -179,10 +191,11 @@ _ = gettext + class ShowAction(Action): - typere=re.compile('[@:]type') - numre=re.compile('[@:]number') + typere = re.compile('[@:]type') + numre = re.compile('[@:]number') def handle(self): """Show a node of a particular class/id.""" @@ -201,10 +214,11 @@ except ValueError: d = {'input': n, 'classname': t} raise exceptions.SeriousError(self._( - '"%(input)s" is not an ID (%(classname)s ID required)')%d) - url = '%s%s%s'%(self.base, t, n) + '"%(input)s" is not an ID (%(classname)s ID required)') % d) + url = '%s%s%s' % (self.base, t, n) raise exceptions.Redirect(url) + class RetireAction(Action): name = 'retire' permissionType = 'Edit' @@ -239,7 +253,7 @@ self.db.commit() self.client.add_ok_message( - self._('%(classname)s %(itemid)s has been retired')%{ + self._('%(classname)s %(itemid)s has been retired') % { 'classname': self.classname.capitalize(), 'itemid': itemid}) @@ -261,7 +275,7 @@ # check permission if not self.hasPermission('Restore', classname=self.classname, - itemid=itemid): + itemid=itemid): raise exceptions.Unauthorised(self._( 'You do not have permission to restore %(class)s' ) % {'class': self.classname}) @@ -271,7 +285,7 @@ self.db.commit() self.client.add_ok_message( - self._('%(classname)s %(itemid)s has been restored')%{ + self._('%(classname)s %(itemid)s has been restored') % { 'classname': self.classname.capitalize(), 'itemid': itemid}) @@ -322,7 +336,7 @@ raise exceptions.Unauthorised(self._( "You do not have permission to store queries")) qid = self.db.query.create(name=queryname, - klass=self.classname, url=url) + klass=self.classname, url=url) else: uid = self.db.getuid() @@ -341,19 +355,21 @@ if queryname != self.db.query.get(qid, 'name'): continue # whoops we found a duplicate; report error and return - message=_("You already own a query named '%s'. Please choose another name.")%(queryname) + message = _("You already own a query named '%s'. " + "Please choose another name.") % \ + (queryname) self.client.add_error_message(message) return # edit the new way, query name not a key any more # see if we match an existing private query qids = self.db.query.filter(None, {'name': old_queryname, - 'private_for': uid}) + 'private_for': uid}) if not qids: # ok, so there's not a private query for the current user # - see if there's one created by them qids = self.db.query.filter(None, {'name': old_queryname, - 'creator': uid}) + 'creator': uid}) if qids and old_queryname: # edit query - make sure we get an exact match on the name @@ -364,14 +380,15 @@ raise exceptions.Unauthorised(self._( "You do not have permission to edit queries")) self.db.query.set(qid, klass=self.classname, - url=url, name=queryname) + url=url, name=queryname) else: # create a query if not self.hasPermission('Create', 'query'): raise exceptions.Unauthorised(self._( "You do not have permission to store queries")) qid = self.db.query.create(name=queryname, - klass=self.classname, url=url, private_for=uid) + klass=self.classname, url=url, + private_for=uid) # and add it to the user's query multilink queries = self.db.user.get(self.userid, 'queries') @@ -391,7 +408,6 @@ ) ) - def fakeFilterVars(self): """Add a faked :filter form variable for each filtering prop.""" cls = self.db.classes[self.classname] @@ -422,16 +438,18 @@ try: float(self.form[key].value) except ValueError: - raise exceptions.FormError("Invalid number: "+self.form[key].value) + raise exceptions.FormError(_("Invalid number: ") + + self.form[key].value) elif isinstance(prop, hyperdb.Integer): try: - val=self.form[key].value - if ( str(int(val)) == val ): + val = self.form[key].value + if (str(int(val)) == val): pass else: raise ValueError except ValueError: - raise exceptions.FormError("Invalid integer: "+val) + raise exceptions.FormError(_("Invalid integer: ") + + val) self.form.value.append(cgi.MiniFieldStorage('@filter', key)) @@ -447,7 +465,7 @@ """ template = self.getFromForm('template') if template and template != 'index': - return req.indexargs_url('', {'@template' : template})[1:] + return req.indexargs_url('', {'@template': template})[1:] return req.indexargs_url('', {})[1:] def getFromForm(self, name): @@ -459,6 +477,7 @@ def getQueryName(self): return self.getFromForm('queryname') + class EditCSVAction(Action): name = 'edit' permissionType = 'Edit' @@ -519,7 +538,8 @@ # confirm correct weight if len(props_without_id) != len(values): self.client.add_error_message( - self._('Not enough values on line %(line)s')%{'line':line}) + self._('Not enough values on line %(line)s') % \ + {'line':line}) return # extract the new values @@ -527,7 +547,7 @@ for name, value in zip(props_without_id, values): # check permission to edit this property on this item if exists and not self.hasPermission('Edit', itemid=itemid, - classname=self.classname, property=name): + classname=self.classname, property=name): raise exceptions.Unauthorised(self._( 'You do not have permission to edit %(class)s' ) % {'class': self.classname}) @@ -575,7 +595,7 @@ if itemid not in found: # check permission to retire this item if not self.hasPermission('Retire', itemid=itemid, - classname=self.classname): + classname=self.classname): raise exceptions.Unauthorised(self._( 'You do not have permission to retire %(class)s' ) % {'class': self.classname}) @@ -586,6 +606,7 @@ self.client.add_ok_message(self._('Items edited OK')) + class EditCommon(Action): '''Utility methods for editing.''' @@ -597,7 +618,7 @@ deps = {} links = {} for cn, nodeid, propname, vlist in all_links: - numeric_id = int (nodeid or 0) + numeric_id = int(nodeid or 0) if not (numeric_id > 0 or (cn, nodeid) in all_props): # link item to link to doesn't (and won't) exist continue @@ -644,13 +665,13 @@ info = ', '.join(map(self._, props)) m.append( self._('%(class)s %(id)s %(properties)s edited ok') - % {'class':cn, 'id':nodeid, 'properties':info}) + % {'class': cn, 'id': nodeid, 'properties': info}) else: # this used to produce a message like: # issue34 - nothing changed # which is confusing if only quiet properties # changed for the class/id. So don't report - # anything is the user didn't explicitly change + # anything if the user didn't explicitly change # a visible (non-quiet) property. pass else: @@ -662,7 +683,7 @@ # and some nice feedback for the user m.append(self._('%(class)s %(id)s created') - % {'class':cn, 'id':newid}) + % {'class': cn, 'id': newid}) # fill in new ids in links if needed in links: @@ -720,6 +741,7 @@ and self.db.user.get(self.nodeid, 'username') != 'anonymous') _cn_marker = [] + def editItemPermission(self, props, classname=_cn_marker, itemid=None): """Determine whether the user has permission to edit this item.""" if itemid is None: @@ -730,7 +752,7 @@ # being changed. for p in props: if not self.hasPermission('Edit', itemid=itemid, - classname=classname, property=p): + classname=classname, property=p): return 0 # Since the user has permission to edit all of the properties, # the edit is OK. @@ -743,9 +765,9 @@ property checks are made. """ - if not classname : + if not classname: classname = self.client.classname - + if not self.hasPermission('Create', classname=classname): return 0 @@ -757,6 +779,7 @@ return 0 return 1 + class EditItemAction(EditCommon): def lastUserActivity(self): if ':lastactivity' in self.form: @@ -788,8 +811,8 @@ def handleCollision(self, props): message = self._('Edit Error: someone else has edited this %s (%s). ' 'View <a target="_blank" href="%s%s">their changes</a> ' - 'in a new window.')%(self.classname, ', '.join(props), - self.classname, self.nodeid) + 'in a new window.') % (self.classname, ', '.join(props), + self.classname, self.nodeid) self.client.add_error_message(message, escape=False) return @@ -831,13 +854,14 @@ # we will want to include index-page args in this URL too if self.nodeid is not None: url += self.nodeid - url += '?@ok_message=%s&@template=%s'%(urllib_.quote(message), - urllib_.quote(self.template)) + url += '?@ok_message=%s&@template=%s' % (urllib_.quote(message), + urllib_.quote(self.template)) if self.nodeid is None: req = templating.HTMLRequest(self.client) url += '&' + req.indexargs_url('', {})[1:] raise exceptions.Redirect(url) + class NewItemAction(EditCommon): def handle(self): ''' Add a new item to the database. @@ -854,7 +878,7 @@ props, links = self.client.parsePropsFromForm(create=1) except (ValueError, KeyError) as message: self.client.add_error_message(self._('Error: %s') - % str(message)) + % str(message)) return # handle the props - edit or create @@ -873,7 +897,7 @@ # Allow an option to stay on the page to create new things if '__redirect_to' in self.form: - raise exceptions.Redirect('%s&@ok_message=%s'%( + raise exceptions.Redirect('%s&@ok_message=%s' % ( self.examine_url(self.form['__redirect_to'].value), urllib_.quote(messages))) @@ -882,6 +906,7 @@ self.base, self.classname, self.nodeid, urllib_.quote(messages), urllib_.quote(self.template))) + class PassResetAction(Action): def handle(self): """Handle password reset requests. @@ -898,8 +923,8 @@ if uid is None: self.client.add_error_message( self._("Invalid One Time Key!\n" - "(a Mozilla bug may cause this message " - "to show up erroneously, please check your email)")) + "(a Mozilla bug may cause this message " + "to show up erroneously, please check your email)")) return # pull the additional email address if exist @@ -918,12 +943,13 @@ # XXX we need to make the "default" page be able to display errors! try: # set the password - cl.set(uid, password=password.Password(newpw, config=self.db.config)) + cl.set(uid, password=password.Password(newpw, + config=self.db.config)) # clear the props from the otk database otks.destroy(otk) otks.commit() # commit the password change - self.db.commit () + self.db.commit() except (ValueError, KeyError) as message: self.client.add_error_message(str(message)) return @@ -937,12 +963,12 @@ # send the email tracker_name = self.db.config.TRACKER_NAME - subject = 'Password reset for %s'%tracker_name + subject = 'Password reset for %s' % tracker_name body = ''' The password has been reset for username "%(name)s". Your password is now: %(password)s -'''%{'name': name, 'password': newpw} +''' % {'name': name, 'password': newpw} if not self.client.standard_message([address], subject, body): return @@ -981,7 +1007,7 @@ # send the email tracker_name = self.db.config.TRACKER_NAME - subject = 'Confirm reset of password for %s'%tracker_name + subject = 'Confirm reset of password for %s' % tracker_name body = ''' Someone, perhaps you, has requested that the password be changed for your username, "%(name)s". If you wish to proceed with the change, please follow @@ -990,7 +1016,7 @@ %(url)suser?@template=forgotten&@action=passrst&otk=%(otk)s You should then receive another email with the new password. -'''%{'name': name, 'tracker': tracker_name, 'url': self.base, 'otk': otk} +''' % {'name': name, 'tracker': tracker_name, 'url': self.base, 'otk': otk} if not self.client.standard_message([address], subject, body): return @@ -999,6 +1025,7 @@ else: self.client.add_ok_message(self._('Email sent to %s.') % address) + class RegoCommon(Action): def finishRego(self): # log the new user in @@ -1012,8 +1039,8 @@ # nice message message = self._('You are now registered, welcome!') - url = '%suser%s?@ok_message=%s'%(self.base, self.userid, - urllib_.quote(message)) + url = '%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) @@ -1021,9 +1048,10 @@ <body><p><a href="%s">%s</a></p> <script nonce="%s" type="text/javascript"> window.setTimeout('window.location = "%s"', 1000); - </script>'''%(message, url, message, + </script>''' % (message, url, message, self.client.client_nonce, url) + class ConfRegoAction(RegoCommon): def handle(self): """Grab the OTK, use it to load up the new user details.""" @@ -1035,6 +1063,7 @@ return return self.finishRego() + class RegisterAction(RegoCommon, EditCommon, Timestamped): name = 'register' permissionType = 'Register' @@ -1057,13 +1086,13 @@ if delaytime > 0: self.timecheck('opaqueregister', delaytime) - + # parse the props from the form try: props, links = self.client.parsePropsFromForm(create=1) except (ValueError, KeyError) as message: self.client.add_error_message(self._('Error: %s') - % str(message)) + % str(message)) return # skip the confirmation step? @@ -1081,7 +1110,7 @@ # fix up the initial roles self.db.user.set(self.nodeid, - roles=self.db.config['NEW_WEB_USER_ROLES']) + roles=self.db.config['NEW_WEB_USER_ROLES']) # commit now that all the tricky stuff is done self.db.commit() @@ -1099,11 +1128,11 @@ user_found = self.db.user.lookup(user_props['username']) # if user is found reject the request. raise Reject( - _("Username '%s' is already used.")%user_props['username']) + _("Username '%s' is already used.") % user_props['username']) except KeyError: # user not found this is what we want. pass - + for propname, proptype in self.db.user.getprops().items(): value = user_props.get(propname, None) if value is None: @@ -1124,8 +1153,8 @@ tracker_name = self.db.config.TRACKER_NAME tracker_email = self.db.config.TRACKER_EMAIL if self.db.config['EMAIL_REGISTRATION_CONFIRMATION']: - subject = 'Complete your registration to %s -- key %s'%(tracker_name, - otk) + 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: @@ -1137,25 +1166,26 @@ %(url)s?@action=confrego&otk=%(otk)s """ % {'name': user_props['username'], 'tracker': tracker_name, - 'url': self.base, 'otk': otk, 'tracker_email': tracker_email} + 'url': self.base, 'otk': otk, 'tracker_email': tracker_email} else: - subject = 'Complete your registration to %s'%(tracker_name) + subject = 'Complete your registration to %s' % (tracker_name) body = """To complete your registration of the user "%(name)s" with %(tracker)s, please visit the following URL: %(url)s?@action=confrego&otk=%(otk)s """ % {'name': user_props['username'], 'tracker': tracker_name, - 'url': self.base, 'otk': otk} + 'url': self.base, 'otk': otk} if not self.client.standard_message([user_props['address']], subject, - body, (tracker_name, tracker_email)): + body, + (tracker_name, tracker_email)): return # commit changes to the database self.db.commit() # redirect to the "you're almost there" page - raise exceptions.Redirect('%suser?@template=rego_progress'%self.base) + raise exceptions.Redirect('%suser?@template=rego_progress' % self.base) def newItemPermission(self, props, classname=None): """Just check the "Register" permission. @@ -1168,6 +1198,7 @@ # technically already checked, but here for clarity return self.hasPermission('Register', classname=classname) + class LogoutAction(Action): def handle(self): """Make us really anonymous - nuke the session too.""" @@ -1192,6 +1223,7 @@ # see that. raise exceptions.Redirect(self.base) + class LoginAction(Action): def handle(self): """Attempt to log a user in. @@ -1232,7 +1264,7 @@ redirect_url_tuple = urllib_.urlparse(clean_url) # now I have a tuple form for the __came_from url try: - query=urllib_.parse_qs(redirect_url_tuple.query) + query = urllib_.parse_qs(redirect_url_tuple.query) if "@error_message" in query: del query["@error_message"] if "@ok_message" in query: @@ -1247,44 +1279,44 @@ query = {} pass - redirect_url = urllib_.urlunparse( (redirect_url_tuple.scheme, - redirect_url_tuple.netloc, - redirect_url_tuple.path, - redirect_url_tuple.params, - urllib_.urlencode(list(sorted(query.items())), doseq=True), - redirect_url_tuple.fragment) - ) + redirect_url = urllib_.urlunparse((redirect_url_tuple.scheme, + redirect_url_tuple.netloc, + redirect_url_tuple.path, + redirect_url_tuple.params, + urllib_.urlencode(list(sorted(query.items())), doseq=True), + redirect_url_tuple.fragment) + ) try: # Implement rate limiting of logins by login name. # Use prefix to prevent key collisions maybe?? # set client.db.config.WEB_LOGIN_ATTEMPTS_MIN to 0 # to disable - if self.client.db.config.WEB_LOGIN_ATTEMPTS_MIN: # if 0 - off - rlkey="LOGIN-" + self.client.user - limit=self.loginLimit - gcra=Gcra() - otk=self.client.db.Otk + if self.client.db.config.WEB_LOGIN_ATTEMPTS_MIN: # if 0 - off + rlkey = "LOGIN-" + self.client.user + limit = self.loginLimit + gcra = Gcra() + otk = self.client.db.Otk try: - val=otk.getall(rlkey) + val = otk.getall(rlkey) gcra.set_tat_as_string(rlkey, val['tat']) except KeyError: # ignore if tat not set, it's 1970-1-1 by default. pass # see if rate limit exceeded and we need to reject the attempt - reject=gcra.update(rlkey, limit) + reject = gcra.update(rlkey, limit) # Calculate a timestamp that will make OTK expire the # unused entry 1 hour in the future ts = time.time() - (60 * 60 * 24 * 7) + 3600 otk.set(rlkey, tat=gcra.get_tat_as_string(rlkey), - __timestamp=ts) + __timestamp=ts) otk.commit() if reject: # User exceeded limits: find out how long to wait - status=gcra.status(rlkey, limit) - raise Reject(_("Logins occurring too fast. Please wait: %s seconds.")%status['Retry-After']) + status = gcra.status(rlkey, limit) + raise Reject(_("Logins occurring too fast. Please wait: %s seconds.") % status['Retry-After']) self.verifyLogin(self.client.user, password) except exceptions.LoginError as err: @@ -1295,13 +1327,13 @@ if '__came_from' in self.form: # set a new error query['@error_message'] = err.args - redirect_url = urllib_.urlunparse( (redirect_url_tuple.scheme, - redirect_url_tuple.netloc, - redirect_url_tuple.path, - redirect_url_tuple.params, - urllib_.urlencode(list(sorted(query.items())), doseq=True), - redirect_url_tuple.fragment ) - ) + redirect_url = urllib_.urlunparse((redirect_url_tuple.scheme, + redirect_url_tuple.netloc, + redirect_url_tuple.path, + redirect_url_tuple.params, + urllib_.urlencode(list(sorted(query.items())), doseq=True), + redirect_url_tuple.fragment ) + ) raise exceptions.Redirect(redirect_url) # if no __came_from, send back to base url with error return @@ -1351,6 +1383,7 @@ return 1 return 0 + class ExportCSVAction(Action): name = 'export' permissionType = 'View' @@ -1376,7 +1409,8 @@ self.client.response_code = 400 raise exceptions.NotFound( self._('Column "%(column)s" not found in %(class)s') - % {'column': html_escape(cname), 'class': request.classname}) + % {'column': html_escape(cname), + 'class': request.classname}) # full-text search if request.search_text: @@ -1399,7 +1433,8 @@ wfile = self.client.request.wfile if self.client.charset != self.client.STORAGE_CHARSET: wfile = codecs.EncodedFile(wfile, - self.client.STORAGE_CHARSET, self.client.charset, 'replace') + self.client.STORAGE_CHARSET, + self.client.charset, 'replace') writer = csv.writer(wfile) @@ -1409,18 +1444,20 @@ def fct(arg): return "[hidden]" return fct + def repr_link(cls, col): """Generate a function which returns the string representation of a link depending on `cls` and `col`.""" def fct(arg): - if arg == None: + if arg is None: return "" else: return str(cls.get(arg, col)) return fct + def repr_list(cls, col): def fct(arg): - if arg == None: + if arg is None: return "" elif type(arg) is list: seq = [str(cls.get(val, col)) for val in arg] @@ -1429,9 +1466,10 @@ seq.sort() return self.list_sep.join(seq) return fct + def repr_date(): def fct(arg): - if arg == None: + if arg is None: return "" else: if (arg.local(self.db.getUserTimezone()).pretty('%H:%M') == @@ -1441,16 +1479,17 @@ fmt = '%Y-%m-%d %H:%M' return arg.local(self.db.getUserTimezone()).pretty(fmt) return fct + def repr_val(): def fct(arg): - if arg == None: + if arg is None: return "" else: return str(arg) return fct props = klass.getprops() - + # Determine translation map. ncols = [] represent = {} @@ -1496,7 +1535,8 @@ # check permission for this property on this item # TODO: Permission filter doesn't work for the 'user' class if not self.hasPermission(self.permissionType, itemid=itemid, - classname=request.classname, property=name): + classname=request.classname, + property=name): repr_function = repr_no_right(request.classname, name) else: repr_function = represent[name] @@ -1504,6 +1544,7 @@ self.client._socket_op(writer.writerow, row) return '\n' + class ExportCSVWithIdAction(Action): ''' A variation of ExportCSVAction that returns ID number rather than names. This is the original csv export function. @@ -1531,7 +1572,8 @@ self.client.response_code = 400 raise exceptions.NotFound( self._('Column "%(column)s" not found in %(class)s') - % {'column': html_escape(cname), 'class': request.classname}) + % {'column': html_escape(cname), + 'class': request.classname}) # full-text search if request.search_text: @@ -1554,7 +1596,8 @@ wfile = self.client.request.wfile if self.client.charset != self.client.STORAGE_CHARSET: wfile = codecs.EncodedFile(wfile, - self.client.STORAGE_CHARSET, self.client.charset, 'replace') + self.client.STORAGE_CHARSET, + self.client.charset, 'replace') writer = csv.writer(wfile) self.client._socket_op(writer.writerow, columns) @@ -1566,13 +1609,14 @@ # is included that can't be accessed? Enabling this # check will just skip the row for the inaccessible item. # This makes it act more like the web interface. - #if not self.hasPermission(self.permissionType, itemid=itemid, + # if not self.hasPermission(self.permissionType, itemid=itemid, # classname=request.classname): # continue for name in columns: # check permission to view this property on this item if not self.hasPermission(self.permissionType, itemid=itemid, - classname=request.classname, property=name): + classname=request.classname, + property=name): # FIXME: is this correct, or should we just # emit a '[hidden]' string. Note that this may # allow an attacker to figure out hidden schema @@ -1595,13 +1639,14 @@ return '\n' + class Bridge(BaseAction): """Make roundup.actions.Action executable via CGI request. - Using this allows users to write actions executable from multiple frontends. - CGI Form content is translated into a dictionary, which then is passed as - argument to 'handle()'. XMLRPC requests have to pass this dictionary - directly. + Using this allows users to write actions executable from multiple + frontends. CGI Form content is translated into a dictionary, which + then is passed as argument to 'handle()'. XMLRPC requests have to + pass this dictionary directly. """ def __init__(self, *args):
