trac-customfieldadminplugin-0.2.6+r10460/0000755000175000017500000000000011677670171016215 5ustar wmbwmbtrac-customfieldadminplugin-0.2.6+r10460/0.10/0000755000175000017500000000000011677670171016573 5ustar wmbwmbtrac-customfieldadminplugin-0.2.6+r10460/0.10/setup.py0000644000175000017500000000121110624306774020274 0ustar wmbwmbfrom setuptools import setup setup(name='TracCustomFieldAdmin', version='0.1', packages=['customfieldadmin'], author='CodeResort.com & Optaros.com', description='Expose ticket custom fields using Trac 0.10 config option API', url='http://trac-hacks.org/wiki/CustomFieldAdminPlugin', license='BSD', entry_points={'trac.plugins': [ 'customfieldadmin.api = customfieldadmin.api', 'customfieldadmin.customfieldadmin = customfieldadmin.customfieldadmin']}, package_data={'customfieldadmin' : ['htdocs/css/*.css','htdocs/js/*.js', 'templates/*.cs', ]}, install_requires=['TracWebAdmin']) trac-customfieldadminplugin-0.2.6+r10460/0.10/customfieldadmin/0000755000175000017500000000000011677670171022122 5ustar wmbwmbtrac-customfieldadminplugin-0.2.6+r10460/0.10/customfieldadmin/__init__.py0000644000175000017500000000000010624306774024215 0ustar wmbwmbtrac-customfieldadminplugin-0.2.6+r10460/0.10/customfieldadmin/templates/0000755000175000017500000000000011677670171024120 5ustar wmbwmbtrac-customfieldadminplugin-0.2.6+r10460/0.10/customfieldadmin/templates/customfieldadmin.cs0000644000175000017500000001150410774445204027771 0ustar wmbwmb

Manage Custom Fields

Modify Custom Field:

Add Custom Field:

 Name TypeLabelOrder

No Custom Fields defined for this project.

