Commits

Odd Simon Simonsen committed 4153ed1

CustomFieldAdminPlugin: Various internal fixes and tweaks with new tests:
* Order and re-order should be correct, with visual warning in admin if not
* API usage improvements whereby the data in use is updated too
* Tweaks to field validation and messages
* Updated strings for translation

Comments (0)

Files changed (8)

0.11/customfieldadmin/admin.py

 
 from trac.config import Option
 from trac.core import *
-from trac.web.chrome import ITemplateProvider, add_stylesheet, add_script
+from trac.web.chrome import ITemplateProvider, add_stylesheet, add_script, \
+                            add_warning
 from trac.admin.api import IAdminPanelProvider
 
 from customfieldadmin.api import CustomFields, _
                     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)
         

0.11/customfieldadmin/api.py

      and add option to only get one named field back.)
     
     Input to methods is a 'cfield' dict supporting these keys:
-        name = name of field (alphanumeric only)
+        name = name of field (ascii alphanumeric only)
         type = text|checkbox|select|radio|textarea
         label = label description
         value = default value for field content
     def verify_custom_field(self, cfield, create=True):
         """ Basic validation of the input for modifying or creating
         custom fields. """
-        # Name, Type and Label is required
-        if not (cfield.get('name') and cfield.get('type') \
-                    and cfield.get('label')):
+        # Requires 'name' and 'type'
+        if not (cfield.get('name') and cfield.get('type')):
             raise TracError(
-                    _("Custom field needs at least a name, type and label."))
+                    _("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
     
     def create_custom_field(self, cfield):
         """ Create the new custom fields (that may just have been deleted as
-        part of 'modify'). Note: Caller is responsible for verifying input
-        before create."""
+        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['label'])
+                        cfield.get('label') or cfield['name'].capitalize())
         # Optional items
         if 'value' in cfield:
             self.config.set('ticket-custom', cfield['name'] + '.value',
             self.config.set('ticket-custom', cfield['name'] + '.cols', cols)
             self.config.set('ticket-custom', cfield['name'] + '.rows', rows)
         # Order
-        order = cfield.get('order', "")
-        if order == "":
-            order = len(self.get_custom_fields())
+        order = cfield.get('order') or count_current_fields + 1
         self.config.set('ticket-custom', cfield['name'] + '.order', order)
-        self._save()
+        self._save(cfield)
 
     def update_custom_field(self, cfield, create=False):
-        """ Updates a custom. Option to 'create' is kept in order to keep
+        """ 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)
                 self.config.remove('ticket-custom', option)
         # Persist permanent deletes
         if not modify:
-            self._save()
+            self._save(cfield)
 
-    def _save(self):
+    def _save(self, cfield=None):
         # Saves a value, clear caches if needed / supported
         self.config.save()
         try:
         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)

0.11/customfieldadmin/locale/customfieldadmin.pot

 msgstr ""
 "Project-Id-Version: TracCustomFieldAdmin 0.2.8\n"
 "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2012-01-23 00:31+0100\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 <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Generated-By: Babel 0.9.6\n"
 
-#: customfieldadmin/admin.py:32
+#: customfieldadmin/admin.py:33
 msgid "Ticket System"
 msgstr ""
 
-#: customfieldadmin/admin.py:33
+#: customfieldadmin/admin.py:34
 msgid "Custom Fields"
 msgstr ""
 
-#: customfieldadmin/admin.py:64
+#: customfieldadmin/admin.py:65
 #, python-format
 msgid "Custom field %(name)s does not exist."
 msgstr ""
 
-#: customfieldadmin/admin.py:93
+#: customfieldadmin/admin.py:94
 msgid "No custom field selected"
 msgstr ""
 
-#: customfieldadmin/api.py:88
-msgid "Custom field needs at least a name, type and label."
+#: customfieldadmin/admin.py:123
+msgid ""
+"Custom Fields are not correctly sorted. This may affect appearance "
+"when viewing tickets."
 msgstr ""
 
-#: customfieldadmin/api.py:93
+#: 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:99
+#: customfieldadmin/api.py:98
 msgid "Custom field name must begin with a character (a-z)."
 msgstr ""
 
-#: customfieldadmin/api.py:103
+#: customfieldadmin/api.py:102
 #, python-format
 msgid "%(field_type)s is not a valid field type"
 msgstr ""
 
-#: customfieldadmin/api.py:108
+#: 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 ""
 msgstr ""
 
 #: customfieldadmin/templates/customfieldadmin.html:28
-#: customfieldadmin/templates/customfieldadmin.html:89
+#: customfieldadmin/templates/customfieldadmin.html:91
 msgid "Type:"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:38
-#: customfieldadmin/templates/customfieldadmin.html:97
+#: customfieldadmin/templates/customfieldadmin.html:39
+#: customfieldadmin/templates/customfieldadmin.html:100
 msgid "Label:"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:43
+#: customfieldadmin/templates/customfieldadmin.html:44
 msgid ""
 "Default value\n"
 "                  (regular text for Text, Textarea, Radio or Select):"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:49
+#: customfieldadmin/templates/customfieldadmin.html:50
 msgid "Format (Text or Textarea):"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:59
+#: 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:69
+#: customfieldadmin/templates/customfieldadmin.html:71
 msgid "Size of Textarea for entry (Textarea only):"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:69
+#: customfieldadmin/templates/customfieldadmin.html:71
 msgid "Columns:"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:70
-#: customfieldadmin/templates/customfieldadmin.html:125
+#: customfieldadmin/templates/customfieldadmin.html:72
+#: customfieldadmin/templates/customfieldadmin.html:128
 msgid "Rows:"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:74
+#: customfieldadmin/templates/customfieldadmin.html:76
 msgid "Cancel"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:75
+#: customfieldadmin/templates/customfieldadmin.html:77
 msgid "Save"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:82
+#: customfieldadmin/templates/customfieldadmin.html:84
 msgid "Add Custom Field:"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:84
+#: customfieldadmin/templates/customfieldadmin.html:86
 msgid "Name:"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:102
+#: customfieldadmin/templates/customfieldadmin.html:105
 msgid "Default value:"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:107
+#: customfieldadmin/templates/customfieldadmin.html:110
 msgid "Format:"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:116
+#: customfieldadmin/templates/customfieldadmin.html:119
 msgid "Options:"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:124
+#: customfieldadmin/templates/customfieldadmin.html:127
 msgid "Size of Textarea:"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:124
+#: customfieldadmin/templates/customfieldadmin.html:127
 msgid "Cols:"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:130
+#: customfieldadmin/templates/customfieldadmin.html:133
 msgid "Add"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:136
+#: customfieldadmin/templates/customfieldadmin.html:139
 msgid "No Custom Fields defined for this project."
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:144
+#: customfieldadmin/templates/customfieldadmin.html:147
 msgid "Name"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:145
+#: customfieldadmin/templates/customfieldadmin.html:148
 msgid "Type"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:146
+#: customfieldadmin/templates/customfieldadmin.html:149
 msgid "Label"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:147
+#: customfieldadmin/templates/customfieldadmin.html:150
 msgid "Order"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:154
+#: customfieldadmin/templates/customfieldadmin.html:157
 msgid "Field cannot be deleted (declared in source code)"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:168
+#: customfieldadmin/templates/customfieldadmin.html:171
 msgid "Currently outside regular range"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:177
+#: customfieldadmin/templates/customfieldadmin.html:180
 msgid "Remove selected items"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:179
+#: customfieldadmin/templates/customfieldadmin.html:182
 msgid "Apply changes"
 msgstr ""
 

0.11/customfieldadmin/locale/ja/LC_MESSAGES/customfieldadmin.po

 msgstr ""
 "Project-Id-Version: TracCustomFieldAdmin 0.2.8\n"
 "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2012-01-23 00:31+0100\n"
+"POT-Creation-Date: 2012-01-24 13:47+0100\n"
 "PO-Revision-Date: 2012-01-21 16:19+0900\n"
 "Last-Translator: Jun Omae <jun66j5@gmail.com>\n"
 "Language-Team: ja <LL@li.org>\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Generated-By: Babel 0.9.6\n"
 
