Monday, August 31, 2015

Implement Kaleo Workflow for Custom Assets/Custom Portlet

Liferay already implemented workflow management for some of assets like Web content, Blogs, wikis, document library and Message Boards. These assets we can enable Kaleo workflow so that all submission of each assets going through the workflow management.

Liferay also have given support to implement workflow management to custom assets or we can say custom portlets and this is one of exciting thing in real time development. Liferay already have given workflow API to implement workflow to our custom assets/custom portlets.

Go through following Post for better understanding about workflow in Liferay portal.


Before implement Kaleo workflow to custom assets we need to check Liferay portal have workflow engine in the portal.

As we know that from Liferay 6.2 onwards Kaleo-web bundled with Liferay portal server so we don’t need to install again.

If older version then we need to download appropriate version form Liferay Market Place and deploy into Liferay portal.

Based on Liferay portal licence it can be available as CE and EE


Prerequisite:

Liferay Portal Should have workflow Engine and Kaleo default implementation for Liferay portal and out of box you can integrate with other workflow management and that should follow the JBPM (jBoss Business Process Management) Notation.

Download Kaleo-web from market place and it will be available as .lpkg and you can place it in Liferay deploy directory so that it will be deployed and this step only for older version not for Liferay 6.2.

You can also install it from Liferay Control panel there you can find market place application from there you can install it.


Now we are ready with Kaleo workflow engine for our Liferay portal so that we can enable this workflow for default assets and custom assets then each submission going through the workflow process.

Example Scenario for Kaleo Implementation for Custom Assets.

Assume we have Feedback form once we submit the feedback that should travel through workflow management and while in the process other authorized users review the feedback based on that it will be approved or rejected and this is our scenario for implementation.

Required Steps;

Create Portlet and Design Feedback form
Create service builder for portlet and define entity for store feedback data.
Implement Workflow for Feedback From/Feedback Custom Asset.

Create Portlet and Design Feedback form

Create Liferay MVC Portlet and Design feedback form with required fields and create Liferay MVC Portlet is very straight forward way.


Create service builder for portlet and define entity for store feedback data

Now create Service Builder for Feedback portlet and define entity in service.xml with required columns.

The following is entity in service.xml


<entity name="Feedback" uuid="true" local-service="true"
remote-service="false">
<column name="feedbackId" type="long" primary="true" />
<column name="feedbackDate" type="Date" />
<column name="feedbackText" type="String" />
<column name="feedbackSubject" type="String" />
<column name="feedBackStatus" type="int" />
<column name="statusByUserId" type="long" />
<column name="statusDate" type="Date" />
<column name="companyId" type="long" />
<column name="groupId" type="long" />
<column name="userId" type="long" />
<order>
<order-column name="feedbackId" order-by="asc" />
<order-column name="feedbackDate" order-by="desc" />
</order>
<finder name="GroupId" return-type="Collection">
<finder-column name="groupId" />
</finder>
<finder name="CompanyId" return-type="Collection">
<finder-column name="companyId" />
</finder>
<finder name="feedbackText" return-type="Collection">
<finder-column name="feedbackText" />
</finder>
<finder name="G_S" return-type="Collection">
<finder-column name="groupId" />
<finder-column name="feedBackStatus" />
</finder>
<reference package-path="com.liferay.portal" entity="User" />
<reference package-path="com.liferay.portlet.asset" entity="AssetEntry" />
<reference package-path="com.liferay.portal" entity="WorkflowDefinitionLink"></reference>
<reference package-path="com.liferay.portal" entity="WorkflowInstanceLink"></reference>
</entity>


Once you define entity run service build so that it will be crated required classes and interfaces. we will use these java classes’ methods to interact with database.

Implement Workflow for Feedback From/Feedback Custom Asset.

We are implementing workflow to custom asset and here Feedback is our custom asset. Liferay workflow management will use these assets to in the workflow management.

Asset Type: Feedback.

Note:

All workflow task will be identified by custom asset that is Feedback

Steps to Impalement Workflow for Custom Asset.

Implement Custom Workflow Handler to Custom Asset/Portlet
Register Custom Workflow handler with Portlet
Implement Asset Renderer Java Class
Implement Asset Renderer Factory Java Class
Register Asset Renderer Factory with Portlet.
Enable Workflow to Custom Asset
Start Workflow Instance to Each Asset Submission

Implement Custom Workflow Handler to Custom Asset/Portlet

When we implement workflow for custom asset/portlet first we need to implement workflow handler and in the implementation we will override some of methods based our requirement.
Custom workflow handle must extends BaseWorkflowHandler or it should implement WorkflowHandler interface.

When we enable workflow for custom asset then custom handle will be invoked and it will call the implemented methods.

The following is Syntax of Custom Workflow Handler.


public class FeedbackWorkflowHandler extends BaseWorkflowHandler{

}


Register Custom Workflow handler with Portlet

Now we need to register custom workflow handle with portlet and we will use Liferay-portlet.xml file. <workflow-handler/> is tag to register the workflow handle and all workflow mechanism for this portlet will be taken care by  registered custom handler.

Implement Asset Renderer Java Class

Asset Renderer class will provide full information of custom asset. And this we will use specially to show full details of asset information when task in the workflow process.
We need to extends BaseAssetRenderer and need to override some of methods

The following is Asset Renderer Syntax Implementation Class


public class FeedbackAssetRenderer extends BaseAssetRenderer{

}


Implement Asset Renderer Factory Java Class

Asset Renderer Factory class will provide asset object to Asset renderer class. And here we will get required asset object and pass to Asser Render.

The following is Syntax Implementation of AssetRendererFactory


public class FeedbackAssetRendererFactory extends BaseAssetRendererFactory {
   
}


Register Asset Renderer Factory with Portlet.

Now we need to register Asset Renderer Factory class with portlet in the Liferay-portlet.xml using <asset-renderer-factory/>. Asset Renderer factory provide required object for Asset and to show full details of asset then we will use asset object to showcase data.

Enable Workflow to Custom Asset

Enable Workflow to custom asset will be taken care by workflow handler and based on our custom handler our asset comes as part default configuration in the workflow there we will enable workflow to custom asset

Generally the following method we will use to enable workflow to Asset

Class: WorkflowDefinitionLinkLocalServiceUtil


public static void updateWorkflowDefinitionLink(long userId,
long companyId, long groupId, java.lang.String className, long classPK,
long typePK, java.lang.String workflowDefinition)


Note:

Our Asset will be comes under default workflow configuration list so we won’t write this code anywhere in the implementation.

Start Workflow Instance to Each Asset Submission

Now we will start workflow instance to each form submission so that asset will be going through workflow engine.

The following is method in WorkflowHandlerRegistryUtil


Method Signature

public static void startWorkflowInstance(
final long companyId, final long groupId, final long userId,
String className, final long classPK, final Object model,
ServiceContext serviceContext,
Map<String, Serializable> workflowContext)

Example Usage:

WorkflowHandlerRegistryUtil.startWorkflowInstance(
feedback.getCompanyId(), feedback.getGroupId(), themeDisplay.getUserId(),
Feedback.class.getName(), feedback.getPrimaryKey(), feedback,
serviceContext);


Before start workflow instance we need to insert entry in Asset Entry table so that when we fetch the workflow records it will be searched in asset entries table.

The following is insert Asset Entry


AssetEntryLocalServiceUtil.updateEntry(userId, feedback.getGroupId(),
Feedback.class.getName(), feedback.getFeedbackId(),
serviceContext.getAssetCategoryIds(),
serviceContext.getAssetTagNames());


Download Portlet


Compete Code Implementation

Portlet Name: FeedBakcWorkFlow-portlet

Portlet.xml file


<?xml version="1.0"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0">
<portlet>
<portlet-name>FeedBakcWorkFlow</portlet-name>
<display-name>FeedBakcWorkFlow</display-name>
<portlet-class>
com.meera.workflow.customasset.FeedBakcWorkFlowAction
</portlet-class>
<init-param>
<name>view-template</name>
<value>/html/workflow/view.jsp</value>
</init-param>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
</supports>
<portlet-info>
<title>FeedBakcWorkFlow</title>
<short-title>FeedBakcWorkFlow</short-title>
<keywords></keywords>
</portlet-info>
<security-role-ref>
<role-name>administrator</role-name>
</security-role-ref>
<security-role-ref>
<role-name>guest</role-name>
</security-role-ref>
<security-role-ref>
<role-name>power-user</role-name>
</security-role-ref>
<security-role-ref>
<role-name>user</role-name>
</security-role-ref>
</portlet>
</portlet-app>


