Changeset 4964


Ignore:
Timestamp:
Dec 6, 2008, 1:06:59 AM (15 years ago)
Author:
osimons
Message:

CustomFieldAdminPlugin: Finally got around to cleaning up the ugly API code...

  • Simplified and easier to read code, like a modify is now just a delete + create.
  • Also disabled order checking, and generally more lenient behavior - like issues mentioned in #2363.
  • Simpler pattern matching thanks to Felix Schwarz.
Location:
customfieldadminplugin/0.11/customfieldadmin
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • customfieldadminplugin/0.11/customfieldadmin/api.py

    r2250 r4964  
    66License: BSD
    77
    8 (c) 2005-2007 ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
     8(c) 2005-2008 ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
    99"""
     10
     11import re
    1012
    1113from trac.core import *
    1214from trac.ticket.model import TicketSystem
    13 import re
     15from trac.util.text import to_unicode
    1416
    1517__all__ = ['CustomFields']
     
    2022    (The get_custom_fields is already part of the API - just redirect here,
    2123     and add option to only get one named field back.)
     24   
     25    Input to methods is a 'customfield' dict supporting these keys:
     26        name = name of field (alphanumeric only)
     27        type = text|checkbox|select|radio|textarea
     28        label = label description
     29        value = default value for field content
     30        options = options for select and radio types (list, leave first empty for optional)
     31        cols = number of columns for text area
     32        rows = number of rows for text area
     33        order = specify sort order for field
    2234    """
    2335   
     
    3446                    return item
    3547            return None        # item not found
    36        
    37     def update_custom_field(self, env, customfield, create=False):
    38         """ Update or create a new custom field (if requested).
    39         customfield is a dictionary with the following possible keys:
    40             name = name of field (alphanumeric only)
    41             type = text|checkbox|select|radio|textarea
    42             label = label description
    43             value = default value for field content
    44             options = options for select and radio types (list, leave first empty for optional)
    45             cols = number of columns for text area
    46             rows = number of rows for text area
    47             order = specify sort order for field
    48         """
     48   
     49    def verify_custom_field(self, env, customfield, create=True):
     50        """ Basic validation of the input for modifying or creating
     51        custom fields. """
    4952        # Name, Type and Label is required
    50         if not (customfield.has_key('name') and customfield.has_key('type') \
    51                 and customfield.has_key('label')):
     53        if not (customfield.get('name') and customfield.get('type') \
     54                and customfield.get('label')):
    5255            raise TracError("Custom field needs at least a name, type and label.")
    5356        # Use lowercase custom fieldnames only
    54         customfield['name'] = str(customfield['name']).lower()
     57        customfield['name'] = to_unicode(customfield['name']).lower()
    5558        # Only alphanumeric characters (and [-_]) allowed for custom fieldname
    56         # Note: This is not pretty, but it works... Anyone have an eaier way of checking ???
    57         matchlen = re.search("[a-z0-9-_]+", customfield['name']).span()
    58         namelen = len(customfield['name'])
    59         if (matchlen[1]-matchlen[0] != namelen):
    60             raise TracError("Only alphanumeric characters allowed for custom field name (a-z or 0-9 or -_).")
    61         # If Create, check that field does not already exist
    62         if create and env.config.get('ticket-custom', customfield['name']):
    63             raise TracError("Can not create as field already exists.")
     59        if re.search('^[a-z0-9-_]+$', customfield['name']) == None:
     60           raise TracError("Only alphanumeric characters allowed for custom field name (a-z or 0-9 or -_).")
    6461        # Check that it is a valid field type
    6562        if not customfield['type'] in ['text', 'checkbox', 'select', 'radio', 'textarea']:
    6663            raise TracError("%s is not a valid field type" % customfield['type'])
    67         # Create/update the field name and type
     64        # Check that field does not already exist (if modify it should already be deleted)
     65        if create and env.config.get('ticket-custom', customfield['name']):
     66            raise TracError("Can not create as field already exists.")
     67   
     68    def create_custom_field(self, env, customfield):
     69        """ Create the new custom fields (that may just have been deleted as part
     70        of 'modify'). Note: Caller is responsible for verifying input before create."""
     71        # Set the mandatory items
    6872        env.config.set('ticket-custom', customfield['name'], customfield['type'])
    69         # Set the field label
    7073        env.config.set('ticket-custom', customfield['name'] + '.label', customfield['label'])
    71         # Set default value if it exist in dictionay with value, else remove it if it exists in config
    72         if customfield.has_key('value') and customfield['value']:
     74        # Optional items
     75        if 'value' in customfield:
    7376            env.config.set('ticket-custom', customfield['name'] + '.value', customfield['value'])
    74         elif env.config.get('ticket-custom', customfield['name'] + '.value'):
    75             env.config.remove('ticket-custom', customfield['name'] + '.value')
    76         # If select or radio set options, or remove if it exists and field no longer need options
    77         if customfield['type'] in ['select', 'radio']:
    78             if not customfield.has_key('options') or customfield['options'] == []:
    79                 raise TracError("No options specified for %s field" % customfield['type'])
     77        if 'options' in customfield:
    8078            env.config.set('ticket-custom', customfield['name'] + '.options', '|'.join(customfield['options']))
    81         elif env.config.get('ticket-custom', customfield['name'] + '.options'):
    82             env.config.remove('ticket-custom', customfield['name'] + '.options')
    83         # Set defaults for textarea if none is specified, remove settings if no longer used
     79        # Textarea
    8480        if customfield['type'] == 'textarea':
    85             if (not customfield.has_key('cols')) or (not str(customfield['cols']).isdigit()):
    86                 customfield['cols'] = "60"
    87             if (not customfield.has_key('rows')) or (not str(customfield['rows']).isdigit()):
    88                 customfield['rows'] = "5"
    89             env.config.set('ticket-custom', customfield['name'] + '.cols', customfield['cols'])
    90             env.config.set('ticket-custom', customfield['name'] + '.rows', customfield['rows'])
    91         elif env.config.get('ticket-custom', customfield['name'] + '.cols'):
    92             env.config.remove('ticket-custom', customfield['name'] + '.cols')
    93         # Set sort setting if it is in customfield dict, remove if no longer present
    94         if create:
    95             last = len(self.get_custom_fields(env))
    96             env.config.set('ticket-custom', customfield['name'] + '.order',
    97                     customfield.get('order',0) or last)
    98         elif customfield.has_key('order') and customfield['order']:
    99             # Exists and have value - note: will not update order conflicting with other fields
    100             if str(customfield['order']).isdigit():
    101                 env.config.set('ticket-custom', customfield['name'] + '.order', customfield['order'])
    102         elif env.config.get('ticket-custom', customfield['name'] + '.order'):
    103             env.config.remove('ticket-custom', customfield['name'] + '.order')
    104         # Save settings
     81            cols = customfield.get('cols') and int(customfield.get('cols', 0)) > 0 \
     82                                                and customfield.get('cols') or 60
     83            rows = customfield.get('rows', 0) and int(customfield.get('rows', 0)) > 0 \
     84                                                and customfield.get('rows') or 5
     85            env.config.set('ticket-custom', customfield['name'] + '.cols', cols)
     86            env.config.set('ticket-custom', customfield['name'] + '.rows', rows)
     87        # Order
     88        order = customfield.get('order', "")
     89        if order == "":
     90            order = len(self.get_custom_fields(env))
     91        env.config.set('ticket-custom', customfield['name'] + '.order', order)
    10592        env.config.save()
    10693
    107     def delete_custom_field(self, env, customfield):
     94    def update_custom_field(self, env, customfield, create=False):
     95        """ Updates a custom. Option to 'create' is kept in order to keep
     96        the API backwards compatible. """
     97        if create:
     98            self.verify_custom_field(env, customfield)
     99            self.create_custom_field(env, customfield)
     100            return
     101        # Check input, then delete and save new
     102        self.verify_custom_field(env, customfield, create=False)
     103        self.delete_custom_field(env, customfield, modify=True)
     104        self.create_custom_field(env, customfield)
     105   
     106    def delete_custom_field(self, env, customfield, modify=False):
    108107        """ Deletes a custom field.
    109108        Input is a dictionary (see update_custom_field), but only ['name'] is required.
     
    111110        if not env.config.get('ticket-custom', customfield['name']):
    112111            return # Nothing to do here - cannot find field
    113         # Need to redo the order of fields that are after the field to be deleted
    114         order_to_delete = env.config.getint('ticket-custom', customfield['name']+'.order')
    115         cfs = self.get_custom_fields(env)
    116         for field in cfs:
    117             if field['order'] > order_to_delete:
    118                 env.config.set('ticket-custom', field['name']+'.order', field['order'] -1 )
     112        if not modify:
     113            # Permanent delete - reorder later fields to lower order
     114            order_to_delete = env.config.getint('ticket-custom', customfield['name']+'.order')
     115            cfs = self.get_custom_fields(env)
     116            for field in cfs:
     117                if field['order'] > order_to_delete:
     118                    env.config.set('ticket-custom', field['name']+'.order', field['order'] -1 )
    119119        # Remove any data for the custom field (covering all bases)
    120120        env.config.remove('ticket-custom', customfield['name'])
     
    125125        env.config.remove('ticket-custom', customfield['name'] + '.rows')
    126126        env.config.remove('ticket-custom', customfield['name'] + '.order')
    127         # Save settings
    128         env.config.save()
     127        # Persist permanent deletes
     128        if not modify:
     129            env.config.save()
  • customfieldadminplugin/0.11/customfieldadmin/customfieldadmin.py

    r4016 r4964  
    55License: BSD
    66
    7 (c) 2005-2007 ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
     7(c) 2005-2008 ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
    88(c) 2007      ::: www.Optaros.com (.....)
    99"""
     
    3939                      'cols': to_unicode(req.args.get('cols')),
    4040                      'rows': to_unicode(req.args.get('rows')),
    41                       'order': req.args.get('order', 0)}
     41                      'order': req.args.get('order', '')}
    4242            return cfdict
    4343       
     
    9090                                  if key.startswith('order_')])
    9191                    values = dict([(val, True) for val in order.values()])
    92                     if len(order) != len(values):
    93                         raise TracError, 'Order numbers must be unique.'
    9492                    cf = cfapi.get_custom_fields(self.env)
    9593                    for cur_cf in cf:
  • customfieldadminplugin/0.11/customfieldadmin/templates/customfieldadmin.html

    r3420 r4964  
    139139              <td>${cf.label}</td>
    140140              <td class="default">
    141                 <select name="order_${cf.name}">
    142                   <option py:for="other in cfadmin.customfields"
    143                       selected="${other.order==cf.order and 'selected' or None}">
    144                       ${other.order}
     141                <select name="order_${cf.name}" py:with="count = len(cfadmin.customfields)">
     142                  <option py:for="num in range(count)"
     143                      selected="${num==cf.order and 'selected' or None}">
     144                      ${num}
    145145                  </option>
     146                  <!--! Extra option in case value is outside regular range -->
     147                  <py:if test="cf.order not in range(count)">
     148                    <option disabled="disabled">&mdash;</option>
     149                    <option selected="selected"
     150                        title="Currently outside regular range">${cf.order}</option>
     151                  </py:if>
    146152                </select>
    147153              </td>
Note: See TracChangeset for help on using the changeset viewer.