Index: blackmagic/blackmagic.py
===================================================================
--- blackmagic/blackmagic.py	(revision 8017)
+++ blackmagic/blackmagic.py	(working copy)
@@ -11,7 +11,7 @@
 from genshi.filters.transform import StreamBuffer
 import re, cPickle
 from trac.perm import IPermissionRequestor, IPermissionPolicy, DefaultPermissionStore, IPermissionStore
-from trac.ticket.model import Ticket
+from trac.ticket.model import Ticket, Type
 
 def istrue(v, otherwise=None):
     if str(v).lower() in ('yes', 'true', '1', 'on'):
@@ -25,7 +25,11 @@
 class BlackMagicTicketTweaks(Component):
     implements(ITemplateStreamFilter, ITemplateProvider, IPermissionRequestor, ITicketManipulator, IPermissionPolicy, IRequestFilter, IPermissionStore)
 
-    permissions = ListOption('blackmagic', 'permissions', [])
+    permissions = ListOption('blackmagic', 'permissions', [],
+        doc="""Comma separated list of extra permissions to add, so they
+        can be assigned to users and used in restrictinos.""")
+    tweaks = ListOption('blackmagic', 'tweaks', [],
+        doc="""Comma separated list of fieldnames to apply tweaks to.""")
     enchants = dict()
         
     #used to store extra permissions to prevent recursion when using non-blackmagic permissions for ticket options
@@ -39,18 +43,58 @@
     blockedTickets = 0
     
     def __init__(self):
-        tweaks = self.config.get('blackmagic','tweaks','');
-        self.env.log.debug("Tweaks %s " %  tweaks)
-        for e in (x.strip() for x in tweaks.split(',')):
-            self.enchants[e]=dict()
-            self.enchants[e]["permission"]=self.config.get('blackmagic', '%s.permission' % e, '').upper()
-            self.enchants[e]["disable"]=self.config.get('blackmagic','%s.disable' % e, False)
-            self.enchants[e]["hide"]=self.config.get('blackmagic','%s.hide' % e, False)
-            self.enchants[e]["label"]=self.config.get('blackmagic','%s.label' % e, None)
-            self.enchants[e]["notice"]=self.config.get('blackmagic','%s.notice' % e, None)
-            self.enchants[e]["tip"]=self.config.get('blackmagic', '%s.tip' % e, None)
-            self.enchants[e]["ondenial"]=self.config.get('blackmagic','%s.ondenial' % e, "disable")
+        self.env.log.debug("Tweaks %s " %  self.tweaks)
+        for e in (x.strip() for x in self.tweaks):
+            d=dict()
+            # For each tweak, register the suboptions. We don't just
+            # self.config.get here, since using Option classes allows other
+            # plugins (like IniAdmin) to find out about these options.
+            d["permission"]=ListOption('blackmagic', '%s.permission' % e, [],
+                doc="""A comma separated list of permissions. If this
+                list is empty, or a user has at least one of these
+                permissions, the %s field is editable. If not, it depends
+                on the value of %s.ondenial what happens.""" % (e, e))
+            d["ondenial"]=Option('blackmagic','%s.ondenial' % e, "disable",
+                doc="""When access to the %s field is denied, (as per
+                the %s.permission option), should the field be disabled
+                ('disable') or hidden ('hide')?""" % (e, e))
+            d["disable"]=BoolOption('blackmagic','%s.disable' % e, False,
+                doc="""Should the %s field be disabled for everyone?
+                Depending on the value of the gray_disabled option, the
+                field label gets a strikethrough or a different
+                colour.""" % e)
+            d["hide"]=BoolOption('blackmagic','%s.hide' % e, False,
+                doc="""Should the %s field be hidden for everyone?""" % e)
+            d["label"]=Option('blackmagic','%s.label' % e, '',
+                doc="""Override the label for the %s field""" % e)
+            d["notice"]=Option('blackmagic','%s.notice' % e, '',
+                doc="""An extra notice to show below the %s field in the
+                ticket editing interface.""" % e)
+            d["tip"]=Option('blackmagic', '%s.tip' % e, '',
+                doc="""A tooltip to show when hovering over the %s field
+                in the ticket editing interface.""" % e)
+            # The Option class is normally meant to be used as a class
+            # attribute (attribute descriptor), but that doesn't work
+            # here. Instead, we call __get__ explicitly to just get at
+            # the value and make it think it is an attribute of self.
+            for a in d:
+                d[a] = d[a].__get__(self, type(self))
+
+            self.enchants[e] = d
         self.env.log.debug("Enchants %s " %  self.enchants)