-#: customfieldadmin/admin.py:32
+#: customfieldadmin/admin.py:33
 msgid "Ticket System"
 msgstr "チケットシステム"
 
-#: customfieldadmin/admin.py:33
+#: customfieldadmin/admin.py:34
 msgid "Custom Fields"
 msgstr "カスタムフィールド"
 
-#: customfieldadmin/admin.py:64
+#: customfieldadmin/admin.py:65
 #, python-format
 msgid "Custom field %(name)s does not exist."
 msgstr "カスタムフィールド %(name)s は存在しません。"
 
-#: customfieldadmin/admin.py:93
+#: customfieldadmin/admin.py:94
 msgid "No custom field selected"
 msgstr "カスタムフィールドが選択されていません"
 
-#: customfieldadmin/api.py:88
-msgid "Custom field needs at least a name, type and label."
-msgstr "カスタムフィールドには少なくとも名前とタイプ、ラベルが必要になります。"
+#: customfieldadmin/admin.py:123
+msgid ""
+"Custom Fields are not correctly sorted. This may affect appearance when "
+"viewing tickets."
+msgstr ""
 
-#: customfieldadmin/api.py:93
+#: 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:99
+#: customfieldadmin/api.py:98
 msgid "Custom field name must begin with a character (a-z)."
 msgstr "カスタムフィールド名は a から z の文字で始めなけれはなりません。"
 
-#: customfieldadmin/api.py:103
+#: customfieldadmin/api.py:102
 #, python-format
 msgid "%(field_type)s is not a valid field type"
 msgstr "%(field_type)s は正しいタイプではありません"
 
-#: customfieldadmin/api.py:108
+#: 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 "カスタムフィールド管理"
 msgstr "名前 (変更出来ません):"
 
 #: customfieldadmin/templates/customfieldadmin.html:28
-#: customfieldadmin/templates/customfieldadmin.html:89
+#: customfieldadmin/templates/customfieldadmin.html:91
 msgid "Type:"
 msgstr "タイプ:"
 
-#: customfieldadmin/templates/customfieldadmin.html:38
-#: customfieldadmin/templates/customfieldadmin.html:97
+#: customfieldadmin/templates/customfieldadmin.html:39
+#: customfieldadmin/templates/customfieldadmin.html:100
 msgid "Label:"
 msgstr "ラベル:"
 
-#: customfieldadmin/templates/customfieldadmin.html:43
+#: customfieldadmin/templates/customfieldadmin.html:44
 #, fuzzy
 msgid ""
 "Default value\n"
 "                  (regular text for Text, Textarea, Radio or Select):"
 msgstr "デフォルト値 (テキスト、テキストエリア、ラジオボタン、ドロップダウンに対する値)"
 
-#: customfieldadmin/templates/customfieldadmin.html:49
+#: customfieldadmin/templates/customfieldadmin.html:50
 msgid "Format (Text or Textarea):"
 msgstr "書式 (テキスト、テキストエリア)"
 
-#: customfieldadmin/templates/customfieldadmin.html:59
+#: customfieldadmin/templates/customfieldadmin.html:60
 #, fuzzy
 msgid ""
 "Options for Radio or Select\n"
 "                  (for Select, empty first line makes field optional):"
 msgstr "ラジオボタン、ドロップダウンで選択させる値 (ドロップダウンの場合、最初の行を空に出来ます)"
 
-#: customfieldadmin/templates/customfieldadmin.html:69
+#: customfieldadmin/templates/customfieldadmin.html:71
 msgid "Size of Textarea for entry (Textarea only):"
 msgstr "テキストエリアのサイズ (テキストエリアのみ):"
 
-#: customfieldadmin/templates/customfieldadmin.html:69
+#: customfieldadmin/templates/customfieldadmin.html:71
 msgid "Columns:"
 msgstr "幅:"
 
