customfieldadminplugin/0000755000175000017500000000000012745255020013762 5ustar wmbwmbcustomfieldadminplugin/0.10/0000755000175000017500000000000012003722137014333 5ustar wmbwmbcustomfieldadminplugin/0.10/customfieldadmin/0000755000175000017500000000000010774445204017674 5ustar wmbwmbcustomfieldadminplugin/0.10/customfieldadmin/templates/0000755000175000017500000000000010774445204021672 5ustar wmbwmbcustomfieldadminplugin/0.10/customfieldadmin/templates/customfieldadmin.cs0000644000175000017500000001150410774445204025551 0ustar wmbwmb

Manage Custom Fields

Modify Custom Field:

Add Custom Field:

 Name TypeLabelOrder

No Custom Fields defined for this project.

customfieldadminplugin/0.10/customfieldadmin/__init__.py0000644000175000017500000000000010624306774021775 0ustar wmbwmbcustomfieldadminplugin/0.10/customfieldadmin/customfieldadmin.py0000644000175000017500000001176210675324756023614 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'))] customfieldadminplugin/0.10/customfieldadmin/htdocs/0000755000175000017500000000000010624306774021162 5ustar wmbwmbcustomfieldadminplugin/0.10/customfieldadmin/htdocs/js/0000755000175000017500000000000010624306774021576 5ustar wmbwmbcustomfieldadminplugin/0.10/customfieldadmin/htdocs/js/CustomFieldAdminPage_actions.js0000755000175000017500000000352110624306774027644 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() customfieldadminplugin/0.10/setup.cfg0000644000175000017500000000006312003722137016153 0ustar wmbwmb[egg_info] tag_build = dev tag_svn_revision = true customfieldadminplugin/0.10/setup.py0000644000175000017500000000121110624306774016054 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']) customfieldadminplugin/0.11/0000755000175000017500000000000012521736164014346 5ustar wmbwmbcustomfieldadminplugin/0.11/customfieldadmin/0000755000175000017500000000000012521736164017675 5ustar wmbwmbcustomfieldadminplugin/0.11/customfieldadmin/templates/0000755000175000017500000000000012157210611021660 5ustar wmbwmbcustomfieldadminplugin/0.11/customfieldadmin/templates/customfieldadmin.html0000644000175000017500000001612312157210611026100 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}
customfieldadminplugin/0.11/customfieldadmin/__init__.py0000644000175000017500000000020511710251631021771 0ustar wmbwmb __version__ = __import__('pkg_resources').get_distribution( 'TracCustomFieldAdmin').version customfieldadminplugin/0.11/customfieldadmin/locale/0000755000175000017500000000000012515446723021136 5ustar wmbwmbcustomfieldadminplugin/0.11/customfieldadmin/locale/.placeholder0000644000175000017500000000027111706405111023404 0ustar wmbwmb# DO NOT REMOVE THIS FILE # # This file ensures that 'locale' directory is included in distibutions even # when Babel is not installed. It prevents lookup and reference errors in Trac. customfieldadminplugin/0.11/customfieldadmin/locale/de/0000755000175000017500000000000012515446723021526 5ustar wmbwmbcustomfieldadminplugin/0.11/customfieldadmin/locale/de/LC_MESSAGES/0000755000175000017500000000000012515446723023313 5ustar wmbwmbcustomfieldadminplugin/0.11/customfieldadmin/locale/de/LC_MESSAGES/customfieldadmin.po0000644000175000017500000001454712036744452027213 0ustar wmbwmb# German translations for TracCustomFieldAdmin. # Copyright (C) 2012 CodeResort.com (BV Network AS) # This file is distributed under the same license as the # TracCustomFieldAdmin project. # Steffen Hoffmann , 2012. # msgid "" msgstr "" "Project-Id-Version: TracCustomFieldAdmin 0.2.8\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2012-01-24 13:47+0100\n" "PO-Revision-Date: 2012-09-22 22:44+0200\n" "Last-Translator: Steffen Hoffmann \n" "Language-Team: de \n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.6\n" #: customfieldadmin/admin.py:33 msgid "Ticket System" msgstr "Ticket-System" #: customfieldadmin/admin.py:34 msgid "Custom Fields" msgstr "Individuelle Felder" #: customfieldadmin/admin.py:65 #, python-format msgid "Custom field %(name)s does not exist." msgstr "Es gibt kein individuelles Feld %(name)s." #: customfieldadmin/admin.py:94 msgid "No custom field selected" msgstr "Kein individuelles Feld gewählt" #: customfieldadmin/admin.py:123 msgid "" "Custom Fields are not correctly sorted. This may affect appearance when " "viewing tickets." msgstr "" "Die individuellen Felder sind nicht ordnungsgemäß sortiert. Das kann ihre" " Darstellung bei der Ticket-Anzeige beeinflussen." #: customfieldadmin/api.py:87 msgid "Custom field requires attributes 'name' and 'type'." msgstr "Das individuelle Feld benötigt die Attribute 'name' und 'type'." #: customfieldadmin/api.py:92 msgid "" "Only alphanumeric characters allowed for custom field name ('a-z' or " "'0-9' or '_'), with 'a-z' as first character." msgstr "" "Nur alphanumerischen Zeichen sind als Name eines individuellem Feldes " "zugelassen ('a-z' oder '0-9' oder '_'), und 'a-z' als erstes Zeichen." #: customfieldadmin/api.py:98 msgid "Custom field name must begin with a character (a-z)." msgstr "" "Der Name des individuellen Feldes muss mit einem Kleinbuchstaben (a-z) " "beginnen." #: customfieldadmin/api.py:102 #, python-format msgid "%(field_type)s is not a valid field type" msgstr "%(field_type)s ist kein gültiger Feldtyp" #: customfieldadmin/api.py:107 msgid "Can not create as field already exists." msgstr "Das Feld kann nicht neu erstellt werden, weil es bereits existiert." #: customfieldadmin/api.py:157 #, python-format msgid "Custom Field '%(name)s' does not exist. Cannot update." msgstr "Es gibt kein individuelles Feld '%(name)s'. Aktualisierung unmöglich." #: customfieldadmin/templates/customfieldadmin.html:11 msgid "Custom Fields Admin" msgstr "Individuelle Felder" #: customfieldadmin/templates/customfieldadmin.html:16 msgid "Manage Custom Fields" msgstr "Verwaltung individueller Felder" #: customfieldadmin/templates/customfieldadmin.html:21 msgid "Modify Custom Field:" msgstr "Änderung des individuellen Feldes:" #: customfieldadmin/templates/customfieldadmin.html:23 msgid "Name (cannot modify):" msgstr "Name (unveränderlich):" #: customfieldadmin/templates/customfieldadmin.html:28 #: customfieldadmin/templates/customfieldadmin.html:91 msgid "Type:" msgstr "Typ:" #: customfieldadmin/templates/customfieldadmin.html:39 #: customfieldadmin/templates/customfieldadmin.html:100 msgid "Label:" msgstr "Bezeichnung:" #: customfieldadmin/templates/customfieldadmin.html:44 msgid "" "Default value\n" " (regular text for Text, Textarea, Radio or Select):" msgstr "" "Standardwert (Vorgabetext für Textzeile, Textfeld, Optionsschaltfläche " "oder Auswahllistenfeld):" #: customfieldadmin/templates/customfieldadmin.html:50 msgid "Format (Text or Textarea):" msgstr "Format (Textzeile oder -feld):" #: customfieldadmin/templates/customfieldadmin.html:60 msgid "" "Options for Radio or Select\n" " (for Select, empty first line makes field optional):" msgstr "" "Wahlmöglichkeiten für Optionsschaltfläche oder Auswahllistenfeld (leere " "erste Zeile macht Auswahllistenfeld optional)" #: customfieldadmin/templates/customfieldadmin.html:71 msgid "Size of Textarea for entry (Textarea only):" msgstr "Grösse des Textfeldes (nur Textfeld):" #: customfieldadmin/templates/customfieldadmin.html:71 msgid "Columns:" msgstr "Spalten:" #: customfieldadmin/templates/customfieldadmin.html:72 #: customfieldadmin/templates/customfieldadmin.html:128 msgid "Rows:" msgstr "Zeilen:" #: customfieldadmin/templates/customfieldadmin.html:76 msgid "Cancel" msgstr "Abbrechen" #: customfieldadmin/templates/customfieldadmin.html:77 msgid "Save" msgstr "Speichern" #: customfieldadmin/templates/customfieldadmin.html:84 msgid "Add Custom Field:" msgstr "Individuelles Feld hinzufügen:" #: customfieldadmin/templates/customfieldadmin.html:86 msgid "Name:" msgstr "Name:" #: customfieldadmin/templates/customfieldadmin.html:105 msgid "Default value:" msgstr "Standardwert:" #: customfieldadmin/templates/customfieldadmin.html:110 msgid "Format:" msgstr "Format:" #: customfieldadmin/templates/customfieldadmin.html:119 msgid "Options:" msgstr "Zusatzangaben:" #: customfieldadmin/templates/customfieldadmin.html:127 msgid "Size of Textarea:" msgstr "Grösse des Textfeldes:" #: customfieldadmin/templates/customfieldadmin.html:127 msgid "Cols:" msgstr "Spalten:" #: customfieldadmin/templates/customfieldadmin.html:133 msgid "Add" msgstr "Hinzufügen" #: customfieldadmin/templates/customfieldadmin.html:139 msgid "No Custom Fields defined for this project." msgstr "Für dieses Projekt wurden keine individuellen Felder definiert." #: customfieldadmin/templates/customfieldadmin.html:147 msgid "Name" msgstr "Name" #: customfieldadmin/templates/customfieldadmin.html:148 msgid "Type" msgstr "Typ" #: customfieldadmin/templates/customfieldadmin.html:149 msgid "Label" msgstr "Bezeichnung" #: customfieldadmin/templates/customfieldadmin.html:150 msgid "Order" msgstr "Reihenfolge" #: customfieldadmin/templates/customfieldadmin.html:157 msgid "Field cannot be deleted (declared in source code)" msgstr "Feld kann nicht entfernt werden (Quellcode-Vorgabe)" #: customfieldadmin/templates/customfieldadmin.html:171 msgid "Currently outside regular range" msgstr "Momentan ausserhalb des üblichen Bereichs" #: customfieldadmin/templates/customfieldadmin.html:180 msgid "Remove selected items" msgstr "Ausgewählte Einträge entfernen" #: customfieldadmin/templates/customfieldadmin.html:182 msgid "Apply changes" msgstr "Änderungen übernehmen" customfieldadminplugin/0.11/customfieldadmin/locale/ja/0000755000175000017500000000000011732327420021520 5ustar wmbwmbcustomfieldadminplugin/0.11/customfieldadmin/locale/ja/LC_MESSAGES/0000755000175000017500000000000011732327420023305 5ustar wmbwmbcustomfieldadminplugin/0.11/customfieldadmin/locale/ja/LC_MESSAGES/customfieldadmin.po0000644000175000017500000001522711732327420027203 0ustar wmbwmb# Japanese translations for TracCustomFieldAdmin. # Copyright (C) 2012 CodeResort.com (BV Network AS) # This file is distributed under the same license as the # TracCustomFieldAdmin project. # Jun Omae , 2012. # msgid "" msgstr "" "Project-Id-Version: TracCustomFieldAdmin 0.2.8\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2012-01-24 13:47+0100\n" "PO-Revision-Date: 2012-03-21 16:53+0900\n" "Last-Translator: Jun Omae \n" "Language-Team: ja \n" "Plural-Forms: nplurals=1; plural=0\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.6\n" #: customfieldadmin/admin.py:33 msgid "Ticket System" msgstr "チケットシステム" #: customfieldadmin/admin.py:34 msgid "Custom Fields" msgstr "カスタムフィールド" #: customfieldadmin/admin.py:65 #, python-format msgid "Custom field %(name)s does not exist." msgstr "カスタムフィールド %(name)s は存在しません。" #: customfieldadmin/admin.py:94 msgid "No custom field selected" msgstr "カスタムフィールドが選択されていません" #: customfieldadmin/admin.py:123 msgid "" "Custom Fields are not correctly sorted. This may affect appearance when " "viewing tickets." msgstr "" "カスタムフィールドの順序が正しくないため、チケット参照時の見た目に影響があ" "る可能性があります。" #: customfieldadmin/api.py:87 msgid "Custom field requires attributes 'name' and 'type'." msgstr "カスタムフィールドには'名前'と'タイプ'の属性が必要になります。" #: customfieldadmin/api.py:92 msgid "" "Only alphanumeric characters allowed for custom field name ('a-z' or " "'0-9' or '_'), with 'a-z' as first character." msgstr "カスタムフィールド名には、英数字のみ ('a-z', '0-9', '_')、最初の文字には 'a-z' が使えます。" #: customfieldadmin/api.py:98 msgid "Custom field name must begin with a character (a-z)." msgstr "カスタムフィールド名は a から z の文字で始めなけれはなりません。" #: customfieldadmin/api.py:102 #, python-format msgid "%(field_type)s is not a valid field type" msgstr "%(field_type)s は正しいタイプではありません" #: customfieldadmin/api.py:107 msgid "Can not create as field already exists." msgstr "既に存在する項目は作成出来ません。" #: customfieldadmin/api.py:157 #, python-format msgid "Custom Field '%(name)s' does not exist. Cannot update." msgstr "カスタムフィールド '%(name)s' が存在しないため、更新できません。" #: customfieldadmin/templates/customfieldadmin.html:11 msgid "Custom Fields Admin" msgstr "カスタムフィールド管理" #: customfieldadmin/templates/customfieldadmin.html:16 msgid "Manage Custom Fields" msgstr "カスタムフィールドの管理" #: customfieldadmin/templates/customfieldadmin.html:21 msgid "Modify Custom Field:" msgstr "カスタムフィールドの変更:" #: customfieldadmin/templates/customfieldadmin.html:23 msgid "Name (cannot modify):" msgstr "名前 (変更出来ません):" #: customfieldadmin/templates/customfieldadmin.html:28 #: customfieldadmin/templates/customfieldadmin.html:91 msgid "Type:" msgstr "タイプ:" #: customfieldadmin/templates/customfieldadmin.html:39 #: customfieldadmin/templates/customfieldadmin.html:100 msgid "Label:" msgstr "ラベル:" #: customfieldadmin/templates/customfieldadmin.html:44 msgid "" "Default value\n" " (regular text for Text, Textarea, Radio or Select):" msgstr "" "デフォルト値 (テキスト、テキストエリア、ラジオボタン、ドロップダウンに対す" "る正規の文字列):" #: customfieldadmin/templates/customfieldadmin.html:50 msgid "Format (Text or Textarea):" msgstr "書式 (テキスト、テキストエリア):" #: customfieldadmin/templates/customfieldadmin.html:60 msgid "" "Options for Radio or Select\n" " (for Select, empty first line makes field optional):" msgstr "" "ラジオボタン、ドロップダウンで選択させる値 " "(ドロップダウンの場合、最初の行を空にすると任意の選択になります):" #: customfieldadmin/templates/customfieldadmin.html:71 msgid "Size of Textarea for entry (Textarea only):" msgstr "テキストエリアのサイズ (テキストエリアのみ):" #: customfieldadmin/templates/customfieldadmin.html:71 msgid "Columns:" msgstr "幅:" #: customfieldadmin/templates/customfieldadmin.html:72 #: customfieldadmin/templates/customfieldadmin.html:128 msgid "Rows:" msgstr "行数:" #: customfieldadmin/templates/customfieldadmin.html:76 msgid "Cancel" msgstr "取り消し" #: customfieldadmin/templates/customfieldadmin.html:77 msgid "Save" msgstr "保存" #: customfieldadmin/templates/customfieldadmin.html:84 msgid "Add Custom Field:" msgstr "カスタムフィールドの追加:" #: customfieldadmin/templates/customfieldadmin.html:86 msgid "Name:" msgstr "名称:" #: customfieldadmin/templates/customfieldadmin.html:105 msgid "Default value:" msgstr "デフォルト値:" #: customfieldadmin/templates/customfieldadmin.html:110 msgid "Format:" msgstr "書式:" #: customfieldadmin/templates/customfieldadmin.html:119 msgid "Options:" msgstr "選択させる値:" #: customfieldadmin/templates/customfieldadmin.html:127 msgid "Size of Textarea:" msgstr "テキストエリアのサイズ:" #: customfieldadmin/templates/customfieldadmin.html:127 msgid "Cols:" msgstr "幅:" #: customfieldadmin/templates/customfieldadmin.html:133 msgid "Add" msgstr "追加" #: customfieldadmin/templates/customfieldadmin.html:139 msgid "No Custom Fields defined for this project." msgstr "このプロジェクトにはカスタムフィールドは設定されていません。" #: customfieldadmin/templates/customfieldadmin.html:147 msgid "Name" msgstr "名称" #: customfieldadmin/templates/customfieldadmin.html:148 msgid "Type" msgstr "タイプ" #: customfieldadmin/templates/customfieldadmin.html:149 msgid "Label" msgstr "ラベル" #: customfieldadmin/templates/customfieldadmin.html:150 msgid "Order" msgstr "順序" #: customfieldadmin/templates/customfieldadmin.html:157 msgid "Field cannot be deleted (declared in source code)" msgstr "削除出来ません (ソースコードにて定義されている)" #: customfieldadmin/templates/customfieldadmin.html:171 msgid "Currently outside regular range" msgstr "現在、正しい範囲から外れています" #: customfieldadmin/templates/customfieldadmin.html:180 msgid "Remove selected items" msgstr "選択した項目を削除" #: customfieldadmin/templates/customfieldadmin.html:182 msgid "Apply changes" msgstr "変更を適用" customfieldadminplugin/0.11/customfieldadmin/locale/customfieldadmin.pot0000644000175000017500000001152211707531277025213 0ustar wmbwmb# Translations template for TracCustomFieldAdmin. # Copyright (C) 2012 CodeResort.com (BV Network AS) # This file is distributed under the same license as the # TracCustomFieldAdmin project. # FIRST AUTHOR , 2012. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: TracCustomFieldAdmin 0.2.8\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2012-01-24 13:47+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.6\n" #: customfieldadmin/admin.py:33 msgid "Ticket System" msgstr "" #: customfieldadmin/admin.py:34 msgid "Custom Fields" msgstr "" #: customfieldadmin/admin.py:65 #, python-format msgid "Custom field %(name)s does not exist." msgstr "" #: customfieldadmin/admin.py:94 msgid "No custom field selected" msgstr "" #: customfieldadmin/admin.py:123 msgid "" "Custom Fields are not correctly sorted. This may affect appearance " "when viewing tickets." msgstr "" #: customfieldadmin/api.py:87 msgid "Custom field requires attributes 'name' and 'type'." msgstr "" #: customfieldadmin/api.py:92 msgid "" "Only alphanumeric characters allowed for custom field name ('a-z' or " "'0-9' or '_'), with 'a-z' as first character." msgstr "" #: customfieldadmin/api.py:98 msgid "Custom field name must begin with a character (a-z)." msgstr "" #: customfieldadmin/api.py:102 #, python-format msgid "%(field_type)s is not a valid field type" msgstr "" #: customfieldadmin/api.py:107 msgid "Can not create as field already exists." msgstr "" #: customfieldadmin/api.py:157 #, python-format msgid "Custom Field '%(name)s' does not exist. Cannot update." msgstr "" #: customfieldadmin/templates/customfieldadmin.html:11 msgid "Custom Fields Admin" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:16 msgid "Manage Custom Fields" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:21 msgid "Modify Custom Field:" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:23 msgid "Name (cannot modify):" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:28 #: customfieldadmin/templates/customfieldadmin.html:91 msgid "Type:" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:39 #: customfieldadmin/templates/customfieldadmin.html:100 msgid "Label:" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:44 msgid "" "Default value\n" " (regular text for Text, Textarea, Radio or Select):" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:50 msgid "Format (Text or Textarea):" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:60 msgid "" "Options for Radio or Select\n" " (for Select, empty first line makes field optional):" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:71 msgid "Size of Textarea for entry (Textarea only):" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:71 msgid "Columns:" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:72 #: customfieldadmin/templates/customfieldadmin.html:128 msgid "Rows:" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:76 msgid "Cancel" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:77 msgid "Save" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:84 msgid "Add Custom Field:" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:86 msgid "Name:" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:105 msgid "Default value:" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:110 msgid "Format:" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:119 msgid "Options:" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:127 msgid "Size of Textarea:" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:127 msgid "Cols:" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:133 msgid "Add" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:139 msgid "No Custom Fields defined for this project." msgstr "" #: customfieldadmin/templates/customfieldadmin.html:147 msgid "Name" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:148 msgid "Type" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:149 msgid "Label" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:150 msgid "Order" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:157 msgid "Field cannot be deleted (declared in source code)" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:171 msgid "Currently outside regular range" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:180 msgid "Remove selected items" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:182 msgid "Apply changes" msgstr "" customfieldadminplugin/0.11/customfieldadmin/locale/ru/0000755000175000017500000000000011720262720021552 5ustar wmbwmbcustomfieldadminplugin/0.11/customfieldadmin/locale/ru/LC_MESSAGES/0000755000175000017500000000000011720262720023337 5ustar wmbwmbcustomfieldadminplugin/0.11/customfieldadmin/locale/ru/LC_MESSAGES/customfieldadmin.po0000644000175000017500000001560311707531277027246 0ustar wmbwmb# Russian translations for TracCustomFieldAdmin. # Copyright (C) 2010 ORGANIZATION # This file is distributed under the same license as the # TracCustomFieldAdmin project. # Dmitri Bogomolov <4glitch@gmail.com>, 2010. # msgid "" msgstr "" "Project-Id-Version: TracCustomFieldAdmin 0.2.5\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2012-01-24 13:47+0100\n" "PO-Revision-Date: 2010-10-04 01:07+0300\n" "Last-Translator: Dmitri Bogomolov <4glitch@gmail.com>\n" "Language-Team: ru \n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.6\n" #: customfieldadmin/admin.py:33 msgid "Ticket System" msgstr "Билеты" #: customfieldadmin/admin.py:34 msgid "Custom Fields" msgstr "Дополнительные поля" #: customfieldadmin/admin.py:65 #, python-format msgid "Custom field %(name)s does not exist." msgstr "Дополнительное поле %(name)s не существует." #: customfieldadmin/admin.py:94 msgid "No custom field selected" msgstr "Не выбрано ни одного поля" #: customfieldadmin/admin.py:123 msgid "" "Custom Fields are not correctly sorted. This may affect appearance when " "viewing tickets." msgstr "" #: customfieldadmin/api.py:87 msgid "Custom field requires attributes 'name' and 'type'." msgstr "" #: customfieldadmin/api.py:92 #, fuzzy msgid "" "Only alphanumeric characters allowed for custom field name ('a-z' or " "'0-9' or '_'), with 'a-z' as first character." msgstr "" "В имени дополнительного поля допустимы только алфавитно-цифровые символы " "(a-z, 0-9 и -_)." #: customfieldadmin/api.py:98 msgid "Custom field name must begin with a character (a-z)." msgstr "Имя дополнительного поля должно начинаться с буквы (a-z." #: customfieldadmin/api.py:102 #, python-format msgid "%(field_type)s is not a valid field type" msgstr "%(field_type)s недопустимый тип поля" #: customfieldadmin/api.py:107 msgid "Can not create as field already exists." msgstr "Не удалось создать, поскольку поле уже существует." #: customfieldadmin/api.py:157 #, fuzzy, python-format msgid "Custom Field '%(name)s' does not exist. Cannot update." msgstr "Дополнительное поле %(name)s не существует." #: customfieldadmin/templates/customfieldadmin.html:11 msgid "Custom Fields Admin" msgstr "Дополнительные поля" #: customfieldadmin/templates/customfieldadmin.html:16 msgid "Manage Custom Fields" msgstr "Управление дополнительными полями" #: customfieldadmin/templates/customfieldadmin.html:21 msgid "Modify Custom Field:" msgstr "Изменить дополнительное поле:" #: customfieldadmin/templates/customfieldadmin.html:23 msgid "Name (cannot modify):" msgstr "Имя (неизменяемое):" #: customfieldadmin/templates/customfieldadmin.html:28 #: customfieldadmin/templates/customfieldadmin.html:91 msgid "Type:" msgstr "Тип:" #: customfieldadmin/templates/customfieldadmin.html:39 #: customfieldadmin/templates/customfieldadmin.html:100 msgid "Label:" msgstr "Описание:" #: customfieldadmin/templates/customfieldadmin.html:44 #, fuzzy msgid "" "Default value\n" " (regular text for Text, Textarea, Radio or Select):" msgstr "По умолчанию (обычный текст для Text, Textarea, Radio или Select):" #: customfieldadmin/templates/customfieldadmin.html:50 msgid "Format (Text or Textarea):" msgstr "Формат (Text или Textarea):" #: customfieldadmin/templates/customfieldadmin.html:60 #, fuzzy msgid "" "Options for Radio or Select\n" " (for Select, empty first line makes field optional):" msgstr "" "Варианты для Radio или Select (для Select пустой первый вариант делает " "поле необязательным):" #: customfieldadmin/templates/customfieldadmin.html:71 msgid "Size of Textarea for entry (Textarea only):" msgstr "Размер поля ввода (только для Textarea):" #: customfieldadmin/templates/customfieldadmin.html:71 msgid "Columns:" msgstr "Столбцы:" #: customfieldadmin/templates/customfieldadmin.html:72 #: customfieldadmin/templates/customfieldadmin.html:128 msgid "Rows:" msgstr "Строки:" #: customfieldadmin/templates/customfieldadmin.html:76 msgid "Cancel" msgstr "Отмена" #: customfieldadmin/templates/customfieldadmin.html:77 msgid "Save" msgstr "Сохранить" #: customfieldadmin/templates/customfieldadmin.html:84 msgid "Add Custom Field:" msgstr "Добавить дополнительное поле:" #: customfieldadmin/templates/customfieldadmin.html:86 msgid "Name:" msgstr "Имя:" #: customfieldadmin/templates/customfieldadmin.html:105 msgid "Default value:" msgstr "Значение по умолчанию:" #: customfieldadmin/templates/customfieldadmin.html:110 msgid "Format:" msgstr "Формат:" #: customfieldadmin/templates/customfieldadmin.html:119 msgid "Options:" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:127 msgid "Size of Textarea:" msgstr "Размер текстового поля:" #: customfieldadmin/templates/customfieldadmin.html:127 msgid "Cols:" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:133 msgid "Add" msgstr "Добавить" #: customfieldadmin/templates/customfieldadmin.html:139 msgid "No Custom Fields defined for this project." msgstr "В этом проекте не объявлено ни одного дополнительного поля." #: customfieldadmin/templates/customfieldadmin.html:147 msgid "Name" msgstr "Имя" #: customfieldadmin/templates/customfieldadmin.html:148 msgid "Type" msgstr "Тип" #: customfieldadmin/templates/customfieldadmin.html:149 msgid "Label" msgstr "Описание" #: customfieldadmin/templates/customfieldadmin.html:150 msgid "Order" msgstr "Порядок" #: customfieldadmin/templates/customfieldadmin.html:157 msgid "Field cannot be deleted (declared in source code)" msgstr "Поле не может быть удалено (объявлено в исходнике)" #: customfieldadmin/templates/customfieldadmin.html:171 msgid "Currently outside regular range" msgstr "" #: customfieldadmin/templates/customfieldadmin.html:180 msgid "Remove selected items" msgstr "Удалить выбранные" #: customfieldadmin/templates/customfieldadmin.html:182 msgid "Apply changes" msgstr "Применить изменения" #~ msgid "Custom field needs at least a name, type and label." #~ msgstr "Для создания дополнительного поля требуются имя, тип и описание." customfieldadminplugin/0.11/customfieldadmin/locale/nb/0000755000175000017500000000000011720262720021523 5ustar wmbwmbcustomfieldadminplugin/0.11/customfieldadmin/locale/nb/LC_MESSAGES/0000755000175000017500000000000011720262720023310 5ustar wmbwmbcustomfieldadminplugin/0.11/customfieldadmin/locale/nb/LC_MESSAGES/customfieldadmin.po0000644000175000017500000001417211707531277027217 0ustar wmbwmb# Norwegian Bokmål translations for TracCustomFieldAdmin. # Copyright (C) 2012 CodeResort.com (BV Network AS) # This file is distributed under the same license as the # TracCustomFieldAdmin project. # Odd Simon Simonsen , 2012. # msgid "" msgstr "" "Project-Id-Version: TracCustomFieldAdmin 0.2.8\n" "Report-Msgid-Bugs-To: simon-code@bvnetwork.no\n" "POT-Creation-Date: 2012-01-24 13:47+0100\n" "PO-Revision-Date: 2012-01-19 11:51+0100\n" "Last-Translator: Odd Simon Simonsen \n" "Language-Team: nb \n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.6\n" #: customfieldadmin/admin.py:33 msgid "Ticket System" msgstr "Sakssystem" #: customfieldadmin/admin.py:34 msgid "Custom Fields" msgstr "Egendefinerte felt" #: customfieldadmin/admin.py:65 #, python-format msgid "Custom field %(name)s does not exist." msgstr "Egendefinert felt %(name)s eksisterer ikke." #: customfieldadmin/admin.py:94 msgid "No custom field selected" msgstr "Egendefinert felt er ikke valgt" #: customfieldadmin/admin.py:123 msgid "" "Custom Fields are not correctly sorted. This may affect appearance when " "viewing tickets." msgstr "" "Egendefinerte felt er ikke riktig sortert. Dette kan påvirke " "hvordan tickets vises." #: customfieldadmin/api.py:87 msgid "Custom field requires attributes 'name' and 'type'." msgstr "Egendefinerte felt må ha 'name' (navn) og 'type'." #: customfieldadmin/api.py:92 msgid "" "Only alphanumeric characters allowed for custom field name ('a-z' or " "'0-9' or '_'), with 'a-z' as first character." msgstr "" "Kun ASCII alfa-numeriske tegner er tillat for Egendefinerte \"\n" "\"felt ('a-z' og '0-9' og '_'), og med 'a-z' som første tegn." #: customfieldadmin/api.py:98 msgid "Custom field name must begin with a character (a-z)." msgstr "Egendefinert navn må begynne med et ASCII tegn ('a-z')." #: customfieldadmin/api.py:102 #, python-format msgid "%(field_type)s is not a valid field type" msgstr "%(field_type)s er ikke en gyldig felt type" #: customfieldadmin/api.py:107 msgid "Can not create as field already exists." msgstr "Feltet eksisterer. Kan ikke opprette." #: customfieldadmin/api.py:157 #, python-format msgid "Custom Field '%(name)s' does not exist. Cannot update." msgstr "Egendefinert felt %(name)s eksisterer ikke. Kan ikke oppdatere." #: customfieldadmin/templates/customfieldadmin.html:11 msgid "Custom Fields Admin" msgstr "Brukerfefinert felt administrasjon" #: customfieldadmin/templates/customfieldadmin.html:16 msgid "Manage Custom Fields" msgstr "Administrere Egendefinerte felt" #: customfieldadmin/templates/customfieldadmin.html:21 msgid "Modify Custom Field:" msgstr "Endre Egendefinert felt" #: customfieldadmin/templates/customfieldadmin.html:23 msgid "Name (cannot modify):" msgstr "Navn (kan ikke endres):" #: customfieldadmin/templates/customfieldadmin.html:28 #: customfieldadmin/templates/customfieldadmin.html:91 msgid "Type:" msgstr "Type:" #: customfieldadmin/templates/customfieldadmin.html:39 #: customfieldadmin/templates/customfieldadmin.html:100 msgid "Label:" msgstr "Etikett:" #: customfieldadmin/templates/customfieldadmin.html:44 msgid "" "Default value\n" " (regular text for Text, Textarea, Radio or Select):" msgstr "Standardverdi (vanlig tekst for Text, Textarea, Radio og Select):" #: customfieldadmin/templates/customfieldadmin.html:50 msgid "Format (Text or Textarea):" msgstr "Format (Text og Textarea):" #: customfieldadmin/templates/customfieldadmin.html:60 msgid "" "Options for Radio or Select\n" " (for Select, empty first line makes field optional):" msgstr "" "Alternativer for Radio og Select (for Select betyr tom \"\n" "\"førsterad at feltet er valgfritt):" #: customfieldadmin/templates/customfieldadmin.html:71 msgid "Size of Textarea for entry (Textarea only):" msgstr "Størrelse for Textarea felt:" #: customfieldadmin/templates/customfieldadmin.html:71 msgid "Columns:" msgstr "Kolonner:" #: customfieldadmin/templates/customfieldadmin.html:72 #: customfieldadmin/templates/customfieldadmin.html:128 msgid "Rows:" msgstr "Rader:" #: customfieldadmin/templates/customfieldadmin.html:76 msgid "Cancel" msgstr "Avbryt" #: customfieldadmin/templates/customfieldadmin.html:77 msgid "Save" msgstr "Lagre" #: customfieldadmin/templates/customfieldadmin.html:84 msgid "Add Custom Field:" msgstr "Legg til Egendefinert felt" #: customfieldadmin/templates/customfieldadmin.html:86 msgid "Name:" msgstr "Navn:" #: customfieldadmin/templates/customfieldadmin.html:105 msgid "Default value:" msgstr "Standardverdi:" #: customfieldadmin/templates/customfieldadmin.html:110 msgid "Format:" msgstr "Format:" #: customfieldadmin/templates/customfieldadmin.html:119 msgid "Options:" msgstr "Alternativer:" #: customfieldadmin/templates/customfieldadmin.html:127 msgid "Size of Textarea:" msgstr "Størrelse for Textarea felt:" #: customfieldadmin/templates/customfieldadmin.html:127 msgid "Cols:" msgstr "Kolonner:" #: customfieldadmin/templates/customfieldadmin.html:133 msgid "Add" msgstr "Legg til" #: customfieldadmin/templates/customfieldadmin.html:139 msgid "No Custom Fields defined for this project." msgstr "Ingen Egendefinerte felt definert for dette prosjektet." #: customfieldadmin/templates/customfieldadmin.html:147 msgid "Name" msgstr "Navn" #: customfieldadmin/templates/customfieldadmin.html:148 msgid "Type" msgstr "Type" #: customfieldadmin/templates/customfieldadmin.html:149 msgid "Label" msgstr "Etikett" #: customfieldadmin/templates/customfieldadmin.html:150 msgid "Order" msgstr "Sortering" #: customfieldadmin/templates/customfieldadmin.html:157 msgid "Field cannot be deleted (declared in source code)" msgstr "Felt kan ikke slettes (definert i kode)" #: customfieldadmin/templates/customfieldadmin.html:171 msgid "Currently outside regular range" msgstr "Utenfor gjeldende verdier" #: customfieldadmin/templates/customfieldadmin.html:180 msgid "Remove selected items" msgstr "Fjern Egendefinert felt" #: customfieldadmin/templates/customfieldadmin.html:182 msgid "Apply changes" msgstr "Lagre endringer" customfieldadminplugin/0.11/customfieldadmin/tests/0000755000175000017500000000000012520456562021037 5ustar wmbwmbcustomfieldadminplugin/0.11/customfieldadmin/tests/__init__.py0000644000175000017500000000076111707117355023154 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() from customfieldadmin.tests import api suite.addTest(makeSuite(api.CustomFieldApiTestCase)) suite.addTest(makeSuite(api.CustomFieldL10NTestCase)) from customfieldadmin.tests import admin suite.addTest(makeSuite(admin.CustomFieldAdminPageTestCase)) return suite customfieldadminplugin/0.11/customfieldadmin/tests/admin.py0000644000175000017500000001446512520456562022513 0ustar wmbwmb# -*- coding: utf-8 -*- """ License: BSD (c) 2005-2012 ::: 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.web.api import RequestDone from trac.web.href import Href from customfieldadmin.admin import CustomFieldAdminPage from customfieldadmin.api import CustomFields 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) self.api = CustomFields(self.env) def tearDown(self): if hasattr(self.env, 'destroy_db'): self.env.destroy_db() del self.env def test_create(self): _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': "textarea", 'label': "testing", 'format': "wiki", 'row': '9', 'columns': '42'}) 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'textarea'), (u'test.cols', u'60'), (u'test.format', u'wiki'), (u'test.label', u'testing'), (u'test.options', u''), (u'test.order', u'1'), (u'test.rows', u'5'), (u'test.value', u'')]) 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'')]) def test_edit_optional_select(self): self.test_add_optional_select() self.assertEquals('select', self.env.config.get('ticket-custom', 'test')) _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={'save': True, 'name': u'test', 'label': u'testing', 'type': u'select', 'value': u'', 'options': u'\r\none\r\ntwo'}) try: self.plugin.render_admin_panel(req, 'ticket', 'customfields', 'test') 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'')]) def test_order_with_mismatched_keys(self): # http://trac-hacks.org/ticket/11540 self.api.create_custom_field({'name': u'one', 'format': 'plain', 'value': '', 'label': u'One', 'type': u'text', 'order': 1}) def redirect(url): raise RequestDone req = Mock(perm=PermissionCache(self.env, 'admin'), authname='admin', chrome={}, href=Href('/'), redirect=redirect, method='POST', args={'apply': True, 'order_two': '1'}) try: self.plugin.render_admin_panel(req, 'ticket', 'customfields', None) except RequestDone, e: pass customfieldadminplugin/0.11/customfieldadmin/tests/api.py0000644000175000017500000001611112307171772022162 0ustar wmbwmb# -*- coding: utf-8 -*- """ License: BSD (c) 2005-2012 ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no) """ import unittest from trac.core import TracError from trac.test import EnvironmentStub, Mock from customfieldadmin.api import CustomFields class CustomFieldApiTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.cf_api = CustomFields(self.env) def tearDown(self): if hasattr(self.env, 'destroy_db'): self.env.destroy_db() del self.env def test_systeminfo(self): try: from trac.loader import get_plugin_info # From ~0.12, Trac handles plugins and versions - no need to test return except ImportError: self.assertTrue(('CustomFieldAdmin', __import__('customfieldadmin', ['__version__']).__version__) \ in self.env.systeminfo) def test_create(self): for f in ['one', 'two', 'three']: cfield = {'name': f, 'type': 'text'} self.cf_api.create_custom_field(cfield) self.assertEquals(self.cf_api.get_custom_fields(), [{'name': u'one', 'format': 'plain', 'value': '', 'label': u'One', 'type': u'text', 'order': 1}, {'name': u'two', 'format': 'plain', 'value': '', 'label': u'Two', 'type': u'text', 'order': 2}, {'name': u'three', 'format': 'plain', 'value': '', 'label': u'Three', 'type': u'text', 'order': 3}]) def test_update(self): cfield = {'name': 'foo', 'type': 'text'} self.cf_api.create_custom_field(cfield) self.assertEquals(cfield, {'name': u'foo', 'format': 'plain', 'value': '', 'label': u'Foo', 'type': u'text', 'order': 1}) cfield['label'] = 'Answer' cfield['value'] = '42' self.cf_api.update_custom_field(cfield=cfield) self.assertEquals(cfield, {'name': u'foo', 'format': 'plain', 'value': '42', 'label': u'Answer', 'type': u'text', 'order': 1}) def test_update_textarea(self): cfield = {'name': 'foo', 'type': 'textarea'} self.cf_api.create_custom_field(cfield) self.assertEquals(cfield, {'name': u'foo', 'format': 'plain', 'value': '', 'label': u'Foo', 'type': u'textarea', 'order': 1, 'cols': 60, 'rows': 5}) cfield['cols'] = 42 cfield['rows'] = 3 self.cf_api.update_custom_field(cfield=cfield) self.assertEquals(cfield, {'name': u'foo', 'format': 'plain', 'value': '', 'label': u'Foo', 'type': u'textarea', 'order': 1, 'cols': 42, 'rows': 3}) def test_update_non_existing(self): try: self.cf_api.update_custom_field(cfield={'name': 'no_field'}) self.fail("Huh. Missing exception!") except Exception, e: self.assertTrue("'no_field'" in e.message) self.assertTrue('does not exist' in e.message) def test_update_non_existing_no_name(self): try: self.cf_api.update_custom_field(cfield={}) self.fail("Huh. Missing exception!") except Exception, e: self.assertTrue("'(none)'" in e.message) self.assertTrue('does not exist' in e.message) def test_delete(self): for f in ['one', 'two', 'three']: cfield = {'name': f, 'type': 'text'} self.cf_api.create_custom_field(cfield) self.assertEquals(True, ('two', 'text') in self.env.config.options('ticket-custom')) self.cf_api.delete_custom_field({'name': 'two'}) self.assertEquals(False, ('two', 'text') in self.env.config.options('ticket-custom')) #import ipdb; ipdb.set_trace() # Note: Should also reorder higher-ordered items self.assertEquals(self.cf_api.get_custom_fields(), [{'name': u'one', 'format': 'plain', 'value': '', 'label': u'One', 'type': u'text', 'order': 1}, {'name': u'three', 'format': 'plain', 'value': '', 'label': u'Three', 'type': u'text', 'order': 2}]) self.assertEquals(None, self.cf_api.get_custom_fields(cfield={'name': 'two'})) def test_delete_unknown_options(self): cf = {'name': 'foo', 'type': 'text', 'label': 'Foo'} self.cf_api.create_custom_field(cf) self.assertEquals('text', self.env.config.get('ticket-custom', 'foo')) self.assertEquals('Foo', self.env.config.get('ticket-custom', 'foo.label')) self.env.config.set('ticket-custom', 'foo.answer', '42') self.cf_api.delete_custom_field(cf, modify=False) self.assertEquals('', self.env.config.get('ticket-custom', 'foo')) self.assertEquals('', self.env.config.get('ticket-custom', 'foo.label')) self.assertEquals('', self.env.config.get('ticket-custom', 'foo.answer')) def test_not_delete_unknown_options_for_modify(self): cf = {'name': 'foo', 'type': 'text', 'label': 'Foo'} self.cf_api.create_custom_field(cf) self.assertEquals('text', self.env.config.get('ticket-custom', 'foo')) self.assertEquals('Foo', self.env.config.get('ticket-custom', 'foo.label')) self.env.config.set('ticket-custom', 'foo.answer', '42') self.cf_api.delete_custom_field(cf, modify=True) self.assertEquals('', self.env.config.get('ticket-custom', 'foo')) self.assertEquals('', self.env.config.get('ticket-custom', 'foo.label')) self.assertEquals('42', self.env.config.get('ticket-custom', 'foo.answer')) def test_verify_unknown_type(self): self.env.config.set('ticket-custom', 'one', 'foo_type') fields = self.cf_api.get_custom_fields() self.assertEquals(1, len(fields)) try: self.cf_api.verify_custom_field(fields[0], create=False) except TracError, e: self.assertTrue("foo_type" in e.message) class CustomFieldL10NTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.cf_api = CustomFields(self.env) def tearDown(self): if hasattr(self.env, 'destroy_db'): self.env.destroy_db() del self.env def test_translation_function(self): from customfieldadmin.api import _ self.assertEquals('foo bar', _("foo bar")) self.assertEquals('foo bar', _("foo %(bar)s", bar='bar')) def test_translation_function_tag(self): from customfieldadmin.api import tag_ from genshi.builder import tag self.assertEquals('

