Applicable: | Jira 7.0 and later. |
Status: | Dashboard items will eventually replace gadgets in Jira. |
Level of experience: | Intermediate. You should have completed at least one beginner tutorial before working through this tutorial. See the list of tutorials in DAC. |
Time estimate: | It should take you approximately 1 hour to complete this tutorial. |
In this tutorial, we will create a new Atlassian gadget for Jira, bundle it inside an app, use a REST resource to provide it with data, and have the gadget talk to the resource. The gadget we'll make will list the projects in your Jira instance that the current user can see.
Atlassian implemented some of the OpenSocial specification in all of its applications. The gadget you'll write here is actually an OpenSocial gadget (also called a "Google Gadget") with some special sauce for Jira. We'll assume a passing familiarity with gadgets, but in case they're new to you, there's a space devoted to gadget authoring.
The gadget app will consist of the following components:
All these components will be contained within a single JAR file. Each component is further discussed in the examples below.
About these instructions
You can use any supported combination of operating system and IDE to create this app. These instructions were written using IntelliJ IDEA 2018.1 on Ubuntu Linux. If you use another operating system or IDE combination, you should use the equivalent operations for your specific environment.
This tutorial was last tested with Jira 7.10.0 using Atlassian Plugin SDK 6.3.10.
To get the most out of this tutorial, you should know the following:
We encourage you to work through this tutorial. If you want to skip ahead or check your work when you are finished, you can find the app source code on Atlassian Bitbucket.
To clone the repository, run the following command:
1 2git clone https://bitbucket.org/atlassian_tutorial/jira-gadget-2.git
Alternatively, you can download the source as a ZIP archive.
In this step, you will create your app skeleton using the Atlassian Plugin SDK.
Set up the Atlassian Plugin SDK and build a project if you did not do that yet.
Open a Terminal and navigate to the directory where you would like to keep your project.
To create an app skeleton, run the following command:
1 2atlas-create-jira-plugin
To identify your app, enter the following information when prompted.
group-id |
|
artifact-id |
|
version |
|
package |
|
Confirm your entries when prompted.
The SDK generates the project home directory with project files, such as the POM (that is, Project Object Model definition file), stub source code, and app resources.
Navigate to the directory created in the previous step.
Delete the test directories.
Setting up testing for your app isn't part of this tutorial. To delete the generated test skeleton, run the following commands:
1 2rm -rf ./src/test/java rm -rf ./src/test/resources/
Delete the unneeded Java class files.
1 2rm -rf ./src/main/java/com/atlassian/plugins/tutorial/*
Import project in your favorite IDE.
It's a good idea to familiarize yourself with the project configuration file, known as the POM. The POM defines general settings for the project, including project dependencies and build settings.
The SDK generates and maintains the POM on its own, for the most part. However, you need to manually tweak some of the included metadata for your project.
Navigate to the project directory created by the SDK and open the pom.xml
file.
Add your company or organization name and your website URL as the name
and url
values of
the organization
element:
1 2<organization> <name>Example Company</name> <url>http://www.example.com/</url> </organization>
Update the description
element:
1 2<description></description>
Save the file.
Navigate to src/main/resources/
and open the app descriptor file called atlassian-plugin.xml
.
Add module declaration for the gadget spec:
1 2<gadget key="tutorial-gadget" name="JIRA Tutorial Gadget" location="gadget.xml"/>
A gadget is a module in atlassian-plugin.xml
, like any other Jira module.
There are three required properties to note:
key
must be unique for all modules in this app.name
is the name under which the plugin module will be displayed in the Jira administration console.location
is the path to the gadget spec file, relative to src/main/resources
.Add the resource
for the message bundle:
1 2<resource type="download" name="i18n/ALL_ALL.xml" location="i18n/ALL_ALL.xml"> <param name="content-type" value="text/xml; charset=UTF-8"/> </resource>
Building URLs to message bundles
At gadget render time, __ATLASSIAN_BASE_URL__
in the gadget's xml file will be automatically substituted with
Jira's configured base URL. The rest of the download URL is made from the atlassian-plugin.xml
app key
(in this case com.atlassian.plugins.tutorial.jira-gadget-tutorial-plugin
) and the resource
's key value
(in this case i18n/ALL_ALL.xml
).
Below is the atlassian-plugin.xml
for our app:
1 2<atlassian-plugin key="${atlassian.plugin.key}" name="JIRA Gadget Tutorial Plugin" pluginsVersion="2"> <!-- Contains plugin metadata. --> <plugin-info> <description>A sample plugin showing how to add a gadget to JIRA.</description> <vendor name="Atlassian" url="http://www.atlassian.com"/> <version>1.0</version> </plugin-info> <!-- Registers the gadget spec as a plugin module. This allows the gadget to appear in the gadget directory and also allows administrators to disable/enable the gadget. --> <gadget key="tutorial-gadget" name="JIRA Tutorial Gadget" location="gadget.xml"/> <!-- Makes the gadget Locale messages available for the gadget's use. --> <resource type="download" name="i18n/ALL_ALL.xml" location="i18n/ALL_ALL.xml"> <param name="content-type" value="text/xml; charset=UTF-8"/> </resource> <!-- Automatically finds all JAX-RS resource classes in the plugin and publishes them. --> <rest key="tutorial-gadget-rest-resources" path="/tutorial-gadget" version="1.0"> <description>Provides the REST resource for the project list.</description> </rest> </atlassian-plugin>
To support the included REST module, add the following dependencies to the pom.xml
file in the
dependencies
element:
1 2<dependency> <groupId>javax.ws.rs</groupId> <artifactId>jsr311-api</artifactId> <version>1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.atlassian.plugins.rest</groupId> <artifactId>atlassian-rest-common</artifactId> <version>1.0.2</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.3</version> <scope>provided</scope> </dependency>
Save the changes.
Now you are ready to write some code to make our gadget work. For complete information on making a gadget spec, see the Atlassian Gadgets documentation.
Navigate to src/main/resources/
and create the gadget spec file called gadget.xml
.
Add the following code to the file:
1 2<?xml version="1.0" encoding="UTF-8" ?> <Module> <ModulePrefs title="__MSG_gadget.title__" directory_title="__MSG_gadget.title__" description="__MSG_gadget.description__"> <Optional feature="gadget-directory"> <Param name="categories"> JIRA </Param> </Optional> <Optional feature="atlassian.util" /> <Optional feature="auth-refresh" /> <Require feature="views" /> <Require feature="settitle"/> <Require feature="oauthpopup" /> #oauth <Locale messages="__ATLASSIAN_BASE_URL__/download/resources/com.atlassian.plugins.tutorial.jira-gadget-tutorial-plugin/i18n/ALL_ALL.xml"/> </ModulePrefs> <Content type="html" view="profile"> <!-- omitted for now --> </Content> </Module>
The ModulePrefs
section is the metadata container for the gadget: title, directory title, description, and so on.
The Content
section contains the HTML and/or JavaScript that drive the gadget's behavior. We have left it
empty here while we look more closely at ModulePrefs
.
There are a few important things to notice:
__MSG_gadget.title__
and __MSG_gadget.description__
properties are substituted at runtime by the
gadget's message bundles specified in Locale
elements. The bundle is at src/main/resources/i18n/ALL_ALL.xml
.Locale
element tells the gadget where to find the message bundles. The messages
attribute takes a URL.
We'll show how to expose the message bundle in the next step.oauthpopup
and auth-refresh
items are needed for the gadget to authenticate to Jira. See more about gadget authentication.Optional
gadget-directory
feature. It specifies that the gadget is for Jira and should
be placed in the Jira
category in the gadget directory browser. Without this, it is much harder to find and
use gadgets from the directory browser.Here's the XML message bundle itself under src/main/resources/i18n/ALL_ALL.xml
:
1 2<messagebundle> <msg name="gadget.title">Test JIRA Tutorial Gadget</msg> <msg name="gadget.description">A sample gadget to install into JIRA.</msg> </messagebundle>
Save the file.
Our gadget will consume a REST resource to show how to get dynamic data into a gadget. A full discussion of how to write a REST resource is beyond the scope of this tutorial, but there is another tutorial for doing so: Writing REST services. We'll use an updated version of the resource developed in that tutorial in our app. See the REST tutorial to learn how to set up your REST resource for this gadget (or just copy the REST stuff from this tutorial's sample code).
In this step, you will develop JavaScript to call the REST resource and display the information in the gadget. At this point, you are strongly encouraged to read the documentation on using the Atlassian gadgets JavaScript framework, as it will help you understand the code you're about to write.
Navigate to src/main/resources/
, open the gadget.xml
file, and let's look at the Content
section:
1 2<Content type="html" view="profile"> <![CDATA[ #requireResource("com.atlassian.jira.gadgets:common") #includeResources() <h1>Hello from the Atlassian Gadget Tutorial!</h1> <script type="text/javascript"> (function () { var gadget = AJS.Gadget({ baseUrl: "__ATLASSIAN_BASE_URL__", useOauth: "/rest/gadget/1.0/currentUser", view: {} }); })(); </script> ]]> </Content>
Here, take note of the following:
#requireResource()/#includeResources()
, which is described in detail in
the Atlassian Gadgets documentation,
is used here to bring in the Jira gadget JavaScript framework. The directive #requireResource
identifies a
web-resource
module inside an app, while #includeResources
writes out the HTML tags for the resource in place.
#includeResources
is smart enough to write style
tags for a CSS web resource, script
for a JavaScript resource,
and so on.
Isn't that WebResourceManager
?
If you've written Confluence or Jira apps before, you may recognize these directives as methods on the
WebResourceManager.
var gadget = AJS.Gadget
constructs a gadget object. This object is from the JavaScript framework and is
documented in the Atlassian gadgets documentation.
In this section, we specify the bare minimum to get a working gadget.
__ATLASSIAN_BASE_URL__
system property that was used in the Locale
element inside ModulePrefs
.view
object allows us to customize how the gadget is displayed on the page. It is documented in detail
in the Atlassian gadgets documentation.We'll use the JavaScript framework's built-in support for Ajax to get the data we need from the resource. The code looks like this:
1 2(function () { var gadget = AJS.Gadget({ baseUrl: "__ATLASSIAN_BASE_URL__", useOauth: "/rest/gadget/1.0/currentUser", view: { template: function(args) { var gadget = this; }, args: [{ key: "projectData", ajaxOptions: function() { return { url: "/rest/tutorial-gadget/1.0/projects.json" }; } }] } }); })();
view
object's most important property is template
, which is a function that generates the gadget's HTML.
How you generate the HTML is up to you, but the most common approach is to use
jQuery's manipulation API.this
inside template()
, allowing use of the Gadget object API.template()
takes a parameter args
, which gives access to data made available in the view
's args
array.
(We will use the jQuery manipulation API and the gadget object in our gadget shortly.)args
array is used to specify what data the gadget needs to use. Each member of the args
array is a spec
for remote data retrieval.key
is the name we'll use to refer to the member after it's been
passed to template()
.ajaxOptions
is an object whose properties are jQuery Ajax options.
In this simple case, where we make only a GET
request to a REST service, the url
property is all that's required.Write the code that will take the returned data and render it:
1 2(function () { var gadget = AJS.Gadget({ baseUrl: "__ATLASSIAN_BASE_URL__", useOauth: "/rest/gadget/1.0/currentUser", view: { template: function(args) { var gadget = this; var projectList = AJS.$("<ul/>"); AJS.$(args.projectData.projects).each(function() { projectList.append( AJS.$("<li/>").append( AJS.$("<a/>").attr({ target: "_parent", title: gadgets.util.escapeString(this.key), href: "__ATLASSIAN_BASE_URL__" + "/browse/" + this.key }).text(this.name) ) ); }); gadget.getView().html(projectList); }, args: [{ key: "projectData", ajaxOptions: function() { return { url: "/rest/tutorial-gadget/1.0/projects.json" }; } }] } }); })();
Save the changes.
Be careful of side effects!
Whenever the gadget is reloaded or resized on the page, it will be re-rendered and
template()
will be called. Make sure that doing so has no side effects.
In this step, you will build and install your app so that you can test your code. If you have not already started the application, start it now:
In Terminal window, navigate to the app root folder (where the pom.xml
is located).
Run the following command:
1 2atlas-run
(If you want to launch the debugger in your IDE, you need to run atlas-debug
command.)
Go to the created Jira instance in your browser. The gadget app has been installed into the application, and you can test your changes.
Make sure you have at least one project in Jira that is viewable by all users.
If you don't already have a dashboard created, follow these steps to create one:
You should see a list of projects viewable by all users display in the gadget.
From this point onwards, you can use QuickReload to reinstall your app behind the scenes as you work.
Rate this page: