Gadgets in JIRA

Exclusive offer: get 50% off this eBook here
JIRA Development Cookbook

JIRA Development Cookbook — Save 50%

Develop and customize plugins, program workflows, work on custom fields, master JQL functions, and more - to effectively customize, manage, and extend JIRA with this book and ebook

$35.99    $18.00
by Jobin Kuruvilla | December 2011 | Enterprise Articles

In this article by Jobin Kuruvilla author of JIRA Development Cookbook, we will cover:

  • Writing JIRA 4 gadgets
  • Invoking REST services from gadgets
  • Configuring user preferences in gadgets
  • Accessing gadgets outside of JIRA

(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" 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.

    • 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" 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>
  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="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!

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":"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>
  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:

JIRA Development Cookbook Develop and customize plugins, program workflows, work on custom fields, master JQL functions, and more - to effectively customize, manage, and extend JIRA with this book and ebook
Published: November 2011
eBook Price: $35.99
Book Price: $59.99
See more
Select your format and quantity:

(For more resources on JIRA, see here.)

Configuring user preferences in gadgets

In the previous two recipes, we saw how to create gadgets from static content and dynamic content. In this recipe, we will go one step further and display the gadget content, based on user input.
The user will configure the gadget during its creation, or modify it later and the gadget content will vary depending on the configuration parameters.

Getting ready

Create the Hello Gadget, populated with dynamic content, as described in the previous recipe.

How to do it...

In this recipe, we will let the user choose whether to display the name in the greeting message or not. There will be a property on the gadget named displayName. If it is set to true, the gadget will display the username and the greeting message will be Hello, Jobin Kuruvilla. If the displayName is set to false, the greeting message will be Hello!
The following are the steps to configure user preferences:

  1. Include the setprefs and the views features under the ModulePrefs element:

    <Require feature="setprefs" />
    <Require feature="views" />

    setprefs is required to persist user preferences, whereas views determines whether the current user can edit the preferences or not.

  2. Include the gadget, the common locale, under ModulePrefs, along with our custom Locale element:

    #supportedLocales("gadget.common")

    This is required to get the gadget configuration language properly.

  3. Include the required UserPref elements. This element defines the various user preferences. The element supports the following fields:
    • name: Required. Name of the user preferences. The value of this can then be accessed using gadget.getPref("name").
    • display_name: Display name of the field. By default, it will be the same as the name.
    • urlparam: Optional string to pass as the parameter name for content type="url".
    • datatype: Data type of the field. Valid options include: string, bool, enum, hidden, or list. Default is string.
    • required: Marks the field as required. Default is false.
    • default_value: Sets a default value.

    In our example, we add the displayName property as follows:

        <UserPref name="displayName" datatype="hidden"
    defaultvalue="true"/>

    The field is marked as hidden so that it won't appear in the OpenSocial gadget configuration form!

  4. Modify the creation of AJS.Gadget to include the config property. config is normally of the form:


    ...
    config: {
        descriptor: function(){...},
        args: {Function, Array}
    },
    ...

    Here, descriptor is a function that returns a new Configuration Descriptor. args is an array of objects or a function that returns one similar to view.
    In our example, we define a function to return a descriptor with the configuration details of the displayName property. It looks like the following:

    config: {
      descriptor: function (args) {
        var gadget = this;
        return  {
          fields: [
            {
              userpref: "displayName",
              label: gadget.getMsg("property.label"),
              description:gadget.getMsg("property.description"),
              type: "select",
              selected: gadget.getPref("displayName"),
              options:[
                {
                   label:"Yes",
                  value:"true"
                },
                {
                  label:"No",
                  value:"false"
                }
              ]
             }
          ]
         };
      }
    }

    Here, there is only one field: displayName. It is of the type select and has a label and description, both populated from the i18n property file using the gadget.getMsg method. The Selected attribute is populated with the current value – gadget.getPref("displayName"). Options are given as an array, as shown in the preceding snippet.
    More details on the various other field types and their properties can be found at http://confluence.atlassian.com/display/GADGETDEV/ Field+Definitions.

  5. Add the new i18n properties to the message bundle:

    <msg name="property.label">Display Name?</msg>
    <msg name="property.description">Example Property from J-Tricks</msg>

  6. Include the UserPref – isConfigured:

    <UserPref name="isConfigured" datatype="hidden" defaultvalue="false"/>

    The user preferences are set every time the gadget loads, and we use this property which is specially designed to prevent this.
    When this property is used, AJS.gadget.fields.nowConfigured() should be added as an additional field under the config descriptor.

  7. Modify the view to display usernames based on the configured property.
    The template function is modified as follows:

    if (gadget.getPref("displayName") == "true")
      var userDetails = AJS.$("<h1/>").text("Hello, "+args.user["fullName"]);
    } else {
      var userDetails = AJS.$("<h1/>").text("Hello!");
     } 

    As you can see, the configured property is retrieved using gadget. getPref("displayName"). If it is true, the username is used.
    The entire Content section now looks like 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__",
     config: {
    descriptor: function (args) {
    var gadget = this;
    return  {
    fields: [
    {
    userpref: "displayName",
    label: gadget.getMsg("property.label"),
    description:gadget.getMsg("property.description"),
    type: "select",
    selected: gadget.getPref("displayName"),
    options:[
                        {
    label:"Yes",
    value:"true"
                 },
                        {
    label:"No",
    value:"false"
                        }
                      ]
    },
    AJS.gadget.fields.nowConfigured()
                    ]
    };
    }
     },
     view: {
    template: function(args) {
    var gadget = this;      
    if (gadget.getPref("displayName") == "true")
                  {
    varuserDetails = AJS.$("<h1/>").text("Hello, "+args.user
    ["fullName"]);
                  } else {
    varuserDetails = AJS.$("<h1/>").text("Hello!");
                  }      
    gadget.getView().html(userDetails);
    },
    args: [{
    key: "user",
    ajaxOptions: function() {
    return {
    url: "/rest/gadget/1.0/currentUser"
                    };
                  }
                }]
     }
     });
          })();
    </script>
    ]]>
    </Content>

  8. Package the gadget and deploy it.

