Creating an Alfresco Task History; The Java style
I have been working with Alfresco for a while now. It is a very comprehensive tool, but I was missing a nice feature in it. A task history in the document details screen. Now i know you can do alot of coding with the scripting engine in Alfresco, but what I wanted was not possible in my opinion and I wanted it to fully integrate in the java code base instead of scripted into pages. Time for me to go to work. In this article I will briefly explain what I have done to make this feature work.
Intended public
This article is written with the Alfresco developer in mind. If you are not atleast a moderate Alfresco developer you might not be able to fully comprehend what is explained. Also I will omit information in this article that I deem basic Alfresco knowledge.
The creation process
Very early on in my effort to build the task history panel I cam eto the conclusion that I not only needed extra coding effort, but also a solid way of integrating my code into the codebase of alfresco. Now Alfresco has provided me and you with a method of doing this. You can find details about this integration process on the Alfresco wiki. I will in this article give you a brief overview on how to integrate your code into the Alfresco codebase, but I will not go into detail on this. What I will describe is the Task history functionality.
Functionality overview
What am I creating is a panel in the document-details page that displays the tasks for me specific for the document in question. Furthermore I want to see active and completed tasks here. ALL tasks that is. To give you a impression of my wish; a picture to illustrate it.
My other wish was that it would be coded in Java and integrate in the Alfresco way with the Alfresco codebase. Given these wishes I started this small project.
What do you need to do?
1.Create a extension project
2.Create a new workflowbean class with the added functionality.
3.Modify the faces-config-custom.xml, supplying a enhanced workflowbean
4.Modify the document-details.jsp, to show the task history
5.Create a faces-config.xml, supplying the new navigation rules for your new jsp.
6.Create a custom webclient.properties file for your new labels.
The extension project
The first thing I did was to create a new extension project. I followed the example SDK custom JSP project structure for my own project. You can read on the wiki or in the source on how to do this. I asume you have the nessesary skill.
The new workflowbean
For my function I have to code some extra lines. It is bad practise to start hacking the alfresco source (unless you have no other option), so I created my own java class extending the existing WorkflowBean class. Here is the added code:
package nl.wowww.alfresco.web.bean.workflow;
import java.util.ArrayList;
import java.util.List;
import org.alfresco.service.cmr.workflow.WorkflowInstance;
import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.cmr.workflow.WorkflowTaskQuery;
import org.alfresco.web.bean.DocumentDetailsBean;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.workflow.WorkflowBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Extended workflowbean.
* This extension adds extra task functionality.
*
* @author Marc de Kwant, www.wowww.nl
*/
public class WowwwWorkflowBean extends WorkflowBean {
protected DocumentDetailsBean documentDetailsBean;
protected List allTasks;
private static final Log logger = LogFactory.getLog(WowwwWorkflowBean.class);
public static final String BEAN_NAME = "WowwwWorkflowBean";
// ------------------------------------------------------------------------------
// Bean Getters and Setters
public List getAllTasks() {
this.allTasks = new ArrayList();
List workflows = new ArrayList();
workflows = workflowService.getWorkflowsForContent(
documentDetailsBean.getDocument().getNodeRef(), false);
workflows.addAll(workflowService.getWorkflowsForContent(
documentDetailsBean.getDocument().getNodeRef(), true));
if (workflows != null && workflows.size() > 0) {
for (WorkflowInstance wi : workflows) {
WorkflowTaskQuery query = new WorkflowTaskQuery();
query.setActive(wi.active);
query.setTaskState(null);
query.setProcessId(wi.id);
List tasks = this.workflowService.queryTasks(query);
// create a list of transient nodes to represents
for (WorkflowTask task : tasks)
{
Node node = createTask(task);
this.allTasks.add(node);
if (logger.isDebugEnabled())
logger.debug("Added task: " + node);
}
}
}
return this.allTasks;
}
/**
* @param documentDetailsBean the documentDetailsBean to set
*/
public void setDocumentDetailsBean(DocumentDetailsBean documentDetailsBean) {
this.documentDetailsBean = documentDetailsBean;
}
}
The code above is what you need to retrieve all tasks, weather complete/active/non-active, and make them available to the front-end.
Faces-config-custom.xml
To make the above bean available to the jsf framework, you need to provide a managed bean configuration. The faces framework provides for this in the form of a faces-config-custom.xml file. I have put the below code into this file:
<managed-bean>
<managed-bean-name>WowwwWorkflowBean</managed-bean-name>
<managed-bean-class>nl.wowww.alfresco.web.bean.workflow.WowwwWorkflowBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>navigationBean</property-name>
<value>#{NavigationBean}</value>
</managed-property>
<managed-property>
<property-name>documentDetailsBean</property-name>
<value>#{DocumentDetailsBean}</value>
</managed-property>
<managed-property>
<property-name>nodeService</property-name>
<value>#{NodeService}</value>
</managed-property>
<managed-property>
<property-name>workflowService</property-name>
<value>#{WorkflowService}</value>
</managed-property>
</managed-bean>
This file represents the only manual manipulation of files in the alfresco codebase since the faces framework gives me no alternate option in this. The file must be put into the WEB-INF folder in the alfresco webclient project and nowhere else.
Document-details.jsp
To display my net Task history panel I created a duplicate of the original document-details jsp and added the following panel/richlist to display the tasks provided by my allTasks method in my new WowwwWorkflowBean class.
<f:loadBundle basename="alfresco.extension.webclient" var="customMsg"/>
<a:panel label="#{customMsg.task_history}" id="task-panel" facetsId="workflow-panel-facets" progressive="true"
border="white" bgcolor="white" titleBorder="lbgrey" expandedTitleBorder="dotted" titleBgcolor="white"
expanded='#{DocumentDetailsBean.panels["task-panel"]}' expandedActionListener="#{DocumentDetailsBean.expandPanel}">
<!-- tasksCompleted -->
<a:richList id="taskHistoryList" viewMode="details" value="#{WowwwWorkflowBean.allTasks}"
var="t" styleClass="recordSet" headerStyleClass="recordSetHeader"
rowStyleClass="recordSetRow" altRowStyleClass="recordSetRowAlt" width="100%"
pageSize="10" initialSortColumn="taskId" initialSortDescending="false">
<!-- task id column -->
<a:column id="col1" width="10" style="text-align:left">
<f:facet name="header">
<a:sortLink label="#{customMsg.taskId}" value="taskId" styleClass="header"/>
</f:facet>
<h:outputText id="tid" value="#{t['bpm:taskId']}" />
</a:column>
<!-- task create date column -->
<a:column id="col2" width="120" style="text-align:left">
<f:facet name="header">
<a:sortLink label="#{customMsg.taskCreateDate}" value="taskCreateDate" styleClass="header"/>
</f:facet>
<h:outputText id="tcreatedate" value="#{t['cm:created']}">
<f:convertDateTime pattern="yyyy-MMM-dd H:mm:ss" />
</h:outputText>
</a:column>
<!-- task description column -->
<a:column id="col3" width="170" style="text-align:left">
<f:facet name="header">
<a:sortLink label="#{customMsg.taskDescription}" value="taskDescription" styleClass="header"/>
</f:facet>
<h:outputText id="tdescription" value="#{t['bpm:description']}" />
</a:column>
<!-- task comment column -->
<a:column id="col5" width="170" style="text-align:left">
<f:facet name="header">
<a:sortLink label="#{customMsg.taskComment}" value="taskComment" styleClass="header"/>
</f:facet>
<h:outputText id="tcomment" value="#{t['bpm:comment']}" />
</a:column>
<!-- task status column -->
<a:column id="col6" width="80" style="text-align:left">
<f:facet name="header">
<a:sortLink label="#{customMsg.taskStatus}" value="taskStatus" styleClass="header"/>
</f:facet>
<h:outputText id="tstatus" value="#{t['bpm:status']}" />
</a:column>
<!-- task start date column -->
<a:column id="col7" width="120" style="text-align:left">
<f:facet name="header">
<a:sortLink label="#{customMsg.taskStartDate}" value="taskStartDate" styleClass="header"/>
</f:facet>
<h:outputText id="tstartdate" value="#{t['bpm:startDate']}">
<f:convertDateTime pattern="yyyy-MMM-dd H:mm:ss" />
</h:outputText>
</a:column>
<!-- task due date column -->
<a:column id="col8" width="120" style="text-align:left">
<f:facet name="header">
<a:sortLink label="#{customMsg.taskDueDate}" value="taskDueDate" styleClass="header"/>
</f:facet>
<h:outputText id="tduedate" value="#{t['bpm:dueDate']}">
<f:convertDateTime pattern="yyyy-MMM-dd H:mm:ss" />
</h:outputText>
</a:column>
</a:richList>
</a:panel>
First I load my custom message bundle and secondly I code the task history panel.
Faces-config.xml
To display our newly created document-details.jsp we need to overrule the navigation rules that are present in the alfresco WEB-INF folder. This is not done as the faces-config-custom but in a more sophisticated manner. In the extension project you put a file named faces-config.xml in the META-INF directory. in this file i have declared the following navigation rules to be overruled:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
"http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config>
<navigation-rule>
<from-view-id>/jsp/*</from-view-id>
<navigation-case>
<from-outcome>showDocDetails</from-outcome>
<to-view-id>/jsp/extension/document-details.jsp</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/jsp/extension/document-details.jsp</from-view-id>
<navigation-case>
<from-outcome>checkoutFile</from-outcome>
<to-view-id>/jsp/dialog/checkout-file.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>checkinFile</from-outcome>
<to-view-id>/jsp/dialog/checkin-file.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>undoCheckoutFile</from-outcome>
<to-view-id>/jsp/dialog/undocheckout-file.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>updateFile</from-outcome>
<to-view-id>/jsp/dialog/update-file.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>deleteFile</from-outcome>
<to-view-id>/jsp/dialog/delete-file.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>editFile</from-outcome>
<to-view-id>/jsp/dialog/edit-file.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>editHtmlInline</from-outcome>
<to-view-id>/jsp/dialog/edit-html-inline.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>editTextInline</from-outcome>
<to-view-id>/jsp/dialog/edit-text-inline.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>editSimpleWorkflow</from-outcome>
<to-view-id>/jsp/dialog/edit-simple-workflow.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>editCategories</from-outcome>
<to-view-id>/jsp/dialog/edit-category.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>createAction</from-outcome>
<to-view-id>/jsp/wizard/create-action/action.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>previewContent</from-outcome>
<to-view-id>/jsp/dialog/preview-file.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>showForum</from-outcome>
<to-view-id>/jsp/forums/forum.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>manageContentUsers</from-outcome>
<to-view-id>/jsp/roles/manage-content-users.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>applyTemplate</from-outcome>
<to-view-id>/jsp/dialog/apply-doc-template.jsp</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/jsp/dialog/edit-simple-workflow.jsp</from-view-id>
<navigation-case>
<from-outcome>cancel</from-outcome>
<to-view-id>/jsp/extension/document-details.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>finish</from-outcome>
<to-view-id>/jsp/extension/document-details.jsp</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/jsp/dialog/edit-category.jsp</from-view-id>
<navigation-case>
<from-outcome>cancel</from-outcome>
<to-view-id>/jsp/extension/document-details.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>finish</from-outcome>
<to-view-id>/jsp/extension/document-details.jsp</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
In this file I overrule all references to document-details with my own reference.
webclient.properties
The final and last thing I did was to create a webclient.properties file and put all my custom labels into it.
task_history=Task History
taskId=Task id
taskDescription=Description
taskCreateDate=Create date
taskStartDate=Start date
taskDueDate=Due date
taskStatus=Status
taskComment=Comment
Final note.
I have omitted all explenation regarding the extension project. This article was intended for a Alfresco developer. And the layout of the article is not that good, but I cannot alter the look and feel of it.
Regards.
Marc de Kwant
wowww.nl