-#: customfieldadmin/templates/customfieldadmin.html:70
-#: customfieldadmin/templates/customfieldadmin.html:125
+#: customfieldadmin/templates/customfieldadmin.html:72
+#: customfieldadmin/templates/customfieldadmin.html:128
 msgid "Rows:"
 msgstr "行数:"
 
-#: customfieldadmin/templates/customfieldadmin.html:74
+#: customfieldadmin/templates/customfieldadmin.html:76
 msgid "Cancel"
 msgstr "取り消し"
 
-#: customfieldadmin/templates/customfieldadmin.html:75
+#: customfieldadmin/templates/customfieldadmin.html:77
 msgid "Save"
 msgstr "保存"
 
-#: customfieldadmin/templates/customfieldadmin.html:82
+#: customfieldadmin/templates/customfieldadmin.html:84
 msgid "Add Custom Field:"
 msgstr "カスタムフィールドの追加"
 
-#: customfieldadmin/templates/customfieldadmin.html:84
+#: customfieldadmin/templates/customfieldadmin.html:86
 msgid "Name:"
 msgstr "名称:"
 
-#: customfieldadmin/templates/customfieldadmin.html:102
+#: customfieldadmin/templates/customfieldadmin.html:105
 msgid "Default value:"
 msgstr "デフォルト値:"
 
-#: customfieldadmin/templates/customfieldadmin.html:107
+#: customfieldadmin/templates/customfieldadmin.html:110
 msgid "Format:"
 msgstr "書式:"
 
-#: customfieldadmin/templates/customfieldadmin.html:116
+#: customfieldadmin/templates/customfieldadmin.html:119
 msgid "Options:"
 msgstr "選択させる値:"
 
-#: customfieldadmin/templates/customfieldadmin.html:124
+#: customfieldadmin/templates/customfieldadmin.html:127
 msgid "Size of Textarea:"
 msgstr "テキストエリアのサイズ"
 
-#: customfieldadmin/templates/customfieldadmin.html:124
+#: customfieldadmin/templates/customfieldadmin.html:127
 msgid "Cols:"
 msgstr "幅"
 
-#: customfieldadmin/templates/customfieldadmin.html:130
+#: customfieldadmin/templates/customfieldadmin.html:133
 msgid "Add"
 msgstr "追加"
 
-#: customfieldadmin/templates/customfieldadmin.html:136
+#: customfieldadmin/templates/customfieldadmin.html:139
 msgid "No Custom Fields defined for this project."
 msgstr "このプロジェクトにはカスタムフィールドは設定されていません。"
 
-#: customfieldadmin/templates/customfieldadmin.html:144
+#: customfieldadmin/templates/customfieldadmin.html:147
 msgid "Name"
 msgstr "名称"
 
-#: customfieldadmin/templates/customfieldadmin.html:145
+#: customfieldadmin/templates/customfieldadmin.html:148
 msgid "Type"
 msgstr "タイプ"
 
-#: customfieldadmin/templates/customfieldadmin.html:146
+#: customfieldadmin/templates/customfieldadmin.html:149
 msgid "Label"
 msgstr "ラベル"
 
-#: customfieldadmin/templates/customfieldadmin.html:147
+#: customfieldadmin/templates/customfieldadmin.html:150
 msgid "Order"
 msgstr "順序"
 
-#: customfieldadmin/templates/customfieldadmin.html:154
+#: customfieldadmin/templates/customfieldadmin.html:157
 msgid "Field cannot be deleted (declared in source code)"
 msgstr "削除出来ません (ソースコードにて定義されている)"
 
-#: customfieldadmin/templates/customfieldadmin.html:168
+#: customfieldadmin/templates/customfieldadmin.html:171
 msgid "Currently outside regular range"
 msgstr "現在、正しい範囲から外れています"
 
-#: customfieldadmin/templates/customfieldadmin.html:177
+#: customfieldadmin/templates/customfieldadmin.html:180
 msgid "Remove selected items"
 msgstr "選択した項目を削除"
 
-#: customfieldadmin/templates/customfieldadmin.html:179
+#: customfieldadmin/templates/customfieldadmin.html:182
 msgid "Apply changes"
 msgstr "変更を適用"
 
