Liferay 6.1开发学习(二十二):在插件工程中使用kaleo工作流
kaleo是Liferay默认的工作流,此篇文章介绍kaleo工作流的与二次开发的Portlet的集成,kaleo的设计本篇文章不涉及。
Liferay默认情况下并没有安装工作流的插件,需要我们到他的官方市场上下载安装。安装过程这里不描述,以上内容假定已经安装了工作流插件。
工作流的集成步骤
第一步:请确保ServiceBuilder的xml文件中的相应实体包含以下内容,并执行ServiceBuilder
- <!-- workflow fields -->
- <column name="companyId" type="long" />
- <column name="groupId" type="long"/>
- <column name="title" type="String"></column>
- <column name="content" type="String"></column>
- <column name="status" type="int"></column>
- <column name="statusByUserId" type="long"></column>
- <column name="statusByUserName" type="String"></column>
- <column name="statusDate" type="Date"></column>
- <reference package-path="com.liferay.portal" entity="WorkflowInstanceLink"></reference>
第二步:在liferay-portlet.xml中添加如下内容(asset-renderer-factory可以先暂时不添加)。
- <asset-renderer-factory>com.huqiwen.asset.NewsAssetRenderFactory</asset-renderer-factory>
- <workflow-handler>com.huqiwen.workflow.NewsWorkflowHandler</workflow-handler>
第三步:编写NewsWorkflowHandler,这类名由自己定义,这两个类的具体定义如下:
NewsWorkflowHandler是工作流处理的核心类,比如状态的改变等。继承BaseWorkflowHandler类,此类的示例代码如下:
- public class NewsWorkflowHandler extends BaseWorkflowHandler {
- public static final String CLASS_NAME = News.class.getName();
- @Override
- public String getClassName() {
- return CLASS_NAME;
- }
- @Override
- public String getType(Locale locale) {
- return ResourceActionsUtil.getModelResource(locale, CLASS_NAME);
- }
- @Override
- public Object updateStatus(int status, Map<String, Serializable> workflowContext)
- throws PortalException, SystemException {
- long userId = GetterUtil.getLong(
- (String)workflowContext.get(WorkflowConstants.CONTEXT_USER_ID));
- long resourcePrimKey = GetterUtil.getLong(
- (String)workflowContext.get(
- WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));
- ServiceContext serviceContext = (ServiceContext)workflowContext.get(
- "serviceContext");
- return NewsLocalServiceUtil.updateStatus(userId, resourcePrimKey, status, serviceContext);
- }
- }
说明:
CLASS_NAME:为我们要添加工作流的实体,比如我这里是新建的一个News的实体。
getType():此方法实际是用来处理国际化的,有些地方可以看到是写的mode.resource+CLASS_NAME,其实这里不重要,这个值的使用就是提供一个key值,用来显示国际化的Key值,对应于国际化资源文件中的key,这里在插件工程的content/Language_zh_CN.properties里面添加此处的说明,比如此示例中我添加的为:model.resource.com.huqiwen.portlet.article.model.News=\u65B0\u95FB\u5BA1\u6838
updateStatus():此方法是用来处理工作流流转的方法,具体的可以照抄上面的内容,return的方法为实体的xxServiceLocalUtil方法,因为我这里的实体名为news,所以如上。
第四步:在我们的实体的处理方法里面添加自定义方法updateStatus,在xxLocalServiceImpl(此处为NewsLocalServiceImpl)里面添加updateStatus方法,方法的定义如下,完成之后再次执行ServiceBuilder:
- public News updateStatus(long userId,long resourcePrimKey,int status,ServiceContext serviceContext) throws NoSuchUserException, SystemException{
- User user = userPersistence.findByPrimaryKey(userId);
- Date now = new Date();
- News news = newsPersistence.fetchByPrimaryKey(resourcePrimKey);
- news.setModifiedDate(serviceContext.getModifiedDate(now));
- news.setStatus(status);
- news.setStatusByUserId(user.getUserId());
- news.setStatusByUserName(user.getFullName());
- news.setStatusDate(serviceContext.getModifiedDate(now));
- newsPersistence.update(news, false);
- return news;
- }
基本上照抄就行,需要注意的是返回的内容为相对应的实体。
到这里时,如果是第二步中没有添加asset-renderer-factory,则我们发布工程,就可以在后台的工作流配置的地方,看到我们添加的工作流了,也可以进行基本的流转的,但是我们仔细测试的话会发则,在我的任务里面,当点击工作流名称的时候不能看到工作流的详情,那要怎么处理呢?接下来我们完善此工作流的处理。
第六步:(第二步里面的asset-renderer-factory确保已经添加),建立NewsAssetRenderFactory类,继承BaseAssetRendererFactory,此类的示例代码如下:
- public class NewsAssetRenderFactory extends BaseAssetRendererFactory {
- public static final String CLASS_NAME = News.class.getName();
- public static final String TYPE = "news";
- @Override
- public AssetRenderer getAssetRenderer(long classPk, int type)
- throws PortalException, SystemException {
- int status = WorkflowConstants.STATUS_ANY;
- /**
- * 如果需要根据不同的状态获取不同的内容,可以在此进行设置
- */
- if (type == TYPE_LATEST_APPROVED) {
- status = WorkflowConstants.STATUS_APPROVED;
- }
- News news = NewsLocalServiceUtil.getNews(classPk);
- return new NewsAssetRender(news);
- }
- @Override
- public String getClassName() {
- return CLASS_NAME;
- }
- @Override
- public String getType() {
- return TYPE;
- }
此类也比较简单,基本上是复制下来使用即可,在自己的类里面将news修改为自己对应的实体,这里又有一个新的类NewsAssetRender,同样需要新建,建立NewsAssetRender,继承BaseAssetRenderer,示例代码如下:
- public class NewsAssetRender extends BaseAssetRenderer {
- public static final String CLASS_NAME = News.class.getName();
- public NewsAssetRender(News news){
- _news = news;
- }
- @Override
- public long getClassPK() {
- return _news.getNewsId();
- }
- @Override
- public long getGroupId() {
- return _news.getGroupId();
- }
- @Override
- public String getSummary(Locale arg0) {
- return HtmlUtil.stripHtml(_news.getContent());
- }
- @Override
- public String getTitle(Locale arg0) {
- return _news.getTitle();
- }
- @Override
- public long getUserId() {
- return _news.getUserId();
- }
- @Override
- public String getUserName() {
- return _news.getUserName();
- }
- @Override
- public String getUuid() {
- return _news.getUuid();
- }
- @Override
- public String render(RenderRequest renderRequest, RenderResponse renderResponse, String template)
- throws Exception {
- if (template.equals(TEMPLATE_FULL_CONTENT)) {
- renderRequest.setAttribute(
- "news", _news);
- return "/html/asset/" + template + ".jsp";
- }
- else {
- return null;
- }
- }
- private News _news;
- }
此类需要说明的方法为render方法,此方法是我们点击查看详情的时候显示的,可以看到他是跳转到了/html/asset/xxx.jsp的页面,其实这个路径为/html/asset/full_content.jsp的页面。
现在我们的portlet插件工程的docroot目录下面建立上面的路径的jsp页面,JSP里面的内容如下:
- <%@page import="com.huqiwen.portlet.article.model.News"%>
- <%@ include file="/html/init.jsp" %>
- <%
- News news = (News)request.getAttribute("news");
- %>
- <%= news.getContent() %>
此处只是示例,所以只是显示了正文,此页面可以根据实际的情况自由编写。
至此,整个工作流的使用介绍完毕,现在deploy插件工程,工作流即集成到了我们的portlet插件工程里面。
工作流的初始化
上面介绍的部分为配置部分,但是工作流什么时候启动呢?一般是我们在添加一个新的内容时启动工作流,启动的代码如下:
WorkflowHandlerRegistryUtil.startWorkflowInstance(companyId,userId, News.class.getName(), news.getNewsId(), news,serviceContext);
此代码在我们添加保存实体后进行。
为了方便对流程的详情进行流利,我们要添加asset的支持,在上面的代码后台,添加如下代码,后面的两个null为分类和标签,如果有使用的话就添加,没有使用的话使用null即可。
AssetEntryLocalServiceUtil.updateEntry(userId, groupId,News.class.getName(), news.getNewsId(), null, null);
上面是当我们添加内容的时候启动了工作流,当我们删除信息时也需要删除工作流,删除的代码如下:
WorkflowInstanceLinkLocalServiceUtil.deleteWorkflowInstanceLinks(news.getCompanyId(), ews.getGroupId(), News.class.getName(),news.getNewsId());
回顾
看到第一步的时候,可能会有疑问,我的实体必须要有title和content字段么?我们仔细分析一下代码其实也不是必须的,title的目标是我们可以看到要审批的内容的标题,content的内容是方便我们对内容进行预览,我们可以标题的地方取name值,正文的地方取summary,或者是整个实体的toString,或者是其他内容的拼接。
本人菜苗一枚,请求培育
这个东西好像在liferay里面有吧?有个图形化操作界面。。。不过好像不是针对某个特定的类。。。
在liferay control panel里面有workflow->Default Configuraion….有什么区别?
顺便问一下,我的liferay control panel是叫做portal configuraion.没关系把?因为我读了官网structure & template那块,感觉它的跟我的有点儿区别。。。。6.1CE
先拜谢了 😆
恩。。。还有就是,工作流到底是针对外部业务的还是针对内部任务的。。。。有点儿晕 😳
不错。最近正在研究liferay.
现在已经下载 kaleo-web 6.1.0 插件要怎么安装
你好,胡总,最近项目在使用这个工作流,想问下。比如你这个news类。 怎么通知news类的主键id(classPk),来查到工作流WorkflowTask类的workflowTaskId呢?忘答复,谢谢!
通知改成通过。