Web Item Built-In Script
A web-item is a button or link that will appear at your chosen location.
What happens when the link is clicked is up to you - some examples might be:
-
Redirect to another location
-
Invoke a script endpoint and run some code, which can in turn, respond with
-
a flag
-
a dialog
-
or redirect to another page
-
Simple Link Example
Let’s start with a simple example: We would like to add a link to Google on the top navigation bar. Fill out the form as follows:

Click Update to register the fragment. Go the Dashboard in a new tab, it should look like:

You can understand what the values of the section attribute mean in the Atlassian documentation on Web UI modules , or you use the section finder tool. |
Clicking the link should take you to Google (obviously). Experiment with the Weight field - try setting it to 1. When you refresh the page you should find that your link is now the left-most one.
So far, so easy, but not very useful.
Tools Menu Link
Next we’ll try to add a link to JIRA issues More Actions menu, which will do a Google search on the current issue summary.
Create a new web item or change the values to the following:

You should now be able to see this on the operations bar of any issue:

Note that the URL is processed with velocity before rendering, and that you have access to variables such as issue
, page
, space
etc (depending on application), which are their expected types.
If you don’t change the key, the module from the first part of the tutorial will be overwritten. |
Acting on the current page/issue
Still not very useful. Let’s say we want to run some code that operates on the current page, for instance to add a property showing that it’s approved.
First we will define a REST Endpoint script, with one endpoint, named approve
:
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.json.JsonOutput
import groovy.transform.BaseScript
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response
@BaseScript CustomEndpointDelegate delegate
approve(httpMethod: "GET") { MultivaluedMap queryParams ->
// the details of getting and modifying the current issue are ommitted for brevity
// def issueId = queryParams.getFirst("issueId") as Long // use the issueId to retrieve this issue
def flag = [
type : 'success',
title: "Issue approved",
close: 'auto',
body : "This issue has been approved for release"
]
Response.ok(JsonOutput.toJson(flag)).build()
}
Verify that this works by browsing to: http://<jira-url>/rest/scriptrunner/latest/custom/approve?issueId=12345
. It should simply respond with some JSON. If you are doing something with the issue be sure to enter a valid issue ID.
Now we wire this to an Approve button on a page using the following web-item
:

The "Do What" must be set as shown, otherwise the flag won’t be displayed. Note also that the URL is relative to the application base URL, so you don’t need to include the context path if one is set. |
The format of the flag is defined in the AUI Flag documentation, the thing you will commonly want to change is the type, to return error and warning messages etc. |
On clicking the Approve button you should see the flag:

As before, experiment with the Weight field to move the menu item up or down. |
The problem we have now is that will probably want to limit approving an issue to just those in a certain group, or with a certain project role. Also, we don’t want to show the menu item if the issue has already been approved. Which brings us to Conditions.
Conditions
The condition is used to define whether the link should be shown or not, and it can use anything available in its context
, plus details of the current user.
If the item is on an a view-issue page, you will get the issue. If it is on a project overview section then you may only get the project.
In JIRA, you are provided in the binding with a jiraHelper
object, which is an instance of JiraHelper
. You can use its methods to retrieve the current project etc. If the issue is available in the context, then that is bound in the script as issue
. This means that you can reuse most of the conditions from workflow functions etc. The issue will be available when selecting a section like opsbar-operations
, that is, sections that only appear on the View Issue page.
Let’s say that we want to only show the Approve menu item where the the current issue does not already have the approved
label… we can use:
! ("approved" in issue.labels*.label)
Enter this in as the Condition. It can either be entered inline or written to a file underneath a script root.
You should be able to see now that it doesn’t appear when the issue is not already labelled approved
.
Conditions must be written defensively. What is available in the For instance, a link in the top section ( If you are using a section like |
As well as using a script, you can use a concrete class that implements |
Dialogs (Advanced)
There is some support for showing dialogs, although if you require complex interactions you will need to write some javascript.
To display a dialog when the user clicks a web-item, select the Run code and display a dialog option. The endpoint needs to return HTML for an AUI dialog2. The following REST endpoint code displays a dialog when clicked:
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.transform.BaseScript
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response
@BaseScript CustomEndpointDelegate delegate
showDialog() { MultivaluedMap queryParams ->
// get a reference to the current page...
// def page = getPage(queryParams)
def dialog =
"""<section role="dialog" id="sr-dialog" class="aui-layer aui-dialog2 aui-dialog2-medium" aria-hidden="true">
<header class="aui-dialog2-header">
<h2 class="aui-dialog2-header-main">Some dialog</h2>
<a class="aui-dialog2-header-close">
<span class="aui-icon aui-icon-small aui-iconfont-close-dialog">Close</span>
</a>
</header>
<div class="aui-dialog2-content">
<p>This is a dialog...</p>
</div>
<footer class="aui-dialog2-footer">
<div class="aui-dialog2-footer-actions">
<button id="dialog-close-button" class="aui-button aui-button-link">Close</button>
</div>
<div class="aui-dialog2-footer-hint">Some hint here if you like</div>
</footer>
</section>
"""
Response.ok().type(MediaType.TEXT_HTML).entity(dialog.toString()).build()
}
The button with the ID dialog-close-button
will be automatically wired to close when clicked, but if you require more complex interactions you should require some javascript and wire the elements like so:
AJS.dialog2.on("show", function (e) {
var targetId = e.target.id;
if (targetId == "my-own-dialog") {
var someDialog = AJS.dialog2(e.target);
$(e.target).find("#dialog-close-button").click(function (e) {
e.preventDefault();
someDialog.hide();
someDialog.remove();
});
// etc
}
});
You can include your additional dialog with a web resource fragment.
On clicking the link the dialog will display:

Redirects
Sometimes you want to run some code and then redirect to another page, or even the same page after updating it. In that case, just return a redirect. The following example adds a label to the current page/issue and then refreshes it, by redirecting to the same page:
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.label.LabelManager
import com.atlassian.sal.api.ApplicationProperties
import com.onresolve.scriptrunner.runner.ScriptRunnerImpl
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.transform.BaseScript
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response
@BaseScript CustomEndpointDelegate delegate
def labelManager = ComponentAccessor.getComponent(LabelManager)
def applicationProperties = ScriptRunnerImpl.getOsgiService(ApplicationProperties)
def issueManager = ComponentAccessor.getIssueManager()
labelIssue(httpMethod: "GET") { MultivaluedMap queryParams ->
def issueId = queryParams.getFirst("issueId") as Long
def issue = issueManager.getIssueObject(issueId)
def label = labelManager.getLabels(issueId)
if (! label) {
labelManager.addLabel(null, issueId, "approved", false)
}
Response.temporaryRedirect(URI.create("${applicationProperties.baseUrl}/browse/${issue.key}")).build()
}
Make sure to select Navigate to a link as the intention setting. |