How it works...

Once the user configurable properties are added, the gadget on its creation will ask the user to configure the displayName property, as shown next. The default value will be true (label :Yes) as we configured it.

When Yes is selected, it appears as:

screenshot:

If you click on the gadget options now, you can see the Edit option, as shown in the following

The following screenshot appears while clicking on Edit:

On selecting No, the message is displayed without the username, as shown in the following screenshot:

There's more...

One of the most popular user preferences in JIRA gadgets, and therefore, worth a special mention, is its ability to auto refresh itself at a configured interval. JIRA has a pre-defined feature that helps us to do it.
There are only a couple of things you need do to implement this feature:

  1. Add the refresh UserPref:

    <UserPref name="refresh" datatype="hidden" defaultvalue="false"/>

  2. Include the enableReload: true property in the view:

    view: {
      enableReload: true,
       template: function(args) {
        .................
      },
      args: [{
        .................
       }]
    }

You will now see an extra Refresh action on the gadget properties, as shown in the next screenshot:

This can be used to refresh the gadget at any time.
On clicking on Edit, the automatic refresh interval can be selected, as shown in the following screenshot:

Accessing gadgets outside of JIRA

We have seen how to write a gadget and add it onto the JIRA Dashboard. But have we made use of all the advantages of an OpenSocial gadget? How about adding them onto other OpenSocial containers such as Gmail or iGoogle?
In this recipe, we will see how to add a gadget in to Gmail. The process is pretty much similar for other containers as well.

How to do it...

The following is a quick step-by-step procedure to add a gadget to Gmail:

  1. Identify the Gadget URL for the gadget that we are going to add. We can find this URL from the JIRA gadgets directory, as shown in the next screenshot. In the example, we choose to add the Favourite Filters gadget:

  2. Go to Gmail Settings | Gadgets|. Enter the URL, as shown in the next screenshot:

    Note that this is the only process that will be different for different containers. We need to enter this URL in the appropriate place for each different container.

  3. Once added, the gadget will appear in the settings as shown in the following screenshot:

  4. The gadget should now be available under the list of gadgets you have in your Gmail sidebars. Save the configurations. In our example, we need to choose whether to display the count of issues or not and the refresh interval.
    Refer to the next screenshot to see how it appears in Gmail.
  5. The gadget now shows no results because we haven't connected to JIRA with a proper username/password. Edit the gadget settings and you will see an option, Login & Approve, which allows you to log in to your JIRA instance and approve the retrieval of data to be displayed in Gmail:

  6. Approve Access, as shown in the following screenshot. The gadget should now show the results:

How it works...

The way it works is identical to that of its behavior in JIRA Dashboards. The gadget will communicate with JIRA using the REST APIs and the data is rendered using the HTML and JavaScript code under the view section in the gadget XML's Content element.


Further resources on this subject:


JIRA Development Cookbook Develop and customize plugins, program workflows, work on custom fields, master JQL functions, and more - to effectively customize, manage, and extend JIRA with this book and ebook
Published: November 2011
eBook Price: $35.99
Book Price: $59.99
See more
Select your format and quantity:

About the Author :


Jobin Kuruvilla

Jobin Kuruvilla is an Atlassian Consultant with experience in customizing JIRA and writing JIRA plugins for various customers. He is currently working with Go2Group as an Atlassian platinum expert, and is involved in managing Atlassian products for big enterprises as well as small starter license installations.

Jobin is the author of JIRA Development Cookbook, Packt Publishing, released in 2011, which is a well-received book in the JIRA community. He also runs a website named J-Tricks (http://www.j-tricks.com), using which he shares numerous tutorials to help the developer community, who he believes have contributed immensely to his personal development. It is indeed those tutorials that sowed the first seeds of JIRA Development Cookbook.

Jobin started his career as a Java/J2EE developer in one of the biggest IT companies in India. After spending his initial years in the SOA world, he got hooked into this amazing product called JIRA, which he came across during the evaluation of a number of third-party tools. Soon, Jobin realized the power of JIRA, and pledged to spread the word. He has been doing it ever since, and he reckons there is a long way to go!

Books From Packt


JIRA 4 Essentials
JIRA 4 Essentials

Firebug 1.5: Editing, Debugging, and Monitoring Web Pages
Firebug 1.5: Editing, Debugging, and Monitoring Web Pages

Tomcat 6 Developer's Guide
Tomcat 6 Developer's Guide

Managing Software Development with Trac and Subversion
Managing Software Development with Trac and Subversion

Java EE 6 with GlassFish 3 Application Server
Java EE 6 with GlassFish 3 Application Server

MySQL Admin Cookbook
MySQL Admin Cookbook

Microsoft SQL Azure Enterprise Application Development
Microsoft SQL Azure Enterprise Application Development

Least Privilege Security for Windows 7, Vista and XP
Least Privilege Security for Windows 7, Vista and XP


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software