Index: instances/python-dev/detectors/tagsauditor.py
===================================================================
new file mode 100644
--- /dev/null
+++ b/instances/python-dev/detectors/tagsauditor.py
@@ -0,0 +1,59 @@
+import re
+from roundup.exceptions import Reject
+
+MAXTAGS = 15
+MAXLEN = 20
+ALLOWED_CHARS = '[a-z_-]+$'
+
+match_valid = re.compile(ALLOWED_CHARS).match
+
+def validate(tag_name):
+ tag_name = str(tag_name)
+ # XXX Should we convert the name to lowercase or let the error happen?
+ tag_name = tag_name.lower()
+ if not match_valid(tag_name):
+ raise Reject('New tag name should match %s' % ALLOWED_CHARS)
+ elif len(tag_name) > MAXLEN:
+ raise Reject('Tag names are limited to %s characters' % MAXLEN)
+ return tag_name
+
+def set_tags(db, cl, nodeid, newvalues):
+ ''' Add or remove tags to/from an issue, create new tags if necessary.'''
+ if not newvalues.has_key('tags_str'):
+ return
+ tag_klass = db.tag
+ tags = newvalues['tags_str'] or ''
+ tags = [tag.strip() for tag in tags.split(',')]
+ if len(tags) > MAXTAGS:
+ raise Reject("Issues can't have more than %s tags" % MAXTAGS)
+ # Invalid names cannot exist in the db, bail out early
+ tags = [validate(tag) for tag in tags]
+ tag_ids = []
+ for tag in tags:
+ try:
+ tid = tag_klass.lookup(tag)
+ tag_ids.append(tid)
+ except KeyError:
+ tid = tag_klass.create(name=tag)
+ tag_ids.append(tid)
+ newvalues['tags'] = tag_ids
+ # updating tags_str is useful for the helper view
+ newvalues['tags_str'] = ', '.join(tags)
+
+
+def check_new_tag(db, cl, nodeid, newvalues):
+ if list(newvalues) != ['name']:
+ # Either missing a name or setting other properties, not something
+ # from us.
+ raise Reject('Invalid new tag')
+ tag_name = newvalues['name']
+ # Shouldn't happen from normal web UI use, but it's possible to create
+ # tags from crafted requests, bypassing set_tags
+ tag_name = validate(tag_name)
+ newvalues['name'] = tag_name
+
+
+def init(db):
+ db.issue.audit('create', set_tags)
+ db.issue.audit('set', set_tags)
+ db.tag.audit('create', check_new_tag)
Index: instances/python-dev/html/issue.index.html
===================================================================
old mode 100644
new mode 100755
--- a/instances/python-dev/html/issue.index.html
+++ b/instances/python-dev/html/issue.index.html
@@ -20,6 +20,30 @@
and request.user.hasRole('Anonymous')" i18n:translate="">
Please login with your username and password.
+
+
@@ -36,6 +60,7 @@
Resolution
Creator
Assigned To
+
Tags
Keywords
Depends On
Type
@@ -80,6 +105,14 @@
tal:content="python:i.creator.plain() or default">