Enterprise Jira users often need to automatically generate dashboards with gadgets, to provide a consistent management-level overview of progress. These could be created either on demand, or automatically in response to version or project-created events.

Some gadgets take a JQL string, but others require a saved filter, so you may also need to generate one or more saved filters at the same time as creating a dashboard. Take care that you share both the dashboard and any dependent saved filters with your target users - in the example we share it globally.

The secret to perfecting this is (as always) to start small and work with a simple script in the Script Console. Once that’s working you can wire it to events such as version created/resolved etc using a Script Listener.

Start by creating a template dashboard using the normal UI. When you are happy you can run the following script, which will dump the configuration of the dashboard to the console. Make sure you start with only a single gadget to keep things simple.

import com.atlassian.jira.bc.JiraServiceContext
import com.atlassian.jira.bc.JiraServiceContextImpl
import com.atlassian.jira.bc.portal.PortalPageService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.portal.PortletConfiguration
import groovy.json.JsonOutput
import groovy.xml.MarkupBuilder

def user = ComponentAccessor.jiraAuthenticationContext.loggedInUser
JiraServiceContext sourceUserServiceCtx = new JiraServiceContextImpl(user)
def portalPageService = ComponentAccessor.getComponent(PortalPageService)

def dashId = 10203L (1)

def output = portalPageService.getPortletConfigurations(sourceUserServiceCtx, dashId).collect { col ->
    col.collect { PortletConfiguration gadget ->
        [
            row              : gadget.row,
            column           : gadget.row,
            color            : gadget.color,
            openSocialSpecUri: gadget.openSocialSpecUri.getOrNull().toString(),
            completeModuleKey: gadget.completeModuleKey.getOrNull().toString(),
            userPrefs        : gadget.userPrefs,
        ]
    }
}

def prettyOutput = JsonOutput.prettyPrint(JsonOutput.toJson(output))
log.debug(prettyOutput)

def writer = new StringWriter()
def builder = new MarkupBuilder(writer)
builder.pre(prettyOutput)
writer
1 Change the dashboard ID to the one you want to inspect

Now, plug those values in to the script below, and execute:

import com.atlassian.fugue.Option
import com.atlassian.gadgets.dashboard.Color
import com.atlassian.gadgets.dashboard.Layout
import com.atlassian.jira.bc.JiraServiceContext
import com.atlassian.jira.bc.JiraServiceContextImpl
import com.atlassian.jira.bc.portal.PortalPageService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.search.SearchRequest
import com.atlassian.jira.issue.search.SearchRequestManager
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.portal.PortalPage
import com.atlassian.jira.portal.PortletConfigurationManager
import com.atlassian.jira.sharing.SharedEntity

def user = ComponentAccessor.jiraAuthenticationContext.loggedInUser
JiraServiceContext sourceUserServiceCtx = new JiraServiceContextImpl(user)

def portalPageService = ComponentAccessor.getComponent(PortalPageService)
def portletConfigurationManager = ComponentAccessor.getComponent(PortletConfigurationManager)
def searchRequestManager = ComponentAccessor.getComponent(SearchRequestManager)
def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)

def dashboardTemplate = new PortalPage.Builder()
    .name("My new dash")
    .description("Info....")
    .owner(user)
    .layout(Layout.AA)
    .permissions(SharedEntity.SharePermissions.GLOBAL) (1)
    .build()

def query = jqlQueryParser.parseQuery("project = JRA") (2)

// share filter globally
def templateSearchRequest = new SearchRequest(query, user, "JRA issues", "my description")
templateSearchRequest.setPermissions(SharedEntity.SharePermissions.GLOBAL)
def newSearchRequest =searchRequestManager.create(templateSearchRequest)

// note that portal page names must be unique per user
if (! portalPageService.validateForCreate(sourceUserServiceCtx, dashboardTemplate)) {
    return sourceUserServiceCtx.errorCollection
}

def targetDash = portalPageService.createPortalPage(sourceUserServiceCtx, dashboardTemplate)

portletConfigurationManager.addDashBoardItem(targetDash.id, 0, 0, (3)
    Option.some(URI.create('rest/gadgets/1.0/g/com.atlassian.jira.gadgets:assigned-to-me-gadget/gadgets/assigned-to-me-gadget.xml')),
    Color.color1,
    [
        'isConfigured': 'true', (4)
        'refresh'     : '15',
        'sortColumn'  : '',
        'columnNames' : 'issuetype|issuekey|summary|priority',
        'num'         : '10'
    ],
    Option.none()
)

portletConfigurationManager.addDashBoardItem(targetDash.id, 1, 0,
    Option.some(URI.create('rest/gadgets/1.0/g/com.atlassian.jira.gadgets:filter-results-gadget/gadgets/filter-results-gadget.xml')),
    Color.color2,
    [
        "filterId"    : newSearchRequest.id.toString(), (5)
        'isConfigured': 'true',
        'refresh'     : 'false',
        "isPopup"     : "false",
        'columnNames' : 'issuetype|issuekey|summary|priority',
        'num'         : '10'
    ],
    Option.none() (6)
)

log.debug("Created dashboard: $targetDash.id")
1 Shared globally, change to suit
2 Sample query - you could plug in versions from a VersionCreated event for instance
3 Numbers are column and row position
4 User configuration for the gadget - take from the dumped configuration
5 Generated search request
6 Module key - empty for built-in gadgets

This will create a new dashboard with two configured gadgets. You should end up with a new dashboard:

gen dash

Jira doesn’t like it if you generate multiple dashboards for the same user with the exact same name. When testing delete the dashboard after each run.

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.