[[PageOutline(2-5,Contents,pullout)]] = Dynamically hide, default, copy, clear, validate, set ticket fields == Description This plugin dynamically changes fields in ticket views based on simple rules. Key features: * Hide a field based on another field's value. * Default a field's value per user preference. * Clear a field's value when another field's value changes. * Copy one field's value to another field. * Validate a field's value. * Set a field's value based on another field's value. In the [#Configuration configuration] section a few worked out examples of how to use this plugin are presented. This plugin requires !JavaScript to be enabled. == Bugs/Feature Requests Existing bugs and feature requests for DynamicFieldsPlugin are [report:9?COMPONENT=DynamicFieldsPlugin here]. If you have any issues, create a [/newticket?component=DynamicFieldsPlugin new ticket]. [[TicketQuery(component=DynamicFieldsPlugin&group=type,format=progress)]] == Download Download the zipped source from [export:dynamicfieldsplugin here]. The plugin is also available on [pypi:TracDynamicFields PyPi]. == Source You can check out DynamicFieldsPlugin from [/svn/dynamicfieldsplugin here] using Subversion, or [source:dynamicfieldsplugin browse the source] with Trac. Install from the [source:dynamicfieldsplugin/trunk trunk] when running Trac 1.0 and later. == Installation 1. Install the plugin by one of the following methods: a. From PyPI using `pip`: {{{#!sh $ pip install TracDynamicFields }}} a. After downloading and unzipping: {{{#!sh $ cd dynamicfieldsplugin/trunk $ python setup.py bdist_egg $ cp dist/TracDynamicFields*.egg /your/trac/location/plugins/ }}} See TracPlugins for more installation details and options. You will likely need to restart Trac's web server after installation. 1. Enable the plugin: {{{#!ini [components] dynfields.* = enabled }}} You can alternatively use the Trac Web Admin GUI to enable any or all rules. == Configuration This plugin currently includes 6 rules. Each rule is specified by modifying the {{{[ticket-custom]}}} section of your {{{trac.ini}}} file. Below are several examples to each rule. === Hide rule Let's say that your team only specifies effort for enhancements, but not for defects or other ticket types. This rule shows a custom {{{effort}}} field only when the {{{type}}} field's value is ''enhancement'': {{{#!ini [ticket-custom] effort.show_when_type = enhancement }}} If effort is specified for either enhancement or defect ticket types, then use pipe-delimited syntax to list both of them: {{{#!ini [ticket-custom] effort.show_when_type = enhancement|defect }}} If your version field is based upon the milestone, then this rule hides the {{{version}}} field when the {{{milestone}}} field's value is empty: {{{#!ini [ticket-custom] version.hide_when_milestone = }}} You may also hide fields based on group membership. For example, if only members of the production team should see a custom {{{duedate}}} field: {{{#!ini [ticket-custom] duedate.show_if_group = production }}} Some of your fields may be auto-generated or used by very few people. To always hide a field: {{{#!ini [ticket-custom] alwayshidden.hide_always = true alwayshidden.clear_on_hide = false }}} Fields that are always hidden will also be hidden on the custom query ({{{/query}}}) page. The {{{clear_on_hide}}} option above specifies that the field's value should not be cleared when hidden (clearing is the default behavior). Sometimes you want to allow users to get access to hidden fields. You can enable specific fields to be shown in ticket views when a user clicks a "Show hidden fields" link: {{{#!ini [ticket-custom] mostlyhidden.show_when_type = enhancement mostlyhidden.link_to_show = true }}} The default is that hidden fields are '''not''' enabled to be shown, ie {{{link_to_show}}} defaults to ''false''. The "Show hidden fields" link is only shown when there are hidden fields and at least one of them is enabled to be shown. === Clear rule In the version/milestone example above, if the {{{milestone}}} changes then we want the {{{version}}} field to be cleared: {{{#!ini [ticket-custom] version.clear_on_change_of = milestone }}} Note that 'clearing' a select field will actually reset it to its default value if no 'empty' option is available. === Copy rule Let's say your workflow includes reassigning a ticket's {{{owner}}} to someone else for review or verification but maintain the person responsible for the work in a custom {{{captain}}} field. Initially the {{{captain}}} should be set to the {{{owner}}}, so to reduce data entry in making this so: {{{#!ini [ticket-custom] captain.copy_from = owner }}} The default copy behavior is to not overwrite a value if one already exists. To always copy the value, add {{{(overwrite)}}} as follows: {{{#!ini [ticket-custom] captain.copy_from = owner (overwrite) }}} === Default rule Let's say your QA team usually creates defect tickets and your product managers usually create enhancement tickets. Here's how to allow each user to set the default value for the ticket {{{type}}}: {{{#!ini [ticket-custom] type.default_value = (pref) }}} For text fields, if the field's value is not empty, the default value will not be applied unless you set an {{{append}}} option to ''true'': {{{#!ini [ticket-custom] cc.default_value = (pref) cc.append = true }}} Using {{{append}}} presumes the field is a comma-delimited list and will append any missing values from the default preference value (which can also be a comma-delimited list) to that field's list. The above example introduces the plugin's user preference facility described below. === Validate rule Some fields may be required, ie can't be empty, or must not contain some specific values. For example: {{{#!ini [ticket-custom] owner.invalid_if = severity.invalid_if = pick one }}} The above will prevent the ticket from being submitted if either the {{{owner}}} field is empty or the {{{severity}}} field's value equals ''pick one''. The value for the {{{invalid_if}}} rule can be empty or any regular expression. Hidden fields do not get validated. You can also specify a condition under which the validation should be applied: {{{#!ini [ticket-custom] phase.invalid_if.1 = verifying phase.invalid_when.1 = .codereviewstatus .pending (msg:Pending code reviews.) phase.invalid_if.2 = verifying phase.invalid_when.2 = .codereviewstatus:last .rejected (msg:The last code review did not pass.) }}} The above example is for the [wiki:CodeReviewerPlugin CodeReviewer plugin] when using its ticket reference macro to prevent a ticket moving a custom {{{phase}}} field past "codereview" if there are either pending reviews or the last changeset has not passed review. The value is a jQuery selector and if it returns one or more items then the validation is applied, else it's skipped. The {{{.1}}} and {{{.2}}} suffix for the option keys are to group the {{{invalid_if}}} and {{{invalid_when}}} parts together. === Set rule When a developer starts working on a ticket, you may want to make sure (s)he sets the milestone accordingly: {{{#!ini [ticket-custom] milestone.set_to_milestone3_when_phase = implementation|verifying|releasing }}} When the {{{phase}}} field changes to either ''implementation'', ''verifying'', or ''releasing'', then the {{{milestone}}} will get set to ''milestone3''. To avoid needing to update the current milestone's value in the rule, you can alternatively use the special ''"!"'' value which specifies to set the field to the first non-empty value: {{{#!ini milestone.set_to_!_when_phase = implementation|verifying|releasing }}} Toggling checkboxes is supported (as of version 1.2.6): {{{#!ini checkbox.set_to_true_when_component = component2 (overwrite) }}} If you want to enable each user to set the value as a preference, you set to a question mark {{{?}}} as follows: {{{#!ini milestone.set_to_?_when_phase = implementation|verifying|releasing (pref) }}} Learn more about user preferences below. The default set behavior is to not overwrite a value if one already exists. To always set the value, add {{{(overwrite)}}} as follows: {{{#!ini [ticket-custom] milestone.set_to_!_when_phase = implementation|verifying|releasing (overwrite) (pref) }}} An {{{(overwrite)}}} must precede a {{{(pref)}}} as shown above. == User preferences Any rule expressed above can be configured to allow users to set preferences for them by appending ''(pref)'' to the end of the rule. For example, here's one of the hide rules from above: {{{#!ini [ticket-custom] alwayshidden.show_when_type = invalid_value (pref) }}} The ''(pref)'' will cause this rule to be added to a new '''Dynamic Fields''' preference panel where the user can disable the rule by unchecking the rule's checkbox. Some rules require user input such as the default value rule above. In that example, the user can both enable/disable the rule as well as set the field's default value for him/her. If you wish to disable a rule as the default preference, append ''(pref:disable)'' to the end of the rule like so: {{{#!ini [ticket-custom] alwayshidden.show_when_type = invalid_value (pref:disable) }}} == Exceptions * `status` cannot be used in rules (#8400) == Extensibility (implementation details) Rules are implemented as Trac extension points to allow for new rules to be added fairly easily. Six rules come packaged to provide the dynamic behaviors described above. Each rule is split between two parts: 1. rule specification (Python in {{{rules.py}}}) 1. rule implementation (!JavaScript in {{{rules.js}}}) The two parts are linked by instantiating the !JavaScript part with the class name of the python part. See the code for details. !JavaScript was chosen over Genshi transforms to dynamically have fields change without requiring a form submission - hence the plugin's name, Dynamic Fields. == About i18n/l10n support The 0.12 branch of this plugin is prepared for localization.[[BR]] But English message texts are still the (POSIX) default. If this isn't your preferred language, you can 1. check if it's already available from the [TracPluginTranslation Trac plugin l10n project] at [https://www.transifex.net/projects/p/Trac_Plugin-L10N/resource/dynfields Transifex] or 1. do it yourself: see the [trac:wiki:CookBook/PluginL10N#Dotranslatorswork l10n cookbook page for Trac plugins] for more details. Contributing your translation is highly appreciated! You could send it to the plugin's maintainer or contribute to [TracPluginTranslation Trac plugin l10n project] via [https://www.transifex.com/projects/p/Trac_Plugin-L10N Transifex]: Top translations for [https://www.transifex.com/projects/p/Trac_Plugin-L10N/resource/dynfields dynfields]: [[Image(https://www.transifex.com/projects/p/Trac_Plugin-L10N/resource/dynfields/chart/image_png, title=Go to Trac_Plugin-L10N project page on Transifex.net, link=https://www.transifex.com/projects/p/Trac_Plugin-L10N/resource/dynfields)]] Preparing the plugin from source now requires the additional step of compiling message catalog files. As long as you stick to the message catalogs served with this plugin directly, there is still nothing special to be done. Just package your plugin from source the standard way: {{{#!sh $ cd dynamicfieldsplugin $ python setup.py bdist_egg }}} and the proper helper function calls to a suitable Babel install will be issued automatically for you. Beware, there is a [t:ticket:9439 Trac ticket] strongly suggesting to do Babel install '''before''' Trac 0.12 install, or your Babel will not be functional with Trac. You're hit by this bug, if your message compilation does not produce any usable catalog. Only if you encounter message catalogs with translations marked 'fuzzy', including them would require special treatment, since automatic compilation trashes them by default. Walk through: {{{#!sh $ cd dynamicfieldsplugin $ python setup.py compile_catalog -f $ python setup.py bdist_egg }}} Again, this is almost obsolete, and only needed to include translations marked as `#, fuzzy` by the translator. For more details see the [t:wiki:CookBook/PluginL10N#Compileanduseit l10n cookbook page for Trac plugins]. == Recent Changes [[ChangeLog(dynamicfieldsplugin, 3)]] == Author/Contributors '''Author:''' [wiki:robguttman] [[BR]] '''Maintainer:''' [[Maintainer]] [[BR]] '''Contributors:''' [wiki:hasienda]