Liferay-portlet.xml file


<?xml version="1.0"?>
<!DOCTYPE liferay-portlet-app PUBLIC "-//Liferay//DTD Portlet Application 6.2.0//EN" "http://www.liferay.com/dtd/liferay-portlet-app_6_2_0.dtd">
<liferay-portlet-app>
<portlet>
<portlet-name>FeedBakcWorkFlow</portlet-name>
<icon>/icon.png</icon>
<asset-renderer-factory>com.meera.workflow.customasset.FeedbackAssetRendererFactory</asset-renderer-factory>
<workflow-handler>com.meera.workflow.customasset.FeedbackWorkflowHandler</workflow-handler>
<header-portlet-css>/css/main.css</header-portlet-css>
<footer-portlet-javascript>
/js/main.js
</footer-portlet-javascript>
<css-class-wrapper>feedbakcworkflow-portlet</css-class-wrapper>
</portlet>
<role-mapper>
<role-name>administrator</role-name>
<role-link>Administrator</role-link>
</role-mapper>
<role-mapper>
<role-name>guest</role-name>
<role-link>Guest</role-link>
</role-mapper>
<role-mapper>
<role-name>power-user</role-name>
<role-link>Power User</role-link>
</role-mapper>
<role-mapper>
<role-name>user</role-name>
<role-link>User</role-link>
</role-mapper>
</liferay-portlet-app>


Liferay-display.xml file


<?xml version="1.0"?>
<!DOCTYPE display PUBLIC "-//Liferay//DTD Display 6.2.0//EN" "http://www.liferay.com/dtd/liferay-display_6_2_0.dtd">
<display>
<category name="category.sample">
</category>
<category name="category.workflow">
<portlet id="FeedBakcWorkFlow"></portlet>
</category>
</display>


service.xml file


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 6.1.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_6_1_0.dtd">
<service-builder package-path="com.meera.workflow.db">
<author>LiferaySavvy</author>
<namespace>LS</namespace>
<entity name="Feedback" uuid="true" local-service="true"
remote-service="false">
<column name="feedbackId" type="long" primary="true" />
<column name="feedbackDate" type="Date" />
<column name="feedbackText" type="String" />
<column name="feedbackSubject" type="String" />
<column name="feedBackStatus" type="int" />
<column name="statusByUserId" type="long" />
<column name="statusDate" type="Date" />
<column name="companyId" type="long" />
<column name="groupId" type="long" />
<column name="userId" type="long" />
<order>
<order-column name="feedbackId" order-by="asc" />
<order-column name="feedbackDate" order-by="desc" />
</order>
<finder name="GroupId" return-type="Collection">
<finder-column name="groupId" />
</finder>
<finder name="CompanyId" return-type="Collection">
<finder-column name="companyId" />
</finder>
<finder name="feedbackText" return-type="Collection">
<finder-column name="feedbackText" />
</finder>
<finder name="G_S" return-type="Collection">
<finder-column name="groupId" />
<finder-column name="feedBackStatus" />
</finder>
<reference package-path="com.liferay.portal" entity="User" />
<reference package-path="com.liferay.portlet.asset" entity="AssetEntry" />
<reference package-path="com.liferay.portal" entity="WorkflowDefinitionLink"></reference>
<reference package-path="com.liferay.portal" entity="WorkflowInstanceLink"></reference>
</entity>
</service-builder>


liferay-plugin-package.properties


name=FeedBakcWorkFlow
module-group-id=liferay
module-incremental-version=1
tags=
short-description=
long-description=
change-log=
page-url=http://www.liferay.com
author=Liferay, Inc.
licenses=LGPL
liferay-versions=6.2.0+
portal-dependency-jars=\
jstl-api.jar,\
jstl-impl.jar
portal-dependency-tlds=\
c.tld


FeedBakcWorkFlowAction.java


