Description

The behaviours functionality allows an administrator to create one more or behaviours. A behaviour defines how fields behave for issues in a given project or issue context. Some examples of behaviours are:

  • Making a field mandatory depending on other data entered on the issue screen (i.e during an issue creation or an issue transition)

  • Making a field read-only dependent on user role or group

  • Doing server-side validation of field data, before the issue screen is submitted

  • Setting a field value dependent on other issue screen data

Once you’ve created the behaviour, apply it to a test project so you can test it works as expected. Then apply it to a real project.

Be sure to read through the behavior limitations before using a behavior or if you’re having problems getting your behavior to work.

Examples

Setting a default Description

This simple example can be used as a tutorial. Further worked examples can be found in the Recipes section - Behaviours Examples.

To create a new Behaviour. Go to the Administration screen, and click the Behaviours link in the Behaviours section, or press gg or . and type Behaviours.

new behaviour

Click on Fields:

click fields

Click the Create Initialiser link:

click create initialiser

Enter the following in the Script section (leave the first two fields blank):

def desc = getFieldById("description")

def defaultValue = """h2. How to reproduce
    * step 1
    * step 2

    h2. Expected Result

    The widget should appear

    h2. Actual Result

    The widget doesn't appear""".replaceAll(/    /, '')

if (! underlyingIssue?.description) { (1)
    desc.setFormValue(defaultValue)
}
1 don’t overwrite the description if it already exists

Then click on the Update button.

Now click the Add One now link to map this behaviour to a project:

add mapping

Select one or more projects, then click Add Mapping:

create mapping

This is a very simple configuration and should be used to check everything is working. For information about Jira Service Desk mappings, see this page.

Create a new issue in the project that you have associated with this behaviour. You should see the default description. (If it doesn’t, see trouble-shooting below).

default desc result

Using a server-side validator to set the Fix Versions required

A common scenario is that, when you mark an issue as resolved with the resolution of Fixed, you want the developer to specify the Fix Version. However, it doesn’t make sense to require a fix version when the resolution is Won’t Fix.

We solve that by using a server-side validator to mark the fix versions field as required, but only when the resolution is Fixed.

Create a new behaviour, or edit your current sample one. Click Add Field, and add the Resolution field.

Our server-side script, when written, will be called the first time this field appears, and any time the user changes it in the dialog.

add field resolution

Now click Add serverside script as we did previously. The script to enter is:

import com.atlassian.jira.issue.resolution.Resolution

def resolutionField = getFieldById("resolution")
def fixVersionsField = getFieldById("fixVersions")

def resolution = resolutionField.getValue() as Resolution

if (resolution.name == "Fixed") {
    fixVersionsField.setRequired(true)
    fixVersionsField.setHidden(false)
}
else {
    fixVersionsField.setRequired(false)
    fixVersionsField.setHidden(true)
}

This example sets the fix versions field to required and shown if the resolution is Fixed, and to optional and hidden if the resolution is anything else.

Now, Resolve an issue. Experiment with changing the resolution value. The fix versions field should change accordingly:

fixed wont fix

A good experiment now would be to modify the script so that instead of hiding the field it’s made read-only. To do, change setHidden to setReadOnly.

If you have created a new behaviour do not forget to map it to a project, or project/issue type combination.
You might want to do something similar whereby if the Duplicate resolution is selected, the Linked Issues field is shown, if there not already a duplicate link for that issue

Live Editing

Using an inline script can be painful as you have to keep clicking buttons and saving. It’s more productive to point to a file, then you can just modify that. It will be reloaded automatically.

script file

Note that if you have a groovy script as opposed to a class, as in the previous examples, the method name should be run.

When configured like this you can modify your code without even leaving the Resolve Issue dialog, let alone doing a page refresh.

The path to the script can be relative to a script root.
Tip for IDE users

If using an IDE you can get code completion by adding the following lines at the beginning of your script:

import com.onresolve.jira.groovy.user.FieldBehaviours
import groovy.transform.BaseScript

