/usr/share/pyshared/customfieldadmin/api.py is in trac-customfieldadmin 0.2.6+r10460-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | # -*- coding: utf-8 -*-
"""
API for administrating custom ticket fields in Trac.
Supports creating, getting, updating and deleting custom fields.
License: BSD
(c) 2005-2011 ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
"""
import re
from trac.core import *
from trac.ticket.api import TicketSystem
__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.)
Input to methods is a 'customfield' dict supporting these 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
format = text|wiki (for text and textarea)
"""
implements()
def get_custom_fields(self, customfield=None):
""" 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(self.env).get_custom_fields()
else: # only return specific item with cfname
all = TicketSystem(self.env).get_custom_fields()
for item in all:
if item['name'] == customfield['name']:
return item
return None # item not found
def verify_custom_field(self, customfield, create=True):
""" Basic validation of the input for modifying or creating
custom fields. """
# Name, Type and Label is required
if not (customfield.get('name') and customfield.get('type') \
and customfield.get('label')):
raise TracError("Custom field needs at least a name, type and label.")
# Use lowercase custom fieldnames only
customfield['name'] = customfield['name'].lower()
# Only alphanumeric characters (and [-_]) allowed for custom fieldname
if re.search('^[a-z][a-z0-9_]+$', customfield['name']) == None:
raise TracError("Only alphanumeric characters allowed for custom field name "
"('a-z' or '0-9' or '_'), with 'a-z' as first character.")
# Name must begin with a character - anything else not supported by Trac
if not customfield['name'][0].isalpha():
raise TracError("Custom field name must begin with a character (a-z).")
# 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'])
# Check that field does not already exist (if modify it should already be deleted)
if create and self.config.get('ticket-custom', customfield['name']):
raise TracError("Can not create as field already exists.")
def create_custom_field(self, customfield):
""" Create the new custom fields (that may just have been deleted as part
of 'modify'). Note: Caller is responsible for verifying input before create."""
# Set the mandatory items
self.config.set('ticket-custom', customfield['name'], customfield['type'])
self.config.set('ticket-custom', customfield['name'] + '.label', customfield['label'])
# Optional items
if 'value' in customfield:
self.config.set('ticket-custom', customfield['name'] + '.value', customfield['value'])
if 'options' in customfield:
if customfield.get('optional', False):
self.config.set('ticket-custom', customfield['name'] + '.options',
'|' + '|'.join(customfield['options']))
else:
self.config.set('ticket-custom', customfield['name'] + '.options',
'|'.join(customfield['options']))
if 'format' in customfield and customfield['type'] in ('text', 'textarea'):
self.config.set('ticket-custom', customfield['name'] + '.format', customfield['format'])
# Textarea
if customfield['type'] == 'textarea':
cols = customfield.get('cols') and int(customfield.get('cols', 0)) > 0 \
and customfield.get('cols') or 60
rows = customfield.get('rows', 0) and int(customfield.get('rows', 0)) > 0 \
and customfield.get('rows') or 5
self.config.set('ticket-custom', customfield['name'] + '.cols', cols)
self.config.set('ticket-custom', customfield['name'] + '.rows', rows)
# Order
order = customfield.get('order', "")
if order == "":
order = len(self.get_custom_fields())
self.config.set('ticket-custom', customfield['name'] + '.order', order)
self.config.save()
def update_custom_field(self, customfield, create=False):
""" Updates a custom. Option to 'create' is kept in order to keep
the API backwards compatible. """
if create:
self.verify_custom_field(customfield)
self.create_custom_field(customfield)
return
# Check input, then delete and save new
self.verify_custom_field(customfield, create=False)
self.delete_custom_field(customfield, modify=True)
self.create_custom_field(customfield)
def delete_custom_field(self, customfield, modify=False):
""" Deletes a custom field.
Input is a dictionary (see update_custom_field), but only ['name'] is required.
"""
if not self.config.get('ticket-custom', customfield['name']):
return # Nothing to do here - cannot find field
if not modify:
# Permanent delete - reorder later fields to lower order
order_to_delete = self.config.getint('ticket-custom', customfield['name']+'.order')
cfs = self.get_custom_fields()
for field in cfs:
if field['order'] > order_to_delete:
self.config.set('ticket-custom', field['name']+'.order', field['order'] -1 )
# Remove any data for the custom field (covering all bases)
for option, _value in self.config.options('ticket-custom'):
if option == customfield['name'] \
or option.startswith(customfield['name'] + '.'):
self.config.remove('ticket-custom', option)
# Persist permanent deletes
if not modify:
self.config.save()
|