trac-customfieldadminplugin-0.2.6+r10460/0.10/customfieldadmin/htdocs/0000755000175000017500000000000011677670171023406 5ustar wmbwmbtrac-customfieldadminplugin-0.2.6+r10460/0.10/customfieldadmin/htdocs/js/0000755000175000017500000000000011677670171024022 5ustar wmbwmb././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootroottrac-customfieldadminplugin-0.2.6+r10460/0.10/customfieldadmin/htdocs/js/CustomFieldAdminPage_actions.jstrac-customfieldadminplugin-0.2.6+r10460/0.10/customfieldadmin/htdocs/js/CustomFieldAdminPage_action0000644000175000017500000000352110624306774031263 0ustar wmbwmb/********** * User Interface function for Trac Custom Field Admin plugin. * License: BSD * (c) 2007 ::: www.Optaros.com (.....) **********/ /** * */ function addcfType_validate( ){ function getLabel( id ){ var labelElements = document.getElementById('addcf').getElementsByTagName("LABEL"); for( var i=0; i order_to_delete: env.config.set('ticket-custom', field['name']+'.order', field['order'] -1 ) # Remove any data for the custom field (covering all bases) env.config.remove('ticket-custom', customfield['name']) env.config.remove('ticket-custom', customfield['name'] + '.label') env.config.remove('ticket-custom', customfield['name'] + '.value') env.config.remove('ticket-custom', customfield['name'] + '.options') env.config.remove('ticket-custom', customfield['name'] + '.cols') env.config.remove('ticket-custom', customfield['name'] + '.rows') env.config.remove('ticket-custom', customfield['name'] + '.order') # Save settings env.config.save() trac-customfieldadminplugin-0.2.6+r10460/0.10/customfieldadmin/customfieldadmin.py0000644000175000017500000001176210675324756026034 0ustar wmbwmb# -*- coding: utf-8 -*- """ Trac WebAdmin plugin for administration of custom fields. License: BSD (c) 2005-2007 ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no) (c) 2007 ::: www.Optaros.com (.....) """ from trac.core import * from trac.web.chrome import ITemplateProvider, add_stylesheet, add_script from webadmin.web_ui import IAdminPageProvider from api import CustomFields from trac.util.text import to_unicode class CustomFieldAdminPage(Component): implements(ITemplateProvider, IAdminPageProvider) # IAdminPageProvider methods def get_admin_pages(self, req): if req.perm.has_permission('TRAC_ADMIN'): yield ('ticket', 'Ticket System', 'customfields', 'Custom Fields') def process_admin_request(self, req, cat, page, customfield): #assert req.perm.has_permission('TRAC_ADMIN') req.perm.assert_permission('TRAC_ADMIN') add_script(req, 'customfieldadmin/js/CustomFieldAdminPage_actions.js') def _customfield_from_req(self, req): cfdict = {'name': to_unicode(req.args.get('name')), 'label': to_unicode(req.args.get('label')), 'type': to_unicode(req.args.get('type')), 'value': to_unicode(req.args.get('value')), 'options': [x.strip() for x in to_unicode(req.args.get('options')).split("\n")], 'cols': to_unicode(req.args.get('cols')), 'rows': to_unicode(req.args.get('rows')), 'order': req.args.get('order', 0)} return cfdict cfapi = CustomFields(self.env) # Detail view? if customfield: exists = [True for cf in cfapi.get_custom_fields(self.env) if cf['name'] == customfield] if not exists: raise TracError("Custom field %s does not exist." % customfield) if req.method == 'POST': if req.args.get('save'): cfdict = _customfield_from_req(self, req) cfapi.update_custom_field(self.env, cfdict) req.redirect(req.href.admin(cat, page)) elif req.args.get('cancel'): req.redirect(req.href.admin(cat, page)) currentcf = cfapi.get_custom_fields(self.env, {'name': customfield}) if currentcf.has_key('options'): optional_line = '' if currentcf.get('optional', False): optional_line = "\n" currentcf['options'] = optional_line + "\n".join(currentcf['options']) req.hdf['admin.customfield'] = currentcf else: if req.method == 'POST': # Add Custom Field if req.args.get('add') and req.args.get('name'): cfdict = _customfield_from_req(self, req) cfapi.update_custom_field(self.env, cfdict, create=True) req.redirect(req.href.admin(cat, page)) # Remove Custom Field elif req.args.get('remove') and req.args.get('sel'): sel = req.args.get('sel') sel = isinstance(sel, list) and sel or [sel] if not sel: raise TracError, 'No custom field selected' for name in sel: cfdict = {'name': name} cfapi.delete_custom_field(self.env, cfdict) req.redirect(req.href.admin(cat, page)) elif req.args.get('apply'): # Change order order = dict([(key[6:], req.args.get(key)) for key in req.args.keys() if key.startswith('order_')]) values = dict([(val, True) for val in order.values()]) if len(order) != len(values): raise TracError, 'Order numbers must be unique.' cf = cfapi.get_custom_fields(self.env) for cur_cf in cf: cur_cf['order'] = order[cur_cf['name']] cfapi.update_custom_field(self.env, cur_cf) req.redirect(req.href.admin(cat, page)) hdf_list = [] for item in cfapi.get_custom_fields(self.env): item['href'] = req.href.admin(cat, page, item['name']) hdf_list.append(item) req.hdf['admin.customfields'] = hdf_list return 'customfieldadmin.cs', None # ITemplateProvider methods def get_templates_dirs(self): from pkg_resources import resource_filename return [resource_filename(__name__, 'templates')] def get_htdocs_dirs(self): from pkg_resources import resource_filename return [('customfieldadmin', resource_filename(__name__, 'htdocs'))] trac-customfieldadminplugin-0.2.6+r10460/0.11/0000755000175000017500000000000011677670171016574 5ustar wmbwmbtrac-customfieldadminplugin-0.2.6+r10460/0.11/setup.cfg0000644000175000017500000000005711606762250020407 0ustar wmbwmb[egg_info] tag_build = tag_svn_revision = true trac-customfieldadminplugin-0.2.6+r10460/0.11/LICENSE.txt0000644000175000017500000000276711144514242020414 0ustar wmbwmbCopyright (c) 2005-2009, CodeResort.com & Optaros.com All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder(s) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. trac-customfieldadminplugin-0.2.6+r10460/0.11/setup.py0000644000175000017500000000140411600631772020273 0ustar wmbwmbfrom setuptools import setup setup(name='TracCustomFieldAdmin', version='0.2.6', packages=['customfieldadmin'], author='CodeResort.com & Optaros.com', description='Admin panel for managing Trac ticket custom fields.', url='http://trac-hacks.org/wiki/CustomFieldAdminPlugin', license='BSD', entry_points={'trac.plugins': [ 'customfieldadmin.api = customfieldadmin.api', 'customfieldadmin.customfieldadmin = customfieldadmin.customfieldadmin']}, package_data={'customfieldadmin' : ['htdocs/css/*.css','htdocs/js/*.js', 'templates/*.html', ]}, exclude_package_data={'': ['tests/*']}, test_suite = 'customfieldadmin.tests.test_suite', tests_require = [], install_requires = []) trac-customfieldadminplugin-0.2.6+r10460/0.11/customfieldadmin/0000755000175000017500000000000011677670171022123 5ustar wmbwmbtrac-customfieldadminplugin-0.2.6+r10460/0.11/customfieldadmin/__init__.py0000644000175000017500000000013411247172666024231 0ustar wmbwmb __version__ = __import__('pkg_resources').get_distribution('TracCustomFieldAdmin').version trac-customfieldadminplugin-0.2.6+r10460/0.11/customfieldadmin/templates/0000755000175000017500000000000011677670171024121 5ustar wmbwmbtrac-customfieldadminplugin-0.2.6+r10460/0.11/customfieldadmin/templates/customfieldadmin.html0000644000175000017500000001555611247556740030350 0ustar wmbwmb Custom Fields Admin

Manage Custom Fields

Modify Custom Field:

Add Custom Field:

No Custom Fields defined for this project.

  Name Type Label Order
${cf.name} ${cf.type} ${cf.label}
trac-customfieldadminplugin-0.2.6+r10460/0.11/customfieldadmin/tests/0000755000175000017500000000000011677670171023265 5ustar wmbwmbtrac-customfieldadminplugin-0.2.6+r10460/0.11/customfieldadmin/tests/__init__.py0000644000175000017500000000057011600631772025366 0ustar wmbwmb# -*- coding: utf-8 -*- """ License: BSD (c) 2005-2011 ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no) """ from unittest import TestSuite, makeSuite def test_suite(): suite = TestSuite() import customfieldadmin.tests.web_ui suite.addTest(makeSuite( customfieldadmin.tests.web_ui.CustomFieldAdminPageTestCase)) return suite trac-customfieldadminplugin-0.2.6+r10460/0.11/customfieldadmin/tests/web_ui.py0000644000175000017500000000631711600631772025106 0ustar wmbwmb# -*- coding: utf-8 -*- """ License: BSD (c) 2005-2011 ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no) """ import unittest from trac.perm import PermissionSystem, PermissionCache from trac.test import EnvironmentStub, Mock from trac.ticket.api import TicketSystem from trac.web.api import RequestDone from trac.web.href import Href from customfieldadmin.customfieldadmin import CustomFieldAdminPage class CustomFieldAdminPageTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() ps = PermissionSystem(self.env) ps.grant_permission('admin', 'TICKET_ADMIN') self.plugin = CustomFieldAdminPage(self.env) def tearDown(self): self.env.destroy_db() del self.env def test_systeminfo(self): from customfieldadmin import __version__ self.assertTrue(('CustomFieldAdmin', __version__) in self.env.systeminfo) def test_add_optional_select(self): # http://trac-hacks.org/ticket/1834 _redirect_url = '' def redirect(url): _redirect_url = url raise RequestDone req = Mock(perm=PermissionCache(self.env, 'admin'), authname='admin', chrome={}, href=Href('/'), redirect=redirect, method='POST', args={'add': True, 'name': "test", 'type': "select", 'label': "testing", 'options': "\r\none\r\ntwo"}) try: self.plugin.render_admin_panel(req, 'ticket', 'customfields', None) except RequestDone, e: self.assertEquals( sorted(list(self.env.config.options('ticket-custom'))), [(u'test', u'select'), (u'test.label', u'testing'), (u'test.options', u'|one|two'), (u'test.order', u'1'), (u'test.value', u'')]) def test_apply_optional_select(self): # Reuse the added custom field that test verified to work self.test_add_optional_select() self.assertEquals('select', self.env.config.get('ticket-custom', 'test')) # Now check that details are maintained across order change # that reads fields, deletes them, and creates them again # http://trac-hacks.org/ticket/1834#comment:5 _redirect_url = '' def redirect(url): _redirect_url = url raise RequestDone req = Mock(perm=PermissionCache(self.env, 'admin'), authname='admin', chrome={}, href=Href('/'), redirect=redirect, method='POST', args={'apply': True, 'order_test': '2'}) try: self.plugin.render_admin_panel(req, 'ticket', 'customfields', None) except RequestDone, e: self.assertEquals( sorted(list(self.env.config.options('ticket-custom'))), [(u'test', u'select'), (u'test.label', u'testing'), (u'test.options', u'|one|two'), (u'test.order', u'2'), (u'test.value', u'')]) trac-customfieldadminplugin-0.2.6+r10460/0.11/customfieldadmin/htdocs/0000755000175000017500000000000011677670171023407 5ustar wmbwmbtrac-customfieldadminplugin-0.2.6+r10460/0.11/customfieldadmin/htdocs/js/0000755000175000017500000000000011677670171024023 5ustar wmbwmbtrac-customfieldadminplugin-0.2.6+r10460/0.11/customfieldadmin/htdocs/js/customfieldadmin.js0000644000175000017500000000243711145020546027677 0ustar wmbwmb/********** * User Interface function for Trac Custom Field Admin plugin. * License: BSD * (c) 2007-2009 ::: www.Optaros.com (cbalan@optaros.com) **********/ (function($){ function toggle_options(type_element){ function label(property){ return $(property).parents('div.field')} switch (type_element.selectedIndex) { case 0: // text label('#options, #cols, #rows').hide(); label('#format').show(); break; case 1: // select label('#options').show(); label('#cols, #rows, #format').hide(); break; case 2: // checkbox label('#options, #cols, #rows, #format').hide(); break; case 3: // radio label('#options').show(); label('#cols, #rows, #format').hide(); break; case 4: // textarea label('#options').hide(); label('#cols, #rows, #format').show(); break; } } $(document).ready(function(){ $('#type').each(function(){ toggle_options(this); $(this).change(function(){ toggle_options(this); }); }); }); })(jQuery); trac-customfieldadminplugin-0.2.6+r10460/0.11/customfieldadmin/api.py0000644000175000017500000001574111600631772023244 0ustar wmbwmb# -*- 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() trac-customfieldadminplugin-0.2.6+r10460/0.11/customfieldadmin/customfieldadmin.py0000644000175000017500000001245111600631772026015 0ustar wmbwmb# -*- coding: utf-8 -*- """ Trac WebAdmin plugin for administration of custom fields. License: BSD (c) 2005-2011 ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no) (c) 2007-2009 ::: www.Optaros.com (.....) """ from trac.config import Option from trac.core import * from trac.web.chrome import ITemplateProvider, add_stylesheet, add_script from trac.admin.api import IAdminPanelProvider from api import CustomFields class CustomFieldAdminPage(Component): implements(ITemplateProvider, IAdminPanelProvider) def __init__(self): self.env.systeminfo.append(('CustomFieldAdmin', __import__('customfieldadmin', ['__version__']).__version__)) # IAdminPanelProvider methods def get_admin_panels(self, req): if 'TICKET_ADMIN' in req.perm: yield ('ticket', 'Ticket System', 'customfields', 'Custom Fields') def render_admin_panel(self, req, cat, page, customfield): req.perm.require('TICKET_ADMIN') add_script(req, 'customfieldadmin/js/customfieldadmin.js') def _customfield_from_req(self, req): cfdict = {'name': req.args.get('name','').encode('utf-8'), 'label': req.args.get('label','').encode('utf-8'), 'type': req.args.get('type','').encode('utf-8'), 'value': req.args.get('value','').encode('utf-8'), 'options': [x.strip().encode('utf-8') for x in req.args.get('options','').split("\n")], 'cols': req.args.get('cols','').encode('utf-8'), 'rows': req.args.get('rows','').encode('utf-8'), 'order': req.args.get('order', '').encode('utf-8'), 'format': req.args.get('format', '').encode('utf-8')} return cfdict cfapi = CustomFields(self.env) cfadmin = {} # Return values for template rendering # Detail view? if customfield: exists = [True for cf in cfapi.get_custom_fields() if cf['name'] == customfield] if not exists: raise TracError("Custom field %s does not exist." % customfield) if req.method == 'POST': if req.args.get('save'): cfdict = _customfield_from_req(self, req) cfapi.update_custom_field(cfdict) req.redirect(req.href.admin(cat, page)) elif req.args.get('cancel'): req.redirect(req.href.admin(cat, page)) currentcf = cfapi.get_custom_fields({'name': customfield}) if currentcf.has_key('options'): optional_line = '' if currentcf.get('optional', False): optional_line = "\n\n" currentcf['options'] = optional_line + "\n".join(currentcf['options']) cfadmin['customfield'] = currentcf cfadmin['display'] = 'detail' else: if req.method == 'POST': # Add Custom Field if req.args.get('add') and req.args.get('name'): cfdict = _customfield_from_req(self, req) cfapi.update_custom_field(cfdict, create=True) req.redirect(req.href.admin(cat, page)) # Remove Custom Field elif req.args.get('remove') and req.args.get('sel'): sel = req.args.get('sel') sel = isinstance(sel, list) and sel or [sel] if not sel: raise TracError, 'No custom field selected' for name in sel: cfdict = {'name': name} cfapi.delete_custom_field(cfdict) req.redirect(req.href.admin(cat, page)) elif req.args.get('apply'): # Change order order = dict([(key[6:], req.args.get(key)) for key in req.args.keys() if key.startswith('order_')]) values = dict([(val, True) for val in order.values()]) cf = cfapi.get_custom_fields() for cur_cf in cf: cur_cf['order'] = order[cur_cf['name']] cfapi.update_custom_field(cur_cf) req.redirect(req.href.admin(cat, page)) cf_list = [] for item in cfapi.get_custom_fields(): item['href'] = req.href.admin(cat, page, item['name']) item['registry'] = ('ticket-custom', item['name']) in Option.registry cf_list.append(item) cfadmin['customfields'] = cf_list cfadmin['display'] = 'list' return ('customfieldadmin.html', {'cfadmin': cfadmin}) # ITemplateProvider methods def get_templates_dirs(self): from pkg_resources import resource_filename return [resource_filename(__name__, 'templates')] def get_htdocs_dirs(self): from pkg_resources import resource_filename return [('customfieldadmin', resource_filename(__name__, 'htdocs'))]