J tricks - Little JIRA Tricks
  • Home
  • Plugins ↓
    • JQL Tricks Plugin
      • JQL Tricks Plugin - Cloud
        • JQLT Cloud Installation
        • JQLT Cloud Configuration
        • JQLT Cloud Usage
        • JQLT Cloud License
        • JQLT Cloud FAQ
      • JQL Tricks Plugin - DC
        • JQLT DC Installation
        • JQLT DC Configuration
        • JQLT DC Usage
          • JQLT Issue Functions
          • JQLT Subtask Functions
          • JQLT Links Functions
          • JQLT Development Functions
          • JQLT Worklog Functions
          • JQLT Project Functions
          • JQLT Component Functions
          • JQLT Version Functions
          • JQLT Group Functions
          • JQLT User Functions
          • JQLT Date Functions
        • JQLT DC License
        • JQLT DC FAQ
        • JQLT DC Known Issues
        • JQLT DC Performance
      • JQL Tricks Cloud Migration
    • Simplified Planner
      • J-Planner Installation
      • J-Planner Configuration
      • J-Planner Usage
        • Creating a plan
        • Editing a plan
        • Deleting a plan
        • Viewing a plan
        • Modifying a plan
      • J-Planner FAQ
    • Atla-Search Plugin
      • Atla-Search Installation
      • Atla-Search Configuration
      • Atla-Search Usage
      • Atla-Search License
      • Atla-Search FAQ
    • Copy to subtask Plugin
    • All Plugins
  • Tutorials
  • The Book
  • Contact Us
  • Home
  • Plugins ↓
    • JQL Tricks Plugin
      • JQL Tricks Plugin - Cloud
        • JQLT Cloud Installation
        • JQLT Cloud Configuration
        • JQLT Cloud Usage
        • JQLT Cloud License
        • JQLT Cloud FAQ
      • JQL Tricks Plugin - DC
        • JQLT DC Installation
        • JQLT DC Configuration
        • JQLT DC Usage
          • JQLT Issue Functions
          • JQLT Subtask Functions
          • JQLT Links Functions
          • JQLT Development Functions
          • JQLT Worklog Functions
          • JQLT Project Functions
          • JQLT Component Functions
          • JQLT Version Functions
          • JQLT Group Functions
          • JQLT User Functions
          • JQLT Date Functions
        • JQLT DC License
        • JQLT DC FAQ
        • JQLT DC Known Issues
        • JQLT DC Performance
      • JQL Tricks Cloud Migration
    • Simplified Planner
      • J-Planner Installation
      • J-Planner Configuration
      • J-Planner Usage
        • Creating a plan
        • Editing a plan
        • Deleting a plan
        • Viewing a plan
        • Modifying a plan
      • J-Planner FAQ
    • Atla-Search Plugin
      • Atla-Search Installation
      • Atla-Search Configuration
      • Atla-Search Usage
      • Atla-Search License
      • Atla-Search FAQ
    • Copy to subtask Plugin
    • All Plugins
  • Tutorials
  • The Book
  • Contact Us

Writing a Workflow Post Function!

8/13/2010

22 Comments

 
Hope you all enjoyed the custom field tutorial! If you haven't got a chance and is interested, have a look here.

Let us now look at workflow post functions. Workflow post functions are very effective and heavily used. They allow you to do a lot of things when you progress on the workflow on an issue. Lot of customizations and work arounds take this route! And hence I am hoping this tutorial will help you wee bit in getting there!!


More theory on the workflow post functions can be found here.

As usual, have your skeleton plugin ready. Let us start with the atlassian-plugin.xml which you can find in your skeleton plugin.

The workflow post function module in the plugin descriptor looks like this:

<workflow-function key="set-usercf" name="Set User CF Post Function"
                class="com.jtricks.SetUserCFFunctionFactory">
    <description>Set Defined User or Current User if not defined!</description>
    <function-class>com.jtricks.SetUserCFFunction</function-class>

    <orderable>true</orderable>
    <unique>false</unique>
    <deletable>true</deletable>

    <resource type="velocity" name="view" location="templates/com/jtricks/view-userCFFunction.vm"/>
    <resource type="velocity" name="input-parameters" location="templates/com/jtricks/edit-userCFFunction.vm"/>
    <resource type="velocity" name="edit-parameters" location="templates/com/jtricks/edit-userCFFunction.vm"/>