+
+        self.ticket_types=dict()
+        for t in Type.select(self.env):
+            o = Option('blackmagic', 'ticket_type.%s' % t.name, '',
+                doc="""If set, this permission is required to create,
+                edit and view tickets with type %s.""" % t.name)
+            self.ticket_types[t.name]=o.__get__(self, type(self))
+        # When no ticet types are defined, add an empty ticket type (since
+        # tickets will have an empty type).
+        if not self.ticket_types:
+            self.ticket_types[''] = ''
+
+        self.env.log.debug("Ticket type permissions %s " %  self.ticket_types)
     
     # IPermissionPolicy(Interface)
     def check_permission(self, action, username, resource, perm):
@@ -70,11 +114,9 @@
             except TracError:
                 return None # Ticket doesn't exist
             #get perm for ticket type
-            ticketperm = self.config.get('blackmagic','ticket_type.%s' % ticket["type"], None)
-            if not ticketperm:
-                ticketperm = None
+            ticketperm = self.ticket_types[ticket["type"]]
             self.env.log.debug("Ticket permissions %s type %s " % (ticketperm,ticket["type"]) );
-            if ticketperm is None:
+            if ticketperm == '':
                 #perm isn't set, return
                 self.env.log.debug("Perm isn't set for ticket type %s" % ticket["type"]);
                 return None
@@ -109,11 +151,9 @@
                     newTypes = []
                     for option in type.get("options"):
                         #get perm for ticket type
-                        ticketperm = self.config.get('blackmagic','ticket_type.%s' % option, None)
-                        if not ticketperm:
-                            ticketperm = None
+                        ticketperm = self.ticket_types[option]
                         self.env.log.debug("Ticket permissions %s type %s " % (ticketperm,option) );
-                        if ticketperm is None or ticketperm in req.perm:
+                        if ticketperm == '' or ticketperm in req.perm:
                             #user has perm, add to newTypes
                             newTypes.append(option)
                             self.env.log.debug("User %s has permission %s" % (req.authname, ticketperm) );
@@ -136,15 +176,15 @@
                                                 if self.enchants[c]["hide"]:
                                                     field["value"]=''
                                                 #hide fields user doesn't have permission to and they have ondenial = hide    
-                                                if self.enchants[c]["permission"] != '' and self.enchants[c]["ondenial"]=="hide":
-                                                    for perm in (x.strip() for x in self.enchants[c]["permission"].split(',')):
+                                                if self.enchants[c]["permission"] != [] and self.enchants[c]["ondenial"]=="hide":
+                                                    for perm in (x.strip().upper() for x in self.enchants[c]["permission"]):
                                                         denied = True
                                                         if perm and perm in req.perm(Ticket(self.env,id).resource):
                                                             denied = False
                                                         if denied:
                                                             field["value"]=''
                                                 #re-label fields            
-                                                if self.enchants[c]["label"] is not None:
+                                                if self.enchants[c]["label"] != '':
                                                     field["header"]["title"]=self.enchants[c]["label"]
         #query page                                                            
         if template == "query.html":
@@ -152,11 +192,9 @@
             newTypes = []
             for option in data["fields"]["type"]["options"]:
                 #get perm for ticket type
-                ticketperm = self.config.get('blackmagic','ticket_type.%s' % option, None)
-                if not ticketperm:
-                    ticketperm = None
+                ticketperm = self.ticket_types[option]
                 self.env.log.debug("Ticket permissions %s type %s " % (ticketperm,option) );
-                if ticketperm is None or ticketperm in req.perm:
+                if ticketperm == '' or ticketperm in req.perm:
                     #user has perm, add to newTypes
                     newTypes.append(option)
                     self.env.log.debug("User %s has permission %s" % (req.authname, ticketperm) );
@@ -170,8 +208,8 @@
                         if self.enchants[c]["hide"]:
                            data["tickets"][i][c]=''
                         #hide fields user doesn't have permission to and they have ondenial = hide    
-                        if self.enchants[c]["permission"] != '' and self.enchants[c]["ondenial"]=="hide":
-                            for perm in (x.strip() for x in self.enchants[c]["permission"].split(',')):
+                        if self.enchants[c]["permission"] != [] and self.enchants[c]["ondenial"]=="hide":
+                            for perm in (x.strip().upper() for x in self.enchants[c]["permission"]):
                                 denied = True
                                 if perm and perm in req.perm(Ticket(self.env,ticket["id"]).resource):
                                     denied = False
