14 min read

(For more resources on JIRA, see here.)

Writing JIRA 4 gadgets

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:

  1. Gadget XML is the most important part of a JIRA Gadget. It holds the specification of the gadget and includes the following:
    • Gadget Characteristics. It includes title, description, author’s name, and so on
    • Screenshot and a thumbnail image. Please note that the screenshot is not used within Atlassian containers such as JIRA or Confluence. We can optionally add it if we want them to be used in other OpenSocial containers
    • Required features that the gadget container must provide for the gadget
    • User preferences which will be configured by the gadget users
    • The Gadget content created using HTML and JavaScript
  2. A screenshot and thumbnail image will be used during preview and while selecting the gadget from the container.
  3. An i18n property file used for internationalization in the gadget.
  4. Optional CSS and JavaScript file used to render the display in the Content section of the gadget.

We will see each of them in the recipe.

Getting ready

Create a skeleton plugin using Atlassian Plugin SDK.

How to do it…

The following are the steps to write our first gadget, one that shows the greetings from JTricks!

  1. Modify the plugin descriptor with the gadget module and the resources required for our gadget:
    • Add the Gadget module in the plugin descriptor:
      <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.

    • Include the thumbnail and screenshot images and downloadable resources in the plugin descriptor. More can be learned at http://confluence.atlassian.com/display/JIRADEV/Downloadable+Plugin+Resources. In our example, the resources are added on to the plugin descriptor as:
      <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.

  2. Add the i18n properties file that will be used in the gadget also as a downloadable resource:
    <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>
  3. Add the screenshot and thumbnail images under the src/main/resources/ images folder. The thumbnail image should be of the size 120 x 60 pixels.
  4. Add the i18n properties file under the src/main/resources/i18n folder. The name of the filer we defined in messages.xml.
    This file is an XML file wrapped within the messagebundle tag. Each property in the file is entered as an XML tag, as shown next:
    <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>
  5. Write the Gadget XML.
    The Gadget XML has a Module element at the root of the XML. It has mainly three elements underneath – ModulePrefs, UserPref, and Content. We will write of each of them in this example. The entire set of attributes and elements and other details of the gadget specification can be read at http://confluence.atlassian. com/display/GADGETDEV/Creating+your+Gadget+XML+Specification.
    • Write the ModulePrefs element. This element holds the information about the gadget. It also has two child elements – Require and Optional, that are used to define the required or optional features for the gadget. The following is how the ModulePrefs element looks in our example after it is populated with all the attributes:
      <ModulePrefs title="__MSG_gadget.title__"  
      title_url="__MSG_gadget.title.url__"
      description="__MSG_gadget.description__"
      author="Jobin Kuruvilla" [email protected] 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.

    • Add the optional gadget directory feature inside ModulePrefs. This is currently supported only in JIRA:
      <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.

    • Include Required features if there are any under the XML tag require. A full list of supported features can be found at http://confluence.atlassian.com/display/GADGETDEV/Including+Features+into+your+Gadget.
    • Add the Locale element to point to the i18n properties file:
      <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.

    • Add User Preferences if required, using the UserPref element. We will omit the same in this example as the ‘Hello Gadget’ doesn’t take any inputs from the user.
    • Add the Content for the gadget. This is where the gadget is rendered using HTML and JavaScript. In our example, we just need to provide the static text ‘Hello From JTricks’ and it is fairly easy.

    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" [email protected] 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>
  6. Package the plugin, deploy it, and test it.

How it works…

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.)

 

There’s more…

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="[email protected]"
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!

Invoking REST services from gadgets

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.

Getting ready

Create the Hello Gadget, as described in the previous recipe.

How to do it…

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:

  1. Include the common Atlassian gadget resources:
    #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.

  2. Construct a gadget object as follows:
    var gadget = AJS.Gadget

    The gadget object has four top-level options:

    • baseUrl: An option to pass the base URL. It is a mandatory option, and we use __ATLASSIAN_BASE_URL__ here which will be rendered as JIRA’s base URL.
    • useOauth: An optional parameter. Used to configure the type of authentication which must be a URL. /rest/gadget/1.0/currentUser is commonly used.
    • config: Another optional parameter. Only used if there are any configuration options for the gadget.
    • view: Used to define the gadget’s view.

    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>
  3. Populate the gadget view.
    The view object has the following properties:
    • enableReload: Optional. Used to reload the gadget at regular intervals.
    • onResizeReload: Optional. Used to reload the gadget when the browser is resized.
    • onResizeAdjustHeight: Optional and used along with the dynamicheight feature. This will adjust the gadget height when the browser is resized.
    • template: Created the actual view.
    • args: An array of objects or function that returns an array of objects. It has two attributes. Key –used to access the data from within the template and ajaxOptions – set of request options used to connect to the server and retrieve data.

    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":"[email protected]"}

    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>
  4. Package the gadget and deploy it.

How it works…

After the modification to the gadget XML, the gadget will now display the method as follows:

LEAVE A REPLY

Please enter your comment!
Please enter your name here