This file is indexed.

/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()