From aa32a118ab7e89231df32a142591f757d44af313 Mon Sep 17 00:00:00 2001 From: Gavin Cornwell Date: Fri, 20 Aug 2010 09:07:02 +0000 Subject: [PATCH] - Added isTaskEditable, isTaskReassignable, isTaskClaimable and isTaskReleasable to WorkflowService - Added isEditable, isReassignable, isClaimable and isReleasable flags to task model in REST API - Added outcome property to task model in REST API (label instead of raw value) - Change "definitionTypeTitle" property to "type" and fixed up fallout - Changed UI to use the isXXX flags above rather than copious amount of repeated checks - Updated workflow details page to use outcome label, isEditable flag and some changes following discussion with Linton - Added tests git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@21890 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../workflow/task-instance.get.json.ftl | 3 +- .../alfresco/repository/workflow/task.lib.ftl | 20 ++-- .../workflow/AbstractWorkflowWebscript.java | 3 +- .../workflow/WorkflowModelBuilder.java | 97 ++++++++++++++----- .../workflow/WorkflowModelBuilderTest.java | 18 +++- .../scripts/workflow/WorkflowRestApiTest.java | 7 +- 6 files changed, 110 insertions(+), 38 deletions(-) diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task-instance.get.json.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task-instance.get.json.ftl index 557cbd0d2d..572ddb1552 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task-instance.get.json.ftl +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task-instance.get.json.ftl @@ -2,5 +2,6 @@ <#import "task.lib.ftl" as taskLib /> { - "data": <@taskLib.taskJSON task=workflowTask detailed=true/> + "data": + <@taskLib.taskJSON task=workflowTask detailed=true/> } \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task.lib.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task.lib.ftl index 7740dfed56..14f40deb0c 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task.lib.ftl +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task.lib.ftl @@ -9,16 +9,21 @@ "name": "${task.name}", "title": "${task.title}", "description": "${task.description}", + "type": "${task.type}", "state": "${task.state}", - "typeDefinitionTitle": "${task.typeDefinitionTitle}", "path": "${task.path}", "isPooled": ${task.isPooled?string}, + "isEditable": ${task.isEditable?string}, + "isReassignable": ${task.isReassignable?string}, + "isClaimable": ${task.isClaimable?string}, + "isReleasable": ${task.isReleasable?string}, + "outcome": <#if task.outcome??>"${task.outcome}"<#else>null, "owner": <#if task.owner??> { - "userName": "${task.owner.userName}", - "firstName": "${task.owner.firstName}", - "lastName": "${task.owner.lastName}" + "userName": "${task.owner.userName}"<#if task.owner.firstName??>, + "firstName": "${task.owner.firstName}"<#if task.owner.lastName??>, + "lastName": "${task.owner.lastName}" }, <#else> null, @@ -27,7 +32,6 @@ <@propertiesJSON properties=task.properties />, "workflowInstance": <@workflowInstanceJSON workflowInstance=task.workflowInstance/><#if detailed>, - "definition": { "id": "${task.definition.id}", @@ -109,9 +113,9 @@ "initiator": <#if workflowInstance.initiator??> { - "userName": "${workflowInstance.initiator.userName}", - "firstName": "${workflowInstance.initiator.firstName}", - "lastName": "${workflowInstance.initiator.lastName}" + "userName": "${workflowInstance.initiator.userName}"<#if workflowInstance.initiator.firstName??>, + "firstName": "${workflowInstance.initiator.firstName}"<#if workflowInstance.initiator.lastName??>, + "lastName": "${workflowInstance.initiator.lastName}" }, <#else> null, diff --git a/source/java/org/alfresco/repo/web/scripts/workflow/AbstractWorkflowWebscript.java b/source/java/org/alfresco/repo/web/scripts/workflow/AbstractWorkflowWebscript.java index 018b915459..0f16eff980 100644 --- a/source/java/org/alfresco/repo/web/scripts/workflow/AbstractWorkflowWebscript.java +++ b/source/java/org/alfresco/repo/web/scripts/workflow/AbstractWorkflowWebscript.java @@ -50,7 +50,8 @@ public abstract class AbstractWorkflowWebscript extends DeclarativeWebScript @Override protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) { - WorkflowModelBuilder modelBuilder = new WorkflowModelBuilder(namespaceService, nodeService, personService, workflowService); + WorkflowModelBuilder modelBuilder = new WorkflowModelBuilder(namespaceService, nodeService, authenticationService, + personService, workflowService); return buildModel(modelBuilder, req, status, cache); } diff --git a/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowModelBuilder.java b/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowModelBuilder.java index 597431668a..bcf4fb7e03 100644 --- a/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowModelBuilder.java +++ b/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowModelBuilder.java @@ -38,6 +38,7 @@ import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.workflow.WorkflowDefinition; import org.alfresco.service.cmr.workflow.WorkflowInstance; @@ -47,6 +48,7 @@ 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.WorkflowTaskQuery; +import org.alfresco.service.cmr.workflow.WorkflowTaskState; import org.alfresco.service.cmr.workflow.WorkflowTransition; import org.alfresco.service.namespace.NamespaceException; import org.alfresco.service.namespace.NamespaceService; @@ -65,16 +67,21 @@ public class WorkflowModelBuilder public static final String TASK_PROPERTIES = "properties"; public static final String TASK_OWNER = "owner"; - public static final String TASK_TYPE_DEFINITION_TITLE = "typeDefinitionTitle"; public static final String TASK_STATE = "state"; public static final String TASK_DESCRIPTION = "description"; public static final String TASK_TITLE = "title"; public static final String TASK_NAME = "name"; + public static final String TASK_TYPE = "type"; public static final String TASK_URL = "url"; public static final String TASK_IS_POOLED = "isPooled"; + public static final String TASK_IS_EDITABLE = "isEditable"; + public static final String TASK_IS_REASSIGNABLE = "isReassignable"; + public static final String TASK_IS_CLAIMABLE = "isClaimable"; + public static final String TASK_IS_RELEASABLE = "isReleasable"; public static final String TASK_ID = "id"; public static final String TASK_PATH = "path"; public static final String TASK_DEFINITION = "definition"; + public static final String TASK_OUTCOME = "outcome"; public static final String TASK_DEFINITION_ID = "id"; public static final String TASK_DEFINITION_URL = "url"; @@ -138,13 +145,17 @@ public class WorkflowModelBuilder private final NodeService nodeService; private final PersonService personService; private final WorkflowService workflowService; + private final AuthenticationService authenticationService; - public WorkflowModelBuilder(NamespaceService namespaceService, NodeService nodeService, PersonService personService, WorkflowService workflowService) + public WorkflowModelBuilder(NamespaceService namespaceService, NodeService nodeService, + AuthenticationService authenticationService, PersonService personService, + WorkflowService workflowService) { this.namespaceService = namespaceService; this.nodeService = nodeService; this.personService = personService; this.workflowService = workflowService; + this.authenticationService = authenticationService; } /** @@ -155,19 +166,27 @@ public class WorkflowModelBuilder */ public Map buildSimple(WorkflowTask task, Collection propertyFilters) { + // get current username + String currentUser = this.authenticationService.getCurrentUserName(); + HashMap model = new HashMap(); model.put(TASK_ID, task.getId()); model.put(TASK_URL, getUrl(task)); model.put(TASK_NAME, task.getName()); + model.put(TASK_TYPE, task.getDefinition().getMetadata().getName().toPrefixString(this.namespaceService)); model.put(TASK_TITLE, task.getTitle()); model.put(TASK_DESCRIPTION, task.getDescription()); model.put(TASK_STATE, task.getState().name()); - model.put(TASK_TYPE_DEFINITION_TITLE, task.getDefinition().getMetadata().getTitle()); model.put(TASK_PATH, getUrl(task.getPath())); + model.put(TASK_OUTCOME, getOutcome(task)); model.put(TASK_IS_POOLED, isPooled(task.getProperties())); + model.put(TASK_IS_EDITABLE, this.workflowService.isTaskEditable(task, currentUser)); + model.put(TASK_IS_REASSIGNABLE, this.workflowService.isTaskReassignable(task, currentUser)); + model.put(TASK_IS_CLAIMABLE, this.workflowService.isTaskClaimable(task, currentUser)); + model.put(TASK_IS_RELEASABLE, this.workflowService.isTaskReleasable(task, currentUser)); Serializable owner = task.getProperties().get(ContentModel.PROP_OWNER); - model.put(TASK_OWNER, getPersonModel( owner)); + model.put(TASK_OWNER, getPersonModel(owner)); // task properties model.put(TASK_PROPERTIES, buildProperties(task, propertyFilters)); @@ -357,7 +376,7 @@ public class WorkflowModelBuilder return model; } - private Object isPooled(Map properties) + private boolean isPooled(Map properties) { Collection actors = (Collection) properties.get(WorkflowModel.ASSOC_POOLED_ACTORS); return actors != null && !actors.isEmpty(); @@ -442,14 +461,19 @@ public class WorkflowModelBuilder return null; String name = (String) nameSer; - NodeRef person = personService.getPerson(name); - Map properties = nodeService.getProperties(person); // TODO Person URL? Map model = new HashMap(); model.put(PERSON_USER_NAME, name); - model.put(PERSON_FIRST_NAME, properties.get(ContentModel.PROP_FIRSTNAME)); - model.put(PERSON_LAST_NAME, properties.get(ContentModel.PROP_LASTNAME)); + + if (personService.personExists(name)) + { + NodeRef person = personService.getPerson(name); + Map properties = nodeService.getProperties(person); + model.put(PERSON_FIRST_NAME, properties.get(ContentModel.PROP_FIRSTNAME)); + model.put(PERSON_LAST_NAME, properties.get(ContentModel.PROP_LASTNAME)); + } + return model; } @@ -490,13 +514,25 @@ public class WorkflowModelBuilder List hiddenTransitions = getHiddenTransitions(workflowTask.getProperties()); for (WorkflowTransition workflowTransition : workflowNode.getTransitions()) { - Map transitionModel = build(workflowTransition, hiddenTransitions); + Map transitionModel = buildTransition(workflowTransition, hiddenTransitions); transitions.add(transitionModel); } model.put(WORKFLOW_NODE_TRANSITIONS, transitions); return model; } + private Map buildTransition(WorkflowTransition workflowTransition, List hiddenTransitions) + { + Map model = new HashMap(); + String id = workflowTransition.getId(); + model.put(WORKFLOW_NODE_TRANSITION_ID, id); + model.put(WORKFLOW_NODE_TRANSITION_TITLE, workflowTransition.getTitle()); + model.put(WORKFLOW_NODE_TRANSITION_DESCRIPTION, workflowTransition.getDescription()); + model.put(WORKFLOW_NODE_TRANSITION_IS_DEFAULT, workflowTransition.isDefault()); + model.put(WORKFLOW_NODE_TRANSITION_IS_HIDDEN, isHiddenTransition(id, hiddenTransitions)); + return model; + } + /** * @param properties * @return @@ -515,19 +551,7 @@ public class WorkflowModelBuilder } return null; } - - public Map build(WorkflowTransition workflowTransition, List hiddenTransitions) - { - Map model = new HashMap(); - String id = workflowTransition.getId(); - model.put(WORKFLOW_NODE_TRANSITION_ID, id); - model.put(WORKFLOW_NODE_TRANSITION_TITLE, workflowTransition.getTitle()); - model.put(WORKFLOW_NODE_TRANSITION_DESCRIPTION, workflowTransition.getDescription()); - model.put(WORKFLOW_NODE_TRANSITION_IS_DEFAULT, workflowTransition.isDefault()); - model.put(WORKFLOW_NODE_TRANSITION_IS_HIDDEN, isHiddenTransition(id, hiddenTransitions)); - return model; - } - + private boolean isHiddenTransition(String transitionId, List hiddenTransitions) { if (hiddenTransitions == null) @@ -554,7 +578,32 @@ public class WorkflowModelBuilder { return null; } - + } + + private String getOutcome(WorkflowTask task) + { + String outcomeLabel = null; + + // there will only be an outcome if the task is completed + if (task.getState().equals(WorkflowTaskState.COMPLETED)) + { + String outcomeId = (String)task.getProperties().get(WorkflowModel.PROP_OUTCOME); + if (outcomeId != null) + { + // find the transition with the matching id and get the label + WorkflowTransition[] transitions = task.getDefinition().getNode().getTransitions(); + for (WorkflowTransition transition : transitions) + { + if (transition.getId().equals(outcomeId)) + { + outcomeLabel = transition.getTitle(); + break; + } + } + } + } + + return outcomeLabel; } private String getUrl(WorkflowTask task) diff --git a/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowModelBuilderTest.java b/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowModelBuilderTest.java index 3e01346d4b..256f376466 100644 --- a/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowModelBuilderTest.java +++ b/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowModelBuilderTest.java @@ -37,6 +37,7 @@ import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.workflow.WorkflowDefinition; import org.alfresco.service.cmr.workflow.WorkflowInstance; @@ -69,6 +70,7 @@ public class WorkflowModelBuilderTest extends TestCase private PersonService personService; private NodeService nodeService; private WorkflowService workflowService; + private AuthenticationService authenticationService; private WorkflowModelBuilder builder; @SuppressWarnings("unchecked") @@ -86,8 +88,14 @@ public class WorkflowModelBuilderTest extends TestCase assertEquals(task.getTitle(), model.get(WorkflowModelBuilder.TASK_TITLE)); assertEquals(task.getDescription(), model.get(WorkflowModelBuilder.TASK_DESCRIPTION)); assertEquals(task.getState().name(), model.get(WorkflowModelBuilder.TASK_STATE)); - assertEquals(task.getDefinition().getMetadata().getTitle(), model.get(WorkflowModelBuilder.TASK_TYPE_DEFINITION_TITLE)); + assertEquals(task.getDefinition().getMetadata().getName().toPrefixString(this.namespaceService), + model.get(WorkflowModelBuilder.TASK_TYPE)); + assertNull(model.get(WorkflowModelBuilder.TASK_OUTCOME)); assertEquals(false, model.get(WorkflowModelBuilder.TASK_IS_POOLED)); + assertEquals(false, model.get(WorkflowModelBuilder.TASK_IS_EDITABLE)); + assertEquals(false, model.get(WorkflowModelBuilder.TASK_IS_REASSIGNABLE)); + assertEquals(false, model.get(WorkflowModelBuilder.TASK_IS_CLAIMABLE)); + assertEquals(false, model.get(WorkflowModelBuilder.TASK_IS_RELEASABLE)); Map owner = (Map) model.get(WorkflowModelBuilder.TASK_OWNER); assertEquals(userName, owner.get(WorkflowModelBuilder.PERSON_USER_NAME)); @@ -319,7 +327,7 @@ public class WorkflowModelBuilderTest extends TestCase private TypeDefinition makeTypeDefinition() { TypeDefinition typeDef = mock(TypeDefinition.class); - when(typeDef.getName()).thenReturn(QName.createQName("The Type Name")); + when(typeDef.getName()).thenReturn(QName.createQName(URI, "The Type Name")); when(typeDef.getTitle()).thenReturn("The Type Title"); when(typeDef.getDescription()).thenReturn("The Type Description"); return typeDef; @@ -393,6 +401,7 @@ public class WorkflowModelBuilderTest extends TestCase personService = mock(PersonService.class); when(personService.getPerson(userName)).thenReturn(person); + when(personService.personExists(userName)).thenReturn(true); nodeService = mock(NodeService.class); Map personProps = new HashMap(); @@ -401,9 +410,12 @@ public class WorkflowModelBuilderTest extends TestCase personProps.put(ContentModel.PROP_LASTNAME, lastName); when(nodeService.getProperties(person)).thenReturn(personProps); when(nodeService.getProperty(person, ContentModel.PROP_USERNAME)).thenReturn(userName); + when(nodeService.getProperty(person, ContentModel.PROP_FIRSTNAME)).thenReturn(firstName); + when(nodeService.getProperty(person, ContentModel.PROP_LASTNAME)).thenReturn(lastName); workflowService = mock(WorkflowService.class); + authenticationService = mock(AuthenticationService.class); - builder = new WorkflowModelBuilder(namespaceService, nodeService, personService, workflowService); + builder = new WorkflowModelBuilder(namespaceService, nodeService, authenticationService, personService, workflowService); } } diff --git a/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowRestApiTest.java b/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowRestApiTest.java index 13f7a414f6..c3ea5362f0 100644 --- a/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowRestApiTest.java +++ b/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowRestApiTest.java @@ -176,8 +176,13 @@ public class WorkflowRestApiTest extends BaseWebScriptTest assertEquals(startTask.title, result.getString("title")); assertEquals(startTask.description, result.getString("description")); assertEquals(startTask.state.name(), result.getString("state")); + assertEquals(startTask.name, result.getString("type")); assertEquals( "api/workflow-paths/" + adhocPath.getId(), result.getString("path")); - assertEquals(false, result.getBoolean("isPooled")); + assertFalse(result.getBoolean("isPooled")); + assertTrue(result.getBoolean("isEditable")); + assertTrue(result.getBoolean("isReassignable")); + assertFalse(result.getBoolean("isClaimable")); + assertFalse(result.getBoolean("isReleasable")); JSONObject owner = result.getJSONObject("owner"); assertEquals(USER1, owner.getString("userName"));