Big honkin' merge from head. Sheesh!

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@3617 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Britt Park
2006-08-27 01:01:30 +00:00
parent 465ae145be
commit b0d02fa6be
241 changed files with 12379 additions and 1061 deletions

View File

@@ -0,0 +1,106 @@
package org.alfresco.web.bean.workflow;
import java.text.MessageFormat;
import java.util.Map;
import javax.faces.context.FacesContext;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.dialog.BaseDialogBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Bean implementation for the "Cancel Workflow" dialog
*
* @author gavinc
*/
public class CancelWorkflowDialog extends BaseDialogBean
{
protected String workflowInstanceId;
protected WorkflowService workflowService;
private static final Log logger = LogFactory.getLog(CancelWorkflowDialog.class);
// ------------------------------------------------------------------------------
// Dialog implementation
@Override
public void init(Map<String, String> parameters)
{
super.init(parameters);
// make sure the workflow instance id has been passed
this.workflowInstanceId = this.parameters.get("workflow-instance-id");
if (this.workflowInstanceId == null || this.workflowInstanceId.length() == 0)
{
throw new IllegalArgumentException("Cancel workflow dialog called without workflow instance id");
}
}
@Override
protected String finishImpl(FacesContext context, String outcome)
throws Exception
{
if (logger.isDebugEnabled())
logger.debug("Cancelling workflow with id: " + this.workflowInstanceId);
// cancel the workflow
this.workflowService.cancelWorkflow(this.workflowInstanceId);
if (logger.isDebugEnabled())
logger.debug("Cancelled workflow with id: " + this.workflowInstanceId);
return outcome;
}
@Override
protected String getErrorMessageId()
{
return "error_cancel_workflow";
}
@Override
public boolean getFinishButtonDisabled()
{
return false;
}
// ------------------------------------------------------------------------------
// Bean Getters and Setters
/**
* Returns the confirmation to display to the user before deleting the content.
*
* @return The formatted message to display
*/
public String getConfirmMessage()
{
String confirmMsg = Application.getMessage(FacesContext.getCurrentInstance(),
"cancel_workflow_confirm");
return MessageFormat.format(confirmMsg,
new Object[] {this.parameters.get("workflow-instance-name")});
}
/**
* Returns the workflow service instance
*
* @return WorkflowService instance
*/
public WorkflowService getWorkflowService()
{
return workflowService;
}
/**
* Sets the workflow service to use
*
* @param workflowService The WorkflowService instance
*/
public void setWorkflowService(WorkflowService workflowService)
{
this.workflowService = workflowService;
}
}

View File

