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!!
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:
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>
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.
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:
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!
}
}
getUserName(descriptor) is the method that retrieves the user from the descriptor and that is done as follows:
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;
}
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 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!
}
}
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.
<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>
view-userCFFunction.vm looks like this:
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 |


RSS Feed