+#~ msgid "Custom field needs at least a name, type and label."
+#~ msgstr "カスタムフィールドには少なくとも名前とタイプ、ラベルが必要になります。"
+

0.11/customfieldadmin/locale/nb/LC_MESSAGES/customfieldadmin.po

 msgstr ""
 "Project-Id-Version: TracCustomFieldAdmin 0.2.8\n"
 "Report-Msgid-Bugs-To: simon-code@bvnetwork.no\n"
-"POT-Creation-Date: 2012-01-23 00:31+0100\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 <simon-code@bvnetwork.no>\n"
 "Language-Team: nb <LL@li.org>\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Generated-By: Babel 0.9.6\n"
 
-#: customfieldadmin/admin.py:32
+#: customfieldadmin/admin.py:33
 msgid "Ticket System"
 msgstr "Sakssystem"
 
-#: customfieldadmin/admin.py:33
+#: customfieldadmin/admin.py:34
 msgid "Custom Fields"
 msgstr "Egendefinerte felt"
 
-#: customfieldadmin/admin.py:64
+#: customfieldadmin/admin.py:65
 #, python-format
 msgid "Custom field %(name)s does not exist."
 msgstr "Egendefinert felt %(name)s eksisterer ikke."
 
-#: customfieldadmin/admin.py:93
+#: customfieldadmin/admin.py:94
 msgid "No custom field selected"
 msgstr "Egendefinert felt er ikke valgt"
 
-#: customfieldadmin/api.py:88
-msgid "Custom field needs at least a name, type and label."
-msgstr "Egendefinerte felt behøver minimun navn, type og etikett."
+#: 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:93
+#: 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."
 "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:99
+#: 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:103
+#: 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:108
+#: 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"
 msgstr "Navn (kan ikke endres):"
 
 #: customfieldadmin/templates/customfieldadmin.html:28
-#: customfieldadmin/templates/customfieldadmin.html:89
+#: customfieldadmin/templates/customfieldadmin.html:91
 msgid "Type:"
 msgstr "Type:"
 
-#: customfieldadmin/templates/customfieldadmin.html:38
-#: customfieldadmin/templates/customfieldadmin.html:97
+#: customfieldadmin/templates/customfieldadmin.html:39
+#: customfieldadmin/templates/customfieldadmin.html:100
 msgid "Label:"
 msgstr "Etikett:"
 
-#: customfieldadmin/templates/customfieldadmin.html:43
-#, fuzzy
+#: 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:49
+#: customfieldadmin/templates/customfieldadmin.html:50
 msgid "Format (Text or Textarea):"
 msgstr "Format (Text og Textarea):"
 
-#: customfieldadmin/templates/customfieldadmin.html:59
-#, fuzzy
+#: customfieldadmin/templates/customfieldadmin.html:60
 msgid ""
 "Options for Radio or Select\n"
 "                  (for Select, empty first line makes field optional):"
 "Alternativer for Radio og Select (for Select betyr tom \"\n"
 "\"førsterad at feltet er valgfritt):"
 
-#: customfieldadmin/templates/customfieldadmin.html:69
+#: customfieldadmin/templates/customfieldadmin.html:71
 msgid "Size of Textarea for entry (Textarea only):"
 msgstr "Størrelse for Textarea felt:"
 
-#: customfieldadmin/templates/customfieldadmin.html:69
+#: customfieldadmin/templates/customfieldadmin.html:71
 msgid "Columns:"
 msgstr "Kolonner:"
 
-#: customfieldadmin/templates/customfieldadmin.html:70
-#: customfieldadmin/templates/customfieldadmin.html:125
+#: customfieldadmin/templates/customfieldadmin.html:72
+#: customfieldadmin/templates/customfieldadmin.html:128
 msgid "Rows:"
 msgstr "Rader:"
 
-#: customfieldadmin/templates/customfieldadmin.html:74
+#: customfieldadmin/templates/customfieldadmin.html:76
 msgid "Cancel"
 msgstr "Avbryt"
 