</workflow-function>

As usual, key should be a unique value. The class here is used to extract the input parameters that are used in defining the post function. To make it clear, the inputs here are not the input while performing the workflow action but the inputs in defining the post function. In our case, we are going to take an input value (user name) and hence we create a class SetUserCFFunctionFactory extending AbstractWorkflowPluginFactory. The class must implement WorkflowPluginFunctionFactory and that will give you some unimplemented methods which we will see soon.

In case if you don't need any input parameters in you post function, you can replace the class with the JIRA class: com.atlassian.jira.plugin.workflow.WorkflowNoInputPluginFactory . An example would be set the 'current user' to a user custom field (instead of taking the user input!).

Now we come to the next important class which is the function-class. This is the place where the actual function is executed and we need to extend AbstractJiraFunctionProvider here. We will more about both these classes shortly.

We also have 3 views in a post function depending on your requirement. view, input-parameters and edit-parameters. As you might have guessed already, input-parameters and edit-parameters are required only if there are user inputs while setting the post function.

The details on the 3 parameters defines the following:
  • orderable - (true/false) Specifies if this function can be re-ordered within the list of functions associated with a transition. The postion within the list determines when the function actually executes.
  • unique - (true/false) Specifies if this function is unique; i.e., if it is possible to add multiple instances of this post function on a single transition.
  • deletable - (true/false) Specifies if this function can be removed from a transition.
Let us now look a bit deeper.

SetUserCFFunctionFactory

As mentioned earlier, this is the class to define the inputs and set the velocity context. In out case we have a single input 'user'. Here is what the class look like:

public class SetUserCFFunctionFactory extends AbstractWorkflowPluginFactory implements
        WorkflowPluginFunctionFactory {

    private static final String USER_NAME = "user";
    private static final String CURRENT_USER = "Current User";

    @Override
    protected void getVelocityParamsForEdit(Map velocityParams, AbstractDescriptor descriptor) {
        velocityParams.put(USER_NAME, getUserName(descriptor));
    }

    @Override
    protected void getVelocityParamsForInput(Map velocityParams) {
        velocityParams.put(USER_NAME, CURRENT_USER);
    }

    @Override
    protected void getVelocityParamsForView(Map velocityParams, AbstractDescriptor descriptor) {
        velocityParams.put(USER_NAME, getUserName(descriptor));
    }

    @SuppressWarnings("unchecked")
    public Map getDescriptorParams(Map conditionParams) {
        if (conditionParams != null && conditionParams.containsKey(USER_NAME))  {
            return EasyMap.build(USER_NAME, extractSingleParam(conditionParams, USER_NAME));
        }
        // Create a 'hard coded' parameter
        return EasyMap.build(USER_NAME, CURRENT_USER);
    }  

    private String getUserName(AbstractDescriptor descriptor){
        //Extract the user Name from the decriptor and return!
    }
}

Let us take it one by one. The 3 methods, as the name suggests, getVelocityParamsForInput, getVelocityParamsForEdit and getVelocityParamsForView are for populating the velocity parameters for the 3 diferent scenarios. In our case, we populate the params with the 'user' variable.

getUserName(descriptor) is the method that retrieves the user from the descriptor and that is done as follows:

private String getUserName(AbstractDescriptor descriptor){

    if (!(descriptor instanceof FunctionDescriptor)) {
        throw new IllegalArgumentException("Descriptor must be a FunctionDescriptor.");
    }

    FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
    String user = (String) functionDescriptor.getArgs().get(USER_NAME);

    if (user!=null && user.trim().length()>0)
        return user;
    else
        return CURRENT_USER;
}

Check if the descriptor is an instance of FunctionDescriptor and if it is, functionDescriptor.getArgs() gets you a Map with all the variables in it. In our case, we retrieve 'user'. Returns 'Current User' if the retrieved value is null or Empty.

We have one more method getDescriptorParams in the class and this just return a map of sanitized parameters which will be passed into workflow plugin instances from the values in array form submitted by velocity. More info on that here.

