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 @@
-<!DOCTYPE html
-    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      xmlns:py="http://genshi.edgewall.org/">
-  <xi:include href="admin.html" />
-  <head>
-    <title>Custom Fields Admin</title>
-  </head>
-
-  <body>
-
-    <h2>Manage Custom Fields</h2>
-
-    <!--! Detail view -->
-    <form py:if="cfadmin.display=='detail'" class="mod" id="addcf" method="POST">
-      <fieldset>
-        <legend>Modify Custom Field:</legend>
-        <div class="field">
-          <label>Name (cannot modify): <strong>${cfadmin.customfield.name}</strong></label>
-        </div>
-        <input name="name" value="${cfadmin.customfield.name}" type="hidden" />
-        <input name="order" value="${cfadmin.customfield.order}" type="hidden" />
-        <div class="field">
-          <label>Type:<br />
-            <select name="type" id="type">
-              <option value="text" selected="${cfadmin.customfield.type=='text' and 'selected' or None}">
-                Text</option>
-              <option value="select" selected="${cfadmin.customfield.type=='select' and 'selected' or None}">
-                Select</option>
-              <option value="checkbox" selected="${cfadmin.customfield.type=='checkbox' and 'selected' or None}">
-                Checkbox</option>
-              <option value="radio" selected="${cfadmin.customfield.type=='radio' and 'selected' or None}">
-                Radio</option>
-              <option value="textarea" selected="${cfadmin.customfield.type=='textarea' and 'selected' or None}">
-                Textarea</option>
-            </select>
-          </label>
-        </div>
-        <div class="field">
-          <label>Label:<br />
-            <input type="text" name="label" value="${cfadmin.customfield.label}" />
-          </label>
-        </div>
-        <div class="field">
-          <label>Default value (regular text for Text, Textarea, or Select):<br />
-            <input type="text" name="value" value="${cfadmin.customfield.value}" />
-          </label>
-        </div>
-        <div class="field">
-          <fieldset class="iefix">
-            <label for="options">
-              Options for Radio or Select (for Select, empty first line makes field optional):
-            </label>
-            <p>
-              <textarea id="options" name="options" rows="5" cols="30">${cfadmin.customfield.options}</textarea>
-            </p>
-          </fieldset>
-        </div>
-        <div class="field">
-          <label for="cols">Size of Textarea for entry (Textarea only):<br />
-            Columns: <input type="text" name="cols" id="cols" size="5" value="${cfadmin.customfield.width}" />
-            Rows: <input type="text" size="5" name="rows" id="rows" value="${cfadmin.customfield.height}" /></label>
-        </div>
-        <div class="buttons">
-          <input type="submit" name="cancel" value="Cancel" />
-          <input type="submit" name="save" value="Save" />
-        </div>
-      </fieldset>
-    </form>
-
-    <form py:if="cfadmin.display=='list'" class="addnew" id="addcf" method="POST">
-      <fieldset>
-        <legend>Add Custom Field:</legend>
-        <div class="field">
-          <label>Name:<br />
-            <input type="text" name="name" id="name" />
-          </label>
-        </div>
-        <div class="field">
-          <label>Type:<br />
-            <select name="type" id="type">
-              <option py:for="value in ['text', 'select', 'checkbox', 'radio', 'textarea']"
-                  value="${value}">${value.capitalize()}</option>
-            </select>
-          </label>
-        </div>
-        <div class="field">
-          <label>Label:<br />
-            <input type="text" name="label" id="label" />
-          </label>
-        </div>
-        <div class="field">
-          <label for="value">Default value:<br />
-            <input type="text" name="value" id="value" />
-          </label>
-        </div>
-        <div class="field">
-          <fieldset class="iefix">
-            <label for="options">Options:</label>
-            <p>
-              <textarea id="options" name="options" rows="4" cols="17"></textarea>
-            </p>
-          </fieldset>
-        </div>
-        <div class="field">
-          <label for="cols">Size of Textarea:<br />
-            Cols: <input type="text" name="cols" id="cols" size="2" />
-            Rows: <input type="text" size="2" name="rows" id="rows" />
-          </label>
-        </div>
-        <div class="buttons">
-          <input type="submit" name="add" value="Add" />
-        </div>
-      </fieldset>
-    </form>
-
-    <div py:if="cfadmin.display=='list'" py:strip="">
-      <p py:if="not len(cfadmin.customfields)" class="help">
-        No Custom Fields defined for this project.
-      </p>
-      <form py:if="len(cfadmin.customfields)" method="POST">
-        <table class="listing" id="cflist">
-          <thead>
-            <tr>
-              <th class="sel">&nbsp;</th>
-              <th>Name</th>
-              <th>Type</th>
-              <th>Label</th>
-              <th>Order</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr py:for="cf in cfadmin.customfields">
-              <td><input type="checkbox" name="sel" value="${cf.name}" /></td>
-              <td><a href="${cf.href}">${cf.name}</a></td>
-              <td>${cf.type}</td>
-              <td>${cf.label}</td>
-              <td class="default">
-                <select name="order_${cf.name}">
-                  <option py:for="other in cfadmin.customfields"
-                      selected="${other.order==cf.order and 'selected' or None}">
-                      ${other.order}
-                  </option>
-                </select>
-              </td>
-            </tr>
-          </tbody>
-        </table>
-        <div class="buttons">
-          <input type="submit" name="remove" value="Remove selected items" />
-          <input type="submit" name="apply" value="Apply changes" />
-        </div>
-      </form>
-    </div>
-
-  </body>
-
-</html>
-
+<!DOCTYPE html
+    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:xi="http://www.w3.org/2001/XInclude"
+      xmlns:py="http://genshi.edgewall.org/">
+  <xi:include href="admin.html" />
+  <head>
+    <title>Custom Fields Admin</title>
+  </head>
+
+  <body>
+
+    <h2>Manage Custom Fields</h2>
+
+    <!--! Detail view -->
+    <form py:if="cfadmin.display=='detail'" class="mod" id="addcf" method="POST">
+      <fieldset>
+        <legend>Modify Custom Field:</legend>
+        <div class="field">
+          <label>Name (cannot modify): <strong>${cfadmin.customfield.name}</strong></label>
+        </div>
+        <input name="name" value="${cfadmin.customfield.name}" type="hidden" />
+        <input name="order" value="${cfadmin.customfield.order}" type="hidden" />
+        <div class="field">
+          <label>Type:<br />
+            <select name="type" id="type">
+              <option value="text" selected="${cfadmin.customfield.type=='text' and 'selected' or None}">
+                Text</option>
+              <option value="select" selected="${cfadmin.customfield.type=='select' and 'selected' or None}">
+                Select</option>
+              <option value="checkbox" selected="${cfadmin.customfield.type=='checkbox' and 'selected' or None}">
+                Checkbox</option>
+              <option value="radio" selected="${cfadmin.customfield.type=='radio' and 'selected' or None}">
+                Radio</option>
+              <option value="textarea" selected="${cfadmin.customfield.type=='textarea' and 'selected' or None}">
+                Textarea</option>
+            </select>
+          </label>
+        </div>
+        <div class="field">
+          <label>Label:<br />
+            <input type="text" name="label" value="${cfadmin.customfield.label}" />
+          </label>
+        </div>
+        <div class="field">
+          <label>Default value (regular text for Text, Textarea, or Select):<br />
+            <input type="text" name="value" value="${cfadmin.customfield.value}" />
+          </label>
+        </div>
+        <div class="field">
+          <fieldset class="iefix">
+            <label for="options">
+              Options for Radio or Select (for Select, empty first line makes field optional):
+            </label>
+            <p>
+              <textarea id="options" name="options" rows="5" cols="30">${cfadmin.customfield.options}</textarea>
+            </p>
+          </fieldset>
+        </div>
+        <div class="field">
+          <label for="cols">Size of Textarea for entry (Textarea only):<br />
+            Columns: <input type="text" name="cols" id="cols" size="5" value="${cfadmin.customfield.width}" />
+            Rows: <input type="text" size="5" name="rows" id="rows" value="${cfadmin.customfield.height}" /></label>
+        </div>
+        <div class="field" py:if="cfadmin.dependable_components_enabled">
+          <fieldset>
+            <legend>Dependable By Components</legend>
+             <p class="help">Make the custom field dependable on components.<br/>
+               The field will only be active when one of the selected components<br/>
+               from the list bellow, is chosen when creating or editing a ticket.<br/>
+               Use the <b><em>CTRL</em></b> key to choose multiple components.</p>
+              <label for="selected_components">Available Components</label><br/>
+              <py:for each="component in cfadmin.components">
+                <input type="checkbox" id="selected_components" name="selected_components"
+                  checked="${component in cfadmin.customfield.components or None}"
+                  value="${component}">${component}</input><br/>
+              </py:for>
+          </fieldset>
+        </div>
+        <div class="buttons">
+          <input type="submit" name="cancel" value="Cancel" />
+          <input type="submit" name="save" value="Save" />
+        </div>
+      </fieldset>
+    </form>
+
+    <form py:if="cfadmin.display=='list'" class="addnew" id="addcf" method="POST">
+      <fieldset>
+        <legend>Add Custom Field:</legend>
+        <div class="field">
+          <label>Name:<br />
+            <input type="text" name="name" id="name" />
+          </label>
+        </div>
+        <div class="field">
+          <label>Type:<br />
+            <select name="type" id="type">
+              <option py:for="value in ['text', 'select', 'checkbox', 'radio', 'textarea']"
+                  value="${value}">${value.capitalize()}</option>
+            </select>
+          </label>
+        </div>
+        <div class="field">
+          <label>Label:<br />
+            <input type="text" name="label" id="label" />
+          </label>
+        </div>
+        <div class="field">
+          <label for="value">Default value:<br />
+            <input type="text" name="value" id="value" />
+          </label>
+        </div>
+        <div class="field">
+          <fieldset class="iefix">
+            <label for="options">Options:</label>
+            <p>
+              <textarea id="options" name="options" rows="4" cols="17"></textarea>
+            </p>
+          </fieldset>
+        </div>
+        <div class="field">
+          <label for="cols">Size of Textarea:<br />
+            Cols: <input type="text" name="cols" id="cols" size="2" />
+            Rows: <input type="text" size="2" name="rows" id="rows" />
+          </label>
+        </div>
+        <div class="field" py:if="cfadmin.dependable_components_enabled">
+          <fieldset>
+            <legend>Dependable By Components</legend>
+             <p class="help">Make the custom field dependable on components.<br/>
+               The field will only be active when one of the selected components<br/>
+               from the list bellow, is chosen when creating or editing a ticket.<br/>
+               Use the <b><em>CTRL</em></b> key to choose multiple components.</p>
+              <label for="selected_components">Available Components</label><br/>
+              <py:for each="component in cfadmin.components">
+                <input type="checkbox" id="selected_components" name="selected_components"
+                  value="${component}">${component}</input><br/>
+              </py:for>
+          </fieldset>
+        </div>
+        <div class="buttons">
+          <input type="submit" name="add" value="Add" />
+        </div>
+      </fieldset>
+    </form>
+
+    <div py:if="cfadmin.display=='list'" py:strip="">
+      <p py:if="not len(cfadmin.customfields)" class="help">
+        No Custom Fields defined for this project.
+      </p>
+      <form py:if="len(cfadmin.customfields)" method="POST">
+        <table class="listing" id="cflist">
+          <thead>
+            <tr>
+              <th class="sel">&nbsp;</th>
+              <th>Name</th>
+              <th>Type</th>
+              <th>Label</th>
+              <th py:if="cfadmin.dependable_components_enabled">Components</th>
+              <th>Order</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr py:for="cf in cfadmin.customfields">
+              <td><input type="checkbox" name="sel" value="${cf.name}" /></td>
+              <td><a href="${cf.href}">${cf.name}</a></td>
+              <td>${cf.type}</td>
+              <td>${cf.label}</td>
+              <td py:if="cfadmin.dependable_components_enabled"><py:for
+                each="component in cf.components">${component}<br/></py:for>
+              </td>
+              <td class="default">
+                <select name="order_${cf.name}">
+                  <option py:for="other in cfadmin.customfields"
+                      selected="${other.order==cf.order and 'selected' or None}">
+                      ${other.order}
+                  </option>
+                </select>
+              </td>
+            </tr>
+          </tbody>
+        </table>
+        <div class="buttons">
+          <input type="submit" name="remove" value="Remove selected items" />
+          <input type="submit" name="apply" value="Apply changes" />
+        </div>
+      </form>
+    </div>
+
+  </body>
+
+</html>
+

