In previous blogs post, we had discussed applying workflow on Liferay portal core assets. It is true that Liferay 6 integrates workflow system like jBPM or Kaleo on any assets, either core assets or custom assets. Out-of-the-box liferay 6 supports workflow working on following core assets: Blogs Entry, Comments, Document Library Document, Message Boards Message, Web Content and Wiki Page.
Furthermore, we also discussed applying workflow on any custom assets like Knowledge Base articles in Liferay 6 through plugins. We did answer the question what it was in that blogs post, but the question how to make it programmatically was pending.
This article will introduce how to add workflow capabilities on any custom assets in plugins. Knowledge Base articles will be used as an example, one of custom assets. And moreover, we will use Liferay portal 6.0.5 GA 3 as testing environment. Of course, you could use Liferay portal 6.0.5 or latest version, for instance, Liferay 6 EE.
Abstracted from the book: Liferay User Interface Developement.

Prepare plugin
First of all, let’s prepare a plugin with workflow capabilities, called Knowledge Base. Note that the plugin Knowledge Base here is used as an example only. You can have your own plugin as well.
Structure
The plugin Knowledge Base has following folder structure under the folder $PLUGIN_SDK_HOME/knowledge-base-portlet.
- admin: view JSP files for portlet Admin;
- aggregator: view JSP files for portlet Aggregator;
- display: view JSP files for portlet Display;
- icons: icon images files,
- js: JavaScript files,
- META-INF: context.xml;
- search: view JSP files for portlet Search;
- WEB-INF: web info specification; includes sub-folders classes, client, lib, service, sql, src, and tld.
As you can see, JSP files like init.jsp and css_init.jsp are resided in the folder $PLUGIN_SDK_HOME/knowledge-base-portlet
Services and Models
As you have been noticed, the plugin Knowledge Base has specified services and models with the package named com.liferay.knowledgebase. You would be able to find details at $PLUGIN_SDK_HOME/knowledge-base-portlet/docroot/WEB-INF/service.xml. Service-Builder in plugins SDK will automatically generate services and models against service.xml, plus XML files like portlet-hbm.xml, portlet-model-hints.xml, portlet-orm.xml, portlet-spring.xml, base-spring.xml, cluster-spring.xml,dynamic-data-source-spring.xml, hibernate-spring.xml, infrastructure-spring.xml, messaging-spring.xml and shard-data-source-spring.xml under the folder $PLUGIN_SDK_HOME/knowledge-base-portlet/docroot/WEB-INF/src/META-INF.
The service.xml specified knowledge base info as entries: Article and Template. The entry Article included columns: article Id as primary key, resource Prim Key, group Id, company Id, user Id, user Name, create Date, modified Date, parent resource Prim Key, version, title, content, description and priority; the entry Template included columns: template Id as primary key, group Id, company Id, user Id, user Name, create Date, modified Date, title, content and description.
By the way, the custom SQL scripts were provided at $PLUGIN_SDK_HOME/knowledge-base-portlet/docroot/WEB-INF/src/custom-sql/default.xml. In addition, resource actions – that is, permission actions specification - were provided at $PLUGIN_SDK_HOME/knowledge-base-portlet/docroot/WEB-INF/src/resource-actions/default.xml.
Of course, you can use Ant target build-wsdd to generate WSDD server configuration file $PLUGIN_SDK_HOME/knowledge-base-portlet/docroot/WEB-INF/server-config.wsdd and to use Ant target build-client plus namespace-mapping.properties to generate web service client JAR file $PLUGIN_SDK_HOME/knowledge-base-portlet/docroot/WEB-INF/client/known-base-portlet-client.jar. In brief, based on your own custom models and services specified in service.xml, you can easily build service, WSDD and web service client in plugins of Liferay 6 or above
Add Workflow Instance Link
First, you have to add workflow instance link and its related columns and finder in service.xml as follows.
<column name="status" type="int" />
<column name="statusByUserId" type="long" />
<column name="statusByUserName" type="String" />
<column name="statusDate" type="Date" />
<!-- ignore details -->
<finder name="R_S" return-type="Collection">
<finder-column name="resourcePrimKey" />
<finder-column name="status" />
</finder>
<!-- ignore details -->
<reference package-path="com.liferay.portal" entity="WorkflowInstanceLink" />
As shown in above code, the column element represents a column in the database, here four columns like status, statusByUserId, statusByUserName and statusDate are required for Knowledge Base workflow; the finder element represents a generated finder method, here the method finder R_S is defined as Collection for return-type with two columns, e.g., resourcePrimkey and status; where the reference element allows you to inject services from another service.xml within the same class loader. For example, if you inject the Resource entity, then you'll be able to reference the Resource services from your service implementation via the methods getResourceLocalService and getResourceService. You'll also be able to reference the Resource services via the variables resourceLocalService and resourceService.
Then, you need to run ANT target build-service to rebuild service based on newly added workflow instance link.
Add Workflow Handler
Liferay 6 provides pluggable workflow implementations, where developers can register their own workflow handler implementation for any entity they build. It will appear automatically in the workflow admin portlet so users can associate workflow entities with available permissions. To make it happening, we need to add Workflow Handler in $PLUGIN_SDK_HOME/knowledge-base-portlet/docroot/WEB-INF/liferay-portlet.xml of plugin as follows.
<workflow-handler>com.liferay.knowledgebase.admin.workflow.ArticleWorkflowHandler</workflow-handler>
As shown in above code, the workflow-handler value must be a class that implements com.liferay.portal.kernel.workflow.BaseWorkflowHandler and is called when workflow is run.
Of course, you need to specify ArticleWorkflowHandler under the package com.liferay.knowledgebase.admin.workflow. The following is snippet.
public class ArticleWorkflowHandler extends BaseWorkflowHandler {
public String getClassName(){/* ignore details */};
public String getType(Locale locale) {/* ignore details */};
public Article updateStatus( int status, Map<String, Serializable> workflowContext) throws PortalException, SystemException {/* ignore details */};
protected String getIconPath(ThemeDisplay themeDisplay) {/* ignore details */};
}
As you can see, ArticleWorkflowHandler extends BaseWorkflowHandler and overrode the methods getClassName, getType, updateStatus and getIconPath. That’s it.
Add the method updateStatus
As mentioned in previous section, you added method updateStatus in ArticleWorkflowHandler. Now you should provide implementation of the method updateStatus in com.liferay.knowledgebase.service.impl.ArticleLocalServiceImpl.java. The following is sample code.
public Article updateStatus(long userId, long resourcePrimKey, int status, ServiceContext serviceContext) throws PortalException, SystemException {
/* ignore details */
// Article
Article article = getLatestArticle(resourcePrimKey, WorkflowConstants.STATUS_ANY);
articlePersistence.update(article, false);
if (status != WorkflowConstants.STATUS_APPROVED) { return article; }
// Articles
// Asset
// Social
// Indexer
// Attachments
// Subscriptions
}
As shown in above code, it first gets latest article by resourcePrimKey and WorkflowConstants.STATUS_ANY. Then it updates the article based on workflow status. And moreover, it updates articles display order, asset tags and categories, social activities, indexer, attachments, subscriptions, etc.
After adding new method at com.liferay.knowledgebase.service.impl.ArticleLocalServiceImpl.java, you need to run ANT target build-service to rebuild services.
Add workflow-related AUI tags
Now it is time to add workflow related AUI tags at $PLUGIN_SDK_HOME/knowledge-base-portlet/docroot/admin/edit_article.jsp. First of all, add AUI input workflow action with value WorkflowConstants.ACTION_SAVE_DRAFT.
<aui:input name="workflowAction" type="hidden" value="<%= WorkflowConstants.ACTION_SAVE_DRAFT %>" />
As shown in above code, the default value of AUI input workflowAction was set as SAVE DRAFT with type hidden. That is, this AUI input is invisible to end users.
Afterwards, it would be better to add workflow messages by UI tag liferay-ui:message, like a-new-version-will-be-created-automatically-if-this-content-is-modified for WorkflowConstants.STATUS_APPROVED, and there-is-a-publication-workflow-in-process for WorkflowConstants.STATUS_PENDING.
<c:choose>
<c:when test="<%= status == WorkflowConstants.STATUS_APPROVED %>"> <div class="portlet-msg-info">
<liferay-ui:message key="a-new-version-will-be-created-automatically-if-this-content-is-modified" />
</div> </c:when>
<c:when test="<%= status == WorkflowConstants.STATUS_PENDING %>">
<div class="portlet-msg-info">
<liferay-ui:message key="there-is-a-publication-workflow-in-process" />
</div></c:when>
</c:choose>
And then add AUI workflow status tag aui:workflow-status at $PLUGIN_SDK_HOME/knowledge-base-portlet/docroot/admin/edit_article.jsp.
<c:if test="<%= article != null %>">
<aui:workflow-status id="<%= String.valueOf(resourcePrimKey) %>" status="<%= status %>" version="<%= GetterUtil.getDouble(String.valueOf(version)) %>" />
</c:if>
Finally you should add JavaScript to implement the function publishArticle() as follows.
function <portlet:namespace />publishArticle() {
document.<portlet:namespace />fm.<portlet:namespace />workflowAction.value = "<%= WorkflowConstants.ACTION_PUBLISH %>";
<portlet:namespace />updateArticle();
}
As you can see, the workflow action value is set as WorkflowConstants.ACTION_PUBLISH.
You did add workflow capabilities on Knowledge Base articles in plugins. From now on, you would be able to apply workflow on Knowledge Base articles through Control Panel, as shown in the blogs post applying-workflow-on-custom-assets-in-liferay-6-through-plugins.
Where would you find sample code - Knowledage base plugin with workflow capabilities?
You can simply download WAR (with source code) of Knowledge Base plugin with workflow capabilities from
knowledge-base-portlet-6.0.6.1.war
And then deploy it. You would be able to see portlets: Knowledge Base Admin (managing knowledge base articles and templates), Knowledge Base Aggregator (publishing knowledge base articles), Knowledge Base Display (displaying knowledge base articles) and Knowledge Base Search (ability to search knowledge base articles).
You may be interested in the latest version of Knowledge Base plugin with workflow capabilities. Thus you can find latest code at SVN like:
svn://svn.liferay.com/repos/public/plugins/trunk/portlets/knowledge-base-portlet
Summary
As you can see, you would be able to add workflow capabilities on any custom assets generated by Service-Builder in plugins. The asset Knowledge Base articles is one of them (custom assets).Try it now, you would be able to have workflow capabilities on your own custom assets.
Last but not least, I'd like to send thanks to Peter Shin and Brian Chan, who did a great job to make Knowledge Base plugin a reality in Liferay portal 6.

