Workflow group / pooled tasks support

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4892 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
David Caruana
2007-01-22 10:34:18 +00:00
parent 9f865ddc57
commit 38dd0bed73
8 changed files with 294 additions and 18 deletions

View File

@@ -270,6 +270,7 @@ language=Language
export=Export export=Export
import=Import import=Import
take_ownership=Take Ownership take_ownership=Take Ownership
return_ownership=Return to Pool
create_forums=Create Forum Space create_forums=Create Forum Space
create_forum=Create Forum create_forum=Create Forum
create_topic=Create Topic create_topic=Create Topic
@@ -1227,6 +1228,7 @@ manage_task_desc=This dialog allows the task to be managed.
view_completed_task_title=View Completed Task view_completed_task_title=View Completed Task
view_completed_task_desc=This dialog allows the completed task details to be viewed. view_completed_task_desc=This dialog allows the completed task details to be viewed.
task_properties=Task Properties task_properties=Task Properties
task_pooled_properties=Task Pool
id=Id id=Id
status=Status status=Status
completed=Completed completed=Completed
@@ -1236,6 +1238,8 @@ my_tasks_todo_title=My Tasks To Do
my_tasks_todo_desc=List of your tasks still to complete my_tasks_todo_desc=List of your tasks still to complete
my_tasks_completed_title=My Completed Tasks my_tasks_completed_title=My Completed Tasks
my_tasks_completed_desc=List of your completed tasks my_tasks_completed_desc=List of your completed tasks
pooled_tasks_title=My Pooled Tasks
pooled_tasks_desc=List of tasks allocated to your pool
due_date=Due Date due_date=Due Date
completed_on=Completed on completed_on=Completed on
outcome=Outcome outcome=Outcome

View File

@@ -148,6 +148,10 @@
<action idref="reassign_task" /> <action idref="reassign_task" />
</action-group> </action-group>
<action-group id="dashlet_pooled_actions">
<action idref="manage_task" />
</action-group>
<action-group id="dashlet_completed_actions"> <action-group id="dashlet_completed_actions">
<action idref="view_completed_task" /> <action idref="view_completed_task" />
<action idref="cancel_workflow" /> <action idref="cancel_workflow" />

View File

@@ -198,6 +198,8 @@
<!-- this dashlet 'tasks-todo' is also shown by default for all users --> <!-- this dashlet 'tasks-todo' is also shown by default for all users -->
<dashlet id="tasks-todo" label-id="my_tasks_todo_title" description-id="my_tasks_todo_desc" <dashlet id="tasks-todo" label-id="my_tasks_todo_title" description-id="my_tasks_todo_desc"
jsp="/jsp/workflow/tasks-todo-dashlet.jsp" allow-narrow="false" /> jsp="/jsp/workflow/tasks-todo-dashlet.jsp" allow-narrow="false" />
<dashlet id="pooled-tasks" label-id="pooled_tasks_title" description-id="pooled_tasks_desc"
jsp="/jsp/workflow/pooled-tasks-todo-dashlet.jsp" allow-narrow="false" />
<dashlet id="tasks-completed" label-id="my_tasks_completed_title" description-id="my_tasks_completed_desc" <dashlet id="tasks-completed" label-id="my_tasks_completed_title" description-id="my_tasks_completed_desc"
jsp="/jsp/workflow/tasks-completed-dashlet.jsp" allow-narrow="false" /> jsp="/jsp/workflow/tasks-completed-dashlet.jsp" allow-narrow="false" />
<dashlet id="my-docs-template" label-id="dashlet_mydocuments" <dashlet id="my-docs-template" label-id="dashlet_mydocuments"

View File