SetUserCFFunction

Time to move to the actual function class. Here is how it looks like:

public class SetUserCFFunction extends AbstractJiraFunctionProvider {

    public void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException {
        MutableIssue issue = getIssue(transientVars);
        String userName = (String) args.get("user");      

        // Do your business Logic now!

    }
}

When you extend the AbstractJiraFunctionProvider, you will have to implement the execute method and that is what will be executed once the workflow action is over. In our case, we retrieve the user name from the map and set it on the 'Test User' customfield on the issue (pure hard coding for the sake of this tutorial!). You can find the code for this purticular example at the end of the tutorial but this is in essence is what you do in the function class.

Templates

Before I warp up, let us quickly look at the velocity templates. I am using the same template 'edit-userCFFunction.vm' for both input-paramers and edit-parameters.

<tr bgcolor="#ffffff">
    <td align="right" valign="top" bgcolor="#fffff0">
        <span class="label">UserName:</span>
    </td>
    <td bgcolor="#ffffff" nowrap>
        <input type="text" name="user" value="$user"/>
        <br><font size="1">Enter the userName to be set on the Test User CustomField</font>
    </td>
</tr>

$user is what we populated in the getVelocityParamsForInput and getVelocityParamsForEdit earlier!

view-userCFFunction.vm looks like this:

The 'Test User' CF will be set with value : $user

This will appear in the post functions tab on the workflow tranisition once added succesfully.

We now have our post function ready. Create a user customfield 'Test User', deploy the plugin into jira-home/plugins/installed-plugins (WEB-INF/lib if you created plugin-1 version), Create the Post Function in a valid workflow and test it!!

More details on post function module can be found here! Njoy!!

Download the full source code below. And feel free to post your comments/feedback!

Note: The tutorial is just to explain the basic concepts of Post Functions. Please don't put too much thought into the business logic! And don't forget to create a user Custom Field 'Test User' if you want to get the tutorial working.


set-usercf-postfunction.zip
File Size: 6 kb
File Type: zip
Download File

22 Comments
Jeff D
9/20/2010 08:12:07 pm

Blimey! Exactly what I was looking for. Thanks.

Reply
J-Tricks
9/21/2010 06:30:05 am

Glad it helped :)

Reply
eva
6/21/2011 06:30:43 am

I did the same steps but I am not seeing the new function on the post-function tab. Help?

Reply
J-Tricks
6/22/2011 03:21:56 am

eva, You mean you are not seeing it when you try to add a post function?

If so, can you check if the plugin is loaded or not bu going to Administration > Plugins?

Reply
Joe C
8/12/2013 02:40:05 pm

I am trying to add the post function to a workflow.
i can see my plugin loaded and enabled under (Admin-> Plugin) .
For the post functions available for the transition are"Events" Do i need to add an event first? how do i associate an event with the post function from the plugin?
Any help would be great. Thanks

Reply
J-Tricks
8/27/2013 08:57:48 am

Sorry, I didn't understand. Throwing an event is just another post function. If your custom post function is enabled in the plugin, you should be able to see it while adding a post function.

See https://confluence.atlassian.com/display/JIRA/Advanced+Workflow+Configuration#AdvancedWorkflowConfiguration-Postfunctions on how to add a post function.

Reply
Joe c
8/12/2013 03:46:49 pm

OK So i found the link on the "Edit transition" popup page.
I think previously i was trying "Update parameters of the Fire Event Function for this transition."

Reply
J-Tricks
8/27/2013 08:59:15 am

Ah ok. Didn't notice that you already figured it out ;)

Reply
Iryna
11/13/2013 08:41:10 pm

Hi.
Do you know how to set a text description that is shown in the list of post-functions (when you look in you workflow), if i don't have any parameters to get. My Function is like this:

<workflow-function key="my-key" name="someText" class="com.atlassian.jira.plugin.workflow.WorkflowNoInputPluginFactory">
<description>descText</description>
<function-class>PostFuctionClass</function-class>
</workflow-function>
and my PostFuctionClass implements FunctionProvider

Reply
J-Tricks
11/19/2013 02:27:11 pm