@BaseScript FieldBehaviours fieldBehaviours

Hidden Fields

Hidden fields are fields that can be hidden from the REST API and various other methods of retrieving them…​ usage of hidden fields is in beta. Let me know your experiences. A field will only be truly hidden if the behaviour marks it as hidden, and it’s of a hideable type. It’s best to avoid these, and hide fields just for the purpose of focusing end-users attention on the fields that they are interested in, and removing irrelevant fields.

Fields that are marked as hidden will be hidden when the record is viewed, in the issue navigator, and elsewhere. (See below for caveats). To get this behaviour though the field type must be one of the types provided by the plugin, currently only Hideable Free Text Field (unlimited text) and Hideable Text Field (< 255 characters). If you need any other let me know.

As far as I know, this is secure when used properly. However, there is one way where it is possible to get information from a hidden field. If a user suspects that a field has a particular string, and they construct a filter having that parameter, the issue will be returned. Although they won’t see the value, it would confirm that the issue did indeed have that value in its hidden field. If this is a concern set the Searcher to None.

The Change Log value for hidden fields are disabled for hidden fields, for all users.

Converting Normal to Hidden Fields

If you have existing fields that you want to make hideable you can convert the type following the Changing Custom Field Types procedure, using the new keys below.

CF Type

Default Type Key

Hideable Type Key

New Searcher Key

Short Text

com.atlassian.jira.plugin.system.customfieldtypes:textfield

com.onresolve.jira.plugin.Behaviours:hideable-textfield

com.onresolve.jira.plugin.Behaviours:hideable-textsearcher

Unlimited Text

com.atlassian.jira.plugin.system.customfieldtypes:textarea

com.onresolve.jira.plugin.Behaviours:hideable-textarea

com.onresolve.jira.plugin.Behaviours:hideable-textsearcher

It is not actually necessary to reindex as the searcher class is the same (although the key is different).

Example - Setting up a Hidden Field

Create (or convert) a new field of type Hideable Text Field (< 255 characters).

Create a behaviour as in the other examples. Add the above field and then set it to be hidden. Now add an exception to this, and choose project role developers. Apply the behaviour to the project. You should see something like this:

img5

The field will now be hidden for any user who is not in the developers project role of the current project.

It may be helpful to use the Switch User functionality when testing schemes based on user name/group/role.

Do not use Shown When type logic. You must use Hidden When or Hidden Except instead.

Migration of Behaviours

Currently there is no automated way of migrating behaviours. There are some REST APIs that you can use that will help migrating the configuration, although care must be taken.

The following REST API provides the mapping between behaviours, and projects/issue types.

/rest/scriptrunner/behaviours/latest/config

This will yield something like:

<Behaviours>
    <project pid="10001" configuration="1"/>
    <project pid="10003">
        <issuetype id="10004" configuration="2"/>
        <issuetype id="10000" configuration="2"/>
    </project>
</Behaviours>

Each configuration ID above is a single behaviour, which you can get with:

/rest/scriptrunner/behaviours/latest/config/2

where 2 is the ID of one of the behaviours above.

This call will give a behaviour configuration like:

<config use-validator-plugin="false" validate-jira-requiredness="false" name="test">
    <field id="customfield_10011" readonly="true"/>
    <field
        id="customfield_10013" validator-method="" validator-class="" validator="server"
        validator-script=" getFieldByName(&quot;TextFieldB&quot;).setFormValue(&quot;Hello World&quot;) "/>
    <field id="customfield_10014" required="true"/>
</config>

As you can see, custom fields are identified by their ID. If you are translating this to another system you will need to check and update any custom field IDs that don’t match. If your dev environment is a copy of your production environment you should be fine.

Likewise, project and issue types are represented by the IDs and might need updating.

Once you have updated the XML, you can post it to your production instance, using eg:

curl  -X POST -u admin:admin --header "X-Atlassian-Token:no-check" --header "Content-Type:application/xml" -d b1.xml <jira-base-url>/rest/scriptrunner/behaviours/latest/config/1