-#: customfieldadmin/templates/customfieldadmin.html:75
+#: customfieldadmin/templates/customfieldadmin.html:77
 msgid "Save"
 msgstr "Lagre"
 
-#: customfieldadmin/templates/customfieldadmin.html:82
+#: customfieldadmin/templates/customfieldadmin.html:84
 msgid "Add Custom Field:"
 msgstr "Legg til Egendefinert felt"
 
-#: customfieldadmin/templates/customfieldadmin.html:84
+#: customfieldadmin/templates/customfieldadmin.html:86
 msgid "Name:"
 msgstr "Navn:"
 
-#: customfieldadmin/templates/customfieldadmin.html:102
+#: customfieldadmin/templates/customfieldadmin.html:105
 msgid "Default value:"
 msgstr "Standardverdi:"
 
-#: customfieldadmin/templates/customfieldadmin.html:107
+#: customfieldadmin/templates/customfieldadmin.html:110
 msgid "Format:"
 msgstr "Format:"
 
-#: customfieldadmin/templates/customfieldadmin.html:116
+#: customfieldadmin/templates/customfieldadmin.html:119
 msgid "Options:"
 msgstr "Alternativer:"
 
-#: customfieldadmin/templates/customfieldadmin.html:124
+#: customfieldadmin/templates/customfieldadmin.html:127
 msgid "Size of Textarea:"
 msgstr "Størrelse for Textarea felt:"
 
-#: customfieldadmin/templates/customfieldadmin.html:124
+#: customfieldadmin/templates/customfieldadmin.html:127
 msgid "Cols:"
 msgstr "Kolonner:"
 
-#: customfieldadmin/templates/customfieldadmin.html:130
+#: customfieldadmin/templates/customfieldadmin.html:133
 msgid "Add"
 msgstr "Legg til"
 
-#: customfieldadmin/templates/customfieldadmin.html:136
+#: 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:144
+#: customfieldadmin/templates/customfieldadmin.html:147
 msgid "Name"
 msgstr "Navn"
 
-#: customfieldadmin/templates/customfieldadmin.html:145
+#: customfieldadmin/templates/customfieldadmin.html:148
 msgid "Type"
 msgstr "Type"
 
-#: customfieldadmin/templates/customfieldadmin.html:146
+#: customfieldadmin/templates/customfieldadmin.html:149
 msgid "Label"
 msgstr "Etikett"
 
-#: customfieldadmin/templates/customfieldadmin.html:147
+#: customfieldadmin/templates/customfieldadmin.html:150
 msgid "Order"
 msgstr "Sortering"
 
-#: customfieldadmin/templates/customfieldadmin.html:154
+#: 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:168
+#: customfieldadmin/templates/customfieldadmin.html:171
 msgid "Currently outside regular range"
 msgstr "Utenfor gjeldende verdier"
 
-#: customfieldadmin/templates/customfieldadmin.html:177
+#: customfieldadmin/templates/customfieldadmin.html:180
 msgid "Remove selected items"
 msgstr "Fjern Egendefinert felt"
 
-#: customfieldadmin/templates/customfieldadmin.html:179
+#: customfieldadmin/templates/customfieldadmin.html:182
 msgid "Apply changes"
 msgstr "Lagre endringer"
-

0.11/customfieldadmin/locale/ru/LC_MESSAGES/customfieldadmin.po

 msgstr ""
 "Project-Id-Version: TracCustomFieldAdmin 0.2.5\n"
 "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2012-01-23 00:31+0100\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 <LL@li.org>\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Generated-By: Babel 0.9.6\n"
 
-#: customfieldadmin/admin.py:32
+#: customfieldadmin/admin.py:33
 msgid "Ticket System"
 msgstr "Билеты"
 
-#: customfieldadmin/admin.py:33
+#: customfieldadmin/admin.py:34
 msgid "Custom Fields"
 msgstr "Дополнительные поля"
 
-#: customfieldadmin/admin.py:64
+#: customfieldadmin/admin.py:65
 #, python-format
 msgid "Custom field %(name)s does not exist."
 msgstr "Дополнительное поле %(name)s не существует."
 