The description is usually what you see in the <description> tag. Is that not working?

Reply
Faz
9/1/2014 06:10:54 am

Hi.

Is it possible to inject a velocity template through a post transition function. I am trying to apply some business logic during the execute function and I wanted to notify the user of the result through a jira status banner. I tried using an event listener but later found out that event listeners are not connected in any way to the UI. Is it the same case for workflow post functions? If so is there any other alternative to inject a velocity template containing some js or injecting some js to the ui.

Reply
J-Tricks
9/2/2014 05:30:04 am

Yes, it is the same thing for post functions too. Never tried injecting the js to UI. Should be possible for sure. Similar to what create issue does. Maybe one for another tutorial.

Reply
Terry
9/4/2014 09:51:10 am

Hi Jobin,

First and foremost, thank you for writing the cookbook. Hands down the most helpful book on programming in Jira. It has really saved my bacon!

Second, I keep running into the need to prompt a user for some input during a post function where I am creating either linked issues or subtasks. For example, I want to prompt the user to select from a list of possible issues to link to, or have them provide a subject for a new issue that will get linked.

Can you point me in the right direction on this?

Reply
j-Tricks
9/4/2014 04:22:45 pm

Glad to know you like the book :)

Regarding your question, do you mean a screen after the transition is done on an issue? That is not possible. You will have to make use of the workflow transition screen to capture any input that you will need from the user.

Reply
Terry
9/5/2014 04:02:21 am

Yes, I would like to capture user input after a transition. The problem with the transition screens is that they act upon the current issue, and I need to create and link a new issue. I was hoping that I was missing something obvious. :-)

J-Tricks
9/7/2014 01:35:33 am

Unfortunately, that is not possible. You will have to capture any inputs as part of the current issue and then use it to create the new one.

A better way probably would be to do it as a custom action instead of doing it as part of workflow. The custom action can also do the workflow transition.

Nadir link
11/21/2014 05:17:26 am

Hello,
Thanks for all tutorials.

Reply
agung
3/17/2015 12:26:30 am

Hello,
Is it possible to get current workflow transition id, current workflow id in post function factory ?

Reply
Georges
5/11/2015 12:05:40 am

Hi,
I am new to developing workflow postfunctions.
I need to know how to create a postfunction that adds a comment to the request. Could you please provide more details like where to write the java classes, etc,..
Thank you.

Reply
J-Tricks
5/11/2015 01:19:59 am

Almost everything will be the same. You just need to write the code to add a comment inside the function class. Look for the following comment.
// Do your business Logic now!

Reply
Georges
5/11/2015 01:57:56 am

Is there any more detailed document that gives step by step directions from start to end?
Also I am using JIRA 6.4.2, is this compatible with it? or I need to switch back to JIRA 6.3.15?

J-Tricks
5/11/2015 10:10:33 am

None that I have seen!

Yes, it is compatible with 6.4.x.


Your comment will be posted after it is approved.


Leave a Reply.

    Enter your email address:

    Author

    Jobin Kuruvilla - Works in Adaptavist as Head of DevOps Professional Services. 

    Author of JIRA Development Cookbook and JIRA 5.x Development Cookbook.


    RSS Feed

    Categories

    All
    Acive Objects
    Ajs
    Book
    Components
    Condition
    Custom Fields
    Customization
    Events
    Gadgets
    Javascript
    Jql
    Listener
    Mail
    Permissions
    Plugin Framework
    Post Function
    Properties
    Remote Invocation
    Reporting
    Rest
    Scheduled Tasks
    Search
    Services
    Soap
    Summit
    User Interface
    Validator
    Webwork Actions
    Workflow

    Archives

    October 2016
    August 2016
    March 2016
    January 2016
    December 2015
    May 2014
    December 2013
    November 2013
    July 2013
    June 2013
    April 2013
    October 2012
    September 2012
    August 2012
    July 2012
    May 2012
    March 2012
    February 2012
    January 2012
    December 2011
    November 2011
    June 2011
    May 2011
    April 2011
    March 2011
    February 2011
    January 2011
    November 2010
    October 2010
    September 2010
    August 2010

SUPPORT
APPS
TUTORIALS
THE BOOK
© J-Tricks