[JIRA] Dev – Where are OSWorkflow Transition Screens?

Found a piece that isn’t well documented. But as usual, I’m explaining my steps and then the solution. It’s no real big deal anyway. 🙂

Here’s the background:
Mission was to write a Groovy script that returns all the transition screens used across all workflows of a given JIRA workflow scheme (for documentation purposes). This looked pretty easier, until a collection of ‘com.opensymphony.workflow.loader.ActionDescriptor’ came up without any linked JavaDocs. That looked like a dead end.

Then, with a little bit of googling I came across ActionDescriptor source code, which wasn’t helpful either. getView() method looked promising because ‘Transition View’ is a synonym used in JIRA UI for ‘Transition Screen’. But disappointingly it was just returning string ‘fieldscreen’ for every transition. Aw.. snap! 😦

Next attempt is called the ‘Bruteforce method’ (not java.lang.reflect.Method of course 😉 ).

Since I was too lazy to read across the ActionDescriptor’s source code:

// workflow is type of com.atlassian.jira.workflow.JiraWorkflow
// I'm not explaining all my code here
workflow.getAllActions().each {
  it.class.getDeclaredMethods().grep {
    it.name.matches( "^get.*" )
  }.each {
    log.debug ( it.name )

It didn’t took much of time. With a bit of guessing I made my first attempt with getter method ‘getMetaAttributes()’. And it had the eye-catching piece I was looking for!

workflow.getAllActions().each {
  log.debug ( it.getMetaAttributes().toString() )
[opsbar-sequence:10, jira.description:, jira.fieldscreen.id:10000]
[opsbar-sequence:20, jira.description:, jira.fieldscreen.id:20000]
[opsbar-sequence:40, jira.description:, jira.fieldscreen.id:30000]
[opsbar-sequence:60, jira.description:, jira.fieldscreen.id:30000]

getMetaAttributes() returns a HashMap. So, finally…

def screens = [:]
workflow.getAllActions().each {
  def screenId = it.getMetaAttributes()["jira.fieldscreen.id"]
  if ( screenId != "" ) {
    def workflowAction = workflow.name + " - " + it.name
    def workflowActions = []
    if ( screens[screenId] ) workflowActions = screens[screenId]
    workflowActions.add ( workflowAction )
    screens[screenId] = workflowActions
for ( e in screens ) {
  log.debug ( "Transition Screen: " + e.key )
  e.value.each {
    log.debug ( "  Workflow action: " + it )

Nice! This HashMap (‘screens’) is all what I wanted. 🙂 Each transition screen, and then where it has been used.

So one last thing… I also wanted to have a one last peek into ActionDescriptor’s source code on GitHub. Only the writeXML() method gave an indication that metaAttributes is actually a HashMap. There was absolutely no indication that screen association information is kept as a ‘meta attribute‘.

Bottom line(s):

  1. If you are a JIRA developer and whenever you want to find screens associated to a workflow, don’t use getView(). Use getMetaAttributes() and then look for the attribute with key “jira.fieldscreen.id”. In other words, transition screen is stored as a meta attribute in each transition. Screen is referred by its numeric ID as a string.
  2. Since OpenSymphony doesn’t seem to go away from JIRA, and some already claim that OpenSymphony is dead, it would be worthwhile if Atlassian maintains their own repository of OSWorkflow docs and special remarks.

[Linux] Tip for heavy users of Ctrl + R

After a lengthy JIRA tip, I’m about to blog a Keyboard Ninja tip for users who do a lot of Ctrl + R thingy to navigate Bash history.

This came up when I didn’t want to use a session manager like PAC but rely on Bash history. I have a server IP address and SSH access.

So, instead of doing just ssh -v shaakunthala@xxx.xxx.xxx.xxx thing, I do this:

ssh -v shaakunthala@xxx.xxx.xxx.xxx #jirauat

Those who are familiar with scripting know this: Whatever you type after the hash sign won’t effect, because Bash interprets that as a comment. However, Bash history records the complete line.

Later, I don’t have to remember the IP address, but, when I type Ctrl + R followed by jirauat, it comes up quickly. Just hit Enter!

(reverse-i-search)`jiraua': ssh -v shaakunthala@xxx.xxx.xxx.xxx #jirauat

And that’s how you easily save few keystrokes plus an additional minute without having to recall numbers.

JIRA project migration with Google Spreadsheets

JIRA to JIRA project migration — most of the time this has been a headache when it comes to customer negotiations and compromises.

In my experience, usually it starts with a discussion round to identify what customer exactly needs. Some customers expect everything, including issue change history, comments, votes, etc. In this case we have to use the XML project import method. With status, resolution, priorities and custom field mapping this is a headache. In the near-perfect JIRA project migrations I’ve performed, I remember I used Groovy scripts to merge field values, convert additional issue types to labels, and etcetera. If you choose XML import, it needs a lot of technical work and beforehand testing when you have a demanding customer and a JIRA instance that isn’t expected to change much.

The other type of customers are simple-living minimalist people. 😉 They keep things simple, needs are simple and they just need the contents to be brought over. They are okay with basic things such as issue summary, description, type and status.

The method I’m going to discuss today is for the customers who lie in-between the two types of customers above. In my particular case I met a customer who needs comments and one of their custom fields to be brought over. File attachments and issue change history were not necessary for them. In this case downloading JIRA issues from Issue Navigator into Excel and using it as a CSV source won’t do the perfect import.

This led me to develop a custom Google Spreadsheets script. It uses JIRA REST API and fetches project issues into the spreadsheet. Then, it can be downloaded as CSV and imported into JIRA. Here’s a reduced version of the code I wrote – just to demonstrate:

var jql = encodeURIComponent("project = ABC ORDER BY key ASC");
var rows = [];
var s = 0;
while (true) {
  var response = fetchJIRA (baseurl, "/rest/api/2/search?jql=" + jql + "&maxResults=200&fields=*all%2Ccomment&startAt=" + s);
  // fetchJIRA () is a custom function I wrote 
  // to call JIRA REST API and return JSON.
  // It is not explained in this article.
  var max = response.maxResults;
  var c = response.issues.length;
  var t = response.total;
  if (c == 0) break;

  response.issues.forEach (function (i) {
    var row = [];
    row.push (i.key);
    row.push (i.fields.summary);
    // .. Include other fields
    rows.push (row);
    s = response.startAt + c;
rows.unshift (["Key", "Summary", ...]);

var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.insertSheet("Issues");
var iR = sheet.getRange(1, 1, rows.length, rows[0].length);

Actually it does more!

  • Status/ issue type/ resolution/ user account/ select-list custom field value mapping:
    Using a function like this maps statuses on-the-fly fetching, so we don’t have to worry about creating a workflow.

    function statusMapper (param) {
    var mapping = {
      "Validate":"In Verification",
    var ret = mapping[param];
    if (ret == undefined) ret = param;
    return ret;
    row.push (statusMapper (i.fields.status.name));
    row.push (typeMapper (i.fields.issuetype.name));
    row.push (i.assignee ? userMapper (i.assignee.name) : "shaakunthala");
    // ^^ Handy if target project doesn't allow unassigned issues.
    // It maps user accounts on-the-fly, 
    // while assigning all unassigned issue to a default user account.
  • Linking parent and sub-task on-the-fly. The script will do what’s asked in the Atlassian’s official documentation. After fetching issues, ParentIDs will be added using VLOOKUP spreadsheet formula applied by the script.
    var issueid_col = sheet.getLastColumn();
    var newCol = issueid_col + 1;
    var parentkey_cell1 = sheet.getRange(2, issueid_col - 1, 1, 1).getA1Notation();
    var formula ="=IFERROR(VLOOKUP(" + parentkey_cell1 + "," + issuesRange.getA1Notation().replace (/[0-9]/g, "") + "," + issueid_col + ",FALSE), \"\")";
    sheet.getRange(1, newCol, 1, 1).setValue("Parent ID");
    sheet.getRange(2, newCol, 1, 1).setFormula(formula)
         .copyTo(sheet.getRange(3, newCol, sheet.getLastRow() - 2, 1));
  • Mapping additional custom fields to labels. This is useful when you want to avoid creating any custom fields.
    if (i.fields.customfield_30000 != null)
      labels_array.push (i.fields.customfield_30000.value);
  • Comments, Affects Versions, Fix Versions, Labels, etc. can be fetched into different sheets of the same spreadsheet to be imported separately. The reason for this is to avoid large and much complicated CSV files that aren’t much human-readable.
    • In this case, still, target project doesn’t have to be empty. If target project is different, relationship between multiple CSV imports can be handled with issue key inserted to a text custom field as a reference ID.

So, here’s the pros and cons.


  1. What’s being transferred over are in human-readable format in the spreadsheet. It may be useful reference. This gives you a better way to verify any mapping and additional labeling you have done.
  2. Google Spreadsheets will take care of the CSV syntax and escaping. You don’t have to worry a thing about CSV errors. Just code what needs to sit in each column.
  3. Less time consuming compared to XML import, but brings over more data compared to minimal CSV import.


  1. Limitations of Google Spreadsheets: Google will terminate the script if it runs for more than 6 minutes. This can happen with larger projects. In this case you can use a Python script instead. But you’ll need to write additional code to map parents and sub-tasks.


Official documentation by Atlassian: https://confluence.atlassian.com/adminjiraserver071/importing-data-from-csv-802592885.html


There could be third-party add-ons that can perform hassle-free project migrations. If you can purchase such an add-on, this kind of approaches may not be necessary.

There is a method to import JSON instead of CSV. This syntax appears to be different from the issue object returned by the REST API. Therefore you’ll still need a custom script to perform JSON import.

Do crashbars protect your legs?

I am a motorcycle rider who encountered with a number motorcycle accidents and still in one piece.

Being a high speed fanatic, I read more and more about motorcycle safety following each accident. I explored ways to avoid trouble without losing the thrill of speed. As motorcycle training in Sri Lanka doesn’t commence in a professional way, most of the motorcycle riders have just a set of self-taught incomplete skills and beliefs.

When reading about motorcycle safety online I found a quite interesting thing called the “Hurt Report”. I’m not sure how far these findings of the Hurt Report are valid in Sri Lanka — and practically I experienced that the finding about crashbars isn’t quite true. Well, at least not for me.

It says,

Crash bars are not an effective injury countermeasure; the reduction of injury to the ankle-foot is balanced by increase of injury to the thigh-upper leg, knee, and lower leg.

So what does my experience say? I had a 150 cc Hero Honda Hunk (2011 model). In Sri Lanka it’s an average intermediate level motorcycle with a considerable body weight. When I ride I usually keep my legs close, each in tight contact with the fuel tank. In one of the two encounters where crashbars involved, right side crashbar bent due to direct impact with road surface. I decided to repair it instead of buying a new one – and voilà – it was not only bent, but also fractured!

I have seen many motorcycle owners ride bikes with crashbars just bent due to previous accidents, but this was the first time I saw one actually bent and cracked.

Now, imagine what could have happened to my kneecap if there was no crashbar and I kept my legs open! It’s a horrific feeling. Crashbars do protect – but also important to have your legs closed.

How I use Facebook on Android

Once installed their app, Facebook thinks they own our mobile devices.

At first, there was the peaceful all-purpose Facebook app. And some time later they separated messaging into another app, creating the Messenger. Initially it was at user’s discretion to use Messenger or Facebook app for messaging. Initially it looked polite but lately everyone was gradually forced to install Messenger. Another app… sigh! Personally, for me messaging is just messaging. I don’t want a childlike fairyland for just sending and receiving messages.

It didn’t stop there. Facecbook took unfair advantage of Samsung TouchWiz feature for displaying notification count on the app icon. When there are unread messages, Facebook app will add that up to notification count, forcing your mind to install the Messenger (or read them on computer to clear notifications).

And now it has come down to infiltrating our privacy through social engineering.

After all these took place, finally something is here that we can thank Facebook for. that is called Messenger Lite. That helps, because I don’t have to install that monstrous Messenger app just to read and send a line on the go. With Facebook app and Messenger Lite installed, here’s how I get maximum use of Facebook without letting it use me and eat up my phone.

Zero notifications

Go to Facebook app settings –> Notifications, and simply turn them all off at once. Who wants to be disturbed for every single Like and comment during an important office meeting?

Who wants fancy sounds?

Settings –> Sounds in the App – Turn it off. Just because I don’t want others to know that I’m using Facebook during a boring office meeting too!

Privacy of your contacts could be your responsibility too

Settings –> Continuous Contacts Upload – Turn it off.

I don’t want to be disturbed by every single new message

Go to Messenger Lite –> Notifications & Sounds.

Notifications: on. Notification previews: off. Vibrate: off. Light: on. Sound: off.

Now, one last thing. Messenger Lite app icon on GoogleNow launcher goes as just “Lite” that’s absurd (they think Messenger Lite is the only lite app?). Now I found a cool app called QuickShortcutMaker which will do the trick (you’ll figure out how to use it). I created a custom icon named “Messenger Lite” and removed the one placed by the official app installer. Now, all set.

However, these are my personal settings. Setting that prevent me from wasting too much attention on Facebook, making me use it only when needed. These settings may not work for you as-is, but I think it provides you with a good suggestion.

Mobitel LiveScreen මගඩිය

මෑතකදි මමයි අම්මයි දෙන්නම අළුත් මොබයිල් ෆෝන් දෙකකට මාරු වුණා. ඉතින් පරණ එකෙන් අළුත් එකට මාරු වෙද්දි අළුත් ෆෝන් එකට ගැළපෙන විධියට සිම් එකත් මාරු කරන්න වුණා. පරණ සිම් එක ඉවත් කරලා අපට Micro SIM එකකුත් Nano SIM එකකුත් තිබුණු පරණ දුරකථන අංක වලටම ලබාගන්න සිදුවුණා.

අළුත් සිම් දාපු හැටියේ මෙන්න එනවා SMS එකක්, Welcome to LiveScreen කියලා… මේ මොකක්ද? අපි ඒ ගැන ඒ හැටි හිතුවේ නෑ. නමුත්, මෙන්න දවසක් දෙකක් ගියාට පස්සෙ එනවා pop-up messages.

Remedies for all your problems! Get 1 Home Remedy daily @Rs.30/ month. Click OK!

මම නම් දන්නවනේ මේ මොකක්ද කියල. හොඳ වෙලාවට අම්මා මේකට අහුවෙලත් නෑ. ෆෝන් එක පාවිච්චි කර කර ඉන්නකොට, එක්කෝ ෆෝන් එක සාක්කුවෙ තියෙද්දි, මේ පොප් අප් message එක එනවා. වැරදිලාවත් OK එක ක්ලික් වුණොත් රුපියල් තිහක් එක්ක බදු මුදලකට කෙළවෙනවා.

පිටකොටුවෙ පේමන්ට් එකේ බාල බඩු විකුණන තොරොම්බල් වෙළෙන්දෙකුගෙ තත්වයට මේ ආයතනත් පත් වෙලා දැන්. මේ ගැන Facebook හරහා කතා කරද්දි අහන ප්‍රශ්න වලින් ලිස්සලා යන්නත් Mobitel ආයතනය ප්‍රවේසම් වෙනවා. 🙂

ඉතින් මේ මගඩිය නවතින වගක් පේන්න නැති නිසා අළුතින් Mobitel SIM එකක් ගන්න අයටත්, පරණ නොම්මරේටම අළුත් SIM එකක් Mobitel වෙතින් ලබාගන්නා අයටත්, SIM එක ෆෝන් එකට දාගෙන ඒක ඇක්ටිව් වුණු ගමන්ම කළ යුතු පළවෙනිම කාරිය විධියට LiveScreen ඉවත් කරන හැටි කියා දෙන්න.

Menu (app drawer) –> SIM Toolkit –> LiveScreen –> Activate –> Deactivate –> Confirm

හැක්ඃ! Confirm කරන්නත් අහනවා.. 😀 හැම දේම අද කාලේ “OK Google, get me a home remedy for…” කියල හොයාගනිද්දි මාසෙට රුපියල් තිහක් ගෙවල දවසට SMS එක ගානේ ගෙන්නගන්න අපට පිස්සු නෑ. 🙂

Wunderlist Today wallpaper for Ubuntu

Wunderlist again. Currently this is the only Microsoft thing that I use – apart of Windows servers involved in my DevOps job.

Wunderlist hasn’t been treating nice for Ubuntu recently. So I thought about working on something that works for Ubuntu. Then here I got an idea of a very small app that will embed your day action items into the desktop wallpaper. Since I’m not very much into software development, this as a pilot project will make me comfortable for building something better for Ubuntu.

To be honest, first I was planning to do some browser DOM hacking and figure out how it works. Soon after, I found out that there’s a public API for Wunderlist (awesome, isn’t it?), and that can avoid the need of DOM hacking. So, that’s how wunderlist-ubu-wallpaper started as my pilot project.

It is still very young, and I’m sure my code needs a little cleanup as well. It’s just a 150 line Python script – not at all a big deal. Readme file has all the information you need to set it up on your Ubuntu. Run it and see what happens to your Ubuntu wallpaper. Isn’t that awesome?

14 years back when I was a kid, I was just making little Windows executables (I was a Windows user back then) with Visual Basic. After that I did programming to a some level when I was a systems administrator, but I was never permitted to release my work to the public for obvious reasons. After that, here comes my very first little code contribution to the world of Open Source.

By the way, my all other action items are overdue! 😳