Skip to main content
Creating a custom dialog in Jira
Share on socials
Person searching a Jira board using a magnifying glass
Krisztian Kovacs
Krisztian Kovacs
29th May, 2018
Jira
Data center icon
Server icon

Creating a custom dialog in Jira

ScriptRunner can read and write any system or custom field. Learn how to manipulate custom fields in Jira to create a custom dialog that derives data from these fields. Sound cool?
In this post, we resume the quest for achieving custom user interface (UI) perfection using ScriptRunner for Jira. We get deeper into the nitty-gritty of manipulating custom fields in Jira by creating a custom dialog that derives data from these fields - sound cool? Let's crack on.

A brief overview

An important component of ScriptRunner is its ability to read and write (manipulate) any system or custom field. I can give you a simple example: take two number fields, multiply them and put the result into a third field. As Jira won't do this by default, now we have a script to build.
Today, we need to copy the code for a custom dialog and replace our Rest EndPoint script (that runs when you click on the 'Send xMatters' button) to show a dialog.
Then we fill that dialog with all sorts of data and in the meantime learn the difference between reading a system field and a custom field.
We will also figure out how it's possible to send the issue key to the Rest EndPoint when we click on the button.
Dialog box from xmatters
We have a dialog box, but now it's time to create a plan to conquer the world or at least fill the dialog box with text and data from Jira.
In this post we will:
  1. Find out what issue the dialog is connected to
    1. How to pass the issue key to the dialog
    2. How to pass data using rest API?
  2. Gather custom field data
    1. Provide data, based on issue key
    2. Put the data in the dialog box
  3. Make the data editable
    1. How to edit the text before sharing
In another post on writing clean code, I provide some handy little methods that you can make use of here. Once we put what you need in one file (in the end) all you need to do is to call each part at the right time.
I always prefer to create my methods first before assembling the entire script. By following this approach, it will make it a lot easier to test the script as well.

Get issue object by key

Since we are dealing with a single issue in Jira, we can use a method that delivers that issue based on the key handed over by the 'button' we created in our previous blog 'Creating buttons and REST endpoints'.

Creating buttons and Rest Endpoints

1static Issue getIssueByKey (String issueKey) {
2    return ComponentAccessor.issueManager.getIssueByCurrentKey(issueKey)

Get custom field data

The sole purpose of this method is to return custom field data as a string. Of course, it could be more sophisticated but, for this script, it's not necessary.
1static String getCustomFieldData(String key, String customFieldID) {
2    Issue issue = ComponentAccessor.issueManager.getIssueByCurrentKey(key)
3    return issue.getCustomFieldValue(ComponentAccessor.getCustomFieldManager()
4        .getCustomFieldObject(customFieldID))
5}

Provide issue key when calling REST URL

It's super easy to provide the issue key to your REST endpoint script. It would also be quite easy to provide more data on the issue by typing issue.summary or issue.report, etc.
However, the only 100% unique identifier of an issue is the key, similar to the unique sequence of numbers and letters on a car licence plate. So if you want to handover information from a script and you have limited space it's best to just send the issue key and then get the issue object by key. This will provide all the information you would ever need to know about the issue.
1/rest/scriptrunner/latest/custom/showXMattersDialog?issueKey=${issue.key}

Trimming the issue key

The challenge we face is simple: the key that is handed over by our button, wants to be an object but is displayed as a string. An object contains brackets. Instead of appearing as 'KEY-123' our issue key is '[KEY-123]'.
Luckily we can remove these brackets in one easy step. Even better, we can go all out and remove any possible risk or character that may challenge our issue key. Here we go:
1static trimIssueKey(String key) {
2    key = key.replaceAll(/^\[|]$/, '')
3    return key
4}

Updating the text area

The plan is to gather all the information we need and put it into a text area:
1static getTextArea(String issueKey, List<String> CustomFieldIDs) {
2    return """
3This is the actual message that will be sent to xMatters with all the variables
4we gather from custom fields.
5It can be edited here before sent.
6 
7Custom Field A: ${getCustomFieldData(issueKey, CustomFieldIDs[0])}
8Another Custom Field: ${getCustomFieldData(issueKey, CustomFieldIDs[1])}
9 
10Thank you for your attention.
11 
12Cheers,
13The A Team"""
14}
When you create a multi-line string, you can avoid the "String1"+"String2"+"String3" by using """ (three quotation marks) and then injecting all sorts of 'codeness' with ${myCode}. That's a convenient way of creating a complex string but also preserving readability.
Putting the information into a text area, involves creating a method that receives two key pieces of information: the issue key and the custom field IDs so we can put them into a text area later.
For this example, the custom field IDs are in a list:
1List<String> CustomFieldIDs = [
2        "customfield_10201",
3        "customfield_10200"
4]
With this approach we now have a text area in our custom dialog that can be modified before sending the dialog to xMatters.
X matters message screenshot

The journey into JavaScript

At this point, you should be aware that if you show data in a text area and let the user modify it, you can no longer use ScriptRunner to submit the data for further processing. Here is the piece of code we need, it's pure JavaScript. There is no other alternative but to use JavaScript once the dialog is set up and presented.
1<script>
2    function submit() {
3         
4    }
5    var el = document.getElementById("submit");
6    if (el.addEventListener)
7        el.addEventListener("click", submit, false);
8    else if (el.attachEvent)
9        el.attachEvent('onclick', submit);
10</script>
I'm not a self-proclaimed JavaScript expert but, luckily in this instance, there is no need for an insanely complicated script. We just need to have three functions:
  1. To find out if the user clicked on the submit button
  2. To run a 'function' when the click happens
  3. To dream about what happens in the submit() function
There is a dark cloud on the horizon: submit(). We'll need to use restAPI within JavaScript now... but that's a tale for another time. For now, let's just focus on showing the data in the dialog.

Showing the dialog

It's not enough to create a couple of methods, you will need a proper structure, so here is the whole script for you to consider:
1import com.atlassian.jira.component.ComponentAccessor
2import com.atlassian.jira.issue.Issue
3import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
4import groovy.transform.BaseScript
5import javax.ws.rs.core.MediaType
6import javax.ws.rs.core.MultivaluedMap
7import javax.ws.rs.core.Response
8@BaseScript CustomEndpointDelegate delegate
9import groovy.json.JsonSlurper
10import groovyx.net.http.HTTPBuilder
11import groovyx.net.http.ContentType
12import static groovyx.net.http.Method.*
13import com.atlassian.jira.issue.fields.rest.json.beans.PriorityJsonBean
14import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
15import groovy.transform.BaseScript
16import org.codehaus.jackson.map.ObjectMapper
17import javax.ws.rs.core.MultivaluedMap
18import javax.ws.rs.core.Response
19//-------------------------------------------------------------------------
20// This script can be applied as a rest-end-point that shows the dialog
21// when a user clicks on the 'send xmatters' button
22// Currently knows button placements for web fragments:
23// - jira.issue.tools
24// - operations-top-level
25//-------------------------------------------------------------------------
26// Please change the following so the script would suit your needs:
27List<String> CustomFieldIDs = [
28        "customfield_10201",
29        "customfield_10200"
30]
31//-------------------------------------------------------------------------
32static trimIssueKey(String key) {
33    key = key.replaceAll(/^\[|]$/, '')
34    return key
35}
36static getCustomFieldData(String key, String customFieldID) {
37    Issue issue = ComponentAccessor.issueManager.getIssueByCurrentKey(key)
38    return issue.getCustomFieldValue(ComponentAccessor.getCustomFieldManager()
39        .getCustomFieldObject(customFieldID))
40}
41static getTextArea(String issueKey, List<String> CustomFieldIDs) {
42    return """
43This is the actual message that will be sent to xMatters with all the variables
44we gather from custom fields.
45It can be edited here before sent.
46 
47Custom Field A: ${getCustomFieldData(issueKey, CustomFieldIDs[0])}
48Another Custom Field: ${getCustomFieldData(issueKey, CustomFieldIDs[1])}
49 
50Thank you for your attention.
51 
52Cheers,
53The A Team"""
54}
55static getDialogText(String issueKey, List<String> CustomFieldIDs) {
56    return """
57<script>
58    function submit() {
59         
60    }
61    var el = document.getElementById("submit");
62    if (el.addEventListener)
63        el.addEventListener("click", submit, false);
64    else if (el.attachEvent)
65        el.attachEvent('onclick', submit);
66</script>
67<section role="dialog" id="sr-dialog"
68    class="aui-layer aui-dialog2 aui-dialog2-medium" aria-hidden="true"
69        data-aui-remove-on-hide="true">
70<header class="aui-dialog2-header">
71    <h2 class="aui-dialog2-header-main">xMatters Message</h2>
72    <a class="aui-dialog2-header-close">
73        <span class="aui-icon aui-icon-small aui-iconfont-close-dialog">
74            Close
75        </span>
76    </a>
77</header>
78<div class="aui-dialog2-content">
79    <p>Header of the dialog, some text about warnings and whatever....</p>
80        <textarea id="sr-dialog-textarea" rows="15" cols="75">
81            ${getTextArea(issueKey, CustomFieldIDs)}
82        </textarea>
83</div>
84<footer class="aui-dialog2-footer">
85    <div class="aui-dialog2-footer-actions">
86        <button class="aui-button" id="submit">Send xMatters</button>
87        <button id="dialog-close-button" class="aui-button aui-button-link"">
88            Close
89        </button>
90    </div>
91    <div class="aui-dialog2-footer-hint">This is a footer message</div>
92</footer>
93</section>
94"""
95}
96showXMattersDialog() {
97    MultivaluedMap queryParams ->
98        String issueKey = queryParams.get("issueKey")
99        issueKey = trimIssueKey(issueKey)
100        String dialog = getDialogText(issueKey, CustomFieldIDs)
101        Response.ok().type(MediaType.TEXT_HTML).entity(dialog).build()
102}
As you can see the main section (showXMattersDialog) of the script is straightforward: it receives the issue key, trims it, gets the dialog body, then shows the dialog to the user.
Now that we have created a dialog with some custom text and custom field values, we will use it to send an alert or communication to our user community. To do this, we will leverage Jira's integration with xMatters to automatically transfer our issue key data and create our notification.

Sending the alert

Check out the tutorial for the next step