@@ -182,13 +220,13 @@
                 c = data["headers"][i]["name"]
                 if c in self.enchants:
                     #re-label fields            
-                    if self.enchants[c]["label"] is not None:
+                    if self.enchants[c]["label"] != '':
                         data["headers"][i]["label"]=self.enchants[c]["label"]
             #fields            
             for c,v in data["fields"].items():
                 if c in self.enchants:
                     #re-label fields            
-                    if self.enchants[c]["label"] is not None:
+                    if self.enchants[c]["label"] != '':
                         data["fields"][c]["label"]=self.enchants[c]["label"]
         return template, data, content_type
     
@@ -205,16 +243,15 @@
         res = []
         self.env.log.debug('Validating ticket: %s' % ticket.id)
 
-        enchants = self.config.get('blackmagic', 'tweaks', '')
         for e,v in self.enchants.items():
             editable = True
             self.env.log.debug('%s' % v)
             if ticket.values.get(e, None) is not None:
                 if v["disable"] or v["hide"]:
                     editable = False
-                elif v["permission"]!='':
+                elif v["permission"]!=[]:
                     editable = False
-                    for perm in (x.strip() for x in v["permission"].split(',')):
+                    for perm in (x.strip().upper() for x in v["permission"]):
                         self.env.log.debug("Checking permission %s" % perm)
                         #user has permission no denied
                         if perm and perm in req.perm(ticket.resource):
@@ -237,10 +274,8 @@
                     self.env.log.debug('Denied access to: %s' % e)
         
         #check if user has perm to create ticket type
-        ticketperm = self.config.get("blackmagic","ticket_type.%s" % ticket["type"],None)
-        if not ticketperm:
-            ticketperm = None
-        if ticketperm is not None and ticketperm not in req.perm:
+        ticketperm = self.ticket_types[ticket["type"]]
+        if ticketperm != '' and ticketperm not in req.perm:
             self.env.log.debug("Ticket validation failed type %s permission %s"% (ticket["type"], ticketperm))
             res.append(('type', "Access denied to ticket type %s" % ticket["type"]))
             
@@ -265,12 +300,12 @@
                 disabled = e["disable"]
                 hidden = e["hide"]
                 #permissions are set for field
-                if e["permission"] != "" and not hidden and not (disabled or disabled and e["ondenial"]=="hide"):
+                if e["permission"] != [] and not hidden and not (disabled or disabled and e["ondenial"]=="hide"):
                     self.env.log.debug("Permissions %s" % e["permission"])
                     #default set to denied
                     denied = True
                     #iterate through permissions
-                    for perm in (x.strip() for x in e["permission"].split(',')):
+                    for perm in (x.strip().upper() for x in e["permission"]):
                         self.env.log.debug("Checking permission %s" % perm)
                         #user has permission no denied
                         if perm and perm in req.perm(data.get("ticket").resource):
@@ -278,7 +313,7 @@
                             denied = False
                     #if denied is true hide/disable dpending on denial setting
                     if denied:
-                        denial = self.config.get('blackmagic', '%s.ondenial' % field, None)
+                        denial = e["ondenial"]
                         if denial:
                             if denial == "disable":
                                 disabled = True
@@ -299,7 +334,7 @@
                     stream = stream | Transformer('//*[@id="field-%s"]' % field).replace(" ")
 
                 #change label
-                if e["label"] is not None:
+                if e["label"] != '':
                     stream |= Transformer('//th[@id="h_%s"]/text()' % field).replace(e["label"])
                     stream = stream | Transformer('//label[@for="field-%s"]/text()' % field).replace(e["label"])
 
@@ -319,16 +354,16 @@
                         #cut label and replace with coloured span
                         stream = stream | Transformer('//label[@for="field-%s"]/text()' % field).cut(buffer).end().select('//label[@for="field-%s"]/' % field).append(tag.span(buffer, style="color:%s" % self.gray_disabled))
 
-                if self.config.get('blackmagic', '%s.notice' % field, None):
+                if e["notice"]:
                     stream = stream | Transformer('//*[@id="field-%s"]' % field).after(
                         tag.br() + tag.small()(
                             tag.em()(
-                                Markup(self.config.get('blackmagic', '%s.notice' % field))
+                                Markup(e["notice"])
                             )
                         )
                     )
 
-                tip = self.config.get('blackmagic', '%s.tip' % field, None)
+                tip = e["tip"]
                 if tip:
                     stream = stream | Transformer('//div[@id="banner"]').before(
                         tag.script(type="text/javascript",
