Trac. Adding a custom select field to a ticket

859 views Asked by At

I'm trying to write a plugin which will add a certain select-type custom field to a ticket. The difference from a regular custom-field of a select type is that this field will get its values from a database and create a select with optgroups.

I create a custom select field via trac config file and modify it with Transformer

The code goes like this:

        db = self.env.get_db_cnx()
        cursor = db.cursor()

        cursor.execute("SELECT name, a_id FROM a_group")
        groups = cursor.fetchall()

        cursor.execute("SELECT id, name FROM activities")
        activities = cursor.fetchall()

        for activity in activities:
            stream = stream | Transformer('.//select[@id="field-activity"]').append(tag.optgroup(label=activity[1], id="act-"+str(activity[0])))

            for group in groups:

                if int(group[1]) == activity[0]:

                    stream = stream | Transformer('.//optgroup[@id="act-' + str(activity[0]) + '"]').append(tag.option(group[0]))

The problem is: when I'm trying to save a newticket, I get an error:

Warning: <field_name_goes_here> is not a valid value for the activity field.

Which is due to the fact, that while I'm using custom field via trac custom-fields functionality - I do not provide any options via trac config file.

The question is - what the best (if any) way to implement this kind of feature?

2

There are 2 answers

0
hasienda On BEST ANSWER

The error comes from '_validate_ticket' in trac.ticket.web_ui. As stated in the source comment header it will "Always validate for known values" of option fields.

Since you already mangle the custom field appearance, it might be worth trying to alter its input field type as well. The idea is to pretend it is a simple (text) input field, that will accept any value without validation, you see?

Other approaches would involve messing with Trac core code, what is not a bright idea, especially if you plan to follow upstream code in general for the years to come.

0
RjOllos On

I think you will want to implement IRequestFilter. In post_process_request you can modify the options for the select. For example, if I wanted to change the Component select to a set of hard-coded values:

    def post_process_request(self, req, template, data, content_type):
    if data and 'fields' in data:
       for entry in data['fields']:
            if 'name' in entry and entry['name'] == 'component':
                entry['options'] = ['component1', 'component2', 'component3']

So you should be able to execute a db query in post_process_request to pull values from the database and populate the dictionary item: entry['options'] = self.env.db_query(...).

You may need to put at least one dummy option in the configuration file for the custom field.