@@ -4,6 +4,7 @@ import java.io.Serializable;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -13,7 +14,12 @@ import javax.transaction.UserTransaction;
import org.alfresco.model.ApplicationModel; import org.alfresco.model.ApplicationModel;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.model.WCMModel;
import org.alfresco.repo.avm.AVMNodeConverter;
import org.alfresco.repo.workflow.WorkflowModel; import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.service.cmr.avm.AVMService;
import org.alfresco.service.cmr.avmsync.AVMDifference;
import org.alfresco.service.cmr.avmsync.AVMSyncService;
import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
@@ -28,14 +34,15 @@ import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.web.app.AlfrescoNavigationHandler; import org.alfresco.web.app.AlfrescoNavigationHandler;
import org.alfresco.web.app.Application; import org.alfresco.web.app.Application;
import org.alfresco.web.app.servlet.FacesHelper; import org.alfresco.web.app.servlet.FacesHelper;
import org.alfresco.web.bean.BrowseBean;
import org.alfresco.web.bean.dialog.BaseDialogBean; import org.alfresco.web.bean.dialog.BaseDialogBean;
import org.alfresco.web.bean.repository.MapNode; import org.alfresco.web.bean.repository.MapNode;
import org.alfresco.web.bean.repository.Node; import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.NodePropertyResolver; import org.alfresco.web.bean.repository.NodePropertyResolver;
import org.alfresco.web.bean.repository.Repository; import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.repository.TransientNode; import org.alfresco.web.bean.repository.TransientNode;
import org.alfresco.web.bean.repository.User;
import org.alfresco.web.bean.wcm.AVMNode; import org.alfresco.web.bean.wcm.AVMNode;
import org.alfresco.web.bean.wcm.AVMWorkflowUtil;
import org.alfresco.web.config.DialogsConfigElement.DialogButtonConfig; import org.alfresco.web.config.DialogsConfigElement.DialogButtonConfig;
import org.alfresco.web.ui.common.Utils; import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.UIActionLink; import org.alfresco.web.ui.common.component.UIActionLink;
@@ -43,14 +50,6 @@ import org.alfresco.web.ui.common.component.data.UIRichList;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.alfresco.web.bean.wcm.AVMWorkflowUtil;
import org.alfresco.model.WCMModel;
import org.alfresco.repo.avm.AVMNodeConverter;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.avm.AVMService;
import org.alfresco.service.cmr.avmsync.AVMDifference;
import org.alfresco.service.cmr.avmsync.AVMSyncService;
/** /**
* Bean implementation for the "Manage Task" dialog. * Bean implementation for the "Manage Task" dialog.
* *
@@ -201,16 +200,34 @@ public class ManageTaskDialog extends BaseDialogBean
// get the transitions available from this task and // get the transitions available from this task and
// show them in the dialog as additional buttons // show them in the dialog as additional buttons
this.transitions = this.task.path.node.transitions; this.transitions = this.task.path.node.transitions;
boolean isPooledTask = isPooledTask();
if (this.transitions != null) if (isPooledTask || this.transitions != null)
{ {
buttons = new ArrayList<DialogButtonConfig>(this.transitions.length); buttons = new ArrayList<DialogButtonConfig>(this.transitions.length + 1);
for (WorkflowTransition trans : this.transitions) if (isPooledTask)
{ {
buttons.add(new DialogButtonConfig(ID_PREFIX + trans.title, trans.title, null, if (this.taskNode.getProperties().get(ContentModel.PROP_OWNER) == null)
"#{DialogManager.bean.transition}", "false", null)); {
} buttons.add(new DialogButtonConfig("button_take_ownership", null, "take_ownership",
"#{DialogManager.bean.takeOwnership}", "false", null));
}
else
{
buttons.add(new DialogButtonConfig("button_return_to_pool", null, "return_ownership",
"#{DialogManager.bean.returnOwnership}", "false", null));
}
}
if (this.transitions != null)
{
for (WorkflowTransition trans : this.transitions)
{
buttons.add(new DialogButtonConfig(ID_PREFIX + trans.title, trans.title, null,
"#{DialogManager.bean.transition}", "false", null));
}
}
} }
} }
@@ -246,6 +263,82 @@ public class ManageTaskDialog extends BaseDialogBean
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
// Event handlers // Event handlers
@SuppressWarnings("unused")
public String takeOwnership()
{
String outcome = getDefaultFinishOutcome();
if (LOGGER.isDebugEnabled())
LOGGER.debug("Taking ownership of task: " + this.task.id);
FacesContext context = FacesContext.getCurrentInstance();
UserTransaction tx = null;
try
{
tx = Repository.getUserTransaction(context);
tx.begin();
// prepare the edited parameters for saving
User user = Application.getCurrentUser(context);
String userName = user.getUserName();
Map<QName, Serializable> params = new HashMap<QName, Serializable>();
params.put(ContentModel.PROP_OWNER, userName);
// update the task with the updated parameters
this.workflowService.updateTask(this.task.id, params, null, null);
// commit the changes
tx.commit();
}
catch (Throwable e)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
Utils.addErrorMessage(formatErrorMessage(e), e);
outcome = this.getErrorOutcome(e);
}
return outcome;
}
@SuppressWarnings("unused")
public String returnOwnership()
{
String outcome = getDefaultFinishOutcome();
if (LOGGER.isDebugEnabled())
LOGGER.debug("Returning ownership of task to pool: " + this.task.id);
FacesContext context = FacesContext.getCurrentInstance();
UserTransaction tx = null;
try
{
tx = Repository.getUserTransaction(context);
tx.begin();
// prepare the edited parameters for saving
Map<QName, Serializable> params = new HashMap<QName, Serializable>();
params.put(ContentModel.PROP_OWNER, null);
// update the task with the updated parameters
this.workflowService.updateTask(this.task.id, params, null, null);
// commit the changes
tx.commit();
}
catch (Throwable e)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
Utils.addErrorMessage(formatErrorMessage(e), e);
outcome = this.getErrorOutcome(e);
}
return outcome;
}
@SuppressWarnings("unused") @SuppressWarnings("unused")
public String transition() public String transition()
{ {
@@ -482,6 +575,17 @@ public class ManageTaskDialog extends BaseDialogBean
return this.taskNode; return this.taskNode;
} }
/**
* Returns whether this is a pooled task
*
* @return true => pooled
*/
public boolean isPooledTask()
{
List pooledActors = (List)taskNode.getAssociations().get(WorkflowModel.ASSOC_POOLED_ACTORS);
return (pooledActors != null && pooledActors.size() > 0);
}
/** /**
* Returns the WorkflowInstance that the current task belongs to * Returns the WorkflowInstance that the current task belongs to
* *

View File

@@ -40,6 +40,7 @@ public class WorkflowBean
protected NodeService nodeService; protected NodeService nodeService;
protected WorkflowService workflowService; protected WorkflowService workflowService;
protected List<Node> tasks; protected List<Node> tasks;
protected List<Node> pooledTasks;
protected List<Node> completedTasks; protected List<Node> completedTasks;
private static final Log logger = LogFactory.getLog(WorkflowBean.class); private static final Log logger = LogFactory.getLog(WorkflowBean.class);
@@ -47,6 +48,55 @@ public class WorkflowBean
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
// Bean Getters and Setters // Bean Getters and Setters
/**
* Returns a list of nodes representing the "pooled" to do tasks the
* current user has.
*
* @return List of to do tasks
*/
public List<Node> getPooledTasks()
{
if (this.pooledTasks == null)
{
// get the current username
FacesContext context = FacesContext.getCurrentInstance();
User user = Application.getCurrentUser(context);
String userName = user.getUserName();
UserTransaction tx = null;
try
{
tx = Repository.getUserTransaction(context, true);
tx.begin();
// get the current pooled tasks for the current user
List<WorkflowTask> tasks = this.workflowService.getPooledTasks(userName);
// create a list of transient nodes to represent
this.pooledTasks = new ArrayList<Node>(tasks.size());
for (WorkflowTask task : tasks)
{
Node node = createTask(task);
this.pooledTasks.add(node);
if (logger.isDebugEnabled())
logger.debug("Added pooled task: " + node);
}
// commit the changes
tx.commit();
}
catch (Throwable e)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
Utils.addErrorMessage("Failed to get pooled tasks: " + e.toString(), e);
}
}
return this.pooledTasks;
}
/** /**
* Returns a list of nodes representing the to do tasks the * Returns a list of nodes representing the to do tasks the
* current user has. * current user has.

View File

@@ -160,6 +160,11 @@ public class UIAssociationEditor extends BaseAssociationEditor
{ {
out.write(User.getFullName(nodeService, targetNode)); out.write(User.getFullName(nodeService, targetNode));
} }
else if (ContentModel.TYPE_AUTHORITY_CONTAINER.equals(nodeService.getType(targetNode)))
{
String groupName = (String)nodeService.getProperty(targetNode, ContentModel.PROP_AUTHORITY_NAME);
out.write(groupName.substring("GROUP_".length()));
}
else else
{ {
out.write(Repository.getDisplayPath(nodeService.getPath(targetNode))); out.write(Repository.getDisplayPath(nodeService.getPath(targetNode)));

View File

@@ -19,6 +19,18 @@
<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> <%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %>
<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> <%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %>
<a:panel id="pooled-panel" label="#{msg.task_pooled_properties}" rendered="#{DialogManager.bean.pooledTask}"
border="white" bgcolor="white" titleBorder="lbgrey" expandedTitleBorder="dotted" titleBgcolor="white" styleClass="mainSubTitle">
<r:propertySheetGrid id="pooled-task-props" value="#{DialogManager.bean.taskNode}" columns="1" >
<r:property id="pooled-task-owner" readOnly="true" name="owner" />
<r:association id="pooled-task-pool" readOnly="true" name="bpm:pooledActors" />
</r:propertySheetGrid>
</a:panel>
<h:outputText id="padding1" styleClass="paddingRow" value="&nbsp;" escape="false" rendered="#{DialogManager.bean.pooledTask}" />
<a:panel id="metadata-panel" label="#{msg.task_properties}" <a:panel id="metadata-panel" label="#{msg.task_properties}"
border="white" bgcolor="white" titleBorder="lbgrey" expandedTitleBorder="dotted" titleBgcolor="white" styleClass="mainSubTitle"> border="white" bgcolor="white" titleBorder="lbgrey" expandedTitleBorder="dotted" titleBgcolor="white" styleClass="mainSubTitle">

View File

@@ -0,0 +1,95 @@
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %>
<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %>
<h:outputText value="#{msg.no_tasks}" rendered="#{empty WorkflowBean.pooledTasks}" />
<a:richList id="pooled-tasks-list" viewMode="details" value="#{WorkflowBean.pooledTasks}" var="r"
styleClass="recordSet" headerStyleClass="recordSetHeader" rowStyleClass="recordSetRow"
altRowStyleClass="recordSetRowAlt" width="100%" pageSize="10"
initialSortColumn="created" initialSortDescending="true"
rendered="#{not empty WorkflowBean.pooledTasks}">
<%-- Primary column for details view mode --%>
<a:column id="col1" primary="true" width="200" style="padding:2px;text-align:left">
<f:facet name="header">
<a:sortLink id="col1-sort" label="#{msg.description}" value="name" mode="case-insensitive" styleClass="header"/>
</f:facet>
<f:facet name="small-icon">
<a:actionLink id="col1-act1" value="#{r['bpm:description']}" image="/images/icons/workflow_task.gif" showLink="false"
actionListener="#{WorkflowBean.setupTaskDialog}" action="dialog:manageTask">
<f:param name="id" value="#{r.id}" />
<f:param name="type" value="#{r.type}" />
</a:actionLink>
</f:facet>
<a:actionLink id="col1-act2" value="#{r['bpm:description']}" actionListener="#{WorkflowBean.setupTaskDialog}"
action="dialog:manageTask">
<f:param name="id" value="#{r.id}" />
<f:param name="type" value="#{r.type}" />
</a:actionLink>
</a:column>
<%-- Task type --%>
<a:column id="col2" style="padding:2px;text-align:left">
<f:facet name="header">
<a:sortLink id="col2-sort" label="#{msg.type}" value="name" mode="case-insensitive" styleClass="header"/>
</f:facet>
<h:outputText id="col2-txt" value="#{r.name}" />
</a:column>
<%-- Task id column --%>
<a:column id="col3" style="padding:2px;text-align:left">
<f:facet name="header">
<a:sortLink id="col3-sort" label="#{msg.id}" value="bpm:taskId" styleClass="header"/>
</f:facet>
<h:outputText id="col3-txt" value="#{r['bpm:taskId']}" />
</a:column>
<%-- Created Date column --%>
<a:column id="col4" style="padding:2px;text-align:left">
<f:facet name="header">
<a:sortLink id="col4-sort" label="#{msg.created}" value="created" styleClass="header"/>
</f:facet>
<h:outputText id="col4-txt" value="#{r.created}">
<a:convertXMLDate type="both" pattern="#{msg.date_time_pattern}" />
</h:outputText>
</a:column>
<%-- Due date column --%>
<a:column id="col5" style="padding:2px;text-align:left">
<f:facet name="header">
<a:sortLink id="col5-sort" label="#{msg.due_date}" value="bpm:dueDate" styleClass="header"/>
</f:facet>
<h:outputText id="col5-txt" value="#{r['bpm:dueDate']}">
<a:convertXMLDate type="both" pattern="#{msg.date_pattern}" />
</h:outputText>
</a:column>
<%-- Status column --%>
<a:column id="col6" style="padding:2px;text-align:left">
<f:facet name="header">
<a:sortLink id="col6-sort" label="#{msg.status}" value="bpm:status" styleClass="header"/>
</f:facet>
<h:outputText id="col6-txt" value="#{r['bpm:status']}" />
</a:column>
<%-- Priority column --%>
<a:column id="col7" style="padding:2px;text-align:left">
<f:facet name="header">
<a:sortLink id="col7-sort" label="#{msg.priority}" value="bpm:priority" styleClass="header"/>
</f:facet>
<h:outputText id="col7-txt" value="#{r['bpm:priority']}" />
</a:column>
<%-- Actions column --%>
<a:column id="col8" actions="true" style="padding:2px;text-align:left">
<f:facet name="header">
<h:outputText id="col8-txt" value="#{msg.actions}"/>
</f:facet>
<r:actions id="col8-actions" value="dashlet_pooled_actions" context="#{r}" showLink="false"
styleClass="inlineAction" />
</a:column>
<a:dataPager id="pager" styleClass="pager" />
</a:richList>