mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Implemented temporary workflow form processor and associated test.
An item kind of "workflow" and itemId of "<workflow-def-name>" are now supported (only "jbpm$wf:adhoc" works though at the moment). Showing the form, selecting an assignee and submitting the form will start the ad hoc workflow and create a task for the selected assignee. NOTE: Support for 'packageItems' is not present yet. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@21021 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -44,6 +44,10 @@ import org.alfresco.service.cmr.repository.ScriptLocation;
|
|||||||
import org.alfresco.service.cmr.repository.ScriptService;
|
import org.alfresco.service.cmr.repository.ScriptService;
|
||||||
import org.alfresco.service.cmr.repository.StoreRef;
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
import org.alfresco.service.cmr.security.PersonService;
|
import org.alfresco.service.cmr.security.PersonService;
|
||||||
|
import org.alfresco.service.cmr.workflow.WorkflowInstance;
|
||||||
|
import org.alfresco.service.cmr.workflow.WorkflowService;
|
||||||
|
import org.alfresco.service.cmr.workflow.WorkflowTask;
|
||||||
|
import org.alfresco.service.cmr.workflow.WorkflowTaskState;
|
||||||
import org.alfresco.service.namespace.NamespaceException;
|
import org.alfresco.service.namespace.NamespaceException;
|
||||||
import org.alfresco.service.namespace.NamespaceService;
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
@@ -64,6 +68,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
private ScriptService scriptService;
|
private ScriptService scriptService;
|
||||||
private PersonService personService;
|
private PersonService personService;
|
||||||
private ContentService contentService;
|
private ContentService contentService;
|
||||||
|
private WorkflowService workflowService;
|
||||||
|
|
||||||
private NodeRef document;
|
private NodeRef document;
|
||||||
private NodeRef associatedDoc;
|
private NodeRef associatedDoc;
|
||||||
@@ -106,6 +111,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
private static final String USER_ONE = "UserOne_FormServiceImplTest";
|
private static final String USER_ONE = "UserOne_FormServiceImplTest";
|
||||||
private static final String NODE_FORM_ITEM_KIND = "node";
|
private static final String NODE_FORM_ITEM_KIND = "node";
|
||||||
private static final String TYPE_FORM_ITEM_KIND = "type";
|
private static final String TYPE_FORM_ITEM_KIND = "type";
|
||||||
|
private static final String WORKFLOW_FORM_ITEM_KIND = "workflow";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called during the transaction setup
|
* Called during the transaction setup
|
||||||
@@ -122,6 +128,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
this.scriptService = (ScriptService)this.applicationContext.getBean("ScriptService");
|
this.scriptService = (ScriptService)this.applicationContext.getBean("ScriptService");
|
||||||
this.personService = (PersonService)this.applicationContext.getBean("PersonService");
|
this.personService = (PersonService)this.applicationContext.getBean("PersonService");
|
||||||
this.contentService = (ContentService)this.applicationContext.getBean("ContentService");
|
this.contentService = (ContentService)this.applicationContext.getBean("ContentService");
|
||||||
|
this.workflowService = (WorkflowService)this.applicationContext.getBean("WorkflowService");
|
||||||
|
|
||||||
AuthenticationComponent authenticationComponent = (AuthenticationComponent) this.applicationContext
|
AuthenticationComponent authenticationComponent = (AuthenticationComponent) this.applicationContext
|
||||||
.getBean("authenticationComponent");
|
.getBean("authenticationComponent");
|
||||||
@@ -1221,6 +1228,77 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
assertEquals(1, assocs.size());
|
assertEquals(1, assocs.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testWorkflowForm() throws Exception
|
||||||
|
{
|
||||||
|
// generate a form for a well known workflow-definition supplying
|
||||||
|
// a legitimate set of fields for the workflow
|
||||||
|
List<String> fields = new ArrayList<String>(8);
|
||||||
|
fields.add("bpm:taskId");
|
||||||
|
fields.add("bpm:description");
|
||||||
|
fields.add("bpm:workflowDueDate");
|
||||||
|
//fields.add("packageItems");
|
||||||
|
|
||||||
|
String workflowDefName = "jbpm$wf:adhoc";
|
||||||
|
Form form = this.formService.getForm(new Item(WORKFLOW_FORM_ITEM_KIND, workflowDefName), fields);
|
||||||
|
|
||||||
|
// check a form got returned
|
||||||
|
assertNotNull("Expecting form to be present", form);
|
||||||
|
|
||||||
|
// check item identifier matches
|
||||||
|
assertEquals(WORKFLOW_FORM_ITEM_KIND, form.getItem().getKind());
|
||||||
|
assertEquals(workflowDefName, form.getItem().getId());
|
||||||
|
|
||||||
|
// check the field definitions
|
||||||
|
Collection<FieldDefinition> fieldDefs = form.getFieldDefinitions();
|
||||||
|
assertNotNull("Expecting to find fields", fieldDefs);
|
||||||
|
assertEquals("Expecting to find " + fields.size() + " fields", fields.size(), fieldDefs.size());
|
||||||
|
|
||||||
|
// check the fields are returned correctly
|
||||||
|
Map<String, FieldDefinition> fieldDefMap = new HashMap<String, FieldDefinition>(fieldDefs.size());
|
||||||
|
for (FieldDefinition fieldDef : fieldDefs)
|
||||||
|
{
|
||||||
|
fieldDefMap.put(fieldDef.getName(), fieldDef);
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the fields
|
||||||
|
PropertyFieldDefinition idField = (PropertyFieldDefinition)fieldDefMap.get("bpm:taskId");
|
||||||
|
PropertyFieldDefinition descriptionField = (PropertyFieldDefinition)fieldDefMap.get("bpm:description");
|
||||||
|
PropertyFieldDefinition dueDateField = (PropertyFieldDefinition)fieldDefMap.get("bpm:workflowDueDate");
|
||||||
|
//AssociationFieldDefinition packageItemsField = (AssociationFieldDefinition)fieldDefMap.get("packageItems");
|
||||||
|
|
||||||
|
// check fields are present
|
||||||
|
assertNotNull("Expecting to find the bpm:taskId field", idField);
|
||||||
|
assertNotNull("Expecting to find the bpm:description field", descriptionField);
|
||||||
|
assertNotNull("Expecting to find the bpm:workflowDueDate field", dueDateField);
|
||||||
|
//assertNotNull("Expecting to find the packageItems field", packageItemsField);
|
||||||
|
|
||||||
|
// get the number of tasks now
|
||||||
|
List<WorkflowTask> tasks = this.workflowService.getAssignedTasks(USER_ONE,
|
||||||
|
WorkflowTaskState.IN_PROGRESS);
|
||||||
|
int tasksBefore = tasks.size();
|
||||||
|
|
||||||
|
// persist the form
|
||||||
|
FormData data = new FormData();
|
||||||
|
data.addFieldData("prop_bpm_description", "This is a new adhoc task");
|
||||||
|
data.addFieldData("assoc_bpm_assignee_added",
|
||||||
|
this.personService.getPerson(USER_ONE).toString());
|
||||||
|
//data.addFieldData("packageItems_added", this.document.toString());
|
||||||
|
|
||||||
|
// persist the data
|
||||||
|
WorkflowInstance workflow = (WorkflowInstance)this.formService.saveForm(
|
||||||
|
new Item(WORKFLOW_FORM_ITEM_KIND, workflowDefName), data);
|
||||||
|
|
||||||
|
// verify that the workflow was started by checking the user has one
|
||||||
|
// more task and the details on the workflow instance
|
||||||
|
tasks = this.workflowService.getAssignedTasks(USER_ONE,
|
||||||
|
WorkflowTaskState.IN_PROGRESS);
|
||||||
|
int tasksAfter = tasks.size();
|
||||||
|
assertTrue("Expecting there to be more tasks", tasksAfter > tasksBefore);
|
||||||
|
|
||||||
|
// check workflow instance details
|
||||||
|
assertEquals(workflowDefName, workflow.definition.name);
|
||||||
|
}
|
||||||
|
|
||||||
public void testNoForm() throws Exception
|
public void testNoForm() throws Exception
|
||||||
{
|
{
|
||||||
// test that a form can not be retrieved for a non-existent item
|
// test that a form can not be retrieved for a non-existent item
|
||||||
|
@@ -18,8 +18,33 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.repo.forms.processor.workflow;
|
package org.alfresco.repo.forms.processor.workflow;
|
||||||
|
|
||||||
import org.alfresco.repo.forms.processor.node.TypeFormProcessor;
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.alfresco.repo.forms.Form;
|
||||||
|
import org.alfresco.repo.forms.FormData;
|
||||||
|
import org.alfresco.repo.forms.FormNotFoundException;
|
||||||
|
import org.alfresco.repo.forms.Item;
|
||||||
|
import org.alfresco.repo.forms.processor.node.ContentModelFormProcessor;
|
||||||
|
import org.alfresco.repo.workflow.WorkflowModel;
|
||||||
|
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
||||||
|
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
|
||||||
|
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||||
|
import org.alfresco.service.cmr.dictionary.TypeDefinition;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
|
||||||
|
import org.alfresco.service.cmr.workflow.WorkflowException;
|
||||||
|
import org.alfresco.service.cmr.workflow.WorkflowInstance;
|
||||||
|
import org.alfresco.service.cmr.workflow.WorkflowPath;
|
||||||
import org.alfresco.service.cmr.workflow.WorkflowService;
|
import org.alfresco.service.cmr.workflow.WorkflowService;
|
||||||
|
import org.alfresco.service.cmr.workflow.WorkflowTask;
|
||||||
|
import org.alfresco.service.cmr.workflow.WorkflowTaskState;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Temporary FormProcessor implementation that can generate and persist
|
* Temporary FormProcessor implementation that can generate and persist
|
||||||
@@ -27,8 +52,12 @@ import org.alfresco.service.cmr.workflow.WorkflowService;
|
|||||||
*
|
*
|
||||||
* @author Gavin Cornwell
|
* @author Gavin Cornwell
|
||||||
*/
|
*/
|
||||||
public class WorkflowFormProcessor extends TypeFormProcessor
|
public class WorkflowFormProcessor extends ContentModelFormProcessor<WorkflowDefinition, WorkflowInstance>
|
||||||
{
|
{
|
||||||
|
/** Logger */
|
||||||
|
private static Log logger = LogFactory.getLog(WorkflowFormProcessor.class);
|
||||||
|
|
||||||
|
/** workflow service */
|
||||||
protected WorkflowService workflowService;
|
protected WorkflowService workflowService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,4 +69,207 @@ public class WorkflowFormProcessor extends TypeFormProcessor
|
|||||||
{
|
{
|
||||||
this.workflowService = workflowService;
|
this.workflowService = workflowService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see
|
||||||
|
* org.alfresco.repo.forms.processor.node.ContentModelFormProcessor#getLogger
|
||||||
|
* ()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Log getLogger()
|
||||||
|
{
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see
|
||||||
|
* org.alfresco.repo.forms.processor.node.NodeFormProcessor#getTypedItem
|
||||||
|
* (org.alfresco.repo.forms.Item)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected WorkflowDefinition getTypedItem(Item item)
|
||||||
|
{
|
||||||
|
WorkflowDefinition workflowDef = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// item id could be the raw workflow definition name or it could be
|
||||||
|
// in a URL friendly format
|
||||||
|
String workflowDefName = item.getId();
|
||||||
|
if (workflowDefName.indexOf("$") == -1)
|
||||||
|
{
|
||||||
|
// decode the itemId
|
||||||
|
workflowDefName = workflowDefName.replace("jbpm_", "jbpm$");
|
||||||
|
workflowDefName = workflowDefName.replace('_', ':');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the workflow definition
|
||||||
|
workflowDef = this.workflowService.getDefinitionByName(workflowDefName);
|
||||||
|
|
||||||
|
if (workflowDef == null)
|
||||||
|
{
|
||||||
|
throw new FormNotFoundException(item,
|
||||||
|
new IllegalArgumentException("Workflow definition does not exist: " + item.getId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (WorkflowException we)
|
||||||
|
{
|
||||||
|
throw new FormNotFoundException(item, we);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the type definition object for the requested type
|
||||||
|
return workflowDef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see
|
||||||
|
* org.alfresco.repo.forms.processor.FilteredFormProcessor#internalGenerate
|
||||||
|
* (java.lang.Object, java.util.List, java.util.List,
|
||||||
|
* org.alfresco.repo.forms.Form, java.util.Map)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void internalGenerate(WorkflowDefinition workflowDef, List<String> fields,
|
||||||
|
List<String> forcedFields, Form form, Map<String, Object> context)
|
||||||
|
{
|
||||||
|
if (logger.isDebugEnabled()) logger.debug("Generating form for item: " + workflowDef);
|
||||||
|
|
||||||
|
// generate the form for the workflow definition
|
||||||
|
form.getItem().setType(workflowDef.name);
|
||||||
|
form.getItem().setUrl("/api/workflow-definition/" + workflowDef.id);
|
||||||
|
|
||||||
|
// get the type of the start task for the workflow definition
|
||||||
|
TypeDefinition typeDef = workflowDef.getStartTaskDefinition().metadata;
|
||||||
|
|
||||||
|
if (fields != null && fields.size() > 0)
|
||||||
|
{
|
||||||
|
generateSelectedFields(null, typeDef, fields, forcedFields, form);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// setup field definitions and data
|
||||||
|
generateAllPropertyFields(typeDef, form);
|
||||||
|
generateAllAssociationFields(typeDef, form);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled()) logger.debug("Generating form: " + form);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the field definitions for all the type's properties.
|
||||||
|
*
|
||||||
|
* @param typeDef The type being setup
|
||||||
|
* @param form The Form instance to populate
|
||||||
|
*/
|
||||||
|
protected void generateAllPropertyFields(TypeDefinition typeDef, Form form)
|
||||||
|
{
|
||||||
|
// iterate round the property defintions and setup field definition
|
||||||
|
Map<QName, PropertyDefinition> propDefs = typeDef.getProperties();
|
||||||
|
for (PropertyDefinition propDef : propDefs.values())
|
||||||
|
{
|
||||||
|
generatePropertyField(propDef, form, this.namespaceService);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get all default aspects for the type and iterate round their
|
||||||
|
// property definitions too
|
||||||
|
List<AspectDefinition> aspects = typeDef.getDefaultAspects(true);
|
||||||
|
for (AspectDefinition aspect : aspects)
|
||||||
|
{
|
||||||
|
propDefs = aspect.getProperties();
|
||||||
|
for (PropertyDefinition propDef : propDefs.values())
|
||||||
|
{
|
||||||
|
generatePropertyField(propDef, form, this.namespaceService);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the field definitions for all the type's associations.
|
||||||
|
*
|
||||||
|
* @param typeDef The type being setup
|
||||||
|
* @param form The Form instance to populate
|
||||||
|
*/
|
||||||
|
protected void generateAllAssociationFields(TypeDefinition typeDef, Form form)
|
||||||
|
{
|
||||||
|
// iterate round the association defintions and setup field definition
|
||||||
|
Map<QName, AssociationDefinition> assocDefs = typeDef.getAssociations();
|
||||||
|
for (AssociationDefinition assocDef : assocDefs.values())
|
||||||
|
{
|
||||||
|
generateAssociationField(assocDef, form, this.namespaceService);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get all default aspects for the type and iterate round their
|
||||||
|
// association definitions too
|
||||||
|
List<AspectDefinition> aspects = typeDef.getDefaultAspects(true);
|
||||||
|
for (AspectDefinition aspect : aspects)
|
||||||
|
{
|
||||||
|
assocDefs = aspect.getAssociations();
|
||||||
|
for (AssociationDefinition assocDef : assocDefs.values())
|
||||||
|
{
|
||||||
|
generateAssociationField(assocDef, form, this.namespaceService);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see
|
||||||
|
* org.alfresco.repo.forms.processor.node.NodeFormProcessor#internalPersist
|
||||||
|
* (java.lang.Object, org.alfresco.repo.forms.FormData)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected WorkflowInstance internalPersist(WorkflowDefinition workflowDef, final FormData data)
|
||||||
|
{
|
||||||
|
if (logger.isDebugEnabled()) logger.debug("Persisting form for: " + workflowDef);
|
||||||
|
|
||||||
|
WorkflowInstance workflow = null;
|
||||||
|
Map<QName, Serializable> params = new HashMap<QName, Serializable>(8);
|
||||||
|
|
||||||
|
// create a package for the workflow
|
||||||
|
NodeRef workflowPackage = this.workflowService.createPackage(null);
|
||||||
|
params.put(WorkflowModel.ASSOC_PACKAGE, workflowPackage);
|
||||||
|
|
||||||
|
// TODO: iterate through form data to collect properties, for now
|
||||||
|
// just hardcode the ones we know
|
||||||
|
params.put(WorkflowModel.PROP_DESCRIPTION,
|
||||||
|
(Serializable)data.getFieldData("prop_bpm_description").getValue());
|
||||||
|
|
||||||
|
NodeRef assignee = new NodeRef(data.getFieldData("assoc_bpm_assignee_added").getValue().toString());
|
||||||
|
ArrayList<NodeRef> assigneeList = new ArrayList<NodeRef>(1);
|
||||||
|
assigneeList.add(assignee);
|
||||||
|
params.put(WorkflowModel.ASSOC_ASSIGNEE, assigneeList);
|
||||||
|
|
||||||
|
// TODO: add any package items
|
||||||
|
|
||||||
|
// TODO: add any context (this could re-use alf_destination)
|
||||||
|
|
||||||
|
// start the workflow to get access to the start task
|
||||||
|
WorkflowPath path = this.workflowService.startWorkflow(workflowDef.getId(), params);
|
||||||
|
if (path != null)
|
||||||
|
{
|
||||||
|
// get hold of the workflow instance for returning
|
||||||
|
workflow = path.instance;
|
||||||
|
|
||||||
|
// 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: " + workflowDef.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the workflow just started
|
||||||
|
return workflow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user