Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

Getting started with JIRA 6.4 dashboard gadget development

Test Testington March 26, 2015

Hi, I need help getting started. The documentation and tutorials seem outdated and don't work. I managed to get "Hello World" to work and I added some UserPrefs, but the "edit" button on the dashboard does nothing. I tried to use some #-directives and they just render as text in the gadget.

I would really appreciate some help getting started in the right direction. A simple tutorial for creating a gadget that works in JIRA 6.4 or something. I just need a config screen, and to query JIRA with JQL (I assume). My gadget is going to draw a graph using Google charts (but that's the easy part).

Please help me!

3 answers

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

4 votes
Volodymyr Krupach
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
March 26, 2015

Hi Test smile

Recently I faced the same problem and only way to get working addon was digging the code of existing atlassian's gadgets and it cost me some time and nerves. I still plan to write the tutorial but as always short on time. For now hare are code snippets to give you a quick start.

in atlassian-plugin.xml you need to have entry for gadget and probably some JavaScript file:

<!-- In case you want to store labels in your-gadget.properties or support i18n -->
  <resource type="i18n" name="i18n" location="your-gadget" />  
 
<web-resource key="your-gadget-resources" name="your-gadget-resources">
    <resource type="download" name="your-gadget.js" location="your-gadget.js">
      <property key="content-type" value="text/javascript" />
    </resource>
  </web-resource>
  <gadget location="your-gadget.xml" key="your-gadget-gadget" name="your-gadget-gadget" />

your-gadget.xml should look like:

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="__MSG_your-gadget.name__" description="__MSG_your-gadget.description__" author="vkrupach">
    <Optional feature="gadget-directory">
      <Param name="categories">
        JIRA
      </Param>
    </Optional>
    <Require feature="setprefs" />
    <Require feature="dynamic-height" />
    <Require feature="views" />
    <Require feature="oauthpopup" />
    #oauth
    #supportedLocales("gadget.common,your-gadget")
  </ModulePrefs>
  <UserPref name="setting1" datatype="hidden" default_value="" />
  <UserPref name="setting2" datatype="hidden" default_value="" />
  <UserPref name="isConfigured" datatype="hidden" default_value="false" />
  <UserPref name="refresh" datatype="hidden" default_value="false" />
  <Content type="html" view="profile,canvas,home"><![CDATA[
    #requireResource("com.atlassian.jira.gadgets:g-filter-results")
    <script type="text/javascript">
      var contextPath = "__ATLASSIAN_BASE_URL__";
    </script>    
    #requireResource("atlassian-plugin.key:your-gadget-resources")
    #includeResources()
  ]]></Content>
</Module>

setting1 and setting2 are config fields specific for the gadget.

#requireResource("atlassian-plugin.key:your-gadget-resources") includes your JavaScript. atlassian-plugin.key should be replaced by key you have in atlassian-plugin.xml: <atlassian-plugin key="..."
 

And main JavaScript "magic part":

(function() {
  var drawCahrt = function(view, setting1, setting2) {
	// your code here
    ...
  };
  AJS.Gadget({
    baseUrl : contextPath, // we set it in your-gadget.xml
    config : {
      onResizeAdjustHeight : true,
      descriptor : function(args) {
        var gadget = this;
        return {
          // I need to validate my setting1 and setting2 on server so there I have java custom REST code. I think you can remove the "action" property. 
          action : "/rest/your-gadget/latest/validate",
          theme : function() {
            // copied this from the "source" addon. Not sure if I need it.
            if (gadgets.window.getViewportDimensions().width &lt; 450) {
              return "gdt top-label";
            } else {
              return "gdt";
            }
          }(),
          // initialize settings field. AJS.gadget.fields.nowConfigured() is for update interval.
          fields : [ {
            userpref : "setting1",
            label : gadget.getMsg("your-gadget.config.setting1"),
            type : "text",
            value : gadget.getPref("setting1")
          }, {
            userpref : "setting2",
            label : gadget.getMsg("your-gadget.config.setting2"),
            type : "text",
            value : gadget.getPref("setting2")
          }, AJS.gadget.fields.nowConfigured() ]
        };
      }
    },
    view : {
      onResizeAdjustHeight : true,
      enableReload : true,
      template : function(args) {
        var gadget = this;
        this.getView().empty();
        var view = AJS.$(this.getView());
        drawCahrt(view, args.setting1FromServer, args.setting2FromServer);
        // JRADEV-3464: Resizing the gadget after a considerable timeout to make sure bottom isn't cutoff
        setTimeout(function() {
          gadget.resize();
        }, 500);
      },
      // Based on my setting1 and setting2 I do some work on the server. By configuring "args" section we ask the gadget to call 2 custom REST methods passing setting1 and setting2 as arguments. Results returned by the REST are stored as setting1FromServer and setting2FromServer and I pass them to my "do job javascript code". You can skip the "args" if you do not need to do anything on the server and just pull the settings via gadgets.util.unescapeString(this.getPref("setting1").
      args : [ {
        key : "setting1FromServer",
        ajaxOptions : function() {
          return {
            url : "/rest/your-gadget/latest/setting1",
            data : {
              setting1 : gadgets.util.unescapeString(this.getPref("setting1"))
            }
          };
        }
      }, {
        key : "setting2FromServer",
        ajaxOptions : function() {
          return {
            url : "/rest/your-gadget/latest/setting2",
            data : {
              setting2 : gadgets.util.unescapeString(this.getPref("setting2"))
            }
          };
        }
      } ]
    }
  });

})();
Test Testington March 26, 2015

Hi Volodymyr, Many thanks for your code. I will try to make sense of it and let you know if it works. The first thing I noticed is that you have atlassian-plugin.xml where as I only have gadget.xml. When I add the gadget to JIRA via URL it points to the gadget.xml so where does the plugin.xml come in?

Volodymyr Krupach
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
March 26, 2015

Hello! Looks like you are developing standalone gadget and I put sample for gadget packaged as atlassian plugin. More info here: https://developer.atlassian.com/display/GADGETS/Packaging+your+Gadget+as+an+Atlassian+Plugin. Anyway I guess that JavaScript AJS.Gadget structure is the same for the gadget. Please update us with your status!

Test Testington March 27, 2015

Hi, I see that #-directives are not supported in the stand alone gadget, but I don't see how to include the required resources without them. I get AJS is undefined error in the console. I would prefer to get the stand alone to work because it sounds like it should be a lot simpler than making a plugin.

Volodymyr Krupach
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
March 27, 2015

Hi! Try to include scripts via <script> tag or start prototyping javascript directly in the gadget.xml

Volodymyr Krupach
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
March 27, 2015

AJS.Gadget is part of JIRA so it's undefined for the standalone gadget. The proven way is to search for working standalone gadget and look into sources.

Test Testington March 27, 2015

Hi, I am really struggling to find a working stand alone gadget that has any of the features I need (config edit screen and querying JIRA). The best I could find is this tutorial which gives me OAuth error: consumer_key_unknown https://developer.atlassian.com/jiradev/jira-platform/gadgets/tutorial-writing-a-jql-standalone-gadget (and I don't have confluence).

Volodymyr Krupach
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
March 27, 2015
Test Testington March 30, 2015

I tried nagy's work around and eventually got it to work by removing the /jira/ from the url, but I think I actually need to access the REST API to retrieve stuff like hours billed. I gave up trying to get the stand alone gadget to work. It is so poorly documented and It seems it's just not possible to create anything functional from the info that's available online. I have now installed the Atlassian Plugin SDK. I guess I am just going to have to build a full gadget plugin.

Volodymyr Krupach
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
March 30, 2015

My guess is that oauth does not work for the stand alone gadgets. Anyone can confirm/disconfirm this?

Volodymyr Krupach
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
April 2, 2015

It's too late and it's just a guess but maybe it will help someone: I guess that "OAuth error: consumer_key_unknown" (please see Test Testington, Mar 27, 2015) is caused by not configured or missconfigured Application Link. Step 7 of the tutotial shows "Oauth Administration: page that was replaced by Application Links in newer JIRA's.

Test Testington April 2, 2015

Hi again. It's true that I did not have an application link, however in my case I am building a JIRA dashboard gadget which should get data from JIRA. It doesn't seem possible to create an application link from JIRA to JIRA. The tutorial is for a confluence gadget and therefore needs an application link to connect to JIRA.

Volodymyr Krupach
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
April 2, 2015

Hi Test! I am a bit confused :-). If it's a standalone gadget, then it's not a part of a JIRA and rather independent web application that can be hosted under separate domain. Please correct me if I am wrong.

