(For more resources on JIRA, see here.)
Gadgets are a big leap in JIRA's reporting features! The fact that JIRA is now an OpenSocial container lets its user add useful gadgets (both JIRA's own and third-party) into its dashboard. At the same time, gadgets written for JIRA can be added in other containers like iGoogle, Gmail, and so on!
In this recipe, we will have a look at writing a very simple gadget, one that says 'Hello from JTricks'. By keeping the content simple, it will let us concentrate more on writing the gadget!
Before we start writing the gadget, it is probably worth understanding the key components of a JIRA gadget:
We will see each of them in the recipe.
Create a skeleton plugin using Atlassian Plugin SDK.
The following are the steps to write our first gadget, one that shows the greetings from JTricks!
<gadget key="hello-gadget" name="Hello Gadget" location="hello-gadget.xml">
<description>Hello Gadget! </description>
</gadget>
As you can see, this has a unique key and points to the location of the gadget XML! You can have as many gadget definitions as you want in your atlassian-plugin.xml file, but in our example, we stick with the preceding one.
<resource type="download" name="screenshot.png" location="/images/screenshot.png"/>
<resource type="download" name="thumbnail.png" location="/images/thumbnail.png"/>
The location is relative to the src/main/resources folder in the plugin. As mentioned before, the screenshot is optional.
<resource type="download" name="i18n/messages.xml" location="i18n/messages.xml">
<param name="content-type" value="text/xml; charset=UTF-8"/>
</resource>
The atlassian-plugin.xml will now look like this:
<atlassian-plugin key="com.jtricks.gadgets" name="Gadgets Plugin" plugins-version="2">
<plugin-info>
<description>Gadgets Example</description>
<version>2.0</version>
<vendor name="JTricks" url="http://www.j-tricks.com/" />
</plugin-info>
<gadget key="hello-gadget" name="Hello Gadget" location="hello-gadget.xml">
<description>Hello Gadget!</description>
</gadget>
<resource type="download" name="screenshot.png" location="/images/screenshot.png"/>
<resource type="download" name="thumbnail.png" location="/images/thumbnail.png"/>
<resource type="download" name="i18n/messages.xml" location="i18n/messages.xml">
<param name="content-type" value="text/xml; charset=UTF-8"/>
</resource>
</atlassian-plugin>
<msg name="gadget.title">Hello Gadget</msg>
The msg tag has a name attribute, which is the property, and the corresponding Value is enclosed in the msg tag. We use three properties in our example and the entire file in our example looks like the following:
<messagebundle>
<msg name="gadget.title">Hello Gadget</msg>
<msg name="gadget.title.url">http://www.j-tricks.com</msg>
<msg name="gadget.description">Example Gadget from J-Tricks</msg>
</messagebundle>
<ModulePrefs title="__MSG_gadget.title__"
title_url="__MSG_gadget.title.url__"
description="__MSG_gadget.description__"
author="Jobin Kuruvilla" author_email=jobinkk@gmail.com screenshot='#staticResourceUrl("com.jtricks.gadgets:hello-gadget", "screenshot.png")'
thumbnail='#staticResourceUrl("com.jtricks.gadgets:hello-gadget", "thumbnail.png")' height="150" >
</ModulePrefs>
As you can see, it holds information like title, title URL (to which the gadget title will link to), description, author name and email, height of the gadget, and URLs to screenshot and thumbnail images.
Anything that starts with __MSG_ and ends with __ is a property that is referred from the i18n properties file.
The height of the gadget is optional and 200, by default. The images are referenced using #staticResourceUrl where the first argument is the fully qualified gadget module key which is of the form ${atlassianplugin- key}:${module-key}. In our example, the plugin key is com. jtricks.gadgets and the module key is hello-gadget.
<Optional feature="gadget-directory">
<Param name="categories">
Other
</Param>
</Optional>
In the example, we add the category as Other!
Other values supported for category are: JIRA, Confluence, FishEye, Crucible, Crowd, Clover, Bamboo, Admin, Charts, and External Content.
You can add the gadget to more than one category by adding the categories within the Param element, each in a new line.
<Locale messages="__ATLASSIAN_BASE_URL__/download/resources/com.jtricks.gad
gets/i18n/messages.xml"/>
Here the property __ATLASSIAN_BASE_URL__ will be automatically substituted with JIRA's configured base URL when the gadget is rendered. The path to the property file here is __ATLASSIAN_BASE_URL__/download/ resources/com.jtricks.gadgets, where com.jtricks.gadgets is the Atlassian plugin key. The path to the XML file /i18n/messages.xml is what is defined in the resource module earlier.
The entire content is wrapped within the <![CDATA[ and ]]>, so that they won't be treated as XML tags. The following is how it looks in our example:
<Content type="html" view="profile">
<![CDATA[ Hello From JTricks ]]>
</Content>
Our gadget's XML is now ready and looks like the following block of code:
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="__MSG_gadget.title__" title_url="__MSG_gadget.title.url__" description="__MSG_gadget.description__" author="Jobin Kuruvilla" author_email=jobinkk@gmail.com screenshot='#staticResourceUrl("com.jtricks.gadgets:hello-gadget", "screenshot.png")' thumbnail='#staticResourceUrl("com.jtricks.gadgets:hello-gadget", "thumbnail.png")' height="150" >
<Optional feature="gadget-directory">
<Param name="categories">
Other
</Param>
</Optional>
<Locale messages="__ATLASSIAN_BASE_URL__/download/resources/com.jtricks.gad
gets/i18n/messages.xml"/>
</ModulePrefs>
<Content type="html" view="profile">
<![CDATA[ Hello From JTricks ]]>
</Content>
</Module>
Once the plugin is deployed, we need to add the gadget in the JIRA dashboard. The following is how it appears in the Add Gadget screen. Note the thumbnail is the one we have in the plugin and also note that it appears in the Other section:
Once it is added, it appears as follows in the Dashboards section:
(Move the mouse over the image to enlarge.)
We can modify the look-and-feel of the gadgets by adding more HTML or gadget preferences! For example, <font color="red">Hello From JTricks</font> will make it appear in red.
We can adjust the size of the gadget using the dynamic-height feature. We should add the following under the ModulePrefs element:
<Require feature="dynamic-height"/>
We should then invoke gadgets.window.adjustHeight(); whenever the content is reloaded. For example, we can do it in a window onload event, as shown next:
<script type="text/javascript" charset="utf-8">
function resize()
{
gadgets.window.adjustHeight();
}
window.onload=resize;
</script>
The gadget xml file, in this case, will look like this:
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="__MSG_gadget.title__"
title_url="__MSG_gadget.title.url__"
description="__MSG_gadget.description__"
author="Jobin Kuruvilla"
author_email="jobinkk@gmail.com"
screenshot='#staticResourceUrl("com.jtricks.gadgets:hello-gadget", "screenshot.png")'
thumbnail='#staticResourceUrl("com.jtricks.gadgets:hello-gadget", "thumbnail.png")'
height="150"
>
<Optional feature="gadget-directory">
<Param name="categories">
Other
</Param>
</Optional>
<Require feature="dynamic-height"/>
<Locale messages="__ATLASSIAN_BASE_URL__/download/resources/com.jtricks.gad
gets/i18n/messages.xml"/>
</ModulePrefs>
<Content type="html" view="profile">
<![CDATA[
<script type="text/javascript" charset="utf-8">
function resize()
{
gadgets.window.adjustHeight();
}
window.onload=resize;
</script>
Hello From JTricks
]]>
</Content>
</Module>
The gadget should now appear as follows:
Note that the size is adjusted to just fit the text!
In the previous recipe, we saw how to write a gadget with static content. In this recipe, we will have a look at creating a gadget with dynamic content or the data that is coming from the JIRA server.
JIRA uses REST services to communicate between the gadgets and the server. In this recipe, we will use an existing REST service.
Create the Hello Gadget, as described in the previous recipe.
Let us consider a simple modification to the existing Hello Gadget to understand the basics of invoking REST services from gadgets. We will try to greet the current user by retrieving the user details from the server instead of displaying the static text: Hello From JTricks.
JIRA ships with some inbuilt REST methods, one of which is to retrieve the details of the current user. The method can be reached in the URL: /rest/gadget/1.0/currentUser.
We will use this method to retrieve the current user's full name and then display it in the gadget greeting. If the user's name is Jobin Kuruvilla, the gadget will display the message as Hello, Jobin Kuruvilla.
As we are only changing the content of the gadget, the only modification is required in the gadget XML, which is hello-gadget.xml in our example. Only the Content element needs to be modified, which will now invoke the REST service and render the content.
The following are the steps:
#requireResource("com.atlassian.jira.gadgets:common")
#includeResources()
#requireResource will bring in the JIRA gadget JavaScript framework into the gadget's context. #includeResources will write out the HTML tags for the resource in place. Check out http://confluence.atlassian.com/display/GADGETDEV/Using+Web+Resources+in+your+Gadget for more details.
var gadget = AJS.Gadget
The gadget object has four top-level options:
In our example, we don't use authentication or any configuration options. We will just go with the baseUrl and view options. The following is how the Gadget is created using JavaScript:
<script type="text/javascript">
(function () {
var gadget = AJS.Gadget({
baseUrl: "__ATLASSIAN_BASE_URL__",
view: {
................
}
});
})();
</script>
In our example, we will use the template and args properties to render the view. First, let us see args because we use the data retrieved here in the template. args will look like the following:
args: [{
key: "user",
ajaxOptions: function() {
return {
url: "/rest/gadget/1.0/currentUser"
};
}
}]
As you can see, we invoke the /rest/gadget/1.0/currentUser method and use the key user to refer the data we retrieved while rendering the view. ajaxOptions uses the jQuery Ajax Options, details of which can be found at http://api.jquery.com/jQuery.ajax#options.
The key user will now hold the user details from the REST method, as follows:
{"username":"jobinkk","fullName":"Jobin Kuruvilla","email":"jobinkk@gmail.com"}
The template function will now use this args object (defined earlier) and its key, user to render the view as follows:
template: function(args) {
var gadget = this;
var userDetails = AJS.$("<h1/>").text("Hello, "+args.user["fullName"]);
gadget.getView().html(userDetails);
}
Here, args.user["fullName"] will retrieve the user's fullName from the REST output. Username or e-mail can be retrieved in a similar fashion.
AJS.$ will construct the view as <h1>Hello, Jobin Kuruvilla</h1>, where Jobin Kuruvilla is the fullName retrieved.
The entire Content section will look as shown in the following lines of code:
<Content type="html" view="profile">
<![CDATA[
#requireResource("com.atlassian.jira.gadgets:common")
#includeResources()
<script type="text/javascript">
(function () {
var gadget = AJS.Gadget({
baseUrl: "__ATLASSIAN_BASE_URL__",
view: {
template: function(args) {
var gadget = this;
var userDetails = AJS.$("<h1/>").text("Hello, "+args.user["fullName"]);
gadget.getView().html(userDetails);
},
args: [{
key: "user",
ajaxOptions: function() {
return {
url: "/rest/gadget/1.0/currentUser"
};
}
}]
}
});
})();
</script>
]]>
</Content>
After the modification to the gadget XML, the gadget will now display the method as follows: