So what are workflow conditions? They determine whether a workflow action is available or not. Considering the importance of workflow in installations and how there is a need to restrict the actions either to a set of people, roles etc or based on some criteria (Eg: the field is not empty!), writing workflow conditions is inevitable.
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 condition module in the plugin descriptor looks like this:
class="com.jtricks.RoleConditionFactory">
<description>Role Based Workflow Condition</description>
<condition-class>
com.jtricks.RoleCondition
</condition-class>
<resource type="velocity" name="view"
location="templates/com/jtricks/view-roleCondition.vm"/>
<resource type="velocity" name="input-parameters"
location="templates/com/jtricks/edit-roleCondition.vm"/>
<resource type="velocity" name="edit-parameters"
location="templates/com/jtricks/edit-roleCondition.vm"/>
</workflow-condition>
Now we come to the next important class which is the condition-class. This is the place where the actual condition is checked and we need to extend AbstractJiraCondition here. We will more about both these classes shortly.
We also have 3 views in a condition depending on your requirement (Much similar to post functions!). 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.
Let us now look a bit deeper.
RoleConditionFactory
As mentioned earlier, this is the class to define the inputs and set the velocity context. In out case we have a single input 'project role'. Here is what the class look like:
private static final String ROLE_NAME = 'role';
private static final String ROLES = 'roles';
private static final String NOT_DEFINED = 'Not Defined';
private final ProjectRoleManager projectRoleManager;
public RoleConditionFactory(ProjectRoleManager projectRoleManager) {
this.projectRoleManager = projectRoleManager;
}
@Override
protected void getVelocityParamsForEdit(Map velocityParams, AbstractDescriptor descriptor) {
velocityParams.put(ROLE_NAME, getRoleName(descriptor));
velocityParams.put(ROLES, getProjectRoles());
}
@Override
protected void getVelocityParamsForInput(Map velocityParams) {
velocityParams.put(ROLES, getProjectRoles());
}
@Override
protected void getVelocityParamsForView(Map velocityParams, AbstractDescriptor descriptor) {
velocityParams.put(ROLE_NAME, getRoleName(descriptor));
}
@SuppressWarnings('unchecked')
public Map getDescriptorParams(Map conditionParams) {
if (conditionParams != null && conditionParams.containsKey(ROLE_NAME))
{
return EasyMap.build(ROLE_NAME, extractSingleParam(conditionParams, ROLE_NAME));
}
// Create a 'hard coded' parameter
return EasyMap.build();
}
private String getRoleName(AbstractDescriptor descriptor){
//Extract Role from the workflow
}
private Collection getProjectRoles(){
//Get list of project Roles
}
}
getRoleName(descriptor) is the method that retrieves the role from the descriptor and that is done as follows:
if (!(descriptor instanceof ConditionDescriptor)) {
throw new IllegalArgumentException('Descriptor must be a ConditionDescriptor.');
}
ConditionDescriptor conditionDescriptor = (ConditionDescriptor) descriptor;
String role = (String) conditionDescriptor.getArgs().get(ROLE_NAME);
if (role!=null && role.trim().length()>0)
return role;
else
return NOT_DEFINED;
}
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.
getProjectRoles() does nothing but retrieves all the project roles:
return Collections.unmodifiableCollection(projRoles);
Let us now move to the actual condition class. Here is how it looks like:
private static final String ROLE_NAME = "role";
public boolean passesCondition(Map transientVars, Map args, PropertySet ps) throws WorkflowException {
Issue issue = getIssue(transientVars);
User user = getCaller(transientVars, args);
Project project = issue.getProjectObject();
String roleName = (String) args.get(ROLE_NAME);
return projectRoleManager.isUserInProjectRole(user, projectRoleManager.getProjectRole(roleName), project);
}
}
Templates
Before We wrap up, let us quickly look at the velocity templates. We are using the same template 'edit-roleCondition.vm' for both input-parameters and edit-parameters.
<td align="right" valign="top" bgcolor="#fffff0">
<span class="label">Project Role:</span>
</td>
<td bgcolor="#ffffff" nowrap>
<select name="role" id="role">
#foreach ($field in $roles)
<option value="$field.name"
#if ($field.name.equals($role))
SELECTED
#end
>$field.name</option>
#end
</select>
<br><font size="1">Select the role in which the user should be present!</font>
</td>
</tr>
view-roleCondition.vm looks like this:
User should have $role Role!
#end
We now have our condition ready. Deploy the plugin into jira-home/plugins/installed-plugins (WEB-INF/lib if you created plugin-1 version), Create the Condition in a valid workflow, Add yourself to the relevant project roles and test it!!
More details on workflow condition module can be found here! Njoy!!
Download the full source code at the end of the page. And let us know your comments/feedback!
Note: The tutorial is just to explain the basic concepts of Conditions. Modify the buisness logic accordingly!

role-based-condition.zip |