-#: customfieldadmin/admin.py:93
+#: customfieldadmin/admin.py:94
 msgid "No custom field selected"
 msgstr "Не выбрано ни одного поля"
 
-#: customfieldadmin/api.py:88
-msgid "Custom field needs at least a name, type and label."
-msgstr "Для создания дополнительного поля требуются имя, тип и описание."
+#: customfieldadmin/admin.py:123
+msgid ""
+"Custom Fields are not correctly sorted. This may affect appearance when "
+"viewing tickets."
+msgstr ""
 
-#: customfieldadmin/api.py:93
+#: 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 "
 "В имени дополнительного поля допустимы только алфавитно-цифровые символы "
 "(a-z, 0-9 и -_)."
 
-#: customfieldadmin/api.py:99
+#: customfieldadmin/api.py:98
 msgid "Custom field name must begin with a character (a-z)."
 msgstr "Имя дополнительного поля должно начинаться с буквы (a-z."
 
-#: customfieldadmin/api.py:103
+#: customfieldadmin/api.py:102
 #, python-format
 msgid "%(field_type)s is not a valid field type"
 msgstr "%(field_type)s недопустимый тип поля"
 
-#: customfieldadmin/api.py:108
+#: 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 "Дополнительные поля"
 msgstr "Имя (неизменяемое):"
 
 #: customfieldadmin/templates/customfieldadmin.html:28
-#: customfieldadmin/templates/customfieldadmin.html:89
+#: customfieldadmin/templates/customfieldadmin.html:91
 msgid "Type:"
 msgstr "Тип:"
 
-#: customfieldadmin/templates/customfieldadmin.html:38
-#: customfieldadmin/templates/customfieldadmin.html:97
+#: customfieldadmin/templates/customfieldadmin.html:39
+#: customfieldadmin/templates/customfieldadmin.html:100
 msgid "Label:"
 msgstr "Описание:"
 
-#: customfieldadmin/templates/customfieldadmin.html:43
+#: 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:49
+#: customfieldadmin/templates/customfieldadmin.html:50
 msgid "Format (Text or Textarea):"
 msgstr "Формат (Text или Textarea):"
 
-#: customfieldadmin/templates/customfieldadmin.html:59
+#: customfieldadmin/templates/customfieldadmin.html:60
 #, fuzzy
 msgid ""
 "Options for Radio or Select\n"
 "Варианты для Radio или Select (для Select пустой первый вариант делает "
 "поле необязательным):"
 
-#: customfieldadmin/templates/customfieldadmin.html:69
+#: customfieldadmin/templates/customfieldadmin.html:71
 msgid "Size of Textarea for entry (Textarea only):"
 msgstr "Размер поля ввода (только для Textarea):"
 
-#: customfieldadmin/templates/customfieldadmin.html:69
+#: customfieldadmin/templates/customfieldadmin.html:71
 msgid "Columns:"
 msgstr "Столбцы:"
 
-#: customfieldadmin/templates/customfieldadmin.html:70
-#: customfieldadmin/templates/customfieldadmin.html:125
+#: customfieldadmin/templates/customfieldadmin.html:72
+#: customfieldadmin/templates/customfieldadmin.html:128
 msgid "Rows:"
 msgstr "Строки:"
 
-#: customfieldadmin/templates/customfieldadmin.html:74
+#: customfieldadmin/templates/customfieldadmin.html:76
 msgid "Cancel"
 msgstr "Отмена"
 
-#: customfieldadmin/templates/customfieldadmin.html:75
+#: customfieldadmin/templates/customfieldadmin.html:77
 msgid "Save"
 msgstr "Сохранить"
 
-#: customfieldadmin/templates/customfieldadmin.html:82
+#: customfieldadmin/templates/customfieldadmin.html:84
 msgid "Add Custom Field:"
 msgstr "Добавить дополнительное поле:"
 
-#: customfieldadmin/templates/customfieldadmin.html:84
+#: customfieldadmin/templates/customfieldadmin.html:86
 msgid "Name:"
 msgstr "Имя:"
 