where b1.xml is the contents of the behaviour configuration, and admin:admin is an admin username and password.

If a behaviour already exists with this ID the command above will overwrite it.

Once you have recreated any behaviours, you can write the mappings to the production server:

curl  -X POST -u admin:admin --header "X-Atlassian-Token:no-check" --header "Content-Type:application/xml" -d bh.xml <jira-base-url>/rest/scriptrunner/behaviours/latest/config

where bh.xml contains the mappings retrieved in Step 1.

This will overwrite any mappings already defined. Make sure you test the procedure on a non-production instance first.
Please check the documentation for curl for the system you are on. We are not able to support you in this procedure.

Troubleshooting

Nothing happens at all

Enable logging (see below) and check the logs.

Limitations

Javascript-based

Behaviours rely on client-side javascript. In most environments users are free to disable javascript in their browser. If security of a field is critical, behaviours alone won’t be enough to protect it from malicious data entry. However, the typical use cases are to prompt the user that they should be entering another field depending on some condition, or to hide a field for clarity’s sake. In these cases, the user is better off with behaviours, and no worse off than they were without them.

If a behaviour makes a field read-only, the issue history can also reveal if that rule has been subverted.

However, one of the intentions of this plugin is to provide a more structured, maintainable way to customize fields than entering javascript in a field description.

Another side effect is that Behaviours can’t override other server-side validation rules, such as field configurations and workflow validators. For example, if you have a field configuration that specifies a field is required, a behaviour can’t really remove that requirement. You can do the opposite, however, and take a non-required field and make it required from the client’s perspective.

Bulk Edit

No support for bulk edits, at the moment. This could be added, however, all issues part of the bulk edit would need to have the same behaviour associated for it to be doable.

Latency

The field behaviours are retrieved from the server after the form has loaded in the browser. In my environment this takes approximately 80 ms so is not noticeable by humans. Over the public internet this may be a lot worse, and so the delay in the styling being applied may be appreciable for the users.

Remote API

Due to limitation 1, i.e. JavaScript based, the behaviours are not enforced when using the remote API.

Multiple behaviours on the same field can lead to confusion

It is possible to apply multiple behaviours in overlapping contexts. For example, you may have a behaviour that is mapped to one project and all issue types, then another behaviour that is mapped to all projects and one particular issue type.

Normally, this is fine. All applicable behaviours are applied in any given context. However, if both behaviours try to manipulate the same field, it’s possible for them to contradict each other. For example, if one behaviour tries to set the field to be read-only, but the other sets the field to be writeable, only one can win. If both behaviours set the field’s value to something specific, only one will be applied.

If a behaviour appears to be working inconsistently, this is a likely cause.

It may be helpful in these circumstances to use one server-side behaviour for that field. You can map it to every applicable context, then provide some detailed logic specifying in one place what ought to happen in which circumstances. See Scripted Conditions for some sample code that you can re-purpose inside a behaviour.

Screens

Behaviours function on the Create Issue, Update/Edit Issue, Assign Issue, and Workflow Transition screens.

On the View Issue screen, trying to edit a field with a behaviour will launch the edit issue screen, instead of the normal inline editor.

Behaviours will not work on the Move Issue screen. They also do not work for any bulk issue operations or any issue edits that bypass the UI such as the REST API and other ScriptRunner scripts.

Getting help

If you have any problems, please include in your bug report:

  1. server logs. You can switch these on by using the enable logging link from the Behaviours Admin screen.

behaviours logging

To do so permanently, include these lines in your log4j.properties file:

log4j.category.com.onresolve.jira.groovy = DEBUG, console, filelog
log4j.additivity.com.onresolve.jira.groovy = false
  1. For the problematic behaviour, click Edit, and include the XML snippet you see there.

  2. Description of any custom field types involved, eg their type.

For how-to questions please ask on Atlassian Answers where there is a very active community. Adaptavist staff are also likely to respond there.

Ask a question about ScriptRunner for JIRA, for for Bitbucket Server, or for Confluence.