How to add workflow capabilities on Knowledge Base articles or any custom assets in plugins

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.

Blogs
Hi Jonas ? Please give some update on how to write custom assets in Liferay 6.0.6.
Example here does not hold valid anymore with the new versions.
<workflowhandler/> doesnt seem to be the way of letting liferay know the workflow handler for the asset.


Thanks
Thanks, Mohd, Let me verify the same and come back to you shortly.
Hallo Jonas, any news on your verification?
@Lars and @Mohd, do you test knowledge-base-portlet in 6.0.6?

svn://svn.liferay.com/repos/public/plugins/branches/6.0.6/portlets/knowledge-base-portlet

You can check out above KB and test it.

Hope that it helps,

Thanks
@Lars and @Mohd, just applied the steps mentioned in the blogs, it is working well.

Steps to build WAR
1) get KB source code (from 6.0.6 branch)
2) fix a default issue
3) following steps mentioned in the blogs
4) deploy WAR

Do you need new WAR file - knowledge-base-portlet-6.0.6.1.war?
@Jonas thanks a lot,, it works.
Can you please write an article on writing plugins around workflow ? may be on custome assets, I believe that will cover many things.
Maybe a tutorial for integrating a workflow (single approver)in the "My greeting portlet" from the Liferay Developer Tutorials.

I tried to do it myself, but i get an XML-error "The markup in the document preceding the root element must be well-formed." when i want to add the workflow instant link to the service.xml
@Mohd and @Lars Thanks for your comments. I am trying to provide more detailed articles for workflow, specially releasing the same in the book: Liferay Cookbook as earlier as possible.
Jonas, great article! How can I apply workflow to approve new users? "Users" is not displayed in my LR 6 EE SP1. Thanks!
Andreas
Hi Andreas, this is a new feature - apply workflow on entities users. It should be available in 6.1 or EE 6 SP2, if you can wait.
Could you pls update , knowledge-base-portlet-6.0.6.1.war it seems not working
Pointing out this URL
http://www.liferay.com/c/document_library/get_file?uuid=0bb28d50-d680-4cd6-a15a-3ab4dbcf5437&groupId=31578
The url's for the war don't seem to be working any more.
And the svn url asks for a username password.
Where can i find this example war?
Thanks Jonas for sharing this..

I have downloaded very latest tomcat-liferay bundle LR-6.1.0-CE-Beta4, still I don't find proper way to implement Workflow on any OOB asset for example "User" model.

Assume that I want to implement Workflow on "User" asset. Meaning when new user is created, it should be assigned to some Work-flow definition e.g. single-approver definition. Then when it is approved by approver then only status may be changed to "Approved".

What I have learned as of now is, in order to have WF on any asset service.xml & liferay-portlet.xml should be changed..correct me if I am wrong.

And I don't find any proper way to modify/override these service.xml & liferay-portlet.xml.

Still for just POC purpose, I changed the following files in Portal source code directly to have work-flow on User asset:

1. service.xml to add columns & reference package path for Work-flow
2. liferay-portlet.xml to add UserWorkflowHandler

Now the problem is after changing this service.xml, portal does not allow me to build services again saying that "Could not find com.liferay.portal.tools.servicebuilder.ServiceBuilder. Make sure you have it in your classpath", though ServiceBuilder class is available under the same package.

What can be root cause here?

Also can you also suggest some other better way for above kind of requirement when Liferay's OOB asset needs to be customized to apply Workflow on it?

Thanks in advance,
Jay.
Great Work....Very helpful article but the Example WAR is not available for Download right now. Please kindly share it once again or provide some fresh link...Thanks
Hi, I'm new member.
I don't know how to import "knowledge-base-portlet" source code to like a normal portlet project - "sample-portlet" in Eclipse

thank a lot emoticon
I found it in this link emoticon
http://www.liferay.com/documentation/liferay-portal/6.0/development/-/ai/importing-existing-projects-into-liferay-ide
comment for new members like me :d
When I try to download the war file i get


Forbidden

You do not have permission to access the requested resource.

http://www.liferay.com/c/document_library/get_file?uuid=0bb28d50-d680-4cd6-a15a-3ab4dbcf5437&groupId=31578