package com.meera.workflow.customasset;
import java.io.IOException;
import java.util.Date;
import javax.mail.internet.AddressException;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import com.liferay.counter.service.CounterLocalServiceUtil;
import com.liferay.portal.NoSuchWorkflowDefinitionLinkException;
import com.liferay.portal.PortalException;
import com.liferay.portal.SystemException;
import com.liferay.portal.kernel.servlet.SessionErrors;
import com.liferay.portal.kernel.servlet.SessionMessages;
import com.liferay.portal.kernel.util.ParamUtil;
import com.liferay.portal.kernel.util.WebKeys;
import com.liferay.portal.kernel.workflow.WorkflowConstants;
import com.liferay.portal.kernel.workflow.WorkflowDefinitionManagerUtil;
import com.liferay.portal.kernel.workflow.WorkflowHandlerRegistryUtil;
import com.liferay.portal.model.WorkflowDefinitionLink;
import com.liferay.portal.service.ServiceContext;
import com.liferay.portal.service.ServiceContextFactory;
import com.liferay.portal.service.WorkflowDefinitionLinkLocalServiceUtil;
import com.liferay.portal.theme.ThemeDisplay;
import com.liferay.portlet.asset.service.AssetEntryLocalServiceUtil;
import com.liferay.util.bridges.mvc.MVCPortlet;
import com.meera.workflow.db.model.Feedback;
import com.meera.workflow.db.service.FeedbackLocalServiceUtil;

/**
* Portlet implementation class FeedBakcWorkFlowAction
*/
public class FeedBakcWorkFlowAction extends MVCPortlet {

public void submitFeedback(ActionRequest actionRequest,
ActionResponse actionResponse) throws IOException,
AddressException,com.liferay.portal.kernel.exception.PortalException, com.liferay.portal.kernel.exception.SystemException {
ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(WebKeys.THEME_DISPLAY);
String feedBackMessage = ParamUtil.getString(actionRequest, "feedbackMessage");
String feedbackSubject = ParamUtil.getString(actionRequest, "feedbackSubject");
ServiceContext serviceContext = ServiceContextFactory.getInstance(Feedback.class.getName(), actionRequest);
try {
Feedback feedback=FeedbackLocalServiceUtil.createFeedback(CounterLocalServiceUtil.increment());
feedback.setCompanyId(themeDisplay.getCompanyId());
feedback.setGroupId(themeDisplay.getScopeGroupId());
feedback.setFeedbackDate(new Date());
feedback.setFeedbackText(feedBackMessage);
feedback.setFeedbackSubject(feedbackSubject);
feedback.setFeedBackStatus(WorkflowConstants.STATUS_DRAFT);
feedback.setUserId(themeDisplay.getUserId());
feedback.setStatusByUserId(themeDisplay.getUserId());
feedback=FeedbackLocalServiceUtil.addFeedback(feedback);
WorkflowDefinitionLink workflowDefinitionLink=null;
try{
workflowDefinitionLink=
WorkflowDefinitionLinkLocalServiceUtil.
getDefaultWorkflowDefinitionLink(themeDisplay.getCompanyId(), Feedback.class.getName(), 0, 0);
}catch (Exception e) {
if(e instanceof NoSuchWorkflowDefinitionLinkException){
SessionMessages.add(actionRequest.getPortletSession(),"workflow-not-enabled");
}
e.printStackTrace();
}
//checking workflow defintion is enabled to feedback asset or not
if(feedback!=null&&workflowDefinitionLink!=null){
//add feedback asset in asset entry table
AssetEntryLocalServiceUtil.updateEntry(themeDisplay.getUserId(), feedback.getGroupId(),
Feedback.class.getName(), feedback.getFeedbackId(),
serviceContext.getAssetCategoryIds(),
serviceContext.getAssetTagNames());
//start workflow instance to feedback.
WorkflowHandlerRegistryUtil.startWorkflowInstance(
feedback.getCompanyId(), feedback.getGroupId(), themeDisplay.getUserId(),
Feedback.class.getName(), feedback.getPrimaryKey(), feedback,
serviceContext);
}
if(feedback==null){
SessionErrors.add(actionRequest.getPortletSession(),"feedback-submit-failed");
}else{
SessionMessages.add(actionRequest.getPortletSession(),"feedback-submit-success");
}
} catch (Exception e) {
if(e instanceof NoSuchWorkflowDefinitionLinkException){
SessionMessages.add(actionRequest.getPortletSession(),"workflow-not-enabled");
}
e.printStackTrace();
}
actionResponse.setRenderParameter("mvcPath", "/html/workflow/feedback.jsp");
}
}


