Custom Script Field
Use custom script fields to create custom fields from a Groovy script. Custom script fields are designed to calculate values based on other issue fields.
Possible examples of information that could be displayed using the custom script field include:
- Link to another project
- Due date based on the priority of the issue
- Link to another platform based on a combination of the issue fields
- Calculate priority based on a combination of issue fields
Limitations to custom script fields
Script fields that use data external to the issue, such as stock prices or today's temperature, will change their value every time the issue is viewed (because they are recalculated when an issue is viewed). However, their values will not be stored in the JQL index unless the issue you are viewing is edited or transitioned. Therefore, JQL searches that use the values of these types of script fields may be inaccurate, as the index value can be out of sync with the live calculated value.
Templates
When configuring your custom script field you need to choose which template to use. The template also determines which issue panel (for example, Dates, People, or the default main panel) the value will be shown in the view issue screen.
You can use the following table as a guideline for which template to use:
| Your script returns… | Template | Issue panel your script field displays in |
|---|---|---|
| A string | Text Field (multi-line) | Main (Details) |
| A date | Date | Dates |
| A date | Date Time | Dates |
| A date | Absolute Date Time | Dates |
| A period of time | Duration | Dates |
| A period of time | Duration (time tracking) | Dates |
| A number | Number Field | Main (Details) |
| A user | User Picker (single user) | People |
| List of users | User Picker (multiple users) | People |
| A group | Group Picker (single group) | Main (Details) |
| List of groups | Group Picker (multiple groups) | Main (Details) |
| A string | HTML Note: Deprecated This template is deprecated. If selected, it will behave the same way as the Text Field. | Main (Details) |
| Any object or collection | Custom | Main (Details) |
| List of versions | Version Picker | Main (Details) |
| A project | Project Picker | Main (Details) |
| A single or multiple issues | Issue(s) | Main (Details) |
Depending on the complexity of the script you will need to test with multiple issues and different inputs.
Detailed example: Display linked issues
The following is a detailed example that guides you through setting up a custom script field. This field will display information from the issues linked to it. This field will be useful for JQL filters and overviewing linked issue information.
In order to create this field, we want to do all of the following:
- Fetch all the issues that make up an Epic
- Collect a certain set of information, such as the issue's Key, Summary, and Status
- Display that information in a table within the issue in a Script field
In the end, you will have a script field that displays linked issues, for example:
This field will allow you to fetch this information in a filter:
Below we describe how to create this custom script field and how to set up this custom script field.
Creating the custom script field script
Final script
You can copy the following script example into a custom script field and it will display linked issues, as shown above:
import com.atlassian.jira.component.ComponentAccessor
import groovy.xml.MarkupBuilder
def issueLinkManager = ComponentAccessor.getIssueLinkManager()
def links = issueLinkManager.getOutwardLinks(issue.id)
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
if (!links) {
return null
}
xml.style(type: "text/css",
'''
#scriptField, #scriptField *{
border: 1px solid black;
}
#scriptField{
border-collapse: collapse;
}
''')
xml.table(id: "scriptField") {
tr {
th("Key")
th("Summary")
th("Status")
}
links.each { issueLink ->
def linkedIssue = issueLink.destinationObject
tr {
td(linkedIssue.key.toString())
td(linkedIssue.summary.toString())
td(linkedIssue.status.getName().toString())
}
}
}
return (writer.toString())Steps for creating this script
Below are the steps we took to get to the final script above.
- Get the linked issues
You can use the following code to get the necessary imports for our script to work, and to get the links to the other issues from our Epic:
import com.atlassian.jira.component.ComponentAccessor import groovy.xml.MarkupBuilder def issueLinkManager = ComponentAccessor.getIssueLinkManager() def links = issueLinkManager.getOutwardLinks(issue.id) def writer = new StringWriter() def xml = new MarkupBuilder(writer)In the above script we are doing the following:
- Using
IssueLinkManagerto access issue links. - Utilizing the
issuevariable to get links from the current issue. - Using
MarkupBuilderto generate HTML safely.
Warning: MarkupBuilder is used to avoid using HTML syntax in Groovy code and to reduce the risk of possible injection attacks. - Using
- Create the table with HTML templatingTip: You can check out this tutorial to learn more about HTML tables. For the examples below we developed our tables using HTML and then wrote them using
MarkupBuilder.You can use the following code to define basic CSS for table styling:
xml.style(type: "text/css", ''' #scriptField, #scriptField *{ border: 1px solid black; } #scriptField{ border-collapse: collapse; } ''')To this styling, you can add our table layout. The headers are Key, Summary and Status:
xml.table(id: "scriptField") { tr { th("Key") th("Summary") th("Status") } - Get the relevant information, and place a new row into the table
You can update the current table using MarkupBuilder to get the relevant information, as follows:
xml.table(id: "scriptField") { tr { th("Key") th("Summary") th("Status") } links.each { issueLink -> def linkedIssue = issueLink.destinationObject tr { td(linkedIssue.key.toString()) td(linkedIssue.summary.toString()) td(linkedIssue.status.getName().toString()) } } }In the above script we are doing the following:
- Using a Closure to go through every link that is collected within the variable
linksin our script, and getting the relevant information that we want to display. - Getting the issue object by calling the property
destinationObject. - Extracting issue key, summary and status information.
- Using a Closure to go through every link that is collected within the variable
- Return of the html element and edge casesTip: When crafting a script, you should always take into consideration your edge cases, the cases that would make your script break. For example, what happens when an epic has no links? If you analyze the flow of the code, you will be able to tell that if we leave the script as is, when our epic task has no links, this script will just output a header, which will not look very well on its own.
You can use the following to evaluate our variable
linksright at the very beginning of our code, to check if it contains anything before we add the header. If it doesn't, we will simply return null and finish the script early:if (!links) { return null }In addition, all of this time, the
xmlvariable has been linked to thewriter. To return this HTML, you can use the following:return (writer.toString())All of these examples make the above final script.
Setting up the custom script field
Understanding inward and outward links
If you check the script field, you will notice the following line:
...
issueLinkManager.getOutwardLinks(issue.id)
...This line retrieves the Outward links of the issue, not the Inward links. Understanding this distinction is crucial in Jira, as links between issues are categorized as Inward or Outward based on the link's origin.
- Outward link: Created from within the issue you're referencing, indicating the link originates from that issue.
- Inward link: Created from another issue, indicating the link originates from outside the issue you're referencing.
For example, if stories were linked to an epic from the stories themselves, rather than from the epic, the table above would display nothing because the links would be Inward, not Outward.