foo bar

', str(tag_(tag.p('foo bar')))) self.assertEquals('

foo bar

', str(tag_(tag.p('foo %(bar)s' % {'bar': 'bar'})))) customfieldadminplugin/0.11/customfieldadmin/admin.py0000644000175000017500000001347612520452071021341 0ustar wmbwmb# -*- coding: utf-8 -*- """ Trac WebAdmin plugin for administration of custom fields. License: BSD (c) 2005-2012 ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no) (c) 2007-2009 ::: www.Optaros.com (.....) """ from pkg_resources import resource_filename from trac.config import Option from trac.core import * from trac.web.chrome import ITemplateProvider, add_script, add_warning from trac.admin.api import IAdminPanelProvider from customfieldadmin.api import CustomFields, _ class CustomFieldAdminPage(Component): implements(ITemplateProvider, IAdminPanelProvider) def __init__(self): # Init CustomFields so translations work from first request # FIXME: It actually only works from SECOND request - Trac bug?! CustomFields(self.env) # IAdminPanelProvider methods def get_admin_panels(self, req): if 'TICKET_ADMIN' in req.perm('admin', 'ticket/customfields'): yield ('ticket', _("Ticket System"), 'customfields', _("Custom Fields")) def render_admin_panel(self, req, cat, page, customfield): req.perm('admin', 'ticket/customfields').require('TICKET_ADMIN') add_script(req, 'customfieldadmin/js/customfieldadmin.js') def _customfield_from_req(self, req): cfield = {'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 cfield cf_api = CustomFields(self.env) cf_admin = {} # Return values for template rendering # Detail view? if customfield: cfield = None for a_cfield in cf_api.get_custom_fields(): if a_cfield['name'] == customfield: cfield = a_cfield break if not cfield: raise TracError(_("Custom field %(name)s does not exist.", name=customfield)) if req.method == 'POST': if req.args.get('save'): cfield.update(_customfield_from_req(self, req)) cf_api.update_custom_field(cfield) req.redirect(req.href.admin(cat, page)) elif req.args.get('cancel'): req.redirect(req.href.admin(cat, page)) if cfield.has_key('options'): optional_line = '' if cfield.get('optional', False): optional_line = "\n\n" cfield['options'] = optional_line + "\n".join(cfield['options']) cf_admin['cfield'] = cfield cf_admin['cf_display'] = 'detail' else: if req.method == 'POST': # Add Custom Field if req.args.get('add') and req.args.get('name'): cfield = _customfield_from_req(self, req) cf_api.update_custom_field(cfield, 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: cfield = {'name': name} cf_api.delete_custom_field(cfield) 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_')]) cfields = cf_api.get_custom_fields() for current_cfield in cfields: new_order = order.get(current_cfield['name'], 0) if new_order: current_cfield['order'] = new_order cf_api.update_custom_field(current_cfield) req.redirect(req.href.admin(cat, page)) cfields = [] orders_in_use = [] for item in cf_api.get_custom_fields(): item['href'] = req.href.admin(cat, page, item['name']) item['registry'] = ('ticket-custom', item['name']) in Option.registry cfields.append(item) orders_in_use.append(int(item.get('order'))) cf_admin['cfields'] = cfields cf_admin['cf_display'] = 'list' if sorted(orders_in_use) != range(1, len(cfields)+1): add_warning(req, _("Custom Fields are not correctly sorted. " \ "This may affect appearance when viewing tickets.")) return ('customfieldadmin.html', cf_admin) # ITemplateProvider methods def get_templates_dirs(self): return [resource_filename(__name__, 'templates')] def get_htdocs_dirs(self): return [('customfieldadmin', resource_filename(__name__, 'htdocs'))] customfieldadminplugin/0.11/customfieldadmin/htdocs/0000755000175000017500000000000011145020546021150 5ustar wmbwmbcustomfieldadminplugin/0.11/customfieldadmin/htdocs/js/0000755000175000017500000000000011145020546021564 5ustar wmbwmbcustomfieldadminplugin/0.11/customfieldadmin/htdocs/js/customfieldadmin.js0000755000175000017500000000243711145020546025462 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); customfieldadminplugin/0.11/customfieldadmin/htdocs/css/0000755000175000017500000000000010624306774021753 5ustar wmbwmbcustomfieldadminplugin/0.11/customfieldadmin/api.py0000644000175000017500000002377012521736164021031 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-2012 ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no) """ import re from pkg_resources import resource_filename from trac.core import * from trac.ticket.api import TicketSystem try: from trac.util.translation import domain_functions add_domain, _, tag_ = \ domain_functions('customfieldadmin', ('add_domain', '_', 'tag_')) except: # fall back to 0.11 behavior, i18n functions are no-ops then from genshi.builder import tag as tag_ from trac.util.translation import gettext as _ def add_domain(*args, **kwargs): pass __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 'cfield' dict supporting these keys: name = name of field (ascii 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() config_options = ['label', 'value', 'options', 'cols', 'rows', 'order', 'format'] def __init__(self): # bind the 'customfieldadmin' catalog to the specified locale directory try: locale_dir = resource_filename(__name__, 'locale') add_domain(self.env.path, locale_dir) except KeyError: # No 'locale', hence no compiled message catalogs. Ignore. pass # TODO: Remove systeminfo compat code when only supporting Trac>=0.12 try: from trac.loader import get_plugin_info except ImportError: from customfieldadmin import __version__ self.env.systeminfo.append(('CustomFieldAdmin', __version__)) def get_custom_fields(self, cfield=None): """ Returns the custom fields from TicketSystem component. Use a cfdict with 'name' key set to find a specific custom field only. """ items = TicketSystem(self.env).get_custom_fields() for item in items: if item['type'] == 'textarea': if 'width' in item: item['cols'] = item.pop('width') item['rows'] = item.pop('height') if cfield and item['name'] == cfield['name']: return item # only return specific item with cfname if cfield: return None # item not found else: return items # return full list def verify_custom_field(self, cfield, create=True): """ Basic validation of the input for modifying or creating custom fields. """ # Requires 'name' and 'type' if not (cfield.get('name') and cfield.get('type')): raise TracError( _("Custom field requires attributes 'name' and 'type'.")) # Use lowercase custom fieldnames only cfield['name'] = cfield['name'].lower() # Only alphanumeric characters (and [-_]) allowed for custom fieldname if re.search('^[a-z][a-z0-9_]+$', cfield['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 cfield['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 cfield['type'] in \ ['text', 'checkbox', 'select', 'radio', 'textarea']: raise TracError(_("%(field_type)s is not a valid field type", field_type=cfield['type'])) # Check that field does not already exist # (if modify it should already be deleted) if create and self.config.get('ticket-custom', cfield['name']): raise TracError(_("Can not create as field already exists.")) if create and [f for f in TicketSystem(self.env).fields if f['name'] == cfield['name']]: raise TracError(_("Can't create a custom field with the " "same name as a built-in field.")) def create_custom_field(self, cfield): """ Create the new custom fields (that may just have been deleted as part of 'modify'). In `cfield`, 'name' and 'type' keys are required. Note: Caller is responsible for verifying input before create.""" # Need count pre-create for correct order count_current_fields = len(self.get_custom_fields()) # Set the mandatory items self.config.set('ticket-custom', cfield['name'], cfield['type']) # Label = capitalize fieldname if not present self.config.set('ticket-custom', cfield['name'] + '.label', cfield.get('label') or cfield['name'].capitalize()) # Optional items if 'value' in cfield: self.config.set('ticket-custom', cfield['name'] + '.value', cfield['value']) if 'options' in cfield: if cfield.get('optional', False) and '' not in cfield['options']: self.config.set('ticket-custom', cfield['name'] + '.options', '|' + '|'.join(cfield['options'])) else: self.config.set('ticket-custom', cfield['name'] + '.options', '|'.join(cfield['options'])) if 'format' in cfield and cfield['type'] in ('text', 'textarea'): self.config.set('ticket-custom', cfield['name'] + '.format', cfield['format']) # Textarea if cfield['type'] == 'textarea': cols = cfield.get('cols') and int(cfield.get('cols', 0)) > 0 \ and cfield.get('cols') or 60 rows = cfield.get('rows', 0) and int(cfield.get('rows', 0)) > 0 \ and cfield.get('rows') or 5 self.config.set('ticket-custom', cfield['name'] + '.cols', cols) self.config.set('ticket-custom', cfield['name'] + '.rows', rows) # Order order = cfield.get('order') or count_current_fields + 1 self.config.set('ticket-custom', cfield['name'] + '.order', order) self._save(cfield) def update_custom_field(self, cfield, create=False): """ Updates a custom field. Option to 'create' is kept in order to keep the API backwards compatible. """ if create: self.verify_custom_field(cfield) self.create_custom_field(cfield) return # Check input, then delete and save new if not self.get_custom_fields(cfield=cfield): raise TracError(_("Custom Field '%(name)s' does not exist. " \ "Cannot update.", name=cfield.get('name') or '(none)')) self.verify_custom_field(cfield, create=False) self.delete_custom_field(cfield, modify=True) self.create_custom_field(cfield) def delete_custom_field(self, cfield, 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', cfield['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', cfield['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 ) supported_options = [cfield['name']] + \ [cfield['name']+'.'+opt for opt in self.config_options] for option, _value in self.config.options('ticket-custom'): if modify and not option in supported_options: # Only delete supported options when modifying # http://trac-hacks.org/ticket/8188 continue if option == cfield['name'] \ or option.startswith(cfield['name'] + '.'): self.config.remove('ticket-custom', option) # Persist permanent deletes if not modify: self._save(cfield) def _save(self, cfield=None): """ Saves a value, clear caches if needed / supported. """ self.config.save() try: # cache support for Trac >= 0.12 del TicketSystem(self.env).custom_fields except AttributeError: # 0.11 cached values internally TicketSystem(self.env)._custom_fields = None # Re-populate contents of cfield with new values and defaults if cfield: stored = self.get_custom_fields(cfield=cfield) if stored: # created or updated (None for deleted so just ignore) cfield.update(stored) customfieldadminplugin/0.11/setup.cfg0000644000175000017500000000106512520461047016163 0ustar wmbwmb[egg_info] tag_build = [extract_messages] add_comments = TRANSLATOR: copyright_holder = CodeResort.com (BV Network AS) output_file = customfieldadmin/locale/customfieldadmin.pot keywords = _ tag_ width = 72 [init_catalog] input_file = customfieldadmin/locale/customfieldadmin.pot output_dir = customfieldadmin/locale domain = customfieldadmin [compile_catalog] directory = customfieldadmin/locale domain = customfieldadmin [update_catalog] input_file = customfieldadmin/locale/customfieldadmin.pot output_dir = customfieldadmin/locale domain = customfieldadmin customfieldadminplugin/0.11/setup.py0000644000175000017500000000256012520460125016051 0ustar wmbwmbfrom setuptools import setup extra = {} try: from trac.util.dist import get_l10n_cmdclass cmdclass = get_l10n_cmdclass() except ImportError: cmdclass = None if cmdclass: extra['cmdclass'] = cmdclass extractors = [ ('*.py', 'python', None), ('**/templates/**.html', 'genshi', None), ] extra['message_extractors'] = { 'customfieldadmin': extractors, } setup(name='TracCustomFieldAdmin', version='0.2.12', 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.admin = customfieldadmin.admin']}, exclude_package_data={'': ['tests/*']}, test_suite = 'customfieldadmin.tests.test_suite', tests_require = [], package_data={'customfieldadmin' : ['htdocs/css/*.css', 'htdocs/js/*.js', 'templates/*.html', 'locale/*/LC_MESSAGES/*.mo',]}, install_requires = ['Genshi >= 0.5', 'Trac >= 0.11'], extras_require = {'Babel': 'Babel>= 0.9.5', 'Trac': 'Trac >= 0.12'}, **extra ) customfieldadminplugin/0.11/LICENSE.txt0000644000175000017500000000276711144514242016174 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.