FeedbackWorkflowHandler.java


package com.meera.workflow.customasset;
import java.io.Serializable;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.workflow.BaseWorkflowHandler;
import com.liferay.portal.kernel.workflow.WorkflowConstants;
import com.liferay.portal.service.ServiceContext;
import com.liferay.portlet.asset.service.AssetEntryLocalServiceUtil;
import com.meera.workflow.db.model.Feedback;
import com.meera.workflow.db.service.FeedbackLocalServiceUtil;

/**
* Portlet implementation class FeedbackAction
*/
public class FeedbackWorkflowHandler extends BaseWorkflowHandler{

public String getClassName() {
return CLASS_NAME;
}

public String getType(Locale locale) {
return "Feedback";
}

public  Feedback updateStatus(int status,
Map<String, Serializable> workflowContext) throws PortalException,
SystemException {
long userId = GetterUtil.getLong(workflowContext.get(WorkflowConstants.CONTEXT_USER_ID));
long resourcePrimKey = GetterUtil.getLong(workflowContext.get(WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));
Feedback feedback = FeedbackLocalServiceUtil.getFeedback(resourcePrimKey);
feedback.setFeedBackStatus(status);
feedback.setStatusByUserId(userId);
feedback.setStatusDate(new Date());
feedback=FeedbackLocalServiceUtil.updateFeedback(feedback);
if (status == WorkflowConstants.STATUS_APPROVED) {
AssetEntryLocalServiceUtil.updateVisible(Feedback.class.getName(),
resourcePrimKey, true);
} else {
AssetEntryLocalServiceUtil.updateVisible(Feedback.class.getName(),
resourcePrimKey, false);
}
return feedback;
}

public static final String CLASS_NAME = Feedback.class.getName();

}


FeedbackAssetRendererFactory.java


package com.meera.workflow.customasset;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portlet.asset.model.AssetRenderer;
import com.liferay.portlet.asset.model.BaseAssetRendererFactory;
import com.meera.workflow.db.model.Feedback;
import com.meera.workflow.db.service.FeedbackLocalServiceUtil;

/**
* Portlet implementation class FeedbackAction
*/
public class FeedbackAssetRendererFactory extends BaseAssetRendererFactory {
@Override
public AssetRenderer getAssetRenderer(long classPK, int type)
throws PortalException, SystemException {
Feedback feedback = FeedbackLocalServiceUtil.getFeedback(classPK);
return new FeedbackAssetRenderer(feedback);
}

@Override
public String getClassName() {
return CLASS_NAME;
}
@Override
public String getType() {
return TYPE;
}
public static final String TYPE = "Feedback";
public static final String CLASS_NAME = Feedback.class.getName();
}


FeedbackAssetRenderer.java


package com.meera.workflow.customasset;
import java.util.Locale;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.service.UserLocalServiceUtil;
import com.liferay.portlet.asset.model.BaseAssetRenderer;
import com.meera.workflow.db.model.Feedback;

/**
* Portlet implementation class FeedbackAction
*/
public class FeedbackAssetRenderer extends BaseAssetRenderer{

private Feedback _feedback;

public FeedbackAssetRenderer(Feedback feedback) {
_feedback = feedback;
}

public long getClassPK() {
return _feedback.getFeedbackId();
}

public long getGroupId() {
return _feedback.getGroupId();
}

public String getSummary(Locale arg0) {
return _feedback.getFeedbackText();
}

public String getTitle(Locale arg0) {
return _feedback.getFeedbackSubject();
}

public long getUserId() {
return _feedback.getUserId();
}

public String getUuid() {
return _feedback.getUuid();
}

public String render(RenderRequest request, RenderResponse response, String template)
throws Exception
{
if (template.equals(TEMPLATE_FULL_CONTENT)) {
request.setAttribute("feedBackObject",_feedback);
return "/html/workflow/view_feedbck.jsp";
}
else
{
return null;
}
}

@Override
public String getUserName() {
// TODO Auto-generated method stub
String userName=null;
try {
userName= UserLocalServiceUtil.getUser(_feedback.getUserId()).getFullName();
} catch (PortalException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SystemException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return userName;
}
return userName;
}

@Override
public String getClassName() {
// TODO Auto-generated method stub
return Feedback.class.getName();
}
}


