Rate this page:
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.
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
git 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.
To create an app skeleton, run the following command:
1
atlas-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 2
rm -rf ./src/test/java
rm -rf ./src/test/resources/
Delete the unneeded Java class files.
1
rm -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.
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 3 4 5 6
``` xml
<organization>
<name>Example Company</name>
<url>http://www.example.com/</url>
</organization>
```
Update the description
element:
1
<description></description>
Save the file.
src/main/resources/
and open the app descriptor file called atlassian-plugin.xml
.Add module declaration for the gadget spec:
1
<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 3 4 5
``` xml
<resource type="download" name="i18n/ALL_ALL.xml" location="i18n/ALL_ALL.xml">
<param name="content-type" value="text/xml; charset=UTF-8"/>
</resource>
```
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
Below is the `atlassian-plugin.xml` for our app:
``` xml
<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 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
``` xml
<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.
src/main/resources/
and create the gadget spec file called gadget.xml
.Add the following code to the file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
<?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.1 2 3 4 5 6 7 8
Here's the XML message bundle itself under `src/main/resources/i18n/ALL_ALL.xml`:
``` xml
<messagebundle>
<msg name="gadget.title">Test JIRA Tutorial Gadget</msg>
<msg name="gadget.description">A sample gadget to install into JIRA.</msg>
</messagebundle>
```
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 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
``` javascript
<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](/server/framework/gadgets/using-web-resources-in-your-gadget/),
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
1 2 3 4 5 6 7
* `var gadget = AJS.Gadget` constructs a gadget object. This object is from the JavaScript framework and is
documented in [the Atlassian gadgets documentation](/server/framework/gadgets/creating-a-gadget-javascript-object/).
In this section, we specify the bare minimum to get a working gadget.
* A gadget must know its base URL to operate properly. This is provided with the same `__ATLASSIAN_BASE_URL__`
system property that was used in the `Locale` element inside `ModulePrefs`.
* The `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 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
(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 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
(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.
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:
pom.xml
is located).Run the following command:
1
atlas-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.
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: