mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Checkpoint for workflow features
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@3518 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
package org.alfresco.web.bean.repository;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* Represents a transient node i.e. it is not and will not be present in the repository.
|
||||
* <p>
|
||||
* This type of node is typically used to drive rich lists where the Map implementation
|
||||
* is required for sorting columns.
|
||||
* </p>
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class TransientMapNode extends TransientNode implements Map<String, Object>
|
||||
{
|
||||
private static final long serialVersionUID = 1120307465342597322L;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* <p>
|
||||
* NOTE: The name is NOT automatically added to the map of properties,
|
||||
* if you need the name of this node to be in the map then add it to
|
||||
* the map passed in to this constructor.
|
||||
* </p>
|
||||
*
|
||||
* @param type The type this node will represent
|
||||
* @param name The name of the node
|
||||
* @param data The properties and associations this node will have
|
||||
*/
|
||||
public TransientMapNode(QName type, String name, Map<QName, Serializable> data)
|
||||
{
|
||||
super(type, name, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Transient map node of type: " + getType() +
|
||||
"\nProperties: " + this.getProperties().toString();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Map implementation - allows the Node bean to be accessed using JSF expression syntax
|
||||
|
||||
/**
|
||||
* @see java.util.Map#clear()
|
||||
*/
|
||||
public void clear()
|
||||
{
|
||||
getProperties().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.Map#containsKey(java.lang.Object)
|
||||
*/
|
||||
public boolean containsKey(Object key)
|
||||
{
|
||||
return getProperties().containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.Map#containsValue(java.lang.Object)
|
||||
*/
|
||||
public boolean containsValue(Object value)
|
||||
{
|
||||
return getProperties().containsKey(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.Map#entrySet()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Set entrySet()
|
||||
{
|
||||
return getProperties().entrySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.Map#get(java.lang.Object)
|
||||
*/
|
||||
public Object get(Object key)
|
||||
{
|
||||
return getProperties().get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.Map#isEmpty()
|
||||
*/
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return getProperties().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.Map#keySet()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Set keySet()
|
||||
{
|
||||
return getProperties().keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.Map#put(K, V)
|
||||
*/
|
||||
public Object put(String key, Object value)
|
||||
{
|
||||
return getProperties().put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.Map#putAll(java.util.Map)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void putAll(Map t)
|
||||
{
|
||||
getProperties().putAll(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.Map#remove(java.lang.Object)
|
||||
*/
|
||||
public Object remove(Object key)
|
||||
{
|
||||
return getProperties().remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.Map#size()
|
||||
*/
|
||||
public int size()
|
||||
{
|
||||
return getProperties().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.Map#values()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Collection values()
|
||||
{
|
||||
return getProperties().values();
|
||||
}
|
||||
}
|
@@ -20,6 +20,7 @@ import org.alfresco.util.GUID;
|
||||
* This type of node is typically used to drive the property sheet when data collection
|
||||
* is required for a type but the node does not need to be stored in the repository. An
|
||||
* example use is the workflow, transient nodes are used to collect workitem metadata.
|
||||
* </p>
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
@@ -33,6 +34,7 @@ public class TransientNode extends Node
|
||||
* NOTE: The name is NOT automatically added to the map of properties,
|
||||
* if you need the name of this node to be in the map then add it to
|
||||
* the map passed in to this constructor.
|
||||
* </p>
|
||||
*
|
||||
* @param type The type this node will represent
|
||||
* @param name The name of the node
|
||||
|
@@ -1,12 +1,24 @@
|
||||
package org.alfresco.web.bean.workflow;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.transaction.UserTransaction;
|
||||
|
||||
import org.alfresco.service.cmr.workflow.WorkflowService;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowTask;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.dialog.BaseDialogBean;
|
||||
import org.alfresco.web.bean.repository.Node;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
import org.alfresco.web.bean.repository.TransientNode;
|
||||
import org.alfresco.web.config.DialogsConfigElement.DialogButtonConfig;
|
||||
import org.alfresco.web.ui.common.Utils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
@@ -17,51 +29,186 @@ import org.apache.commons.logging.LogFactory;
|
||||
*/
|
||||
public class ManageWorkItemDialog extends BaseDialogBean
|
||||
{
|
||||
private static final Log logger = LogFactory.getLog(ManageWorkItemDialog.class);
|
||||
protected WorkflowService workflowService;
|
||||
protected Node workItemNode;
|
||||
protected WorkflowTask workItem;
|
||||
protected String[] transitions;
|
||||
|
||||
protected static final String ID_PREFIX = "transition_";
|
||||
protected static final String CLIENT_ID_PREFIX = "dialog:" + ID_PREFIX;
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ManageWorkItemDialog.class);
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Dialog implementation
|
||||
|
||||
@Override
|
||||
public void init(Map<String, String> parameters)
|
||||
{
|
||||
super.init(parameters);
|
||||
|
||||
String taskId = this.parameters.get("id");
|
||||
this.workItem = this.workflowService.getTaskById(taskId);
|
||||
|
||||
if (this.workItem != null)
|
||||
{
|
||||
// setup a transient node to represent the work item we're managing
|
||||
WorkflowTaskDefinition taskDef = this.workItem.definition;
|
||||
this.workItemNode = new TransientNode(taskDef.metadata.getName(),
|
||||
"task_" + System.currentTimeMillis(), this.workItem.properties);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Created node for work item: " + this.workItemNode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String finishImpl(FacesContext context, String outcome)
|
||||
throws Exception
|
||||
{
|
||||
return null;
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Saving work item with params: " + this.workItemNode.getProperties());
|
||||
|
||||
// prepare the edited parameters for saving
|
||||
Map<QName, Serializable> params = WorkflowBean.prepareWorkItemParams(this.workItemNode);
|
||||
|
||||
// update the task with the updated parameters
|
||||
this.workflowService.updateTask(this.workItem.id, params, null, null);
|
||||
|
||||
return outcome;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<DialogButtonConfig> getAdditionalButtons()
|
||||
{
|
||||
List<DialogButtonConfig> buttons = new ArrayList<DialogButtonConfig>(1);
|
||||
buttons.add(new DialogButtonConfig("reassign-button",
|
||||
"Reassign", null, "#{ManageWorkItemDialog.reassign}", "false", null));
|
||||
List<DialogButtonConfig> buttons = null;
|
||||
|
||||
if (this.workItem != null)
|
||||
{
|
||||
// get the transitions available from this work item and
|
||||
// show them in the dialog as additional buttons
|
||||
this.transitions = this.workItem.path.node.transitions;
|
||||
|
||||
if (this.transitions != null)
|
||||
{
|
||||
buttons = new ArrayList<DialogButtonConfig>(this.transitions.length);
|
||||
|
||||
for (String trans : this.transitions)
|
||||
{
|
||||
// TODO: Tidy this up when the service returns the list of labels
|
||||
String label = trans;
|
||||
if (label.length() == 0)
|
||||
{
|
||||
label = "Done";
|
||||
}
|
||||
|
||||
buttons.add(new DialogButtonConfig(ID_PREFIX + trans, label, null,
|
||||
"#{DialogManager.bean.transition}", "false", null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buttons;
|
||||
return buttons;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFinishButtonLabel()
|
||||
{
|
||||
return Application.getMessage(FacesContext.getCurrentInstance(), "save");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getFinishButtonDisabled()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Event handlers
|
||||
|
||||
public void approve()
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public String transition()
|
||||
{
|
||||
logger.info("approve button was pressed");
|
||||
}
|
||||
|
||||
public void reject()
|
||||
{
|
||||
logger.info("reject button was pressed");
|
||||
}
|
||||
|
||||
public void reassign()
|
||||
{
|
||||
logger.info("reassign button was pressed");
|
||||
String outcome = getDefaultFinishOutcome();
|
||||
|
||||
// to find out which transition button was pressed we need
|
||||
// to look for the button's id in the request parameters,
|
||||
// the first non-null result is the button that was pressed.
|
||||
FacesContext context = FacesContext.getCurrentInstance();
|
||||
Map reqParams = context.getExternalContext().getRequestParameterMap();
|
||||
|
||||
String selectedTransition = null;
|
||||
for (String trans : this.transitions)
|
||||
{
|
||||
Object result = reqParams.get(CLIENT_ID_PREFIX + trans);
|
||||
if (result != null)
|
||||
{
|
||||
// this was the button that was pressed
|
||||
selectedTransition = trans;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedTransition != null)
|
||||
{
|
||||
UserTransaction tx = null;
|
||||
|
||||
try
|
||||
{
|
||||
tx = Repository.getUserTransaction(context);
|
||||
tx.begin();
|
||||
|
||||
// prepare the edited parameters for saving
|
||||
Map<QName, Serializable> params = WorkflowBean.prepareWorkItemParams(this.workItemNode);
|
||||
|
||||
// update the task with the updated parameters
|
||||
this.workflowService.updateTask(this.workItem.id, params, null, null);
|
||||
|
||||
// signal the selected transition to the workflow task
|
||||
this.workflowService.endTask(this.workItem.id, selectedTransition);
|
||||
|
||||
// commit the changes
|
||||
tx.commit();
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Ended work item with transition: " + selectedTransition);
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
// reset the flag so we can re-attempt the operation
|
||||
isFinished = false;
|
||||
|
||||
// rollback the transaction
|
||||
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
|
||||
Utils.addErrorMessage(formatErrorMessage(e));
|
||||
outcome = this.getErrorOutcome(e);
|
||||
}
|
||||
}
|
||||
|
||||
return outcome;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Bean Getters and Setters
|
||||
|
||||
public boolean getApproveDisabled()
|
||||
|
||||
/**
|
||||
* Returns the Node representing the work item
|
||||
*
|
||||
* @return The node
|
||||
*/
|
||||
public Node getWorkItemNode()
|
||||
{
|
||||
return true;
|
||||
return this.workItemNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the workflow service to use
|
||||
*
|
||||
* @param workflowService
|
||||
* WorkflowService instance
|
||||
*/
|
||||
public void setWorkflowService(WorkflowService workflowService)
|
||||
{
|
||||
this.workflowService = workflowService;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
package org.alfresco.web.bean.workflow;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -10,18 +9,14 @@ import java.util.ResourceBundle;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.model.SelectItem;
|
||||
|
||||
import org.alfresco.service.cmr.repository.AssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowPath;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowService;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowTask;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowTaskState;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.repository.Node;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
import org.alfresco.web.bean.repository.TransientNode;
|
||||
import org.alfresco.web.bean.wizard.BaseWizardBean;
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -74,7 +69,8 @@ public class StartWorkflowWizard extends BaseWizardBean
|
||||
logger.debug("Starting workflow with params: " + this.startTaskNode.getProperties());
|
||||
|
||||
// start the workflow to get access to the start task
|
||||
WorkflowPath path = this.workflowService.startWorkflow(this.selectedWorkflow, prepareTaskParams());
|
||||
WorkflowPath path = this.workflowService.startWorkflow(this.selectedWorkflow,
|
||||
WorkflowBean.prepareWorkItemParams(this.startTaskNode));
|
||||
if (path != null)
|
||||
{
|
||||
// extract the start task
|
||||
@@ -239,48 +235,4 @@ public class StartWorkflowWizard extends BaseWizardBean
|
||||
{
|
||||
this.workflowService = workflowService;
|
||||
}
|
||||
|
||||
protected Map<QName, Serializable> prepareTaskParams()
|
||||
{
|
||||
Map<QName, Serializable> params = new HashMap<QName, Serializable>();
|
||||
|
||||
// marshal the properties and associations captured by the property sheet
|
||||
// back into a Map to pass to the workflow service
|
||||
|
||||
// go through all the properties in the transient node and add them to
|
||||
// params map
|
||||
Map<String, Object> props = this.startTaskNode.getProperties();
|
||||
for (String propName : props.keySet())
|
||||
{
|
||||
QName propQName = Repository.resolveToQName(propName);
|
||||
params.put(propQName, (Serializable)props.get(propName));
|
||||
}
|
||||
|
||||
// go through any associations that have been added to the start task
|
||||
// and build a list of NodeRefs representing the targets
|
||||
Map<String, Map<String, AssociationRef>> assocs = this.startTaskNode.getAddedAssociations();
|
||||
for (String assocName : assocs.keySet())
|
||||
{
|
||||
QName assocQName = Repository.resolveToQName(assocName);
|
||||
|
||||
// get the associations added and create list of targets
|
||||
Map<String, AssociationRef> addedAssocs = assocs.get(assocName);
|
||||
List<NodeRef> targets = new ArrayList<NodeRef>(addedAssocs.size());
|
||||
for (AssociationRef assoc : addedAssocs.values())
|
||||
{
|
||||
targets.add(assoc.getTargetRef());
|
||||
}
|
||||
|
||||
// add the targets for this particular association
|
||||
params.put(assocQName, (Serializable)targets);
|
||||
}
|
||||
|
||||
// String reviewer = (String)this.startTaskNode.getProperties().get(
|
||||
// ContentModel.PROP_NAME);
|
||||
// Map<QName, Serializable> params = new HashMap<QName, Serializable>(1);
|
||||
// params.put(QName.createQName(NamespaceService.DEFAULT_URI, "reviewer"), reviewer);
|
||||
|
||||
|
||||
return params;
|
||||
}
|
||||
}
|
||||
|
140
source/java/org/alfresco/web/bean/workflow/WorkflowBean.java
Normal file
140
source/java/org/alfresco/web/bean/workflow/WorkflowBean.java
Normal file
@@ -0,0 +1,140 @@
|
||||
package org.alfresco.web.bean.workflow;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.service.cmr.repository.AssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowService;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowTask;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowTaskState;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.ISO9075;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.repository.Node;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
import org.alfresco.web.bean.repository.TransientMapNode;
|
||||
import org.alfresco.web.bean.repository.User;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Managed bean used for handling workflow related features
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class WorkflowBean
|
||||
{
|
||||
protected WorkflowService workflowService;
|
||||
protected List<Node> workItems;
|
||||
|
||||
private static final Log logger = LogFactory.getLog(WorkflowBean.class);
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Bean Getters and Setters
|
||||
|
||||
public List<Node> getWorkItemsToDo()
|
||||
{
|
||||
// get the current username
|
||||
FacesContext fc = FacesContext.getCurrentInstance();
|
||||
User user = Application.getCurrentUser(fc);
|
||||
String userName = ISO9075.encode(user.getUserName());
|
||||
|
||||
// get the current in progress tasks for the current user
|
||||
List<WorkflowTask> tasks = this.workflowService.getAssignedTasks(
|
||||
userName, WorkflowTaskState.IN_PROGRESS);
|
||||
|
||||
// create a list of transient nodes to represent
|
||||
this.workItems = new ArrayList<Node>(tasks.size());
|
||||
for (WorkflowTask task : tasks)
|
||||
{
|
||||
createWorkItem(task);
|
||||
}
|
||||
|
||||
return this.workItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the workflow service to use
|
||||
*
|
||||
* @param workflowService WorkflowService instance
|
||||
*/
|
||||
public void setWorkflowService(WorkflowService workflowService)
|
||||
{
|
||||
this.workflowService = workflowService;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Helper methods
|
||||
|
||||
public static Map<QName, Serializable> prepareWorkItemParams(Node node)
|
||||
{
|
||||
Map<QName, Serializable> params = new HashMap<QName, Serializable>();
|
||||
|
||||
// marshal the properties and associations captured by the property sheet
|
||||
// back into a Map to pass to the workflow service
|
||||
|
||||
// go through all the properties in the transient node and add them to
|
||||
// params map
|
||||
Map<String, Object> props = node.getProperties();
|
||||
for (String propName : props.keySet())
|
||||
{
|
||||
QName propQName = Repository.resolveToQName(propName);
|
||||
params.put(propQName, (Serializable)props.get(propName));
|
||||
}
|
||||
|
||||
// go through any associations that have been added to the start task
|
||||
// and build a list of NodeRefs representing the targets
|
||||
Map<String, Map<String, AssociationRef>> assocs = node.getAddedAssociations();
|
||||
for (String assocName : assocs.keySet())
|
||||
{
|
||||
QName assocQName = Repository.resolveToQName(assocName);
|
||||
|
||||
// get the associations added and create list of targets
|
||||
Map<String, AssociationRef> addedAssocs = assocs.get(assocName);
|
||||
List<NodeRef> targets = new ArrayList<NodeRef>(addedAssocs.size());
|
||||
for (AssociationRef assoc : addedAssocs.values())
|
||||
{
|
||||
targets.add(assoc.getTargetRef());
|
||||
}
|
||||
|
||||
// add the targets for this particular association
|
||||
params.put(assocQName, (Serializable)targets);
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and populates a TransientNode to represent the given
|
||||
* workflow task from the repository workflow engine
|
||||
*
|
||||
* @param task The task to create a representation of
|
||||
*/
|
||||
protected void createWorkItem(WorkflowTask task)
|
||||
{
|
||||
// get the type of the task
|
||||
WorkflowTaskDefinition taskDef = task.definition;
|
||||
|
||||
// create the basic transient node
|
||||
TransientMapNode node = new TransientMapNode(taskDef.metadata.getName(),
|
||||
task.name, task.properties);
|
||||
|
||||
// add properties for the other useful metadata
|
||||
node.getProperties().put(ContentModel.PROP_NAME.toString(), task.name);
|
||||
node.getProperties().put("type", taskDef.metadata.getTitle());
|
||||
node.getProperties().put("id", task.id);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Created node for work item with id '" + task.id + "' " + node);
|
||||
|
||||
this.workItems.add(node);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user