Init.jsp


<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %>
<%@ taglib uri="http://liferay.com/tld/portlet" prefix="liferay-portlet" %>
<%@ taglib uri="http://liferay.com/tld/security" prefix="liferay-security" %>
<%@ taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme" %>
<%@ taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui" %>
<%@ taglib uri="http://liferay.com/tld/util" prefix="liferay-util" %>
<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page import="javax.portlet.PortletPreferences" %>
<%@ page import="com.liferay.portal.kernel.util.Validator" %>
<%@ page import="com.liferay.portal.kernel.util.ParamUtil" %>
<%@ page import="com.liferay.portlet.PortletPreferencesFactoryUtil" %>
<%@ page import="com.liferay.portal.util.PortalUtil" %>
<%@ page import="com.liferay.portal.kernel.dao.search.ResultRow" %>
<%@ page import="com.liferay.portal.kernel.util.ParamUtil" %>
<%@ page import="com.liferay.portal.kernel.util.WebKeys" %>
<%@ page import="com.liferay.portal.model.User" %>
<%@ page import="com.liferay.portal.service.UserLocalServiceUtil" %>
<%@ page import="javax.portlet.PortletURL" %>
<%@page import="javax.portlet.WindowState" %>
<%@page import="java.text.Format"%>
<%@page import="com.liferay.portal.kernel.util.FastDateFormatFactoryUtil"%>
<%@page import="com.liferay.portal.kernel.util.GetterUtil"%>
<%@page import="com.liferay.portal.kernel.util.StringPool"%>
<%@page import="java.util.*"%>
<%@page import="com.liferay.portal.kernel.util.OrderByComparator"%>
<portlet:defineObjects />
<liferay-theme:defineObjects />



View.jsp


<%@ include file="init.jsp"%>
<%
String tabs1 = ParamUtil.getString(request, "tabs1", "Aproved Feedback");
PortletURL portletURL = renderResponse.createRenderURL();
portletURL.setParameter("tabs1", tabs1);
%>
<portlet:renderURL var="addFeedBackURL">
<portlet:param name="mvcPath" value="/html/workflow/feedback.jsp"/>
</portlet:renderURL>
<h4>Kaleo workflow for custom Assets</h4>
<a href="<%=addFeedBackURL%>" style="font-weight:bold;font-size:18px;">Add New FeedBack</a><br/><br/>
<liferay-ui:tabs
names="Aproved Feedback,Pending Feedback"
portletURL="<%= portletURL %>"
/>
<c:choose>
<c:when test='<%= tabs1.equals("Pending Feedback") %>'>
<liferay-util:include page="/html/workflow/view_pending_feedback.jsp" servletContext="<%= application %>" />
</c:when>
<c:otherwise>

<liferay-util:include page="/html/workflow/view_approved_feedback.jsp" servletContext="<%= application %>" />
</c:otherwise>
</c:choose>


view_approved_feedback.jsp


<%@page import="com.liferay.portal.kernel.util.ListUtil"%>
<%@page import="com.meera.workflow.db.model.Feedback"%>
<%@page import="com.liferay.portal.kernel.workflow.WorkflowConstants"%>
<%@page import="com.meera.workflow.db.service.FeedbackLocalServiceUtil"%>
<%@page import="javax.portlet.PortletURL"%>
<%@ include file="init.jsp"%>
<liferay-portlet:renderURL varImpl="iteratorURL">
<portlet:param name="mvcPath" value="/html/workflow/view_approved_feedback.jsp" />
</liferay-portlet:renderURL>
<liferay-ui:search-container emptyResultsMessage="there-are-no-students"
headerNames="FeedBack Subject,Message, Stauts"
iteratorURL="<%=iteratorURL %>"
delta="10" deltaConfigurable="true">
<liferay-ui:search-container-results>
<%
List<Feedback> feedbackList= FeedbackLocalServiceUtil.findByG_S(themeDisplay.getScopeGroupId(),WorkflowConstants.STATUS_APPROVED);
results=ListUtil.subList(feedbackList,searchContainer.getStart(), searchContainer.getEnd());
searchContainer.setTotal(feedbackList.size());
searchContainer.setResults(results);
%>
</liferay-ui:search-container-results>
<liferay-ui:search-container-row className="Feedback"
keyProperty="feedbackId" modelVar="currentFeedback">
<liferay-ui:search-container-column-text
name="FeedBack Subject" property="feedbackSubject" />
<liferay-ui:search-container-column-text
name="Message" property="feedbackText" />
<liferay-ui:search-container-column-text name="status"><%=WorkflowConstants.LABEL_APPROVED %></liferay-ui:search-container-column-text>
</liferay-ui:search-container-row>
<liferay-ui:search-iterator searchContainer="<%=searchContainer %>" />
</liferay-ui:search-container>


view_pending_feedback.jsp


<%@page import="com.liferay.portal.kernel.workflow.WorkflowDefinitionManagerUtil"%>
<%@page import="com.liferay.portal.kernel.workflow.WorkflowHandlerRegistryUtil"%>
<%@page import="com.liferay.portal.kernel.util.ListUtil"%>
<%@page import="com.meera.workflow.db.model.Feedback"%>
<%@page import="com.liferay.portal.kernel.workflow.WorkflowConstants"%>
<%@page import="com.meera.workflow.db.service.FeedbackLocalServiceUtil"%>
<%@page import="javax.portlet.PortletURL"%>
<%@ include file="init.jsp"%>
<liferay-portlet:renderURL varImpl="iteratorURL">
<portlet:param name="mvcPath" value="/html/workflow/view_pending_feedback.jsp" />
</liferay-portlet:renderURL>
<liferay-ui:search-container emptyResultsMessage="there-are-no-students"
headerNames="FeedBack Subject,Message, Stauts"
iteratorURL="<%=iteratorURL %>"
delta="10"
deltaConfigurable="true">
<liferay-ui:search-container-results>
<%
List<Feedback> feedbackList= FeedbackLocalServiceUtil.findByG_S(themeDisplay.getScopeGroupId(),WorkflowConstants.STATUS_DRAFT);
results=ListUtil.subList(feedbackList,searchContainer.getStart(), searchContainer.getEnd());
searchContainer.setTotal(feedbackList.size());
searchContainer.setResults(results);
%>
</liferay-ui:search-container-results>
<liferay-ui:search-container-row className="Feedback"
keyProperty="feedbackId" modelVar="currentFeedback">
<liferay-ui:search-container-column-text
name="FeedBack Subject" property="feedbackSubject" />
<liferay-ui:search-container-column-text
name="Message" property="feedbackText" />
<liferay-ui:search-container-column-text name="status"><%=WorkflowConstants.LABEL_PENDING %></liferay-ui:search-container-column-text>
</liferay-ui:search-container-row>
<liferay-ui:search-iterator searchContainer="<%=searchContainer %>"/>
</liferay-ui:search-container>


feedback.jsp