@@ -0,0 +1,476 @@
package org.alfresco.web.bean.workflow;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
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.WorkflowTransition;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.dialog.BaseDialogBean;
import org.alfresco.web.bean.repository.MapNode;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.NodePropertyResolver;
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.alfresco.web.ui.common.component.UIActionLink;
import org.alfresco.web.ui.common.component.data.UIRichList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Bean implementation for the "Manage WorkItem" dialog.
*
* @author gavinc
*/
public class ManageWorkItemDialog extends BaseDialogBean
{
protected WorkflowService workflowService;
protected Node workItemNode;
protected WorkflowTask workItem;
protected WorkflowTransition[] transitions;
protected List<Node> resources;
protected WorkItemCompleteResolver completeResolver = new WorkItemCompleteResolver();
protected UIRichList packageItemsRichList;
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);
}
}
@Override
protected String finishImpl(FacesContext context, String outcome)
throws Exception
{
if (logger.isDebugEnabled())
logger.debug("Saving work item: " + this.workItemNode.getId());
// 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 = 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 (WorkflowTransition trans : this.transitions)
{
buttons.add(new DialogButtonConfig(ID_PREFIX + trans.title, trans.title, null,
"#{DialogManager.bean.transition}", "false", null));
}
}
}
return buttons;
}
@Override
public String getFinishButtonLabel()
{
return Application.getMessage(FacesContext.getCurrentInstance(), "save");
}
@Override
public boolean getFinishButtonDisabled()
{
return false;
}
// ------------------------------------------------------------------------------
// Event handlers
@SuppressWarnings("unused")
public String transition()
{
String outcome = getDefaultFinishOutcome();
if (logger.isDebugEnabled())
logger.debug("Transitioning work item: " + this.workItemNode.getId());
// 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 (WorkflowTransition trans : this.transitions)
{
Object result = reqParams.get(CLIENT_ID_PREFIX + trans.title);
if (result != null)
{
// this was the button that was pressed
selectedTransition = trans.id;
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)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
Utils.addErrorMessage(formatErrorMessage(e), e);
outcome = this.getErrorOutcome(e);
}
}
return outcome;
}
/**
* Removes an item from the workflow package
*
* @param event The event containing a reference to the item to remove
*/
public void removePackageItem(ActionEvent event)
{
logger.info("remove package item: " + event);
}
/**
* Toggles the complete flag for a workflow package item
*
* @param event The event containing a reference to the item to toggle the status for
*/
public void togglePackageItemComplete(ActionEvent event)
{
UserTransaction tx = null;
try
{
FacesContext context = FacesContext.getCurrentInstance();
tx = Repository.getUserTransaction(context);
tx.begin();
UIActionLink link = (UIActionLink)event.getComponent();
Map<String, String> params = link.getParameterMap();
// create the node ref for the item we are toggling
NodeRef nodeRef = new NodeRef(Repository.getStoreRef(),
(String)params.get("id"));
// get the existing list of completed items
List<NodeRef> completedItems = (List<NodeRef>)this.workItem.properties.get(
WorkflowModel.PROP_COMPLETED_ITEMS);
if (completedItems == null)
{
// if it doesn't exist yet create the list and add the noderef
completedItems = new ArrayList<NodeRef>(1);
completedItems.add(nodeRef);
this.workItem.properties.put(WorkflowModel.PROP_COMPLETED_ITEMS,
(Serializable)completedItems);
}
else
{
if (completedItems.contains(nodeRef))
{
// the item is already in the list remove it
completedItems.remove(nodeRef);
// NOTE: There is a bug somwehere which causes the list to be
// returned as a byte array instead of a list if an empty
// list is persisted, therefore if the list is now empty
// set the completed items back to null
if (completedItems.size() == 0)
{
this.workItem.properties.put(WorkflowModel.PROP_COMPLETED_ITEMS, null);
}
}
else
{
// the noderef is not in the list yet so just add it
completedItems.add(nodeRef);
}
}
// update the task with the updated parameters
this.workflowService.updateTask(this.workItem.id, this.workItem.properties,
null, null);
// commit the transaction
tx.commit();
// reset the rich list if the change was successful
this.packageItemsRichList.setValue(null);
}
catch (Throwable err)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err);
this.resources = Collections.<Node>emptyList();
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
}
// ------------------------------------------------------------------------------
// Bean Getters and Setters
/**
* Sets the rich list being used for the workflow package items
*
* @param richList The rich list instance
*/
public void setPackageItemsRichList(UIRichList richList)
{
this.packageItemsRichList = richList;
}
/**
* Returns the rich list being used for the workflow package items
*
* @return The rich list instance
*/
public UIRichList getPackageItemsRichList()
{
return this.packageItemsRichList;
}
/**
* Returns the Node representing the work item
*
* @return The node
*/
public Node getWorkItemNode()
{
return this.workItemNode;
}
/**
* Returns the action group the current task uses for the workflow package
*
* @return action group id
*/
public String getPackageActionGroup()
{
return (String)this.workItem.properties.get(
WorkflowModel.PROP_PACKAGE_ACTION_GROUP);
}
/**
* Returns the action group the current task uses for each workflow package item
*
* @return action group id
*/
public String getPackageItemActionGroup()
{
return (String)this.workItem.properties.get(
WorkflowModel.PROP_PACKAGE_ITEM_ACTION_GROUP);
}
/**
* Returns a list of resources associated with this work item
* i.e. the children of the workflow package
*
* @return The list of nodes
*/
public List<Node> getResources()
{
NodeRef workflowPackage = null;
Serializable obj = this.workItem.properties.get(WorkflowModel.ASSOC_PACKAGE);
// TODO: remove this workaroud where JBPM may return a String and not the NodeRef
if (obj instanceof NodeRef)
{
workflowPackage = (NodeRef)obj;
}
else if (obj instanceof String)
{
workflowPackage = new NodeRef((String)obj);
}
this.resources = new ArrayList<Node>(4);
if (workflowPackage != null)
{
UserTransaction tx = null;
try
{
FacesContext context = FacesContext.getCurrentInstance();
tx = Repository.getUserTransaction(context, true);
tx.begin();
if (logger.isDebugEnabled())
logger.debug("Found workflow package for work item '" +
this.workItem.id + "': " + workflowPackage );
List<ChildAssociationRef> childRefs = this.nodeService.getChildAssocs(workflowPackage,
ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
for (ChildAssociationRef ref: childRefs)
{
// create our Node representation from the NodeRef
NodeRef nodeRef = ref.getChildRef();
if (this.nodeService.exists(nodeRef))
{
// find it's type so we can see if it's a node we are interested in
QName type = this.nodeService.getType(nodeRef);
// make sure the type is defined in the data dictionary
TypeDefinition typeDef = this.dictionaryService.getType(type);
if (typeDef != null)
{
// look for content nodes or links to content
// NOTE: folders within workflow packages are ignored for now
if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT) ||
ContentModel.TYPE_FILELINK.equals(type))
{
// create our Node representation
MapNode node = new MapNode(nodeRef, this.nodeService, true);
this.browseBean.setupCommonBindingProperties(node);
// add property resolvers to show path information
node.addPropertyResolver("path", this.browseBean.resolverPath);
node.addPropertyResolver("displayPath", this.browseBean.resolverDisplayPath);
// add a property resolver to indicate whether the item has been completed or not
node.addPropertyResolver("completed", this.completeResolver);
this.resources.add(node);
}
}
else
{
if (logger.isWarnEnabled())
logger.warn("Found invalid object in database: id = " + nodeRef + ", type = " + type);
}
}
}
// commit the transaction
tx.commit();
}
catch (Throwable err)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err);
this.resources = Collections.<Node>emptyList();
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
}
else if (logger.isDebugEnabled())
{
logger.debug("Failed to find workflow package for work item: " + this.workItem.id);
}
return this.resources;
}
/**
* Sets the workflow service to use
*
* @param workflowService
* WorkflowService instance
*/
public void setWorkflowService(WorkflowService workflowService)
{
this.workflowService = workflowService;
}
// ------------------------------------------------------------------------------
// Helper methods
// ------------------------------------------------------------------------------
// Inner classes
/**
* Property resolver to determine if the given node has been flagged as complete
*/
protected class WorkItemCompleteResolver implements NodePropertyResolver
{
public Object get(Node node)
{
String result = Application.getMessage(FacesContext.getCurrentInstance(), "no");
List<NodeRef> completedItems = (List<NodeRef>)workItem.properties.get(
WorkflowModel.PROP_COMPLETED_ITEMS);
if (completedItems != null && completedItems.size() > 0 &&
completedItems.contains(node.getNodeRef()))
{
result = Application.getMessage(FacesContext.getCurrentInstance(), "yes");
}
return result;
}
}
}

