Custom Script Field Examples

This page contains a number of custom script field examples:

Examples

Don’t forget to reindex if you want your newly created scripted custom field to appear in existing issues.

For the examples below, you must first navigate to the Custom Script Field page:

  1. From ScriptRunner, select the Fields tab.
  2. Select Create Script Field.
  3. Select Custom Script Field.
  4. Create a custom script field based on the examples provided below. 

Calculate a number based on other fields

You can calculate a number based on other fields. In the following example, we already have a number field called Severity, and we want a new value that is the product of the Priority and Severity:

  1. Enter the name for the custom script field. In this example, we enter Critical Points
  2. Optional: enter a description. In this example, we enter Calculate critical points based on Priority and Severity
  3. Optional: add a field note. 
  4. Select the Number Field template.
  5. Enter the following script into the script editor:

    groovy
    def severity = getCustomFieldValue("Severity") if (severity) { return severity * Integer.parseInt(issue.priority.id) } else { return null }
  6. Optional: enter an issue key and select Preview to preview this custom script field
  7. Select Add.
  8. Configure the context and screens for this custom script field.

Be careful and test this against an issue with no Severity value set.

Show the total time this issue has been In Progress

You can create a custom field that shows the total time an issue has been in the In Progress state—summing up multiple times if necessary:

  1. Enter the name for the custom script field. In this example, we enter Time In Progress.
  2. Optional: enter a description. In this example, we enter Total time this issue has been In Progress
  3. Optional: add a field note. 
  4. Select the Duration template.
  5. Enter the following script into the script editor:

    import com.atlassian.jira.component.ComponentAccessor
    import com.atlassian.jira.issue.history.ChangeItemBean
    
    def changeHistoryManager = ComponentAccessor.getChangeHistoryManager()
    
    def inProgressName = "In Progress"
    
    List<Long> rt = [0L]
    def changeItems = changeHistoryManager.getChangeItemsForField(issue, "status")
    changeItems.reverse().each { ChangeItemBean item ->
        def timeDiff = System.currentTimeMillis() - item.created.getTime()
        if (item.fromString == inProgressName) {
            rt << -timeDiff
        }
        if (item.toString == inProgressName) {
            rt << timeDiff
        }
    }
    
    def total = rt.sum() as Long
    (total / 1000) as long ?: 0L

  6. Optional: enter an issue key and select Preview to preview this custom script field
  7. Select Add.
  8. Make sure the Search Template/Searcher for this custom field is Duration Searcher.

    You can edit the Searcher by selecting the configured searcher on the Script Fields page.

  9. Configure the context and screens for this custom script field.

Show the number of attachments the issue has

This example is superseded by the JQL function hasAttachments.

You can create a custom script field to show the number of attachments. This could easily be modified to show the number with a specific extension etc.

  1. Enter the name for the custom script field. In this example, we enter Number of Attachments
  2. Optional: enter a description.
  3. Optional: add a field note. 
  4. Select the Number Field template.
  5. Enter the following script into the script editor:

    groovy
    def numberAttachments = issue.attachments.size() // use the following instead for number of PDFs // def numberAttachments = issue.attachments.findAll {a -> // a.filename.toLowerCase().endsWith(".pdf") // }.size() return numberAttachments ? numberAttachments as Double : null
  6. Optional: enter an issue key and select Preview to preview this custom script field
  7. Select Add.
  8. Make sure the Search Template/Searcher for this custom field is Number Searcher.

    You can edit the Searcher by selecting the configured searcher on the Script Fields page.

  9. Configure the context and screens for this custom script field.

Show all previous versions in a given project

You can create a custom script field to display every version in a given project prior to the oldest fix version:

  1. Enter the name for the custom script field. In this example, we enter Previous Versions
  2. Optional: enter a description. 
  3. Optional: add a field note. 
  4. Select the Version Picker template.
  5. Enter the following script into the script editor:

    package com.onresolve.jira.groovy.test.scriptfields.scripts
    
    import com.atlassian.jira.component.ComponentAccessor
    import com.atlassian.jira.issue.comparator.VersionComparator
    
    def versionManager = ComponentAccessor.getVersionManager()
    def versions = versionManager.getVersions(issue.projectObject)
    def comparator = new VersionComparator()
    def lowestFixVersion = issue.fixVersions.min(comparator)
    def returnedVersions = versions.findAll {
        comparator.compare(it, lowestFixVersion) < 0
    }
    log.debug("All prior versions: ${returnedVersions}")
    (lowestFixVersion ? returnedVersions : null)

    The script returns a list of Version objects.

  6. Optional: enter an issue key and select Preview to preview this custom script field
  7. Select Add.
  8. Make sure the Search Template/Searcher for this custom field is Version Searcher.

    You can edit the Searcher by selecting the configured searcher on the Script Fields page.

  9. Configure the context and screens for this custom script field.

Display an information message

As an admin, you might want to display extra information about an issue, and not be able to search it. You can create a custom script field where your script can output HTML, and you effectively don’t use a velocity template. 

In the following example we want to draw attention to an issue when it is blocked by other issues that are not resolved, and not assigned.

  1. Enter the name for the custom script field. In this example, we enter Blocking Issues Warning
  2. Enter a description. 
  3. Optional: add a field note. 
  4. Select the Text Field template.
  5. Enter the following script into the script editor:

    In the code below we use a MarkupBuilder, in order to avoid cross-site forgery attacks.  In addition, we return null if there are no blocking issues so the field is not displayed at all.

    groovy
    import com.atlassian.jira.component.ComponentAccessor import groovy.xml.MarkupBuilder import com.atlassian.jira.config.properties.APKeys def blockingIssues = issue.getInwardLinks() .findAll { issueLink -> issueLink.issueLinkType.name == "Blocks" } .collect { issueLink -> issueLink.sourceObject } .findAll { linkedIssue -> !linkedIssue.assignee && !linkedIssue.resolution } if (blockingIssues) { def baseUrl = ComponentAccessor.getApplicationProperties().getString(APKeys.JIRA_BASEURL) def writer = new StringWriter() def builder = new MarkupBuilder(writer) builder.div(class: "aui-message aui-message-error shadowed") { p(class: "title") { strong("This issue is blocked by the following unresolved, unassigned issue(s):") } ul { blockingIssues.each { anIssue -> li { a(href: "$baseUrl/browse/${anIssue.key}", "${anIssue.key}: ${anIssue.summary} (${anIssue.status.name})") } } } } return writer.toString() } else { return null }
  6. Optional: enter an issue key and select Preview to preview this custom script field
  7. Select Add.
  8. Make sure the Search Template/Searcher for this custom field is Free Text Searcher.

    You can edit the Searcher by selecting the configured searcher on the Script Fields page.

  9. Configure the context and screens for this custom script field.

The field appears as follows:

Show the component lead

As an admin, you might want to display the lead for the component selected for an issue, making use of the User display template.

  1. Enter the name for the custom script field. In this example, we enter Component Lead.
  2. Enter a description. 
  3. Optional: add a field note. 
  4. Select the User Picker (single user) template.
  5. Enter the following script into the script editor:

    groovy
    def components = issue.components if (components) { return components.first().componentLead }

    We return a User object, and use the User Picker template so the user is displayed with a clickable link and mouseover popup.

    Make sure you return an ApplicationUser object and not a User. If you do the latter the template will show Anonymous.

  6. Optional: enter an issue key and select Preview to preview this custom script field
  7. Select Add.
  8. Make sure the Search Template/Searcher for this custom field is User Picker Searcher.

    You can edit the Searcher by selecting the configured searcher on the Script Fields page.

  9. Configure the context and screens for this custom script field.

The field appears as follows:

Show multiple component leads

As an admin, you might want to display all unique component leads for the component selected for an issue:

  1. Enter the name for the custom script field. In this example, we enter Component Leads
  2. Enter a description. 
  3. Optional: add a field note. 
  4. Select the User Picker (multiple users) template.
  5. Enter the following script into the script editor:

    groovy
    issue.components*.componentLead .unique() .findAll()
  6. Optional: enter an issue key and select Preview to preview this custom script field

  7. Select Add.

  8. Make sure the Search Template/Searcher for this custom field is Multi User Picker Searcher.

    You can edit the Searcher by selecting the configured searcher on the Script Fields page.

  9. Configure the context and screens for this custom script field.

The field appears as follows:

Show the work remaining in linked issues

In this example, we create a custom script field to show the work remaining in all issues of the Composition link types.

Sometimes you may want to to use a custom Composition link type to break down large tasks rather than subtasks. However, when you do this you can’t see the rolled up remaining estimate. The following custom field shows the amount of time remaining on all issues that this issue comprises.

