diff --git a/trac/ticket/api.py b/trac/ticket/api.py
|
a
|
b
|
|
| 264 | 264 | 'type': config.get(name), |
| 265 | 265 | 'order': config.getint(name + '.order', 0), |
| 266 | 266 | 'label': config.get(name + '.label') or name.capitalize(), |
| | 267 | 'hint': config.get(name + '.hint') or None, |
| 267 | 268 | 'value': config.get(name + '.value', '') |
| 268 | 269 | } |
| 269 | 270 | if field['type'] == 'select' or field['type'] == 'radio': |
diff --git a/trac/ticket/templates/ticket.html b/trac/ticket/templates/ticket.html
|
a
|
b
|
|
| 303 | 303 | <py:for each="idx, field in enumerate(row)" |
| 304 | 304 | py:with="value = ticket.get_value_or_default(field.name)"> |
| 305 | 305 | <th class="col${idx + 1}" py:if="idx == 0 or not fullrow"> |
| 306 | | <label for="field-${field.name}" py:if="field" |
| 307 | | py:strip="field.type == 'radio'">${field.edit_label or field.label or field.name}:</label> |
| | 306 | <label for="${field.type != 'radio' and ('field-' + field.name) or None}" |
| | 307 | py:if="field">${field.edit_label or field.label or field.name}:</label> |
| 308 | 308 | </th> |
| 309 | 309 | <td class="col${idx + 1}" py:if="idx == 0 or not fullrow" |
| 310 | 310 | colspan="${fullrow and 3 or None}"> |
| 311 | 311 | <py:choose test="field.type" py:if="field"> |
| 312 | | <select py:when="'select'" id="field-${field.name}" name="field_${field.name}"> |
| | 312 | <select py:when="'select'" id="field-${field.name}" name="field_${field.name}" title="${field.hint}"> |
| 313 | 313 | <option py:if="field.optional"></option> |
| 314 | 314 | <option py:for="option in field.options" |
| 315 | 315 | selected="${value == option or None}" |
| … |
… |
|
| 323 | 323 | </optgroup> |
| 324 | 324 | </select> |
| 325 | 325 | <textarea py:when="'textarea'" id="field-${field.name}" name="field_${field.name}" |
| 326 | | cols="${field.width}" rows="${field.height}" |
| | 326 | cols="${field.width}" rows="${field.height}" title="${field.hint}" |
| 327 | 327 | py:content="value"></textarea> |
| 328 | 328 | <span py:when="'checkbox'"> |
| 329 | 329 | <input type="checkbox" id="field-${field.name}" name="field_${field.name}" |
| 330 | | checked="${value == '1' and 'checked' or None}" value="1" /> |
| | 330 | checked="${value == '1' and 'checked' or None}" value="1" title="${field.hint}" /> |
| 331 | 331 | <input type="hidden" name="field_checkbox_${field.name}" value="1" /> |
| 332 | 332 | </span> |
| 333 | | <label py:when="'radio'" |
| 334 | | py:for="idx, option in enumerate(field.options)"> |
| 335 | | <input type="radio" name="field_${field.name}" value="${option}" |
| 336 | | checked="${value == option or None}" /> |
| 337 | | ${option} |
| 338 | | </label> |
| | 333 | <span py:when="'radio'" title="${field.hint}"> |
| | 334 | <label py:for="option in field.options"> |
| | 335 | <input type="radio" name="field_${field.name}" value="${option}" |
| | 336 | checked="${value == option or None}" /> |
| | 337 | ${option} |
| | 338 | </label> |
| | 339 | </span> |
| 339 | 340 | <py:otherwise><!--! Text input fields --> |
| 340 | 341 | <py:choose> |
| 341 | 342 | <span py:when="field.cc_entry"><!--! Special case for Cc: field --> |
| … |
… |
|
| 352 | 353 | </span> |
| 353 | 354 | <!--! All the other text input fields --> |
| 354 | 355 | <input py:otherwise="" type="text" id="field-${field.name}" |
| 355 | | name="field_${field.name}" value="${value}" /> |
| | 356 | name="field_${field.name}" value="${value}" title="${field.hint}" /> |
| 356 | 357 | </py:choose> |
| 357 | 358 | </py:otherwise> |
| 358 | 359 | </py:choose> |
diff --git a/trac/ticket/tests/api.py b/trac/ticket/tests/api.py
|
a
|
b
|
|
| 24 | 24 | self.env.config.set('ticket-custom', 'test', 'text') |
| 25 | 25 | self.env.config.set('ticket-custom', 'test.label', 'Test') |
| 26 | 26 | self.env.config.set('ticket-custom', 'test.value', 'Foo bar') |
| | 27 | self.env.config.set('ticket-custom', 'test.hint', 'An explanation') |
| 27 | 28 | fields = TicketSystem(self.env).get_custom_fields() |
| 28 | 29 | self.assertEqual({'name': 'test', 'type': 'text', 'label': 'Test', |
| 29 | | 'value': 'Foo bar', 'order': 0}, |
| | 30 | 'value': 'Foo bar', 'order': 0, 'hint': 'An explanation'}, |
| 30 | 31 | fields[0]) |
| 31 | 32 | |
| 32 | 33 | def test_custom_field_select(self): |
| … |
… |
|
| 34 | 35 | self.env.config.set('ticket-custom', 'test.label', 'Test') |
| 35 | 36 | self.env.config.set('ticket-custom', 'test.value', '1') |
| 36 | 37 | self.env.config.set('ticket-custom', 'test.options', 'option1|option2') |
| | 38 | self.env.config.set('ticket-custom', 'test.hint', 'An explanation') |
| 37 | 39 | fields = TicketSystem(self.env).get_custom_fields() |
| 38 | 40 | self.assertEqual({'name': 'test', 'type': 'select', 'label': 'Test', |
| 39 | 41 | 'value': '1', 'options': ['option1', 'option2'], |
| 40 | | 'order': 0}, |
| | 42 | 'order': 0, 'hint': 'An explanation'}, |
| 41 | 43 | fields[0]) |
| 42 | 44 | |
| 43 | 45 | def test_custom_field_optional_select(self): |
| … |
… |
|
| 48 | 50 | fields = TicketSystem(self.env).get_custom_fields() |
| 49 | 51 | self.assertEqual({'name': 'test', 'type': 'select', 'label': 'Test', |
| 50 | 52 | 'value': '1', 'options': ['option1', 'option2'], |
| 51 | | 'order': 0, 'optional': True}, |
| | 53 | 'order': 0, 'optional': True, 'hint': None}, |
| 52 | 54 | fields[0]) |
| 53 | 55 | |
| 54 | 56 | def test_custom_field_textarea(self): |
| … |
… |
|
| 57 | 59 | self.env.config.set('ticket-custom', 'test.value', 'Foo bar') |
| 58 | 60 | self.env.config.set('ticket-custom', 'test.cols', '60') |
| 59 | 61 | self.env.config.set('ticket-custom', 'test.rows', '4') |
| | 62 | self.env.config.set('ticket-custom', 'test.hint', 'An explanation') |
| 60 | 63 | fields = TicketSystem(self.env).get_custom_fields() |
| 61 | 64 | self.assertEqual({'name': 'test', 'type': 'textarea', 'label': 'Test', |
| 62 | 65 | 'value': 'Foo bar', 'width': 60, 'height': 4, |
| 63 | | 'order': 0}, |
| | 66 | 'order': 0, 'hint': 'An explanation'}, |
| 64 | 67 | fields[0]) |
| 65 | 68 | |
| 66 | 69 | def test_custom_field_order(self): |