View File

@@ -0,0 +1,199 @@
package org.alfresco.web.bean.workflow;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.namespace.NamespaceService;
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.Repository;
import org.alfresco.web.ui.common.SortableSelectItem;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.UIGenericPicker;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Bean implementation for the "Reassign Work Item" dialog
*
* @author gavinc
*/
public class ReassignWorkItemDialog extends BaseDialogBean
{
protected String workItemId;
protected WorkflowService workflowService;
protected PersonService personService;
private static final Log logger = LogFactory.getLog(ReassignWorkItemDialog.class);
// ------------------------------------------------------------------------------
// Dialog implementation
@Override
public void init(Map<String, String> parameters)
{
super.init(parameters);
this.workItemId = this.parameters.get("workitem-id");
if (this.workItemId == null || this.workItemId.length() == 0)
{
throw new IllegalArgumentException("Reassign workitem dialog called without task id");
}
}
@Override
protected String finishImpl(FacesContext context, String outcome)
throws Exception
{
if (logger.isDebugEnabled())
logger.debug("Reassigning work item with id: " + this.workItemId);
UIComponent picker = context.getViewRoot().findComponent("dialog:dialog-body:user-picker");
if (picker != null && picker instanceof UIGenericPicker)
{
UIGenericPicker userPicker = (UIGenericPicker)picker;
String[] user = userPicker.getSelectedResults();
if (user != null && user.length > 0)
{
// create a map to hold the new owner property then update the task
String userName = user[0];
Map<QName, Serializable> params = new HashMap<QName, Serializable>(1);
params.put(ContentModel.PROP_OWNER, userName);
this.workflowService.updateTask(this.workItemId, params, null, null);
}
else
{
if (logger.isWarnEnabled())
logger.warn("Failed to find selected user, reassign was unsuccessful");
}
}
else
{
if (logger.isWarnEnabled())
logger.warn("Failed to find user-picker component, reassign was unsuccessful");
}
if (logger.isDebugEnabled())
logger.debug("Reassigning work item with id: " + this.workItemId);
return outcome;
}
@Override
protected String getErrorMessageId()
{
return "error_reassign_workitem";
}
// ------------------------------------------------------------------------------
// Bean Getters and Setters
/**
* Property accessed by the Generic Picker component.
*
* @return the array of filter options to show in the users/groups picker
*/
public SelectItem[] getFilters()
{
ResourceBundle bundle = Application.getBundle(FacesContext.getCurrentInstance());
return new SelectItem[] {new SelectItem("0", bundle.getString("users"))};
}
/**
* Query callback method executed by the Generic Picker component.
* This method is part of the contract to the Generic Picker, it is up to the backing bean
* to execute whatever query is appropriate and return the results.
*
* @param filterIndex Index of the filter drop-down selection
* @param contains Text from the contains textbox
*
* @return An array of SelectItem objects containing the results to display in the picker.
*/
public SelectItem[] pickerCallback(int filterIndex, String contains)
{
FacesContext context = FacesContext.getCurrentInstance();
SelectItem[] items;
UserTransaction tx = null;
try
{
tx = Repository.getUserTransaction(context, true);
tx.begin();
// build xpath to match available User/Person objects
NodeRef peopleRef = personService.getPeopleContainer();
// NOTE: see SearcherComponentTest
String xpath = "*[like(@" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + "firstName, '%" + contains + "%', false)" +
" or " + "like(@" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + "lastName, '%" + contains + "%', false)]";
List<NodeRef> nodes = searchService.selectNodes(
peopleRef,
xpath,
null,
this.namespaceService,
false);
items = new SelectItem[nodes.size()];
for (int index=0; index<nodes.size(); index++)
{
NodeRef personRef = nodes.get(index);
String firstName = (String)this.nodeService.getProperty(personRef, ContentModel.PROP_FIRSTNAME);
String lastName = (String)this.nodeService.getProperty(personRef, ContentModel.PROP_LASTNAME);
String username = (String)this.nodeService.getProperty(personRef, ContentModel.PROP_USERNAME);
SelectItem item = new SortableSelectItem(username, firstName + " " + lastName, lastName);
items[index] = item;
}
Arrays.sort(items);
// commit the transaction
tx.commit();
}
catch (Throwable err)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err);
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
items = new SelectItem[0];
}
return items;
}
/**
* Sets the workflow service to use
*
* @param workflowService The WorkflowService instance
*/
public void setWorkflowService(WorkflowService workflowService)
{
this.workflowService = workflowService;
}
/**
* @param permissionService The PermissionService to set.
*/
public void setPersonService(PersonService personService)
{
this.personService = personService;
}
}