The outbound link is comprises and the inbound link would be is comprised of. See the Atlassian documentation for Configuring issue linking to create your own custom links. 

  1. Enter the name for the custom script field. 
  2. In this example, we enter Work remaining
  3. Enter a description. 
  4. Optional: add a field note. 
  5. Select the Duration (time-tracking) template. This makes the display of the custom field match that of the Estimate field.
  6. Enter the following script into the script editor:

    // Sum up the estimates from comprising issues.
    def sumOfChildIssueEstimates = issue.getOutwardLinks()
            .sum(0) { link ->
                def estimate = link.destinationObject.getEstimate()
                // If there's a link to an issue which does not comprise this issue,
                // ignore the estimate.
                link.issueLinkType.name == "Composition" && estimate ? estimate : 0L
            } as long
    
    // If there are no estimates in the comprising tickets, the result is just the
    // same as the remaining estimate, so let's not display it.
    if (!sumOfChildIssueEstimates) {
        return 0L
    }
    
    // Add the estimate from the base ticket.
    long parentIssueEstimate = issue.getEstimate() ?: 0L
    sumOfChildIssueEstimates + parentIssueEstimate

    Line 7: Change the link type name to suit

    It is important this script returns a Long, so that we can search on it.

  7. Optional: enter an issue key and select Preview to preview this custom script field

  8. Select Add.

  9. Make sure the Search Template/Searcher for this custom field is Duration Searcher.

    You can edit the Searcher by selecting the configured searcher on the Script Fields page.

  10. Configure the context and screens for this custom script field.

The field appears as follows:

Indexing

You may notice a further problem. When work is logged on one of the linked issues, the issues that link to it don’t get reindexed, so a search won’t return the correct results.

To handle this, we use a custom Script Listener to follow is comprised of links and reindex those issues:

  1. From ScriptRunner, select the Listeners tab.
  2. Select Create Listener
  3. Select Custom Listener.
  4. Enter the name for the listener. In this example, we enter Reindex related issues
  5. Select the projects for this listener to be applied to
  6. Select the Issue Updated, Work Logged On Issue, Issue Worklog Updated, and Issue Worklog Deleted events.
  7. Enter the following script into the editor:
    event.issue.getInwardLinks() // event is an IssueEvent
            .each { issueLink ->
                if (issueLink.issueLinkType.name == "Composition") {
                    issueLink.getSourceObject().reindex()
                }
            }

    Line 3: Change the link type name to suit
  8. Select Add.

Show the work remaining in all issues in an epic

In this example, we create a custom script field to show the work remaining in all issues in an epic. We have other requirements for this script field:

  • We only want this custom script field to display on Epic issues. 
  • We want to include sub-tasks of issues in an epic in the total work remaining.
  • We want to include any time remaining on the epic itself.

Proceed with the example as follows:

  1. Enter the name for the custom script field. 
  2. In this example, we enter Work remaining in all issues in this epic
  3. Enter a description. 
  4. Optional: add a field note. 
  5. Select the Duration (time-tracking) template. This makes the display of the custom field match that of the Estimate field.
  6. Enter the following script into the script editor:

    package com.onresolve.jira.groovy.test.scriptfields.scripts
    
    if (issue.issueType.name != "Epic") {
        return
    }
    
    // Sum up the estimates from comprising issues.
    def sumOfChildIssueEstimates = issue.getOutwardLinks { excludeSystemLinks = false }
            .sum(0) { link ->
    
                if (link.issueLinkType.name != "Epic-Story Link") {
                    return 0L
                }
    
                def estimate = link.destinationObject.getEstimate() ?: 0L
                def issueSubtasks = link.destinationObject.subTaskObjects
                def sumOfSubTasksEstimates = 0L
    
                // Include subtask estimates, if any exist
                if (issueSubtasks) {
                    sumOfSubTasksEstimates = issueSubtasks.sum(0) { subtask ->
                        subtask.getEstimate()
                    } as long
                }
    
                // Failsafe against tickets comprised only of subtask estimates
                estimate + sumOfSubTasksEstimates
    
            } as long
    
    // If there are no estimates in the comprising tickets, the result is just the
    // same as the remaining estimate, so let's not display it.
    if (!sumOfChildIssueEstimates) {
        return 0L
    }
    
    // Add the estimate from the base ticket.
    long parentIssueEstimate = issue.getEstimate() ?: 0L
    sumOfChildIssueEstimates + parentIssueEstimate
    

    Sub-tasks

    We do not include sub-tasks of the epic itself when calculating the work remaining.

    It is important this script returns a Long, so that we can search on it.

  7. Optional: enter an issue key and select Preview to preview this custom script field

  8. Select Add.

  9. Make sure the Search Template/Searcher for this custom field is Duration Searcher.

    You can edit the Searcher by selecting the configured searcher on the Script Fields page.

  10. Configure the context and screens for this custom script field.
    The field appears as follows:
  11. Optional: If you notice issues with indexing, create a script listener that automatically indexes issues (as described in the Indexing section above).

    If you use the script in the Indexing section above, make sure you replace the script with the following:

    groovy
    event.issue.getInwardLinks { excludeSystemLinks = false } // event is an IssueEvent .each { issueLink -> if (issueLink.issueLinkType.name == "Epic-Story Link") { issueLink.getSourceObject().reindex() } }


Further Examples

On this page