-#: customfieldadmin/templates/customfieldadmin.html:102
+#: customfieldadmin/templates/customfieldadmin.html:105
 msgid "Default value:"
 msgstr "Значение по умолчанию:"
 
-#: customfieldadmin/templates/customfieldadmin.html:107
+#: customfieldadmin/templates/customfieldadmin.html:110
 msgid "Format:"
 msgstr "Формат:"
 
-#: customfieldadmin/templates/customfieldadmin.html:116
+#: customfieldadmin/templates/customfieldadmin.html:119
 msgid "Options:"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:124
+#: customfieldadmin/templates/customfieldadmin.html:127
 msgid "Size of Textarea:"
 msgstr "Размер текстового поля:"
 
-#: customfieldadmin/templates/customfieldadmin.html:124
+#: customfieldadmin/templates/customfieldadmin.html:127
 msgid "Cols:"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:130
+#: customfieldadmin/templates/customfieldadmin.html:133
 msgid "Add"
 msgstr "Добавить"
 
-#: customfieldadmin/templates/customfieldadmin.html:136
+#: customfieldadmin/templates/customfieldadmin.html:139
 msgid "No Custom Fields defined for this project."
 msgstr "В этом проекте не объявлено ни одного дополнительного поля."
 
-#: customfieldadmin/templates/customfieldadmin.html:144
+#: customfieldadmin/templates/customfieldadmin.html:147
 msgid "Name"
 msgstr "Имя"
 
-#: customfieldadmin/templates/customfieldadmin.html:145
+#: customfieldadmin/templates/customfieldadmin.html:148
 msgid "Type"
 msgstr "Тип"
 
-#: customfieldadmin/templates/customfieldadmin.html:146
+#: customfieldadmin/templates/customfieldadmin.html:149
 msgid "Label"
 msgstr "Описание"
 
-#: customfieldadmin/templates/customfieldadmin.html:147
+#: customfieldadmin/templates/customfieldadmin.html:150
 msgid "Order"
 msgstr "Порядок"
 
-#: customfieldadmin/templates/customfieldadmin.html:154
+#: customfieldadmin/templates/customfieldadmin.html:157
 msgid "Field cannot be deleted (declared in source code)"
 msgstr "Поле не может быть удалено (объявлено в исходнике)"
 
-#: customfieldadmin/templates/customfieldadmin.html:168
+#: customfieldadmin/templates/customfieldadmin.html:171
 msgid "Currently outside regular range"
 msgstr ""
 
-#: customfieldadmin/templates/customfieldadmin.html:177
+#: customfieldadmin/templates/customfieldadmin.html:180
 msgid "Remove selected items"
 msgstr "Удалить выбранные"
 
-#: customfieldadmin/templates/customfieldadmin.html:179
+#: customfieldadmin/templates/customfieldadmin.html:182
 msgid "Apply changes"
 msgstr "Применить изменения"
 
+#~ msgid "Custom field needs at least a name, type and label."
+#~ msgstr "Для создания дополнительного поля требуются имя, тип и описание."
+

0.11/customfieldadmin/templates/customfieldadmin.html

               <td>${cf.label}</td>
               <td class="default">
                 <select name="order_${cf.name}" py:with="count = len(cfields)">
-                  <option py:for="num in range(count)"
+                  <option py:for="num in range(1, count+1)"
                       selected="${num==cf.order and 'selected' or None}">
                       ${num}
                   </option>
                   <!--! Extra option in case value is outside regular range -->
-                  <py:if test="cf.order not in range(count)">
+                  <py:if test="cf.order not in range(1, count+1)">
                     <option disabled="disabled">&mdash;</option>
                     <option title="Currently outside regular range"
                             selected="selected">${cf.order}</option>

0.11/customfieldadmin/tests/api.py

 
     def test_create(self):
         for f in ['one', 'two', 'three']:
-            cfield = {'name': f, 'type': 'text', 'label': f.capitalize()}
+            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': '',
                      {'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_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 = customfield = {'name': 'foo', 'type': 'text', 'label': 'Foo'}
         self.cf_api.create_custom_field(cf)