View File

@@ -0,0 +1,329 @@
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 java.util.ResourceBundle;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
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.NamespaceService;
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;
import org.apache.commons.logging.LogFactory;
/**
* Bean implementation for the Start Workflow Wizard.
*
* @author gavinc
*/
public class StartWorkflowWizard extends BaseWizardBean
{
protected String selectedWorkflow;
protected List<SelectItem> availableWorkflows;
protected Map<String, WorkflowDefinition> workflows;
protected WorkflowService workflowService;
protected Node startTaskNode;
protected boolean nextButtonDisabled = false;
private static final Log logger = LogFactory.getLog(StartWorkflowWizard.class);
// ------------------------------------------------------------------------------
// Wizard implementation
@Override
public void init(Map<String, String> parameters)
{
super.init(parameters);
// reset the selected workflow
if (this.availableWorkflows != null && this.availableWorkflows.size() > 0)
{
this.selectedWorkflow = (String)this.availableWorkflows.get(0).getValue();
}
else
{
this.selectedWorkflow = null;
}
this.startTaskNode = null;
}
@Override
protected String finishImpl(FacesContext context, String outcome)
throws Exception
{
// TODO: Deal with workflows that don't require any data
if (logger.isDebugEnabled())
logger.debug("Starting workflow: " + this.selectedWorkflow);
// prepare the parameters from the current state of the property sheet
Map<QName, Serializable> params = WorkflowBean.prepareWorkItemParams(this.startTaskNode);
// create a workflow package for the attached items and add them
String itemToWorkflowId = this.parameters.get("item-to-workflow");
if (itemToWorkflowId != null && itemToWorkflowId.length() > 0)
{
// create the node ref for the item and determine its type
NodeRef itemToWorkflow = new NodeRef(Repository.getStoreRef(), itemToWorkflowId);
QName type = this.nodeService.getType(itemToWorkflow);
NodeRef workflowPackage = null;
if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT) ||
this.dictionaryService.isSubClass(type, ContentModel.TYPE_FILELINK))
{
// create a workflow package and add the given item to workflow as a child
workflowPackage = this.workflowService.createPackage(null);
this.nodeService.addChild(workflowPackage, itemToWorkflow,
ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,
QName.createValidLocalName((String)this.nodeService.getProperty(
itemToWorkflow, ContentModel.PROP_NAME))));
}
// add the workflow package to the parameter map
params.put(WorkflowModel.ASSOC_PACKAGE, workflowPackage);
}
// setup the context for the workflow (this is the space the workflow was launched from)
Node workflowContext = this.navigator.getCurrentNode();
if (workflowContext != null)
{
params.put(WorkflowModel.PROP_CONTEXT, (Serializable)workflowContext.getNodeRef());
}
// start the workflow to get access to the start task
WorkflowPath path = this.workflowService.startWorkflow(this.selectedWorkflow, params);
if (path != null)
{
// extract the start task
List<WorkflowTask> tasks = this.workflowService.getTasksForWorkflowPath(path.id);
if (tasks.size() == 1)
{
WorkflowTask startTask = tasks.get(0);
if (logger.isDebugEnabled())
logger.debug("Found start task:" + startTask);
if (startTask.state == WorkflowTaskState.IN_PROGRESS)
{
// end the start task to trigger the first 'proper'
// task in the workflow
this.workflowService.endTask(startTask.id, null);
}
}
if (logger.isDebugEnabled())
logger.debug("Started workflow: " + this.selectedWorkflow);
}
return outcome;
}
@Override
public String next()
{
String stepName = Application.getWizardManager().getCurrentStepName();
if ("options".equals(stepName) && this.startTaskNode == null)
{
// retrieve the start task for the selected workflow, get the task
// definition and create a transient node to allow the property
// sheet to collect the required data.
WorkflowDefinition flowDef = this.workflows.get(this.selectedWorkflow);
if (logger.isDebugEnabled())
logger.debug("Selected workflow: "+ flowDef);
WorkflowTaskDefinition taskDef = flowDef.startTaskDefinition;
if (taskDef != null)
{
if (logger.isDebugEnabled())
logger.debug("Start task definition: " + taskDef);
// create an instance of a task from the data dictionary
this.startTaskNode = new TransientNode(taskDef.metadata.getName(),
"task_" + System.currentTimeMillis(), null);
}
}
return null;
}
@Override
public boolean getNextButtonDisabled()
{
return this.nextButtonDisabled;
}
// ------------------------------------------------------------------------------
// Bean Getters and Setters
/**
* Returns the workflow selected by the user
*
* @return The selected workflow
*/
public String getSelectedWorkflow()
{
return selectedWorkflow;
}
/**
* Sets the selected workflow
*
* @param selectedWorkflow The workflow selected
*/
public void setSelectedWorkflow(String selectedWorkflow)
{
this.selectedWorkflow = selectedWorkflow;
}
/**
* Returns the Node representing the start task metadata required
*
* @return The Node for the start task
*/
public Node getTaskMetadataNode()
{
return this.startTaskNode;
}
/**
* Returns the action group the current task uses for the workflow package
*
* @return action group id
*/
public String getPackageActionGroup()
{
String actionGroup = null;
WorkflowDefinition flowDef = this.workflows.get(this.selectedWorkflow);
WorkflowTaskDefinition taskDef = flowDef.startTaskDefinition;
if (taskDef != null)
{
PropertyDefinition propDef = taskDef.metadata.getProperties().get(
WorkflowModel.PROP_PACKAGE_ACTION_GROUP);
if (propDef != null)
{
actionGroup = propDef.getDefaultValue();
}
}
return actionGroup;
}
/**
* Returns the action group the current task uses for each workflow package item
*
* @return action group id
*/
public String getPackageItemActionGroup()
{
String actionGroup = null;
WorkflowDefinition flowDef = this.workflows.get(this.selectedWorkflow);
WorkflowTaskDefinition taskDef = flowDef.startTaskDefinition;
if (taskDef != null)
{
PropertyDefinition propDef = taskDef.metadata.getProperties().get(
WorkflowModel.PROP_PACKAGE_ITEM_ACTION_GROUP);
if (propDef != null)
{
actionGroup = propDef.getDefaultValue();
}
}
return actionGroup;
}
/**
* @return Returns the summary data for the wizard.
*/
public String getSummary()
{
ResourceBundle bundle = Application.getBundle(FacesContext.getCurrentInstance());
String workflowName = null;
for (SelectItem item : this.availableWorkflows)
{
if (item.getValue().equals(this.selectedWorkflow))
{
workflowName = item.getLabel();
break;
}
}
return buildSummary(
new String[] {bundle.getString("start_workflow")},
new String[] {workflowName});
}
/**
* Returns a list of workflows that can be started.
*
* @return List of SelectItem objects representing the workflows
*/
public List<SelectItem> getStartableWorkflows()
{
if (this.availableWorkflows == null)
{
this.availableWorkflows = new ArrayList<SelectItem>(4);
this.workflows = new HashMap<String, WorkflowDefinition>(4);
List<WorkflowDefinition> workflowDefs = this.workflowService.getDefinitions();
for (WorkflowDefinition workflowDef : workflowDefs)
{
String label = workflowDef.title;
if (workflowDef.description != null && workflowDef.description.length() > 0)
{
label = label + " (" + workflowDef.description + ")";
}
this.availableWorkflows.add(new SelectItem(workflowDef.id, label));
this.workflows.put(workflowDef.id, workflowDef);
}
// set the initial selected workflow to the first in the list, unless there are no
// workflows, in which disable the next button
if (this.availableWorkflows.size() > 0)
{
this.selectedWorkflow = (String)this.availableWorkflows.get(0).getValue();
}
else
{
this.nextButtonDisabled = true;
}
}
return availableWorkflows;
}
/**
* Sets the workflow service to use
*
* @param workflowService WorkflowService instance
*/
public void setWorkflowService(WorkflowService workflowService)
{
this.workflowService = workflowService;
}
}

