diff --git a/source/java/org/alfresco/repo/forms/FormServiceImplTest.java b/source/java/org/alfresco/repo/forms/FormServiceImplTest.java index abe7815de7..10f94b2aea 100644 --- a/source/java/org/alfresco/repo/forms/FormServiceImplTest.java +++ b/source/java/org/alfresco/repo/forms/FormServiceImplTest.java @@ -44,6 +44,10 @@ import org.alfresco.service.cmr.repository.ScriptLocation; import org.alfresco.service.cmr.repository.ScriptService; import org.alfresco.service.cmr.repository.StoreRef; 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.NamespaceService; import org.alfresco.service.namespace.QName; @@ -64,6 +68,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest private ScriptService scriptService; private PersonService personService; private ContentService contentService; + private WorkflowService workflowService; private NodeRef document; private NodeRef associatedDoc; @@ -106,6 +111,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest private static final String USER_ONE = "UserOne_FormServiceImplTest"; private static final String NODE_FORM_ITEM_KIND = "node"; private static final String TYPE_FORM_ITEM_KIND = "type"; + private static final String WORKFLOW_FORM_ITEM_KIND = "workflow"; /** * Called during the transaction setup @@ -122,6 +128,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest this.scriptService = (ScriptService)this.applicationContext.getBean("ScriptService"); this.personService = (PersonService)this.applicationContext.getBean("PersonService"); this.contentService = (ContentService)this.applicationContext.getBean("ContentService"); + this.workflowService = (WorkflowService)this.applicationContext.getBean("WorkflowService"); AuthenticationComponent authenticationComponent = (AuthenticationComponent) this.applicationContext .getBean("authenticationComponent"); @@ -1221,6 +1228,77 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest 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 fields = new ArrayList(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 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 fieldDefMap = new HashMap(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 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 { // test that a form can not be retrieved for a non-existent item diff --git a/source/java/org/alfresco/repo/forms/processor/workflow/WorkflowFormProcessor.java b/source/java/org/alfresco/repo/forms/processor/workflow/WorkflowFormProcessor.java index 1ace22484d..00da13452f 100644 --- a/source/java/org/alfresco/repo/forms/processor/workflow/WorkflowFormProcessor.java +++ b/source/java/org/alfresco/repo/forms/processor/workflow/WorkflowFormProcessor.java @@ -18,8 +18,33 @@ */ 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.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 @@ -27,8 +52,12 @@ import org.alfresco.service.cmr.workflow.WorkflowService; * * @author Gavin Cornwell */ -public class WorkflowFormProcessor extends TypeFormProcessor +public class WorkflowFormProcessor extends ContentModelFormProcessor { + /** Logger */ + private static Log logger = LogFactory.getLog(WorkflowFormProcessor.class); + + /** workflow service */ protected WorkflowService workflowService; /** @@ -40,4 +69,207 @@ public class WorkflowFormProcessor extends TypeFormProcessor { 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 fields, + List forcedFields, Form form, Map 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 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 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 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 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 params = new HashMap(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 assigneeList = new ArrayList(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 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; + } }