<%@page import="com.liferay.portal.kernel.servlet.SessionErrors"%>
<%@page import="com.liferay.portal.kernel.servlet.SessionMessages"%>
<%@page import="com.liferay.portal.kernel.portlet.LiferayWindowState"%>
<%@page import="javax.portlet.ActionRequest"%>
<%@ include file="init.jsp"%>
<portlet:actionURL var="submitFeedBack" windowState="<%=LiferayWindowState.NORMAL.toString()%>" name="submitFeedback">
</portlet:actionURL>
<a href="<portlet:renderURL />">&laquo;Home</a>
<div class="separator"></div>
<fieldset>
<legend>Feedback Form</legend>
<c:if test='<%= SessionMessages.contains(renderRequest.getPortletSession(),"feedback-submit-success")%>'>
<liferay-ui:success key="feedback-submit-success" message="Feedback has been submited successfully." />
</c:if>
<c:if test='<%= SessionMessages.contains(renderRequest.getPortletSession(),"workflow-not-enabled")%>'>
<liferay-ui:success key="workflow-not-enabled" message="Feedback not enabled with workflow please enable workflow." />
</c:if>
<c:if test='<%= SessionErrors.contains(renderRequest.getPortletSession(),"feedback-submit-failed")%>'>
<liferay-ui:success key="feedback-submit-failed" message="There is problem of submit feedback please try again.." />
</c:if>
<aui:form action="<%=submitFeedBack%>" method="post" name="feedbackForm">
<aui:layout>
<aui:column>
<aui:input    label="Feedback Subject" name="feedbackSubject" id="feedbackSubject" type="text" style="width:600px;height:20px;">
<aui:validator name="required" />
</aui:input>
</aui:column>
</aui:layout>

<aui:layout>
<aui:input name="feedbackMessage" id="feedbackMessage" label="Feedback Message" type="textarea" style="width:600px;height:100px;">
<aui:validator name="required" />
</aui:input>
<span style=" float: left;">Characters Left&nbsp;</span><p id="<portlet:namespace/>textCounter"></p>
</aui:layout>
<aui:layout>
<aui:column>
&nbsp;
</aui:column>
</aui:layout>
<aui:layout>
<aui:column>
<aui:button type="submit" value="Submit Feedback" name="submit"
></aui:button>
</aui:column>
</aui:layout>
</aui:form>
</fieldset><aui:script>
AUI().use('aui-char-counter', function(A) {
new A.CharCounter({
counter : '#<portlet:namespace/>textCounter',
input : '#<portlet:namespace/>feedbackMessage',
maxLength : 75,
on : {
maxLength : function(event) {
alert('The max length limit was reached');
}
}
});
});
</aui:script>



view_feedbck.jsp


<%@page import="com.meera.workflow.db.model.impl.FeedbackBaseImpl"%>
<%@page import="com.meera.workflow.db.service.FeedbackLocalServiceUtil"%>
<%@page import="com.liferay.portal.kernel.servlet.SessionMessages"%>
<%@page import="com.liferay.portal.kernel.portlet.LiferayWindowState"%>
<%@page import="javax.portlet.ActionRequest"%>
<%@page import="com.meera.workflow.db.model.Feedback"%>
<%@ include file="init.jsp"%>
<%
Feedback feedback=null;
if(request.getAttribute("feedBackObject")!=null)
feedback = (Feedback)request.getAttribute("feedBackObject");
%>
<c:if test='<%=feedback!=null%>'>
<h2><%=feedback.getFeedbackSubject()%></h2>
<br/>
Message:
<div>
<%=feedback.getFeedbackText()%>
</div>
</c:if>


Usage of Portlet

Download portlet from download link there you can see source and war file and .war file you can deploy into your Liferay portal. Make sure your portal version. You can also use source to create war file and deploy into your portal server.

Portlet available under “workflow” category you can add to your desire page.

Enable Workflow to Feedback Asset/Portlet

Please go through the following post for better understanding to use workflow in Liferay.


Now Login as Portal Administrator and go to Control panel there click on Workflow link in the configuration section.



In the Workflow configuration go to default workflow configuration tab there you can see the Feedback Asset in the list for that select workflow level and save the configuration so that Feedback asset is now enable with given workflow



Now go the page where you drag and drop the Feedback Workflow portlet there you can find Add New Feedback Link and click on that you can see feedback form.



Now fill the form and save it then the submission going through the workflow process.


We already enable Single Level Approval so login as content reviewer role so that you can find feedback entry in his/her workflow task List there he/she can approve /Reject.

Click on My Account




In the my workflow task list you can see Feedback Submission and we already know this is role based task so first we need to assign to me and then we can approve the task



Once you assign to you then task will come under Assign to me section there you can approve/reject the task



This is how you can complete your feedback submission through workflow.
Now go to feedback portlet there you can see Approved Feedback List and Pending feedback List

The following approved feedback list


The following so Pending feedback List



Author


Popular Posts

Recent Posts

Recent Posts Widget