View File

@@ -0,0 +1,279 @@
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 javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
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.cmr.workflow.WorkflowTransition;
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.alfresco.web.ui.common.Utils;
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 NodeService nodeService;
protected WorkflowService workflowService;
protected List<Node> workItems;
protected List<Node> completedWorkItems;
private static final Log logger = LogFactory.getLog(WorkflowBean.class);
// ------------------------------------------------------------------------------
// Bean Getters and Setters
/**
* Returns a list of nodes representing the to do work items the
* current user has.
*
* @return List of to do work items
*/
public List<Node> getWorkItemsToDo()
{
// get the current username
FacesContext context = FacesContext.getCurrentInstance();
User user = Application.getCurrentUser(context);
String userName = ISO9075.encode(user.getUserName());
UserTransaction tx = null;
try
{
tx = Repository.getUserTransaction(context, true);
tx.begin();
// 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)
{
Node node = createWorkItem(task);
this.workItems.add(node);
if (logger.isDebugEnabled())
logger.debug("Added to do work item: " + 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 to do work items: " + e.toString(), e);
}
return this.workItems;
}
/**
* Returns a list of nodes representing the completed work items the
* current user has.
*
* @return List of completed work items
*/
public List<Node> getWorkItemsCompleted()
{
// get the current username
FacesContext context = FacesContext.getCurrentInstance();
User user = Application.getCurrentUser(context);
String userName = ISO9075.encode(user.getUserName());
UserTransaction tx = null;
try
{
tx = Repository.getUserTransaction(context, true);
tx.begin();
// get the current in progress tasks for the current user
List<WorkflowTask> tasks = this.workflowService.getAssignedTasks(
userName, WorkflowTaskState.COMPLETED);
// create a list of transient nodes to represent
this.completedWorkItems = new ArrayList<Node>(tasks.size());
for (WorkflowTask task : tasks)
{
Node node = createWorkItem(task);
this.completedWorkItems.add(node);
if (logger.isDebugEnabled())
logger.debug("Added completed work item: " + 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 completed work items: " + e.toString(), e);
}
return this.completedWorkItems;
}
/**
* Sets the workflow service to use
*
* @param workflowService WorkflowService instance
*/
public void setWorkflowService(WorkflowService workflowService)
{
this.workflowService = workflowService;
}
/**
* Sets the node service to use
*
* @param nodeService NodeService instance
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
// ------------------------------------------------------------------------------
// 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);
}
if (logger.isDebugEnabled())
logger.debug("Prepared parameters: " + params);
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 TransientMapNode 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.title, task.properties);
// add properties for the other useful metadata
node.getProperties().put(ContentModel.PROP_NAME.toString(), task.title);
node.getProperties().put("type", taskDef.metadata.getTitle());
node.getProperties().put("id", task.id);
// add the name of the source space (if there is one)
// TODO: remove this workaroud where JBPM may return a String and not the NodeRef
Serializable obj = task.properties.get(WorkflowModel.PROP_CONTEXT);
NodeRef context = null;
if (obj instanceof NodeRef)
{
context = (NodeRef)obj;
}
else if (obj instanceof String)
{
context = new NodeRef((String)obj);
}
if (context != null)
{
String name = Repository.getNameForNode(this.nodeService, context);
node.getProperties().put("sourceSpaceName", name);
node.getProperties().put("sourceSpaceId", context.getId());
}
// add extra properties for completed tasks
if (task.state.equals(WorkflowTaskState.COMPLETED))
{
// add the outcome label for any completed task
String outcome = null;
String transition = (String)task.properties.get(WorkflowModel.PROP_OUTCOME);
if (transition != null)
{
WorkflowTransition[] transitions = task.definition.node.transitions;
for (WorkflowTransition trans : transitions)
{
if (trans.id.equals(transition))
{
outcome = trans.title;
break;
}
}
if (outcome != null)
{
node.getProperties().put("outcome", outcome);
}
}
// add the workflow instance id and name this taks belongs to
node.getProperties().put("workflowInstanceId", task.path.instance.id);
node.getProperties().put("workflowInstanceName", task.path.instance.definition.title);
}
return node;
}
}