Index: customfieldadmin/api.py
===================================================================
--- customfieldadmin/api.py (revision 3318)
+++ customfieldadmin/api.py (working copy)
@@ -1,128 +1,150 @@
-# -*- coding: utf-8 -*-
-"""
-API for administrating custom ticket fields in Trac.
-Supports creating, getting, updating and deleting custom fields.
-
-License: BSD
-
-(c) 2005-2007 ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
-"""
-
-from trac.core import *
-from trac.ticket.model import TicketSystem
-import re
-
-__all__ = ['CustomFields']
-
-class CustomFields(Component):
- """ These methods should be part of TicketSystem API/Data Model.
- Adds update_custom_field and delete_custom_field methods.
- (The get_custom_fields is already part of the API - just redirect here,
- and add option to only get one named field back.)
- """
-
- def get_custom_fields(self, env, customfield={}):
- """ Returns the custom fields from TicketSystem component.
- Use a cfdict with 'name' key set to find a specific custom field only
- """
- if not customfield: # return full list
- return TicketSystem(env.compmgr).get_custom_fields()
- else: # only return specific item with cfname
- all = TicketSystem(env.compmgr).get_custom_fields()
- for item in all:
- if item['name'] == customfield['name']:
- return item
- return None # item not found
-
- def update_custom_field(self, env, customfield, create=False):
- """ Update or create a new custom field (if requested).
- customfield is a dictionary with the following possible keys:
- name = name of field (alphanumeric only)
- type = text|checkbox|select|radio|textarea
- label = label description
- value = default value for field content
- options = options for select and radio types (list, leave first empty for optional)
- cols = number of columns for text area
- rows = number of rows for text area
- order = specify sort order for field
- """
- # Name, Type and Label is required
- if not (customfield.has_key('name') and customfield.has_key('type') \
- and customfield.has_key('label')):
- raise TracError("Custom field needs at least a name, type and label.")
- # Use lowercase custom fieldnames only
- customfield['name'] = str(customfield['name']).lower()
- # Only alphanumeric characters (and [-_]) allowed for custom fieldname
- # Note: This is not pretty, but it works... Anyone have an eaier way of checking ???
- matchlen = re.search("[a-z0-9-_]+", customfield['name']).span()
- namelen = len(customfield['name'])
- if (matchlen[1]-matchlen[0] != namelen):
- raise TracError("Only alphanumeric characters allowed for custom field name (a-z or 0-9 or -_).")
- # If Create, check that field does not already exist
- if create and env.config.get('ticket-custom', customfield['name']):
- raise TracError("Can not create as field already exists.")
- # Check that it is a valid field type
- if not customfield['type'] in ['text', 'checkbox', 'select', 'radio', 'textarea']:
- raise TracError("%s is not a valid field type" % customfield['type'])
- # Create/update the field name and type
- env.config.set('ticket-custom', customfield['name'], customfield['type'])
- # Set the field label
- env.config.set('ticket-custom', customfield['name'] + '.label', customfield['label'])
- # Set default value if it exist in dictionay with value, else remove it if it exists in config
- if customfield.has_key('value') and customfield['value']:
- env.config.set('ticket-custom', customfield['name'] + '.value', customfield['value'])
- elif env.config.get('ticket-custom', customfield['name'] + '.value'):
- env.config.remove('ticket-custom', customfield['name'] + '.value')
- # If select or radio set options, or remove if it exists and field no longer need options
- if customfield['type'] in ['select', 'radio']:
- if not customfield.has_key('options') or customfield['options'] == []:
- raise TracError("No options specified for %s field" % customfield['type'])
- env.config.set('ticket-custom', customfield['name'] + '.options', '|'.join(customfield['options']))
- elif env.config.get('ticket-custom', customfield['name'] + '.options'):
- env.config.remove('ticket-custom', customfield['name'] + '.options')
- # Set defaults for textarea if none is specified, remove settings if no longer used
- if customfield['type'] == 'textarea':
- if (not customfield.has_key('cols')) or (not str(customfield['cols']).isdigit()):
- customfield['cols'] = "60"
- if (not customfield.has_key('rows')) or (not str(customfield['rows']).isdigit()):
- customfield['rows'] = "5"
- env.config.set('ticket-custom', customfield['name'] + '.cols', customfield['cols'])
- env.config.set('ticket-custom', customfield['name'] + '.rows', customfield['rows'])
- elif env.config.get('ticket-custom', customfield['name'] + '.cols'):
- env.config.remove('ticket-custom', customfield['name'] + '.cols')
- # Set sort setting if it is in customfield dict, remove if no longer present
- if create:
- last = len(self.get_custom_fields(env))
- env.config.set('ticket-custom', customfield['name'] + '.order',
- customfield.get('order',0) or last)
- elif customfield.has_key('order') and customfield['order']:
- # Exists and have value - note: will not update order conflicting with other fields
- if str(customfield['order']).isdigit():
- env.config.set('ticket-custom', customfield['name'] + '.order', customfield['order'])
- elif env.config.get('ticket-custom', customfield['name'] + '.order'):
- env.config.remove('ticket-custom', customfield['name'] + '.order')
- # Save settings
- env.config.save()
-
- def delete_custom_field(self, env, customfield):
- """ Deletes a custom field.
- Input is a dictionary (see update_custom_field), but only ['name'] is required.
- """
- if not env.config.get('ticket-custom', customfield['name']):
- return # Nothing to do here - cannot find field
- # Need to redo the order of fields that are after the field to be deleted
- order_to_delete = env.config.getint('ticket-custom', customfield['name']+'.order')
- cfs = self.get_custom_fields(env)
- for field in cfs:
- if field['order'] > order_to_delete:
- env.config.set('ticket-custom', field['name']+'.order', field['order'] -1 )
- # Remove any data for the custom field (covering all bases)
- env.config.remove('ticket-custom', customfield['name'])
- env.config.remove('ticket-custom', customfield['name'] + '.label')
- env.config.remove('ticket-custom', customfield['name'] + '.value')
- env.config.remove('ticket-custom', customfield['name'] + '.options')
- env.config.remove('ticket-custom', customfield['name'] + '.cols')
- env.config.remove('ticket-custom', customfield['name'] + '.rows')
- env.config.remove('ticket-custom', customfield['name'] + '.order')
- # Save settings
- env.config.save()
+# -*- coding: utf-8 -*-
+"""
+API for administrating custom ticket fields in Trac.
+Supports creating, getting, updating and deleting custom fields.
+
+License: BSD
+
+(c) 2005-2007 ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
+(c) 2008 ::: UfSoft.org - Pedro Algarvio(ufs@ufsoft.org)
+"""
+
+from trac.core import *
+from trac.ticket.model import TicketSystem, Component
+import re
+
+
+__all__ = ['CustomFields']
+
+class CustomFields(Component):
+ """ These methods should be part of TicketSystem API/Data Model.
+ Adds update_custom_field and delete_custom_field methods.
+ (The get_custom_fields is already part of the API - just redirect here,
+ and add option to only get one named field back.)
+ """
+
+ def get_custom_fields(self, env, customfield={}):
+ """ Returns the custom fields from TicketSystem component.
+ Use a cfdict with 'name' key set to find a specific custom field only
+ """
+ self.get_ticket_components(env)
+ all = TicketSystem(env.compmgr).get_custom_fields()
+ for item in all:
+ if env.is_component_enabled('customfieldadmin.web_ui.ComponentDependableCustomTickets'):
+ item['components'] = env.config.get('ticket-custom',
+ "%s.components"%item['name'],
+ '').split('|')
+ else:
+ item['components'] = []
+ if not customfield: # return full list
+ return all
+ else: # only return specific item with cfname
+ for item in all:
+ if item['name'] == customfield['name']:
+ return item
+ return None # item not found
+
+ def update_custom_field(self, env, customfield, create=False):
+ """ Update or create a new custom field (if requested).
+ customfield is a dictionary with the following possible keys:
+ name = name of field (alphanumeric only)
+ type = text|checkbox|select|radio|textarea
+ label = label description
+ value = default value for field content
+ options = options for select and radio types (list, leave first empty for optional)
+ cols = number of columns for text area
+ rows = number of rows for text area
+ order = specify sort order for field
+ components = components which the custom field depends on
+ """
+ # Name, Type and Label is required
+ if not (customfield.has_key('name') and customfield.has_key('type') \
+ and customfield.has_key('label')):
+ raise TracError("Custom field needs at least a name, type and label.")
+ # Use lowercase custom fieldnames only
+ customfield['name'] = str(customfield['name']).lower()
+ # Only alphanumeric characters (and [-_]) allowed for custom fieldname
+ # Note: This is not pretty, but it works... Anyone have an eaier way of checking ???
+ matchlen = re.search("[a-z0-9-_]+", customfield['name']).span()
+ namelen = len(customfield['name'])
+ if (matchlen[1]-matchlen[0] != namelen):
+ raise TracError("Only alphanumeric characters allowed for custom field name (a-z or 0-9 or -_).")
+ # If Create, check that field does not already exist
+ if create and env.config.get('ticket-custom', customfield['name']):
+ raise TracError("Can not create as field already exists.")
+ # Check that it is a valid field type
+ if not customfield['type'] in ['text', 'checkbox', 'select', 'radio', 'textarea']:
+ raise TracError("%s is not a valid field type" % customfield['type'])
+ # Create/update the field name and type
+ env.config.set('ticket-custom', customfield['name'], customfield['type'])
+ # Set the field label
+ env.config.set('ticket-custom', customfield['name'] + '.label', customfield['label'])
+ # Set default value if it exist in dictionay with value, else remove it if it exists in config
+ if customfield.has_key('value') and customfield['value']:
+ env.config.set('ticket-custom', customfield['name'] + '.value', customfield['value'])
+ elif env.config.get('ticket-custom', customfield['name'] + '.value'):
+ env.config.remove('ticket-custom', customfield['name'] + '.value')
+ # If select or radio set options, or remove if it exists and field no longer need options
+ if customfield['type'] in ['select', 'radio']:
+ if not customfield.has_key('options') or customfield['options'] == []:
+ raise TracError("No options specified for %s field" % customfield['type'])
+ env.config.set('ticket-custom', customfield['name'] + '.options', '|'.join(customfield['options']))
+ elif env.config.get('ticket-custom', customfield['name'] + '.options'):
+ env.config.remove('ticket-custom', customfield['name'] + '.options')
+ # Set defaults for textarea if none is specified, remove settings if no longer used
+ if customfield['type'] == 'textarea':
+ if (not customfield.has_key('cols')) or (not str(customfield['cols']).isdigit()):
+ customfield['cols'] = "60"
+ if (not customfield.has_key('rows')) or (not str(customfield['rows']).isdigit()):
+ customfield['rows'] = "5"
+ env.config.set('ticket-custom', customfield['name'] + '.cols', customfield['cols'])
+ env.config.set('ticket-custom', customfield['name'] + '.rows', customfield['rows'])
+ elif env.config.get('ticket-custom', customfield['name'] + '.cols'):
+ env.config.remove('ticket-custom', customfield['name'] + '.cols')
+ # Set dependable components if set
+ if env.is_component_enabled('customfieldadmin.web_ui.ComponentDependableCustomTickets'):
+ if customfield['components']:
+ env.config.set('ticket-custom', customfield['name'] + '.components', '|'.join(customfield['components']))
+ else:
+ env.config.remove('ticket-custom', customfield['name'] + '.components')
+ # Set sort setting if it is in customfield dict, remove if no longer present
+ if create:
+ last = len(self.get_custom_fields(env))
+ env.config.set('ticket-custom', customfield['name'] + '.order',
+ customfield.get('order',0) or last)
+ elif customfield.has_key('order') and customfield['order']:
+ # Exists and have value - note: will not update order conflicting with other fields
+ if str(customfield['order']).isdigit():
+ env.config.set('ticket-custom', customfield['name'] + '.order', customfield['order'])
+ elif env.config.get('ticket-custom', customfield['name'] + '.order'):
+ env.config.remove('ticket-custom', customfield['name'] + '.order')
+ # Save settings
+ env.config.save()
+
+ def delete_custom_field(self, env, customfield):
+ """ Deletes a custom field.
+ Input is a dictionary (see update_custom_field), but only ['name'] is required.
+ """
+ if not env.config.get('ticket-custom', customfield['name']):
+ return # Nothing to do here - cannot find field
+ # Need to redo the order of fields that are after the field to be deleted
+ order_to_delete = env.config.getint('ticket-custom', customfield['name']+'.order')
+ cfs = self.get_custom_fields(env)
+ for field in cfs:
+ if field['order'] > order_to_delete:
+ env.config.set('ticket-custom', field['name']+'.order', field['order'] -1 )
+ # Remove any data for the custom field (covering all bases)
+ env.config.remove('ticket-custom', customfield['name'])
+ env.config.remove('ticket-custom', customfield['name'] + '.label')
+ env.config.remove('ticket-custom', customfield['name'] + '.value')
+ env.config.remove('ticket-custom', customfield['name'] + '.options')
+ env.config.remove('ticket-custom', customfield['name'] + '.cols')
+ env.config.remove('ticket-custom', customfield['name'] + '.rows')
+ env.config.remove('ticket-custom', customfield['name'] + '.order')
+ if env.is_component_enabled('customfieldadmin.web_ui.ComponentDependableCustomTickets'):
+ env.config.remove('ticket-custom', customfield['name'] + '.components')
+ # Save settings
+ env.config.save()
+
+ def get_ticket_components(self, env):
+ return [component.name for component in Component.select(self.env)]
Property changes on: customfieldadmin/api.py
___________________________________________________________________
Name: svn:eol-style
+ native
Index: customfieldadmin/web_ui.py
===================================================================
--- customfieldadmin/web_ui.py (revision 0)
+++ customfieldadmin/web_ui.py (revision 0)
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+"""
+Injects the needed javascript to make custom fields depend on selected
+component.
+
+License: BSD
+
+(c) 2008 ::: UfSoft.org - Pedro Algarvio(ufs@ufsoft.org)
+"""
+
+from trac.core import *
+from trac.web.api import ITemplateStreamFilter
+
+from genshi.filters.transform import Transformer
+from genshi.builder import tag
+
+from api import CustomFields
+
+
+class ComponentDependableCustomTickets(Component):
+ implements(ITemplateStreamFilter)
+
+ def filter_stream(self, req, method, filename, stream, data):
+ if not (req.path_info.startswith('/newticket') or \
+ req.path_info.startswith('/ticket')):
+ return stream
+
+ cfapi = CustomFields(self.env)
+ fields = {}
+ for f in cfapi.get_custom_fields(self.env):
+ components_field = '%s.components' % f['name']
+ self.env.log.debug("Gathering components from field %s",
+ components_field)
+ components = self.config.get('ticket-custom',
+ components_field, '').split('|')
+ self.env.log.debug("Gathered %r", components)
+ fields["field-%s" % f['name']] = components
+
+ js = """\
+jQuery(document).ready( function() {
+ jQuery("#field-component").change( function() {
+ switch (this.value) {
+"""
+ for f in fields.keys():
+ for c in fields[f]:
+ js +=' case "%s":\n' % c
+ js +=' jQuery("#%s").enable(true);\n' % f
+ for k in fields.keys():
+ if k != f:
+ js += ' jQuery("#%s").enable(false);\n' % k
+ js += ' break;\n'
+
+ js += ' default:\n'
+ for f in fields.keys():
+ js += ' jQuery("#%s").enable(false);\n' % f
+ js += ' };\n'
+ js += ' });\n'
+ js += ' jQuery("#field-component").change();\n'
+ js += ' jQuery("#field-component").submit( function() {\n'
+ for f in fields.keys():
+ js += ' jQuery("#%s").enable(true);\n' % f
+ js += ' });\n'
+ js += '});\n'
+ return stream | Transformer('body').\
+ append(tag.script(js, type="text/javascript"))
+
Property changes on: customfieldadmin/web_ui.py
___________________________________________________________________
Name: svn:keywords
+ Id URL LastChangedDate Rev LastChangedBy
Name: svn:eol-style
+ native
Index: customfieldadmin/__init__.py
===================================================================
--- customfieldadmin/__init__.py (revision 3318)
+++ customfieldadmin/__init__.py (working copy)
@@ -0,0 +1 @@
+import web_ui
Property changes on: customfieldadmin/__init__.py
___________________________________________________________________
Name: svn:eol-style
+ native
Index: customfieldadmin/templates/customfieldadmin.html
===================================================================
--- customfieldadmin/templates/customfieldadmin.html (revision 3318)
+++ customfieldadmin/templates/customfieldadmin.html (working copy)
@@ -1,161 +1,194 @@
-
-
-
- No Custom Fields defined for this project. -
- -+ No Custom Fields defined for this project. +
+ +