Property changes on: customfieldadmin/templates/customfieldadmin.html
___________________________________________________________________
Name: svn:eol-style
   + native

Index: customfieldadmin/customfieldadmin.py
===================================================================
--- customfieldadmin/customfieldadmin.py	(revision 3318)
+++ customfieldadmin/customfieldadmin.py	(working copy)
@@ -1,118 +1,126 @@
-# -*- coding: utf-8 -*-
-"""
-Trac WebAdmin plugin for administration of custom fields.
-
-License: BSD
-
-(c) 2005-2007 ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
-(c) 2007      ::: www.Optaros.com (.....)
-"""
-
-from trac.core import *
-from trac.web.chrome import ITemplateProvider, add_stylesheet, add_script
-from trac.admin.api import IAdminPanelProvider
-from api import CustomFields
-from trac.util.text import to_unicode
-
-
-class CustomFieldAdminPage(Component):
-    
-    implements(ITemplateProvider, IAdminPanelProvider)
-
-    # IAdminPanelProvider methods
-    
-    def get_admin_panels(self, req):
-        if req.perm.has_permission('TRAC_ADMIN'):
-            yield ('ticket', 'Ticket System', 'customfields', 'Custom Fields') 
-
-    def render_admin_panel(self, req, cat, page, customfield):
-        #assert req.perm.has_permission('TRAC_ADMIN')
-        req.perm.assert_permission('TRAC_ADMIN')
-        
-        add_script(req, 'customfieldadmin/js/CustomFieldAdminPage_actions.js')
-
-        def _customfield_from_req(self, req):
-            cfdict = {'name': to_unicode(req.args.get('name')),
-                      'label': to_unicode(req.args.get('label')),
-                      'type': to_unicode(req.args.get('type')),
-                      'value': to_unicode(req.args.get('value')),
-                      'options': [x.strip() for x in to_unicode(req.args.get('options')).split("\n")],
-                      'cols': to_unicode(req.args.get('cols')),
-                      'rows': to_unicode(req.args.get('rows')),
-                      'order': req.args.get('order', 0)}
-            return cfdict
-        
-        cfapi = CustomFields(self.env)
-        cfadmin = {} # Return values for template rendering
-        
-        # Detail view?
-        if customfield:
-            exists = [True for cf in cfapi.get_custom_fields(self.env) if cf['name'] == customfield]
-            if not exists:
-                raise TracError("Custom field %s does not exist." % customfield)
-            if req.method == 'POST':
-                if req.args.get('save'):
-                    cfdict = _customfield_from_req(self, req) 
-                    cfapi.update_custom_field(self.env, cfdict)
-                    req.redirect(req.href.admin(cat, page))
-                elif req.args.get('cancel'):
-                    req.redirect(req.href.admin(cat, page))
-            currentcf = cfapi.get_custom_fields(self.env, {'name': customfield})
-            if currentcf.has_key('options'):
-                optional_line = ''
-                if currentcf.get('optional', False):
-                    optional_line = "\n\n"
-                currentcf['options'] = optional_line + "\n".join(currentcf['options'])
-            cfadmin['customfield'] = currentcf
-            cfadmin['display'] = 'detail'
-        else:
-            if req.method == 'POST':
-                # Add Custom Field
-                if req.args.get('add') and req.args.get('name'):
-                    cfdict = _customfield_from_req(self, req)
-                    cfapi.update_custom_field(self.env, cfdict, create=True)
-                    req.redirect(req.href.admin(cat, page))
-                         
-                # Remove Custom Field
-                elif req.args.get('remove') and req.args.get('sel'):
-                    sel = req.args.get('sel')
-                    sel = isinstance(sel, list) and sel or [sel]
-                    if not sel:
-                        raise TracError, 'No custom field selected'
-                    for name in sel:
-                        cfdict =  {'name': name}
-                        cfapi.delete_custom_field(self.env, cfdict)
-                    req.redirect(req.href.admin(cat, page))
-
-                elif req.args.get('apply'):
-                    # Change order
-                    order = dict([(key[6:], req.args.get(key)) for key
-                                  in req.args.keys()
-                                  if key.startswith('order_')])
-                    values = dict([(val, True) for val in order.values()])
-                    if len(order) != len(values):
-                        raise TracError, 'Order numbers must be unique.'
-                    cf = cfapi.get_custom_fields(self.env)
-                    for cur_cf in cf:
-                        cur_cf['order'] = order[cur_cf['name']]
-                        cfapi.update_custom_field(self.env, cur_cf)
-                    req.redirect(req.href.admin(cat, page))
-
-            cf_list = []
-            for item in cfapi.get_custom_fields(self.env):
-                item['href'] = req.href.admin(cat, page, item['name'])
-                cf_list.append(item)
-            cfadmin['customfields'] = cf_list
-            cfadmin['display'] = 'list'
-
-        return ('customfieldadmin.html', {'cfadmin': cfadmin})
-        
-
-    # ITemplateProvider methods
-    def get_templates_dirs(self):
-        from pkg_resources import resource_filename
-        return [resource_filename(__name__, 'templates')]
-
-    def get_htdocs_dirs(self):
-        from pkg_resources import resource_filename
-        return [('customfieldadmin', resource_filename(__name__, 'htdocs'))]
+# -*- coding: utf-8 -*-
+"""
+Trac WebAdmin plugin for administration of custom fields.
+
+License: BSD
+
+(c) 2005-2007 ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
+(c) 2007      ::: www.Optaros.com (.....)
+(c) 2008      ::: UfSoft.org - Pedro Algarvio(ufs@ufsoft.org)
+"""
+
+from trac.core import *
+from trac.web.chrome import ITemplateProvider, add_stylesheet, add_script
+from trac.admin.api import IAdminPanelProvider
+from api import CustomFields
+from trac.util.text import to_unicode
+
+
+class CustomFieldAdminPage(Component):
+    
+    implements(ITemplateProvider, IAdminPanelProvider)
+
+    # IAdminPanelProvider methods
+    
+    def get_admin_panels(self, req):
+        if req.perm.has_permission('TRAC_ADMIN'):
+            yield ('ticket', 'Ticket System', 'customfields', 'Custom Fields') 
+
+    def render_admin_panel(self, req, cat, page, customfield):
+        #assert req.perm.has_permission('TRAC_ADMIN')
+        req.perm.assert_permission('TRAC_ADMIN')
+        
+        add_script(req, 'customfieldadmin/js/CustomFieldAdminPage_actions.js')
+
+        def _customfield_from_req(self, req):
+            cfdict = {'name': to_unicode(req.args.get('name')),
+                      'label': to_unicode(req.args.get('label')),
+                      'type': to_unicode(req.args.get('type')),
+                      'value': to_unicode(req.args.get('value')),
+                      'options': [x.strip() for x in to_unicode(req.args.get('options')).split("\n")],
+                      'cols': to_unicode(req.args.get('cols')),
+                      'rows': to_unicode(req.args.get('rows')),
+                      'order': req.args.get('order', 0)}
+            if self.env.is_component_enabled('customfieldadmin.web_ui.ComponentDependableCustomTickets'):
+                cfdict['components'] = req.args.getlist('selected_components')
+            return cfdict
+        
+        cfapi = CustomFields(self.env)
+        cfadmin = {} # Return values for template rendering
+        if self.env.is_component_enabled('customfieldadmin.web_ui.ComponentDependableCustomTickets'):
+            cfadmin['dependable_components_enabled'] = True
+            cfadmin['components'] = cfapi.get_ticket_components(self.env)
+        else:
+            cfadmin['dependable_components_enabled'] = False
+        
+        # Detail view?
+        if customfield:
+            exists = [True for cf in cfapi.get_custom_fields(self.env) if cf['name'] == customfield]
+            if not exists:
+                raise TracError("Custom field %s does not exist." % customfield)
+            if req.method == 'POST':
+                if req.args.get('save'):
+                    cfdict = _customfield_from_req(self, req) 
+                    cfapi.update_custom_field(self.env, cfdict)
+                    req.redirect(req.href.admin(cat, page))
+                elif req.args.get('cancel'):
+                    req.redirect(req.href.admin(cat, page))
+            currentcf = cfapi.get_custom_fields(self.env, {'name': customfield})
+            if currentcf.has_key('options'):
+                optional_line = ''
+                if currentcf.get('optional', False):
+                    optional_line = "\n\n"
+                currentcf['options'] = optional_line + "\n".join(currentcf['options'])
+            cfadmin['customfield'] = currentcf
+            cfadmin['display'] = 'detail'
+        else:
+            if req.method == 'POST':
+                # Add Custom Field
+                if req.args.get('add') and req.args.get('name'):
+                    cfdict = _customfield_from_req(self, req)
+                    cfapi.update_custom_field(self.env, cfdict, create=True)
+                    req.redirect(req.href.admin(cat, page))
+                         
+                # Remove Custom Field
+                elif req.args.get('remove') and req.args.get('sel'):
+                    sel = req.args.get('sel')
+                    sel = isinstance(sel, list) and sel or [sel]
+                    if not sel:
+                        raise TracError, 'No custom field selected'
+                    for name in sel:
+                        cfdict =  {'name': name}
+                        cfapi.delete_custom_field(self.env, cfdict)
+                    req.redirect(req.href.admin(cat, page))
+
+                elif req.args.get('apply'):
+                    # Change order
+                    order = dict([(key[6:], req.args.get(key)) for key
+                                  in req.args.keys()
+                                  if key.startswith('order_')])
+                    values = dict([(val, True) for val in order.values()])
+                    if len(order) != len(values):
+                        raise TracError, 'Order numbers must be unique.'
+                    cf = cfapi.get_custom_fields(self.env)
+                    for cur_cf in cf:
+                        cur_cf['order'] = order[cur_cf['name']]
+                        cfapi.update_custom_field(self.env, cur_cf)
+                    req.redirect(req.href.admin(cat, page))
+
+            cf_list = []
+            for item in cfapi.get_custom_fields(self.env):
+                item['href'] = req.href.admin(cat, page, item['name'])
+                cf_list.append(item)
+            cfadmin['customfields'] = cf_list
+            cfadmin['display'] = 'list'
+
+        return ('customfieldadmin.html', {'cfadmin': cfadmin})
+        
+
+    # ITemplateProvider methods
+    def get_templates_dirs(self):
+        from pkg_resources import resource_filename
+        return [resource_filename(__name__, 'templates')]
+
+    def get_htdocs_dirs(self):
+        from pkg_resources import resource_filename
+        return [('customfieldadmin', resource_filename(__name__, 'htdocs'))]

Property changes on: customfieldadmin/customfieldadmin.py
___________________________________________________________________
Name: svn:eol-style
   + native

