Commits

Anonymous committed 54b0f4e Draft

adding an ITicketFieldProvider interface, adding the extension point to the TicketSystem and modifying the specification of Trac's default select and radio fields to use it

Comments (0)

Files changed (1)

trac/ticket/api.py

     def milestone_deleted(milestone):
         """Called when a milestone is deleted."""
 
+class ITicketFieldProvider(Interface):
+    """Extension point interface for components that provide fields for the
+    ticket system."""
+
+    def get_select_fields():
+        """Returns a list of select fields, each as a tuple of
+        (rank, field)
+        where field is a dictionary that defines:
+            * name: the field name 
+            * label: the label to display, preferably wrapped with N_()
+            * cls: the model describing the field
+        the following keys can also usefully be defined:
+            * optional: a boolean specifying that the select can be empty
+        
+        The rank is expected to be an integer to specify the sorting of the
+        select and radio fields. This is not intended to allow for the extent
+        of configurability of the custom fields but allows a plugin to mix in
+        fields as if they are a first class member of the ticket system.
+        """
+
+    def get_radio_fields():
+        """Returns a list of radio fields, each as a tuple of
+        (rank, field)
+        See the documentation for get_select_fields for descriptions of rank and
+        field.
+        Note that in contrast to get_select_fields, radio fields will all be
+        specified as optional.
+        """
+
+class TicketFields(Component):
+    implements(ITicketFieldProvider)
+
+    def get_select_fields(self):
+        """Default select and radio fields"""
+        from trac.ticket import model
+        selects = [(10, {'name': 'type', 'label': N_('Type'), 
+                         'cls': model.Type}),
+                   (30, {'name':'priority', 'label': N_('Priority'), 
+                         'cls': model.Priority}),
+                   (40, {'name': 'milestone', 'label': N_('Milestone'), 
+                         'cls': model.Milestone, 'optional': True}),
+                   (50, {'name': 'component', 'label': N_('Component'), 
+                         'cls': model.Component}),
+                   (60, {'name': 'version', 'label': N_('Version'), 
+                         'cls': model.Version, 'optional': True}),
+                   (70, {'name': 'severity', 'label': N_('Severity'), 
+                         'cls': model.Severity})]
+        return selects
+
+    def get_radio_fields(self):
+        """Default radio fields"""
+        from trac.ticket import model
+        radios = [(20, {'name': 'status', 'label': N_('Status'),
+                        'cls': model.Status}),
+                  (80, {'name': 'resolution', 'label': N_('Resolution'), 
+                        'cls': model.Resolution})]
+        return radios
 
 class TicketSystem(Component):
     implements(IPermissionRequestor, IWikiSyntaxProvider, IResourceManager)
 
+    ticket_field_providers = ExtensionPoint(ITicketFieldProvider)
     change_listeners = ExtensionPoint(ITicketChangeListener)
     milestone_change_listeners = ExtensionPoint(IMilestoneChangeListener)
     
                        'label': N_('Description')})
 
         # Default select and radio fields
-        selects = [('type', N_('Type'), model.Type),
-                   ('status', N_('Status'), model.Status),
-                   ('priority', N_('Priority'), model.Priority),
-                   ('milestone', N_('Milestone'), model.Milestone),
-                   ('component', N_('Component'), model.Component),
-                   ('version', N_('Version'), model.Version),
-                   ('severity', N_('Severity'), model.Severity),
-                   ('resolution', N_('Resolution'), model.Resolution)]
-        for name, label, cls in selects:
+        selects = []
+        [selects.extend(field_provider.get_select_fields()) 
+                    for field_provider in self.ticket_field_providers]
+        [select.update({'type': 'select'}) for n, select in selects]
+        radios = []
+        [radios.extend(field_provider.get_radio_fields()) 
+                    for field_provider in self.ticket_field_providers]
+        [radio.update({'type': 'radio',
+                       'optional': True}) for n, radio in radios]
+
+        selects.extend(radios)
+        selects.sort()
+        for rank, field in selects:
+            cls = field['cls']
+            name = field['name']
             options = [val.name for val in cls.select(self.env, db=db)]
             if not options:
                 # Fields without possible values are treated as if they didn't
                 # exist
                 continue
-            field = {'name': name, 'type': 'select', 'label': label,
-                     'value': getattr(self, 'default_' + name, ''),
-                     'options': options}
-            if name in ('status', 'resolution'):
-                field['type'] = 'radio'
-                field['optional'] = True
-            elif name in ('milestone', 'version'):
-                field['optional'] = True
+            field.update({'value': getattr(self, 'default_' + name, ''),
+                          'options': options})
             fields.append(field)
 
         # Advanced text fields