Test Testington April 2, 2015

I may be wrong (I am very new to all this), but a stand alone gadget is just an xml file (containing xml, html, css, js) which you host on a separate domain and then you load it into JIRA. JIRA caches it and renders the cached version in an iframe on the dashboard. I am not sure how the OAuth should work between JIRA and the cached gadget.

Volodymyr Krupach
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
April 2, 2015

I am not sure but guess that it should be under separate server. Here are some trails (https://developer.atlassian.com/about/templates-for-writing-tutorials/template-for-standalone-gadget-tutorial#TemplateforStandaloneGadgetTutorial-Step3.MaketheGadgetAvailableonaServer): Because you are developing a standalone gadget, you can host your gadget specification on any server that will make it available to a dashboard. Anyway this area is very poorly documented.

RobL December 12, 2016

Has anything recently changed?  I used this tutorial last year, and wrote a gadget; that used this technique to call Object/functions in secondary files, which worked as follows 

#requireResource("com.my-org.atla.plugin.my-plugin:cool-plugin")
#includeResources()

&lt;script type="text/javascript"&gt;
    CoolPlugin.setURL("__ATLASSIAN_BASE_URL__");
&lt;/script&gt;

Then CoolPlugin did the AJS.Gadget work in a function, along with setURL and UI stuff.

Around oct/nov i updated SDK etc, and now the same code, no longer sees CoolPlugin object as valid.

Appreciated!

 

Volodymyr Krupach
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 12, 2016

"gadget" plugin module is not supported since JIRA 7.1.x and you have to write "dashboard-item" plugin module.

You may have a look on Universal Gadget for JIRA plugin and it's sources. This project demonstrates how to write common gadgets for JIRA Server and JIRA Cloud.

RobL December 13, 2016

Wow, thanks man. I have a really tough time finding anything up to date for gadget/plugin doc/sdk examples.  I'll do some reading.  I can probably get it working again; but really want/need it to load from secondary resource files, and not all inline in CDATA block.  i'm sure i'm not alone there wink 

 

thanks Volodymyr! 

RobL December 13, 2016

thanks for the sample code, its very useful. 

However, i'm still struggling to use the 'replacement mode', as the reference to gadget.xml seems to not work.  

i've loaded up your entire source, in my local - and it works.  So i might just re-factor the entire thing to be standalone mode, using your code base as my starting point / editing.   Kinda lame sad 

Volodymyr Krupach
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 13, 2016

gadget.xml is not needed/supported for new "dashboard-item" plugin module. Just use mine sample as start point or try to find some dashboard-item in JIRA sources.

RobL December 13, 2016

yup - will do - thanks again dude smile.   I've wasted so many hours bouncing-around Atlassian non-existent, 4 year stale docs, or vaguely updated new docs.  Your projects a god-send. 

 

Cheers!

1 vote
Volodymyr Krupach
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
April 3, 2015

Googled more and finally found a way to store the preferences for standalone gadgets.

You will need to handle showing/hiding of the Configuration screen on you own (i.e. onclick for Edit menu option to show and hide on save) and your configuration options will be rather HTML elements than custom UserPref elements supported by the packaged JIRA gedget plugins.

Other way is to include all scripts that are pushed to the gadget plugins by #requireResource("com.atlassian.jira.gadgets:common") veocity macro, so you will have all javascripts provided by JIRA for plugin gadgets (and can use AJS.Gadget), but it does not look as a right approach.

Why this is not documented and we have to dig the info by pieces from the whole internet smile?

<?xml version="1.0" encoding="UTF-8" ?>

<Module>
  <ModulePrefs title="TEST: Save prefs" height="100">
  	<Require feature="dynamic-height"/>
	<Require feature="setprefs"/>
  </ModulePrefs>
  	<UserPref name="server" display_name="Server" datatype="string" default_value="http://arrogantbastard:8090/jira"/>
	<UserPref name="prefVal" display_name="FILTERVAL" datatype="hidden" />
  <Content type="html">
  <![CDATA[

	 <input type="text" id="txtInput"><input type="button" id="btnInput" value="Save pref">
	 <div id="divTest"></div>
	 
    <script type="text/javascript">
		
		(function() {
			
			var prefs;
			
			gadgets.util.registerOnLoadHandler(function() {
				prefs = new gadgets.Prefs();
				document.getElementById("divTest").innerHTML = "Current prefVal == \""+prefs.getString("prefVal")+"\"<br>";
			});
			
			function log(text) {
				if(console) console.log(text);
			}
			
			document.getElementById("txtInput").onkeyup = function(e) {
				var keycode;
				if(window.event) keycode =  window.event.keyCode;
				else if(e) keycode = e.which;
				if(keycode == 13) savePref(this.value);
			}
			document.getElementById("btnInput").onclick = function() {
				savePref(document.getElementById("txtInput").value);
			}
		  	
			function savePref(prefValue) {
				//console.log(prefs);
				var d = document.getElementById("divTest");
				d.innerHTML = "Current prefVal == \""+prefs.getString("prefVal")+"\"<br>";
				d.innerHTML += "Saving new setting... (prefVal = \""+prefValue+"\")<br>";
				//console.log("prefVal = \""+prefs.getString("prefVal")+"\". Saving new setting...");
				prefs.set("prefVal", prefValue);
				d.innerHTML += "Setting saved. prefVal == \""+prefs.getString("prefVal")+"\"<br>";
				//console.log("Setting saved.... Filter (prefs) @savePref = "+prefs.getString("prefVal"));
				//console.log(" ");
			}
			
		})();
    </script>

  ]]>
  </Content>
</Module>

The code snippet is from here: https://ecosystem.atlassian.net/browse/AG-162

Test Testington April 7, 2015

Thanks for the suggestions. I'll check them out when I have time and let you know.

0 votes
Volodymyr Krupach
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
April 3, 2015

Hello Test again smile.

Your last comment initiated a background thread in my mind that revealed to me that you are right and for simple gadget that access only JIRA REST we do not need any oauth. I mistakenly thought that standalone gadgets are served from the original server, but as I see they are parsed and served from the JIRA. Not sure if they are cashed since in my dev environment my local changes are picked up right away.

So I got "Hello word" from google gadgets tutorial, added ajax to call\ first JIRA REST method, put it under local apache, added to my dev JIRA through "Add Gadget to Directory" and it actually works.

Here is my quick dirty code:

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="hello world example" />
  <Content type="html">
     <![CDATA[
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script> 
$.ajax({
  url: "http://192.168.1.10:2990/jira/rest/api/2/application-properties",
})
  .done(function( msg ) {
    alert( "Data msg: " + msg );
  });
</script>
       Hello, world!
     ]]>
  </Content>
</Module>

Probably that's what you had at very begining smile.

Missing part is the Config options but I see that JIRA pushed to the iframe some javascripts that actually somehow should support storing of the configs. Can someone please contribute how to work with configs in Standalone gadgets.

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

TAGS
AUG Leaders

Atlassian Community Events