Using the Script Engine in Workflow¶
Liferay’s workflow engine can be scripted using Groovy scripts embedded in XML workflow definitions and run during workflow execution.
Here are the workflow scripting topics:
Adding Scripts to Workflow Nodes¶
Workflow scripts can be invoked from actions in these workflow node types:
<fork>
<join>
<state>
<task>
Here’s the format for an action that invokes a script:
<actions>
<action>
<script>
<![CDATA[script code goes here]]>
</script>
<script-language>groovy</script-language>
</action>
...
</actions>
One common operation is to set the workflow status. For example, this script sets the workflow status to approved.
<script>
<![CDATA[
import com.liferay.portal.kernel.workflow.WorkflowStatusManagerUtil;
import com.liferay.portal.kernel.workflow.WorkflowConstants;
WorkflowStatusManagerUtil.updateStatus(WorkflowConstants.getLabelStatus("approved"), workflowContext);
]]>
</script>
<script-language>groovy</script-language>
Predefined Variables¶
Some variables are common to all node types, while others are exclusively available to task
nodes.
Variables Common to All Node Types¶
These variables are available from anywhere that you can run a workflow script:
Variable |
Description |
Usage |
---|---|---|
|
A workflow instance and corresponding instance token (the |
Use the injected token to retrieve its ID by calling |
|
The |
Technically, the logic works like this: if the |
|
The workflow context contains information you can use in scripts. |
The context is typically passed as a parameter, but all of the |
Variables Injected into Task Nodes¶
These variables are injected into Task Nodes:
Variable |
Description |
Usage |
---|---|---|
|
The task’s token itself is available in the workflow script. |
Use it to get its ID, for use in other useful programmatic workflow activities, like programmatic assignment. |
|
||
|
Lists the task’s assignees. |
|
|
If a task timer exists, get its ID by calling |
Script Example¶
At virtually any point in a workflow, you can use Liferay’s script engine to access workflow APIs or other Liferay APIs. Here are a few practical ways you can use workflow scripts:
Getting a list of Users who have a specific Role
Sending an email to the designated content approver with a list of people to contact if he is unable to review the content
Creating an alert to be displayed in the Alerts portlet for any User assigned to approve content
The following workflow script is written using Groovy and is used with a Condition
Node. The script uses Liferay’s asset framework to determine an asset’s category and uses the category to determine the correct approval process automatically. If the asset is in the legal
category, it is sent to the Legal Review
task upon submission. Otherwise, the asset is sent to the Default Review
task.
<script>
<![CDATA[
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.workflow.WorkflowConstants;
import com.liferay.portal.kernel.workflow.WorkflowHandler;
import com.liferay.portal.kernel.workflow.WorkflowHandlerRegistryUtil;
import com.liferay.asset.kernel.model.AssetCategory;
import com.liferay.asset.kernel.model.AssetEntry;
import com.liferay.asset.kernel.model.AssetRenderer;
import com.liferay.asset.kernel.model.AssetRendererFactory;
import com.liferay.asset.kernel.service.AssetEntryLocalServiceUtil;
import java.util.List;
String className = (String)workflowContext.get(
WorkflowConstants.CONTEXT_ENTRY_CLASS_NAME);
WorkflowHandler workflowHandler =
WorkflowHandlerRegistryUtil.getWorkflowHandler(className);
AssetRendererFactory assetRendererFactory =
workflowHandler.getAssetRendererFactory();
long classPK =
GetterUtil.getLong((String)workflowContext.get
(WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));
AssetRenderer assetRenderer =
workflowHandler.getAssetRenderer(classPK);
AssetEntry assetEntry = assetRendererFactory.getAssetEntry(
assetRendererFactory.getClassName(), assetRenderer.getClassPK());
List<AssetCategory> assetCategories = assetEntry.getCategories();
returnValue = "Default Review";
for (AssetCategory assetCategory : assetCategories) {
String categoryName = assetCategory.getName();
if (categoryName.equals("legal")) {
returnValue = "Legal Review";
return;
}
}
]]>
</script>
<script-language>groovy</script-language>
Note
A Condition node script’s returnValue
variable must be a valid transition name to determine the next task or state.
For Liferay Portal, a valid transition name is the transition’s <name>
element value as entered in the XML file or in the source view of the Process Builder. For Liferay DXP, when you view the source of the definition in the Process Builder, you must instead use the value of the transition ID as specified in the transition’s <id>
element.
See Crafting Workflow Definitions for links to downloadable workflow script examples.
Calling OSGi Services¶
Service Trackers retrieve OSGi services that are available. If the Service Tracker returns null for the service, that service is unavailable and you can do something appropriate in response.
Here’s a workflow script written in Groovy that uses a JournalArticleLocalService
to get an article count:
import com.liferay.journal.model.JournalArticle;
import com.liferay.journal.service.JournalArticleLocalService;
import com.liferay.portal.scripting.groovy.internal.GroovyExecutor;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.util.tracker.ServiceTracker;
ServiceTracker<JournalArticleLocalService, JournalArticleLocalService> st;
try {
Bundle bundle = FrameworkUtil.getBundle(GroovyExecutor.class);
st = new ServiceTracker(bundle.getBundleContext(), JournalArticleLocalService.class, null);
st.open();
JournalArticleLocalService jaService = st.waitForService(500);
if (jaService == null) {
_log.warn("The required service 'JournalArticleLocalService' is not available.");
}
else {
java.util.List<JournalArticle>articles = jaService.getArticles();
if (articles != null) {
_log.info("Article count: " + articles.size());
} else {
_log.info("no articles");
}
}
}
catch(Exception e) {
//Handle error appropriately
}
finally {
if (st != null) {
st.close();
}
}
The script tracks the service using the OSGi bundle of the class that executes the script. Since a com.liferay.portal.scripting.groovy.internal.GroovyExecutor
instance executes the script, the instance’s bundle is used to track the service.
Bundle bundle = FrameworkUtil.getBundle(GroovyExecutor.class);
Liferay’s Kaleo Workflow Engine and Liferay’s Script Engine makes for a powerful combination. When configuring your permissions, be aware of the potential consequences of poorly or maliciously written scripts inside a workflow definition.