Merge pull request #2420 from KevinVdV/DS-4239-migrate-workflow-xml-to-spring

[DS-4239] Migrate the workflow.xml to spring
Merging since there are 2 approvals and it's a dependency for https://github.com/DSpace/DSpace/pull/2646
This commit is contained in:
benbosman
2020-01-29 09:19:00 +01:00
committed by GitHub
20 changed files with 1065 additions and 466 deletions

View File

@@ -48,6 +48,7 @@ import org.dspace.harvest.HarvestedCollection;
import org.dspace.harvest.service.HarvestedCollectionService; import org.dspace.harvest.service.HarvestedCollectionService;
import org.dspace.workflow.factory.WorkflowServiceFactory; import org.dspace.workflow.factory.WorkflowServiceFactory;
import org.dspace.xmlworkflow.WorkflowConfigurationException; import org.dspace.xmlworkflow.WorkflowConfigurationException;
import org.dspace.xmlworkflow.XmlWorkflowFactoryImpl;
import org.dspace.xmlworkflow.factory.XmlWorkflowFactory; import org.dspace.xmlworkflow.factory.XmlWorkflowFactory;
import org.dspace.xmlworkflow.state.Workflow; import org.dspace.xmlworkflow.state.Workflow;
import org.dspace.xmlworkflow.storedcomponents.CollectionRole; import org.dspace.xmlworkflow.storedcomponents.CollectionRole;
@@ -370,11 +371,11 @@ public class CollectionServiceImpl extends DSpaceObjectServiceImpl<Collection> i
Workflow workflow = null; Workflow workflow = null;
try { try {
workflow = workflowFactory.getWorkflow(collection); workflow = workflowFactory.getWorkflow(collection);
} catch (IOException | WorkflowConfigurationException e) { } catch (WorkflowConfigurationException e) {
log.error(LogManager.getHeader(context, "setWorkflowGroup", log.error(LogManager.getHeader(context, "setWorkflowGroup",
"collection_id=" + collection.getID() + " " + e.getMessage()), e); "collection_id=" + collection.getID() + " " + e.getMessage()), e);
} }
if (!StringUtils.equals(XmlWorkflowFactory.LEGACY_WORKFLOW_NAME, workflow.getID())) { if (!StringUtils.equals(XmlWorkflowFactoryImpl.LEGACY_WORKFLOW_NAME, workflow.getID())) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"setWorkflowGroup can be used only on collection with the default basic dspace workflow. " "setWorkflowGroup can be used only on collection with the default basic dspace workflow. "
+ "Instead, the collection: " + "Instead, the collection: "

View File

@@ -13,14 +13,15 @@ import java.util.List;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group; import org.dspace.eperson.Group;
import org.dspace.eperson.factory.EPersonServiceFactory;
import org.dspace.eperson.service.GroupService; import org.dspace.eperson.service.GroupService;
import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory;
import org.dspace.xmlworkflow.storedcomponents.CollectionRole; import org.dspace.xmlworkflow.storedcomponents.CollectionRole;
import org.dspace.xmlworkflow.storedcomponents.WorkflowItemRole; import org.dspace.xmlworkflow.storedcomponents.WorkflowItemRole;
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem; import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
import org.dspace.xmlworkflow.storedcomponents.service.CollectionRoleService; import org.dspace.xmlworkflow.storedcomponents.service.CollectionRoleService;
import org.dspace.xmlworkflow.storedcomponents.service.WorkflowItemRoleService; import org.dspace.xmlworkflow.storedcomponents.service.WorkflowItemRoleService;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
/** /**
* The role that is responsible for a certain step * The role that is responsible for a certain step
@@ -32,38 +33,36 @@ import org.dspace.xmlworkflow.storedcomponents.service.WorkflowItemRoleService;
* @author Ben Bosman (ben at atmire dot com) * @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com) * @author Mark Diggory (markd at atmire dot com)
*/ */
public class Role { public class Role implements BeanNameAware {
@Autowired
private GroupService groupService;
@Autowired
private CollectionRoleService collectionRoleService;
@Autowired
private WorkflowItemRoleService workflowItemRoleService;
private GroupService groupService = EPersonServiceFactory.getInstance().getGroupService();
private CollectionRoleService collectionRoleService = XmlWorkflowServiceFactory.getInstance()
.getCollectionRoleService();
private WorkflowItemRoleService workflowItemRoleService = XmlWorkflowServiceFactory.getInstance()
.getWorkflowItemRoleService();
private String id; private String id;
private String name; private String name;
private String description; private String description;
private boolean isInternal; private boolean isInternal = false;
private Scope scope; private Scope scope = Scope.COLLECTION;
public static enum Scope { @Override
public void setBeanName(String s) {
this.id = s;
}
public enum Scope {
REPOSITORY, REPOSITORY,
COLLECTION, COLLECTION,
ITEM ITEM
} }
public Role(String id, String name, String description, boolean isInternal, Scope scope) {
this.id = id;
this.name = name;
this.description = description;
this.isInternal = isInternal;
this.scope = scope;
}
public String getId() { public String getId() {
return id; return id;
} }
public String getName() { public String getName() {
return name; return name;
} }
@@ -118,4 +117,41 @@ public class Role {
} }
} }
/**
* The name specified in the name attribute of a role will be used to lookup the in DSpace.
* The lookup will depend on the scope specified in the "scope" attribute:
* @param name
*/
@Required
public void setName(String name) {
this.name = name;
}
/**
* Set the description of the role
* @param description the description
*/
public void setDescription(String description) {
this.description = description;
}
/**
* Set the scope attribute, depending on the scope the users will be retrieved in the following manner:
* * collection: The collection value specifies that the group will be configured at the level of the collection.
* * repository: The repository scope uses groups that are defined at repository level in DSpace.
* item: The item scope assumes that a different action in the workflow will assign a number of EPersons or
* Groups to a specific workflow-item in order to perform a step.
* @param scope the scope parameter
*/
public void setScope(Scope scope) {
this.scope = scope;
}
/**
* Optional attribute which isn't really used at the moment, false by default
* @param internal if the role is internal
*/
public void setInternal(boolean internal) {
isInternal = internal;
}
} }

View File

@@ -13,8 +13,8 @@ import java.io.StringWriter;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Date; import java.util.Date;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -236,13 +236,13 @@ public class WorkflowUtils extends Util {
} }
public static HashMap<String, Role> getCollectionRoles(Collection thisCollection) public static Map<String, Role> getCollectionRoles(Collection thisCollection)
throws IOException, WorkflowConfigurationException, SQLException { throws IOException, WorkflowConfigurationException, SQLException {
Workflow workflow = xmlWorkflowFactory.getWorkflow(thisCollection); Workflow workflow = xmlWorkflowFactory.getWorkflow(thisCollection);
LinkedHashMap<String, Role> result = new LinkedHashMap<String, Role>(); LinkedHashMap<String, Role> result = new LinkedHashMap<String, Role>();
if (workflow != null) { if (workflow != null) {
//Make sure we find one //Make sure we find one
HashMap<String, Role> allRoles = workflow.getRoles(); Map<String, Role> allRoles = workflow.getRoles();
//We have retrieved all our roles, not get the ones which can be configured by the collection //We have retrieved all our roles, not get the ones which can be configured by the collection
for (String roleId : allRoles.keySet()) { for (String roleId : allRoles.keySet()) {
Role role = allRoles.get(roleId); Role role = allRoles.get(roleId);
@@ -257,13 +257,13 @@ public class WorkflowUtils extends Util {
} }
public static HashMap<String, Role> getCollectionAndRepositoryRoles(Collection thisCollection) public static Map<String, Role> getCollectionAndRepositoryRoles(Collection thisCollection)
throws IOException, WorkflowConfigurationException, SQLException { throws IOException, WorkflowConfigurationException, SQLException {
Workflow workflow = xmlWorkflowFactory.getWorkflow(thisCollection); Workflow workflow = xmlWorkflowFactory.getWorkflow(thisCollection);
LinkedHashMap<String, Role> result = new LinkedHashMap<String, Role>(); LinkedHashMap<String, Role> result = new LinkedHashMap<String, Role>();
if (workflow != null) { if (workflow != null) {
//Make sure we find one //Make sure we find one
HashMap<String, Role> allRoles = workflow.getRoles(); Map<String, Role> allRoles = workflow.getRoles();
//We have retrieved all our roles, not get the ones which can be configured by the collection //We have retrieved all our roles, not get the ones which can be configured by the collection
for (String roleId : allRoles.keySet()) { for (String roleId : allRoles.keySet()) {
Role role = allRoles.get(roleId); Role role = allRoles.get(roleId);
@@ -279,13 +279,13 @@ public class WorkflowUtils extends Util {
} }
public static HashMap<String, Role> getAllExternalRoles(Collection thisCollection) public static Map<String, Role> getAllExternalRoles(Collection thisCollection)
throws IOException, WorkflowConfigurationException, SQLException { throws IOException, WorkflowConfigurationException, SQLException {
Workflow workflow = xmlWorkflowFactory.getWorkflow(thisCollection); Workflow workflow = xmlWorkflowFactory.getWorkflow(thisCollection);
LinkedHashMap<String, Role> result = new LinkedHashMap<String, Role>(); LinkedHashMap<String, Role> result = new LinkedHashMap<String, Role>();
if (workflow != null) { if (workflow != null) {
//Make sure we find one //Make sure we find one
HashMap<String, Role> allRoles = workflow.getRoles(); Map<String, Role> allRoles = workflow.getRoles();
//We have retrieved all our roles, not get the ones which can be configured by the collection //We have retrieved all our roles, not get the ones which can be configured by the collection
for (String roleId : allRoles.keySet()) { for (String roleId : allRoles.keySet()) {
Role role = allRoles.get(roleId); Role role = allRoles.get(roleId);

View File

@@ -7,31 +7,12 @@
*/ */
package org.dspace.xmlworkflow; package org.dspace.xmlworkflow;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.annotation.PostConstruct;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.TransformerException;
import org.apache.logging.log4j.Logger;
import org.apache.xpath.XPathAPI;
import org.dspace.content.Collection; import org.dspace.content.Collection;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.xmlworkflow.factory.XmlWorkflowFactory; import org.dspace.xmlworkflow.factory.XmlWorkflowFactory;
import org.dspace.xmlworkflow.state.Step;
import org.dspace.xmlworkflow.state.Workflow; import org.dspace.xmlworkflow.state.Workflow;
import org.dspace.xmlworkflow.state.actions.UserSelectionActionConfig; import org.springframework.beans.factory.annotation.Required;
import org.dspace.xmlworkflow.state.actions.WorkflowActionConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/** /**
* The workflowfactory is responsible for parsing the * The workflowfactory is responsible for parsing the
@@ -45,239 +26,28 @@ import org.w3c.dom.NodeList;
*/ */
public class XmlWorkflowFactoryImpl implements XmlWorkflowFactory { public class XmlWorkflowFactoryImpl implements XmlWorkflowFactory {
private Logger log = org.apache.logging.log4j.LogManager.getLogger(XmlWorkflowFactoryImpl.class); public static final String LEGACY_WORKFLOW_NAME = "defaultWorkflow";
@Autowired(required = true) private Map<String, Workflow> workflowMapping;
protected ConfigurationService configurationService;
protected HashMap<String, Workflow> workflowCache;
protected String path;
@PostConstruct
protected void init() {
path = configurationService
.getProperty("dspace.dir") + File.separator + "config" + File.separator + "workflow.xml";
}
// private static String pathActions = ConfigurationManager.getProperty("dspace.dir")+"/config/workflow-actions.xml";
protected XmlWorkflowFactoryImpl() {
}
@Override @Override
public Workflow getWorkflow(Collection collection) throws IOException, WorkflowConfigurationException { public Workflow getWorkflow(Collection collection) throws WorkflowConfigurationException {
//Initialize our cache if we have none
if (workflowCache == null) {
workflowCache = new HashMap<>();
}
// Attempt to retrieve our workflow object // Attempt to retrieve our workflow object
if (workflowCache.get(collection.getHandle()) == null) { if (workflowMapping.get(collection.getHandle()) == null) {
try { final Workflow defaultWorkflow = workflowMapping.get(LEGACY_WORKFLOW_NAME);
// No workflow cache found for the collection, check if we have a workflowId for this collection if (defaultWorkflow != null) {
File xmlFile = new File(path); return defaultWorkflow;
Document input = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlFile);
Node mainNode = input.getFirstChild();
Node workflowMap = XPathAPI.selectSingleNode(mainNode,
"//workflow-map/name-map[@collection='" + collection
.getHandle() + "']");
if (workflowMap == null) {
//No workflowId found for this collection, so retrieve & use the default workflow
if (workflowCache.get("default") == null) {
String workflowID = XPathAPI
.selectSingleNode(mainNode, "//workflow-map/name-map[@collection='default']")
.getAttributes().getNamedItem("workflow").getTextContent();
if (workflowID == null) {
throw new WorkflowConfigurationException(
"No mapping is present for collection with handle:" + collection.getHandle());
}
Node workflowNode = XPathAPI.selectSingleNode(mainNode, "//workflow[@id='" + workflowID + "']");
Workflow wf = new Workflow(workflowID, getRoles(workflowNode));
Step step = createFirstStep(wf, workflowNode);
wf.setFirstStep(step);
workflowCache.put("default", wf);
workflowCache.put(collection.getHandle(), wf);
return wf;
} else {
return workflowCache.get("default");
}
} else {
//We have a workflowID so retrieve it & resolve it to a workflow, also store it in our cache
String workflowID = workflowMap.getAttributes().getNamedItem("workflow").getTextContent();
Node workflowNode = XPathAPI.selectSingleNode(mainNode, "//workflow[@id='" + workflowID + "']");
Workflow wf = new Workflow(workflowID, getRoles(workflowNode));
Step step = createFirstStep(wf, workflowNode);
wf.setFirstStep(step);
workflowCache.put(collection.getHandle(), wf);
return wf;
}
} catch (Exception e) {
log.error("Error while retrieving workflow for collection: " + collection.getHandle(), e);
throw new WorkflowConfigurationException(
"Error while retrieving workflow for the following collection: " + collection.getHandle());
} }
} else { } else {
return workflowCache.get(collection.getHandle()); return workflowMapping.get(collection.getHandle());
}
}
protected Step createFirstStep(Workflow workflow, Node workflowNode)
throws TransformerException, WorkflowConfigurationException {
String firstStepID = workflowNode.getAttributes().getNamedItem("start").getTextContent();
Node stepNode = XPathAPI.selectSingleNode(workflowNode, "step[@id='" + firstStepID + "']");
if (stepNode == null) {
throw new WorkflowConfigurationException(
"First step does not exist for workflow: " + workflowNode.getAttributes().getNamedItem("id")
.getTextContent());
}
Node roleNode = stepNode.getAttributes().getNamedItem("role");
Role role = null;
if (roleNode != null) {
role = workflow.getRoles().get(roleNode.getTextContent());
}
String userSelectionActionID = stepNode.getAttributes().getNamedItem("userSelectionMethod").getTextContent();
UserSelectionActionConfig userSelection = createUserAssignmentActionConfig(userSelectionActionID);
return new Step(firstStepID, workflow, role, userSelection, getStepActionConfigs(stepNode),
getStepOutcomes(stepNode), getNbRequiredUser(stepNode));
}
protected Map<Integer, String> getStepOutcomes(Node stepNode)
throws TransformerException, WorkflowConfigurationException {
try {
NodeList outcomesNodeList = XPathAPI.selectNodeList(stepNode, "outcomes/step");
Map<Integer, String> outcomes = new HashMap<Integer, String>();
//Add our outcome, should it be null it will be interpreted as the end of the line (last step)
for (int i = 0; i < outcomesNodeList.getLength(); i++) {
Node outcomeNode = outcomesNodeList.item(i);
int index = Integer.parseInt(outcomeNode.getAttributes().getNamedItem("status").getTextContent());
if (index < 0) {
throw new WorkflowConfigurationException(
"Outcome configuration error for step: " + stepNode.getAttributes().getNamedItem("id")
.getTextContent());
}
outcomes.put(index, outcomeNode.getTextContent());
}
return outcomes;
} catch (Exception e) {
log.error(
"Outcome configuration error for step: " + stepNode.getAttributes().getNamedItem("id").getTextContent(),
e);
throw new WorkflowConfigurationException(
"Outcome configuration error for step: " + stepNode.getAttributes().getNamedItem("id")
.getTextContent());
}
}
protected int getNbRequiredUser(Node stepnode) {
if (stepnode.getAttributes().getNamedItem("requiredUsers") != null) {
return Integer.parseInt(stepnode.getAttributes().getNamedItem("requiredUsers").getTextContent());
}
return 1;
}
private static List<String> getStepActionConfigs(Node stepNode) throws TransformerException {
NodeList actionConfigNodes = XPathAPI.selectNodeList(stepNode, "actions/action");
List<String> actionConfigIDs = new ArrayList<String>();
for (int i = 0; i < actionConfigNodes.getLength(); i++) {
actionConfigIDs.add(actionConfigNodes.item(i).getAttributes().getNamedItem("id").getTextContent());
}
return actionConfigIDs;
}
@Override
public Step createStep(Workflow workflow, String stepID) throws WorkflowConfigurationException, IOException {
try {
File xmlFile = new File(path);
Document input = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlFile);
Node mainNode = input.getFirstChild();
Node stepNode = XPathAPI
.selectSingleNode(mainNode, "//workflow[@id='" + workflow.getID() + "']/step[@id='" + stepID + "']");
if (stepNode == null) {
throw new WorkflowConfigurationException("Step does not exist for workflow: " + workflow.getID());
}
Node roleNode = stepNode.getAttributes().getNamedItem("role");
Role role = null;
if (roleNode != null) {
role = workflow.getRoles().get(roleNode.getTextContent());
}
String userSelectionActionID = stepNode.getAttributes().getNamedItem("userSelectionMethod")
.getTextContent();
UserSelectionActionConfig userSelection = createUserAssignmentActionConfig(userSelectionActionID);
return new Step(stepID, workflow, role, userSelection, getStepActionConfigs(stepNode),
getStepOutcomes(stepNode), getNbRequiredUser(stepNode));
} catch (Exception e) {
log.error("Error while creating step with :" + stepID, e);
throw new WorkflowConfigurationException(
"Step: " + stepID + " does not exist for workflow: " + workflow.getID());
} }
throw new WorkflowConfigurationException(
"Error while retrieving workflow for the following collection: " + collection.getHandle());
} }
protected UserSelectionActionConfig createUserAssignmentActionConfig(String userSelectionActionID) { @Required
return DSpaceServicesFactory.getInstance().getServiceManager() public void setWorkflowMapping(Map<String, Workflow> workflowMapping) {
.getServiceByName(userSelectionActionID, UserSelectionActionConfig.class); this.workflowMapping = workflowMapping;
} }
}
@Override
public WorkflowActionConfig createWorkflowActionConfig(String actionID) {
return DSpaceServicesFactory.getInstance().getServiceManager()
.getServiceByName(actionID, WorkflowActionConfig.class);
}
protected LinkedHashMap<String, Role> getRoles(Node workflowNode) throws WorkflowConfigurationException {
NodeList roleNodes = null;
try {
roleNodes = XPathAPI.selectNodeList(workflowNode, "roles/role");
} catch (Exception e) {
log.error("Error while resolving nodes", e);
throw new WorkflowConfigurationException("Error while retrieving roles");
}
LinkedHashMap<String, Role> roles = new LinkedHashMap<String, Role>();
for (int i = 0; i < roleNodes.getLength(); i++) {
String roleID = roleNodes.item(i).getAttributes().getNamedItem("id").getTextContent();
String roleName = roleNodes.item(i).getAttributes().getNamedItem("name").getTextContent();
Node descriptionNode = roleNodes.item(i).getAttributes().getNamedItem("description");
String roleDescription = null;
if (descriptionNode != null) {
roleDescription = descriptionNode.getTextContent();
}
Node scopeNode = roleNodes.item(i).getAttributes().getNamedItem("scope");
String roleScope = null;
if (scopeNode != null) {
roleScope = scopeNode.getTextContent();
}
Node internalNode = roleNodes.item(i).getAttributes().getNamedItem("internal");
String roleInternal;
boolean internal = false;
if (internalNode != null) {
roleInternal = internalNode.getTextContent();
internal = Boolean.parseBoolean(roleInternal);
}
Role.Scope scope;
if (roleScope == null || roleScope.equalsIgnoreCase("collection")) {
scope = Role.Scope.COLLECTION;
} else if (roleScope.equalsIgnoreCase("item")) {
scope = Role.Scope.ITEM;
} else if (roleScope.equalsIgnoreCase("repository")) {
scope = Role.Scope.REPOSITORY;
} else {
throw new WorkflowConfigurationException(
"An invalid role scope has been specified it must either be item or collection.");
}
Role role = new Role(roleID, roleName, roleDescription, internal, scope);
roles.put(roleID, role);
}
return roles;
}
}

View File

@@ -7,14 +7,9 @@
*/ */
package org.dspace.xmlworkflow.factory; package org.dspace.xmlworkflow.factory;
import java.io.IOException;
import java.sql.SQLException;
import org.dspace.content.Collection; import org.dspace.content.Collection;
import org.dspace.xmlworkflow.WorkflowConfigurationException; import org.dspace.xmlworkflow.WorkflowConfigurationException;
import org.dspace.xmlworkflow.state.Step;
import org.dspace.xmlworkflow.state.Workflow; import org.dspace.xmlworkflow.state.Workflow;
import org.dspace.xmlworkflow.state.actions.WorkflowActionConfig;
/** /**
* The xmlworkflowfactory is responsible for parsing the * The xmlworkflowfactory is responsible for parsing the
@@ -28,11 +23,13 @@ import org.dspace.xmlworkflow.state.actions.WorkflowActionConfig;
*/ */
public interface XmlWorkflowFactory { public interface XmlWorkflowFactory {
public final String LEGACY_WORKFLOW_NAME = "default";
public Workflow getWorkflow(Collection collection) throws IOException, WorkflowConfigurationException, SQLException; /**
* Retrieve the workflow configuration for a single collection
public Step createStep(Workflow workflow, String stepID) throws WorkflowConfigurationException, IOException; *
* @param collection the collection for which we want our workflow
public WorkflowActionConfig createWorkflowActionConfig(String actionID); * @return the workflow configuration
* @throws WorkflowConfigurationException occurs if there is a configuration error in the workflow
*/
public Workflow getWorkflow(Collection collection) throws WorkflowConfigurationException;
} }

View File

@@ -7,22 +7,22 @@
*/ */
package org.dspace.xmlworkflow.state; package org.dspace.xmlworkflow.state;
import java.io.IOException;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.workflow.WorkflowException;
import org.dspace.xmlworkflow.Role; import org.dspace.xmlworkflow.Role;
import org.dspace.xmlworkflow.WorkflowConfigurationException; import org.dspace.xmlworkflow.WorkflowConfigurationException;
import org.dspace.xmlworkflow.factory.XmlWorkflowFactory;
import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory;
import org.dspace.xmlworkflow.state.actions.UserSelectionActionConfig; import org.dspace.xmlworkflow.state.actions.UserSelectionActionConfig;
import org.dspace.xmlworkflow.state.actions.WorkflowActionConfig; import org.dspace.xmlworkflow.state.actions.WorkflowActionConfig;
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem; import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
import org.dspace.xmlworkflow.storedcomponents.service.InProgressUserService; import org.dspace.xmlworkflow.storedcomponents.service.InProgressUserService;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
/** /**
* A class that contains all the data of an xlworkflow step * A class that contains all the data of an xlworkflow step
@@ -32,46 +32,36 @@ import org.dspace.xmlworkflow.storedcomponents.service.InProgressUserService;
* @author Ben Bosman (ben at atmire dot com) * @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com) * @author Mark Diggory (markd at atmire dot com)
*/ */
public class Step { public class Step implements BeanNameAware {
protected InProgressUserService inProgressUserService = XmlWorkflowServiceFactory.getInstance()
.getInProgressUserService();
protected XmlWorkflowFactory xmlWorkflowFactory = XmlWorkflowServiceFactory.getInstance().getWorkflowFactory();
@Autowired
protected InProgressUserService inProgressUserService;
private UserSelectionActionConfig userSelectionMethod; private UserSelectionActionConfig userSelectionMethod;
private HashMap<String, WorkflowActionConfig> actionConfigsMap; private List<WorkflowActionConfig> actions;
private List<String> actionConfigsList; private Map<Integer, Step> outcomes = new HashMap<>();
private Map<Integer, String> outcomes;
private String id; private String id;
private Role role; private Role role;
private Workflow workflow; private Workflow workflow;
private int requiredUsers; private int requiredUsers = 1;
public Step(String id, Workflow workflow, Role role, UserSelectionActionConfig userSelectionMethod, /**
List<String> actionConfigsList, Map<Integer, String> outcomes, int requiredUsers) { * Get an WorkflowActionConfiguration object for the provided action identifier
this.actionConfigsMap = new HashMap<>(); * @param actionID the action id for which we want our action config
this.outcomes = outcomes; * @return The corresponding WorkflowActionConfiguration
this.userSelectionMethod = userSelectionMethod; * @throws WorkflowConfigurationException occurs if the provided action isn't part of the step
this.role = role; */
this.actionConfigsList = actionConfigsList; public WorkflowActionConfig getActionConfig(String actionID) throws WorkflowConfigurationException {
this.id = id; // First check the userSelectionMethod as this is not a regular "action"
userSelectionMethod.setStep(this); if (userSelectionMethod != null && StringUtils.equals(userSelectionMethod.getId(), actionID)) {
this.requiredUsers = requiredUsers; return userSelectionMethod;
this.workflow = workflow;
}
public WorkflowActionConfig getActionConfig(String actionID) {
if (actionConfigsMap.get(actionID) != null) {
return actionConfigsMap.get(actionID);
} else {
WorkflowActionConfig action = xmlWorkflowFactory.createWorkflowActionConfig(actionID);
action.setStep(this);
actionConfigsMap.put(actionID, action);
return action;
} }
for (WorkflowActionConfig actionConfig : actions) {
if (StringUtils.equals(actionConfig.getId(), actionID)) {
return actionConfig;
}
}
throw new WorkflowConfigurationException("Action configuration not found for: " + actionID);
} }
/** /**
@@ -80,8 +70,7 @@ public class Step {
* @return a boolean * @return a boolean
*/ */
public boolean hasUI() { public boolean hasUI() {
for (String actionConfigId : actionConfigsList) { for (WorkflowActionConfig actionConfig : actions) {
WorkflowActionConfig actionConfig = getActionConfig(actionConfigId);
if (actionConfig.requiresUI()) { if (actionConfig.requiresUI()) {
return true; return true;
} }
@@ -89,8 +78,12 @@ public class Step {
return false; return false;
} }
public String getNextStepID(int outcome) /**
throws WorkflowException, IOException, WorkflowConfigurationException, SQLException { * Get the next step based on out the outcome
* @param outcome the outcome of the previous step
* @return the next stepp or NULL if there is no step configured for this outcome
*/
public Step getNextStep(int outcome) {
return outcomes.get(outcome); return outcomes.get(outcome);
} }
@@ -109,9 +102,9 @@ public class Step {
} }
public WorkflowActionConfig getNextAction(WorkflowActionConfig currentAction) { public WorkflowActionConfig getNextAction(WorkflowActionConfig currentAction) {
int index = actionConfigsList.indexOf(currentAction.getId()); int index = actions.indexOf(currentAction);
if (index < actionConfigsList.size() - 1) { if (index < actions.size() - 1) {
return getActionConfig(actionConfigsList.get(index + 1)); return actions.get(index + 1);
} else { } else {
return null; return null;
} }
@@ -125,7 +118,6 @@ public class Step {
return workflow; return workflow;
} }
/** /**
* Check if enough users have finished this step for it to continue * Check if enough users have finished this step for it to continue
* *
@@ -146,6 +138,78 @@ public class Step {
return role; return role;
} }
// public boolean skipStep() { /**
// } * Set the user selection configuration, this is required as every step requires one
* @param userSelectionMethod the user selection method configuration
*/
@Required
public void setUserSelectionMethod(UserSelectionActionConfig userSelectionMethod) {
this.userSelectionMethod = userSelectionMethod;
userSelectionMethod.setStep(this);
}
/**
* Set the outcomes as a map, if no outcomes are configured this step will be last step in the workflow
* @param outcomes the map containing the outcomes.
*/
public void setOutcomes(Map<Integer, Step> outcomes) {
this.outcomes = outcomes;
}
/**
* Get the processing actions for the step. Processing actions contain the logic required to execute the required
* operations in each step.
* @return the actions configured for this step
*/
public List<WorkflowActionConfig> getActions() {
return actions;
}
/**
* Set the processing actions for the step. Processing actions contain the logic required to execute the required
* operations in each step.
* @param actions the list of actions
*/
@Required
public void setActions(List<WorkflowActionConfig> actions) {
for (WorkflowActionConfig workflowActionConfig : actions) {
workflowActionConfig.setStep(this);
}
this.actions = actions;
}
/**
* Set the workflow this step belongs to
* @param workflow the workflow configuration
*/
protected void setWorkflow(Workflow workflow) {
this.workflow = workflow;
}
/**
* Store the name of the bean in the identifier
* @param s the bean name
*/
@Override
public void setBeanName(String s) {
id = s;
}
/**
* Set the number of required users that need to execute this step before it is completed,
* the default is a single user
* @param requiredUsers the number of required users
*/
public void setRequiredUsers(int requiredUsers) {
this.requiredUsers = requiredUsers;
}
/**
* Set the role of which users role should execute this step
* @param role the role to be configured for this step
*/
public void setRole(Role role) {
this.role = role;
}
} }

View File

@@ -7,18 +7,18 @@
*/ */
package org.dspace.xmlworkflow.state; package org.dspace.xmlworkflow.state;
import java.io.IOException;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.List;
import java.util.Map;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.workflow.WorkflowException;
import org.dspace.xmlworkflow.Role; import org.dspace.xmlworkflow.Role;
import org.dspace.xmlworkflow.WorkflowConfigurationException; import org.dspace.xmlworkflow.WorkflowConfigurationException;
import org.dspace.xmlworkflow.factory.XmlWorkflowFactory; import org.dspace.xmlworkflow.state.actions.ActionResult;
import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory;
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem; import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Required;
/** /**
* Class that contains all the steps and roles involved in a certain * Class that contains all the steps and roles involved in a certain
@@ -29,21 +29,11 @@ import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
* @author Ben Bosman (ben at atmire dot com) * @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com) * @author Mark Diggory (markd at atmire dot com)
*/ */
public class Workflow { public class Workflow implements BeanNameAware {
protected XmlWorkflowFactory xmlWorkflowFactory = XmlWorkflowServiceFactory.getInstance().getWorkflowFactory();
private String id; private String id;
private Step firstStep; private Step firstStep;
private HashMap<String, Step> steps; private List<Step> steps;
private LinkedHashMap<String, Role> roles;
public Workflow(String workflowID, LinkedHashMap<String, Role> roles) {
this.id = workflowID;
this.roles = roles;
this.steps = new HashMap<String, Step>();
}
public Step getFirstStep() { public Step getFirstStep() {
return firstStep; return firstStep;
@@ -56,46 +46,72 @@ public class Workflow {
/* /*
* Return a step with a given id * Return a step with a given id
*/ */
public Step getStep(String stepID) throws WorkflowConfigurationException, IOException { public Step getStep(String stepID) throws WorkflowConfigurationException {
if (steps.get(id) != null) { for (Step step : steps) {
return steps.get(id); if (step.getId().equals(stepID)) {
} else { return step;
Step step = xmlWorkflowFactory.createStep(this, stepID);
if (step == null) {
throw new WorkflowConfigurationException("Step definition not found for: " + stepID);
} }
steps.put(stepID, step);
return step;
} }
throw new WorkflowConfigurationException("Step definition not found for: " + stepID);
} }
public Step getNextStep(Context context, XmlWorkflowItem wfi, Step currentStep, int outcome) public Step getNextStep(Context context, XmlWorkflowItem wfi, Step currentStep, int outcome)
throws IOException, WorkflowConfigurationException, WorkflowException, SQLException { throws WorkflowConfigurationException, SQLException {
String nextStepID = currentStep.getNextStepID(outcome); Step nextStep = currentStep.getNextStep(outcome);
if (nextStepID != null) { if (nextStep != null) {
Step nextStep = getStep(nextStepID);
if (nextStep == null) {
throw new WorkflowException(
"Error while processing outcome, the following action was undefined: " + nextStepID);
}
if (nextStep.isValidStep(context, wfi)) { if (nextStep.isValidStep(context, wfi)) {
return nextStep; return nextStep;
} else { } else {
return getNextStep(context, wfi, nextStep, 0); return getNextStep(context, wfi, nextStep, ActionResult.OUTCOME_COMPLETE);
} }
} else { } else {
//No next step, archive it //No next step, archive it
return null; return null;
} }
} }
@Required
public void setFirstStep(Step firstStep) { public void setFirstStep(Step firstStep) {
firstStep.setWorkflow(this);
this.firstStep = firstStep; this.firstStep = firstStep;
} }
public HashMap<String, Role> getRoles() { /**
* Get the steps that need to be executed in this workflow before the item is archived
* @return the workflow steps
*/
public List<Step> getSteps() {
return steps;
}
/**
* Set the steps that need to be executed in this workflow before the item is archived
* @param steps the workflow steps
*/
@Required
public void setSteps(List<Step> steps) {
for (Step step : steps) {
step.setWorkflow(this);
}
this.steps = steps;
}
/**
* Get the roles that are used in this workflow
* @return a map containing the roles, the role name will the key, the role itself the value
*/
public Map<String, Role> getRoles() {
Map<String, Role> roles = new HashMap<>();
for (Step step : steps) {
if (step.getRole() != null) {
roles.put(step.getRole().getName(), step.getRole());
}
}
return roles; return roles;
} }
@Override
public void setBeanName(String s) {
id = s;
}
} }

View File

@@ -19,12 +19,14 @@ import org.dspace.core.Context;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.service.EPersonService; import org.dspace.eperson.service.EPersonService;
import org.dspace.workflow.WorkflowException; import org.dspace.workflow.WorkflowException;
import org.dspace.xmlworkflow.Role;
import org.dspace.xmlworkflow.state.Step; import org.dspace.xmlworkflow.state.Step;
import org.dspace.xmlworkflow.state.actions.ActionResult; import org.dspace.xmlworkflow.state.actions.ActionResult;
import org.dspace.xmlworkflow.storedcomponents.WorkflowItemRole; import org.dspace.xmlworkflow.storedcomponents.WorkflowItemRole;
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem; import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
import org.dspace.xmlworkflow.storedcomponents.service.WorkflowItemRoleService; import org.dspace.xmlworkflow.storedcomponents.service.WorkflowItemRoleService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
/** /**
* Processing class for an action where an assigned user can * Processing class for an action where an assigned user can
@@ -42,7 +44,7 @@ public class SelectReviewerAction extends ProcessingAction {
public static final int RESULTS_PER_PAGE = 5; public static final int RESULTS_PER_PAGE = 5;
private String roleId; private Role role;
@Autowired(required = true) @Autowired(required = true)
protected EPersonService ePersonService; protected EPersonService ePersonService;
@@ -90,7 +92,7 @@ public class SelectReviewerAction extends ProcessingAction {
//We have a reviewer, assign him, the workflowitemrole will be translated into a task in the autoassign //We have a reviewer, assign him, the workflowitemrole will be translated into a task in the autoassign
WorkflowItemRole workflowItemRole = workflowItemRoleService.create(c); WorkflowItemRole workflowItemRole = workflowItemRoleService.create(c);
workflowItemRole.setEPerson(reviewer); workflowItemRole.setEPerson(reviewer);
workflowItemRole.setRoleId(getRoleId()); workflowItemRole.setRoleId(getRole().getId());
workflowItemRole.setWorkflowItem(wfi); workflowItemRole.setWorkflowItem(wfi);
workflowItemRoleService.update(c, workflowItemRole); workflowItemRoleService.update(c, workflowItemRole);
return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE); return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE);
@@ -100,11 +102,12 @@ public class SelectReviewerAction extends ProcessingAction {
return new ActionResult(ActionResult.TYPE.TYPE_ERROR); return new ActionResult(ActionResult.TYPE.TYPE_ERROR);
} }
public String getRoleId() { public Role getRole() {
return roleId; return role;
} }
public void setRoleId(String roleId) { @Required
this.roleId = roleId; public void setRole(Role role) {
this.role = role;
} }
} }

View File

@@ -0,0 +1,17 @@
--
-- The contents of this file are subject to the license and copyright
-- detailed in the LICENSE and NOTICE files at the root of the source
-- tree and available online at
--
-- http://www.dspace.org/license/
--
---------------------------------------------------------------
-- DS-4239 Migrate the workflow.xml to spring
---------------------------------------------------------------
-- This script will rename the default workflow "default" name
-- to the new "defaultWorkflow" identifier
---------------------------------------------------------------
UPDATE cwf_pooltask SET workflow_id='defaultWorkflow' WHERE workflow_id='default';
UPDATE cwf_claimtask SET workflow_id='defaultWorkflow' WHERE workflow_id='default';

View File

@@ -0,0 +1,17 @@
--
-- The contents of this file are subject to the license and copyright
-- detailed in the LICENSE and NOTICE files at the root of the source
-- tree and available online at
--
-- http://www.dspace.org/license/
--
---------------------------------------------------------------
-- DS-4239 Migrate the workflow.xml to spring
---------------------------------------------------------------
-- This script will rename the default workflow "default" name
-- to the new "defaultWorkflow" identifier
---------------------------------------------------------------
UPDATE cwf_pooltask SET workflow_id='defaultWorkflow' WHERE workflow_id='default';
UPDATE cwf_claimtask SET workflow_id='defaultWorkflow' WHERE workflow_id='default';

View File

@@ -0,0 +1,17 @@
--
-- The contents of this file are subject to the license and copyright
-- detailed in the LICENSE and NOTICE files at the root of the source
-- tree and available online at
--
-- http://www.dspace.org/license/
--
---------------------------------------------------------------
-- DS-4239 Migrate the workflow.xml to spring
---------------------------------------------------------------
-- This script will rename the default workflow "default" name
-- to the new "defaultWorkflow" identifier
---------------------------------------------------------------
UPDATE cwf_pooltask SET workflow_id='defaultWorkflow' WHERE workflow_id='default';
UPDATE cwf_claimtask SET workflow_id='defaultWorkflow' WHERE workflow_id='default';

View File

@@ -0,0 +1,190 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<bean name="xmlWorkflowFactory" class="org.dspace.xmlworkflow.XmlWorkflowFactoryImpl">
<property name="workflowMapping">
<util:map>
<entry key="defaultWorkflow"
value-ref="defaultWorkflow"/>
<entry key="123456789/4" value-ref="selectSingleReviewer"/>
<!-- <entry key="123456789/5" value-ref="scoreReview"/>-->
</util:map>
</property>
</bean>
<!--Standard DSpace workflow-->
<bean name="defaultWorkflow" class="org.dspace.xmlworkflow.state.Workflow">
<property name="firstStep" ref="reviewstep"/>
<property name="steps">
<util:list>
<ref bean="reviewstep"/>
<ref bean="editstep"/>
<ref bean="finaleditstep"/>
</util:list>
</property>
</bean>
<bean name="reviewstep" class="org.dspace.xmlworkflow.state.Step">
<property name="userSelectionMethod" ref="claimaction"/>
<property name="role" ref="reviewer"/>
<property name="outcomes">
<util:map>
<entry key="#{ T(org.dspace.xmlworkflow.state.actions.ActionResult).OUTCOME_COMPLETE}"
value-ref="editstep"/>
</util:map>
</property>
<property name="actions">
<util:list>
<ref bean="reviewaction"/>
</util:list>
</property>
</bean>
<bean name="reviewer" class="org.dspace.xmlworkflow.Role">
<property name="scope" value="#{ T(org.dspace.xmlworkflow.Role.Scope).COLLECTION}"/>
<property name="name" value="Reviewer"/>
<property name="description"
value="The people responsible for this step are able to edit the metadata of incoming submissions, and then accept or reject them."/>
</bean>
<bean name="editstep" class="org.dspace.xmlworkflow.state.Step">
<property name="userSelectionMethod" ref="claimaction"/>
<property name="role" ref="editor" />
<property name="outcomes">
<util:map>
<entry key="#{ T(org.dspace.xmlworkflow.state.actions.ActionResult).OUTCOME_COMPLETE}"
value-ref="finaleditstep"/>
</util:map>
</property>
<property name="actions">
<list>
<ref bean="editaction"/>
</list>
</property>
</bean>
<bean name="editor" class="org.dspace.xmlworkflow.Role">
<property name="scope" value="#{ T(org.dspace.xmlworkflow.Role.Scope).COLLECTION}"/>
<property name="name" value="Editor"/>
<property name="description"
value="The people responsible for this step are able to edit the metadata of incoming submissions, and then accept or reject them."/>
</bean>
<bean name="finaleditstep" class="org.dspace.xmlworkflow.state.Step">
<property name="userSelectionMethod" ref="claimaction"/>
<property name="role" ref="finaleditor" />
<property name="actions">
<list>
<ref bean="finaleditaction"/>
</list>
</property>
</bean>
<bean name="finaleditor" class="org.dspace.xmlworkflow.Role">
<property name="scope" value="#{ T(org.dspace.xmlworkflow.Role.Scope).COLLECTION}"/>
<property name="name" value="Final Editor"/>
<property name="description"
value="The people responsible for this step are able to edit the metadata of incoming submissions, but will not be able to reject them."/>
</bean>
<!--Workflow where a reviewManager can select a single review who will then either accept/reject the item-->
<bean name="selectSingleReviewer" class="org.dspace.xmlworkflow.state.Workflow">
<property name="firstStep" ref="selectReviewerStep"/>
<property name="steps">
<util:list>
<ref bean="selectReviewerStep"/>
<ref bean="singleUserReviewStep"/>
</util:list>
</property>
</bean>
<bean name="selectReviewerStep" class="org.dspace.xmlworkflow.state.Step">
<property name="userSelectionMethod" ref="claimaction"/>
<property name="role" ref="reviewmanagers">
</property>
<property name="actions">
<list>
<ref bean="selectrevieweraction"/>
</list>
</property>
<property name="outcomes">
<util:map>
<entry key="#{ T(org.dspace.xmlworkflow.state.actions.ActionResult).OUTCOME_COMPLETE}"
value-ref="singleUserReviewStep"/>
</util:map>
</property>
</bean>
<bean name="reviewmanagers" class="org.dspace.xmlworkflow.Role">
<property name="scope" value="#{ T(org.dspace.xmlworkflow.Role.Scope).REPOSITORY}"/>
<property name="name" value="ReviewManagers"/>
</bean>
<bean name="singleUserReviewStep" class="org.dspace.xmlworkflow.state.Step">
<property name="userSelectionMethod" ref="autoassignAction"/>
<property name="role" ref="scoreAssignedReviewer">
</property>
<property name="actions">
<list>
<ref bean="singleuserreviewaction"/>
</list>
</property>
<property name="outcomes">
<util:map>
<entry key="#{ T(org.dspace.xmlworkflow.state.actions.processingaction.SingleUserReviewAction).OUTCOME_REJECT}"
value-ref="selectReviewerStep"/>
</util:map>
</property>
</bean>
<bean name="scoreAssignedReviewer" class="org.dspace.xmlworkflow.Role">
<property name="scope" value="#{ T(org.dspace.xmlworkflow.Role.Scope).ITEM}"/>
<property name="name" value="Reviewer"/>
</bean>
<!--Workflow where a number of users will perform reviews on an item and depending on the scores the item will be archived/rejected-->
<bean name="scoreReview" class="org.dspace.xmlworkflow.state.Workflow">
<property name="firstStep" ref="scoreReviewStep"/>
<property name="steps">
<util:list>
<ref bean="scoreReviewStep"/>
<ref bean="evaluationStep"/>
</util:list>
</property>
</bean>
<bean name="scoreReviewStep" class="org.dspace.xmlworkflow.state.Step">
<property name="userSelectionMethod" ref="claimaction"/>
<property name="role" ref="scoreReviewers"/>
<property name="outcomes">
<util:map>
<entry key="#{ T(org.dspace.xmlworkflow.state.actions.ActionResult).OUTCOME_COMPLETE}"
value-ref="evaluationStep"/>
</util:map>
</property>
<property name="actions">
<list>
<ref bean="scorereviewaction"/>
</list>
</property>
<property name="requiredUsers" value="2"/>
</bean>
<bean name="evaluationStep" class="org.dspace.xmlworkflow.state.Step">
<property name="userSelectionMethod" ref="noUserSelectionAction"/>
<property name="actions">
<list>
<ref bean="evaluationaction"/>
</list>
</property>
</bean>
<bean name="scoreReviewers" class="org.dspace.xmlworkflow.Role">
<property name="scope" value="#{ T(org.dspace.xmlworkflow.Role.Scope).COLLECTION}"/>
<property name="name" value="ScoreReviewers"/>
</bean>
</beans>

View File

@@ -0,0 +1,79 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.xmlworkflow;
import static junit.framework.TestCase.assertEquals;
import org.dspace.AbstractUnitTest;
import org.dspace.utils.DSpace;
import org.dspace.xmlworkflow.state.Workflow;
import org.junit.Test;
/**
* Tests that check that the spring beans (of type {@link Role}) in workflow.xml get created correctly
*
* @author Maria Verdonck (Atmire) on 19/12/2019
*/
public class RoleTest extends AbstractUnitTest {
private Workflow defaultWorkflow
= new DSpace().getServiceManager().getServiceByName("defaultWorkflow", Workflow.class);
private Workflow selectSingleReviewer
= new DSpace().getServiceManager().getServiceByName("selectSingleReviewer", Workflow.class);
private Workflow scoreReview
= new DSpace().getServiceManager().getServiceByName("scoreReview", Workflow.class);
@Test
public void defaultWorkflow_RoleReviewer() {
Role role = defaultWorkflow.getRoles().get("Reviewer");
assertEquals(role.getDescription(),
"The people responsible for this step are able to edit the metadata of incoming submissions, " +
"and then accept or reject them.");
assertEquals(role.getName(), "Reviewer");
assertEquals(role.getScope(), Role.Scope.COLLECTION);
}
@Test
public void defaultWorkflow_RoleEditor() {
Role role = defaultWorkflow.getRoles().get("Editor");
assertEquals(role.getDescription(), "The people responsible for this step are able to edit the " +
"metadata of incoming submissions, and then accept or reject them.");
assertEquals(role.getName(), "Editor");
assertEquals(role.getScope(), Role.Scope.COLLECTION);
}
@Test
public void defaultWorkflow_RoleFinalEditor() {
Role role = defaultWorkflow.getRoles().get("Final Editor");
assertEquals(role.getDescription(), "The people responsible for this step are able to edit the " +
"metadata of incoming submissions, but will not be able to reject them.");
assertEquals(role.getName(), "Final Editor");
assertEquals(role.getScope(), Role.Scope.COLLECTION);
}
@Test
public void selectSingleReviewer_RoleReviewManagers() {
Role role = selectSingleReviewer.getRoles().get("ReviewManagers");
assertEquals(role.getName(), "ReviewManagers");
assertEquals(role.getScope(), Role.Scope.REPOSITORY);
}
@Test
public void selectSingleReviewer_RoleReviewer() {
Role role = selectSingleReviewer.getRoles().get("Reviewer");
assertEquals(role.getName(), "Reviewer");
assertEquals(role.getScope(), Role.Scope.ITEM);
}
@Test
public void scoreReview_RoleScoreReviewers() {
Role role = scoreReview.getRoles().get("ScoreReviewers");
assertEquals(role.getName(), "ScoreReviewers");
assertEquals(role.getScope(), Role.Scope.COLLECTION);
}
}

View File

@@ -0,0 +1,111 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.xmlworkflow;
import static junit.framework.TestCase.assertEquals;
import static org.junit.Assert.fail;
import java.sql.SQLException;
import org.apache.logging.log4j.Logger;
import org.dspace.AbstractUnitTest;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.CommunityService;
import org.dspace.utils.DSpace;
import org.dspace.xmlworkflow.factory.XmlWorkflowFactory;
import org.dspace.xmlworkflow.state.Workflow;
import org.junit.Before;
import org.junit.Test;
/**
* Tests that check that the spring bean {@link org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactoryImpl}
* in workflow.xml gets created correctly
*
* @author Maria Verdonck (Atmire) on 19/12/2019
*/
public class XmlWorkflowFactoryTest extends AbstractUnitTest {
private CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService();
private CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService();
private XmlWorkflowFactory xmlWorkflowFactory
= new DSpace().getServiceManager().getServiceByName("xmlWorkflowFactory",
XmlWorkflowFactoryImpl.class);
private Community owningCommunity;
/**
* log4j category
*/
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(XmlWorkflowFactoryTest.class);
/**
* This method will be run before every test as per @Before. It will
* initialize resources required for the tests.
*
* Other methods can be annotated with @Before here or in subclasses
* but no execution order is guaranteed
*/
@Before
@Override
public void init() {
super.init();
try {
//we have to create a new community in the database
context.turnOffAuthorisationSystem();
this.owningCommunity = communityService.create(null, context);
//we need to commit the changes so we don't block the table for testing
context.restoreAuthSystemState();
} catch (SQLException e) {
log.error("SQL Error in init", e);
fail("SQL Error in init: " + e.getMessage());
} catch (AuthorizeException e) {
log.error("Authorization Error in init", e);
fail("Authorization Error in init: " + e.getMessage());
}
}
@Test
public void workflowMapping_NonMappedCollection() throws WorkflowConfigurationException {
Collection collection = this.findOrCreateCollectionWithHandle("123456789/6");
Workflow workflow = xmlWorkflowFactory.getWorkflow(collection);
assertEquals(workflow.getID(), "defaultWorkflow");
}
@Test
public void workflowMapping_MappedCollection() throws WorkflowConfigurationException {
Collection collection = this.findOrCreateCollectionWithHandle("123456789/4");
Workflow workflow = xmlWorkflowFactory.getWorkflow(collection);
assertEquals(workflow.getID(), "selectSingleReviewer");
}
private Collection findOrCreateCollectionWithHandle(String handle) {
try {
context.turnOffAuthorisationSystem();
for (Collection collection : this.collectionService.findAll(context)) {
if (collection.getHandle().equalsIgnoreCase(handle)) {
return collection;
}
}
Collection collection = this.collectionService.create(context, owningCommunity, handle);
context.restoreAuthSystemState();
return collection;
} catch (SQLException e) {
log.error("SQL Error in findOrCreateCollectionWithHandle", e);
fail("SQL Error in findOrCreateCollectionWithHandle: " + e.getMessage());
} catch (AuthorizeException e) {
log.error("Authorization Error in findOrCreateCollectionWithHandle", e);
fail("Authorization Error in findOrCreateCollectionWithHandle: " + e.getMessage());
}
return null;
}
}

View File

@@ -0,0 +1,113 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.xmlworkflow.state;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNull;
import java.util.List;
import org.dspace.AbstractUnitTest;
import org.dspace.utils.DSpace;
import org.dspace.xmlworkflow.WorkflowConfigurationException;
import org.dspace.xmlworkflow.state.actions.WorkflowActionConfig;
import org.junit.Test;
/**
* Tests that check that the spring beans (of type {@link Step}) in workflow.xml get created correctly
*
* @author Maria Verdonck (Atmire) on 19/12/2019
*/
public class StepTest extends AbstractUnitTest {
private Workflow defaultWorkflow
= new DSpace().getServiceManager().getServiceByName("defaultWorkflow", Workflow.class);
private Workflow selectSingleReviewer
= new DSpace().getServiceManager().getServiceByName("selectSingleReviewer", Workflow.class);
private Workflow scoreReview
= new DSpace().getServiceManager().getServiceByName("scoreReview", Workflow.class);
@Test
public void defaultWorkflow_ReviewStep() throws WorkflowConfigurationException {
Step step = defaultWorkflow.getStep("reviewstep");
assertEquals(step.getUserSelectionMethod().getId(), "claimaction");
assertEquals(step.getRole().getName(), "Reviewer");
List<WorkflowActionConfig> actions = step.getActions();
assert (this.containsActionNamed(actions, "reviewaction"));
assertEquals(step.getNextStep(0).getId(), "editstep");
}
@Test
public void defaultWorkflow_EditStep() throws WorkflowConfigurationException {
Step step = defaultWorkflow.getStep("editstep");
assertEquals(step.getUserSelectionMethod().getId(), "claimaction");
assertEquals(step.getRole().getName(), "Editor");
List<WorkflowActionConfig> actions = step.getActions();
assert (this.containsActionNamed(actions, "editaction"));
assertEquals(step.getNextStep(0).getId(), "finaleditstep");
}
@Test
public void defaultWorkflow_FinalEditStep() throws WorkflowConfigurationException {
Step step = defaultWorkflow.getStep("finaleditstep");
assertEquals(step.getUserSelectionMethod().getId(), "claimaction");
assertEquals(step.getRole().getName(), "Final Editor");
List<WorkflowActionConfig> actions = step.getActions();
assert (this.containsActionNamed(actions, "finaleditaction"));
assertNull(step.getNextStep(0));
}
@Test
public void selectSingleReviewer_SelectReviewerStep() throws WorkflowConfigurationException {
Step step = selectSingleReviewer.getStep("selectReviewerStep");
assertEquals(step.getUserSelectionMethod().getId(), "claimaction");
assertEquals(step.getRole().getName(), "ReviewManagers");
List<WorkflowActionConfig> actions = step.getActions();
assert (this.containsActionNamed(actions, "selectrevieweraction"));
assertEquals(step.getNextStep(0).getId(), "singleUserReviewStep");
}
@Test
public void selectSingleReviewer_SingleUserReviewStep() throws WorkflowConfigurationException {
Step step = selectSingleReviewer.getStep("singleUserReviewStep");
assertEquals(step.getUserSelectionMethod().getId(), "autoassignAction");
assert (step.getRole().getName().equals("Reviewer"));
List<WorkflowActionConfig> actions = step.getActions();
assert (this.containsActionNamed(actions, "singleuserreviewaction"));
assertEquals(step.getNextStep(1).getId(), "selectReviewerStep");
}
@Test
public void scoreReview_ScoreReviewStep() throws WorkflowConfigurationException {
Step step = scoreReview.getStep("scoreReviewStep");
assertEquals(step.getUserSelectionMethod().getId(), "claimaction");
assertEquals(step.getRole().getName(), "ScoreReviewers");
List<WorkflowActionConfig> actions = step.getActions();
assert (this.containsActionNamed(actions, "scorereviewaction"));
assertEquals(step.getNextStep(0).getId(), "evaluationStep");
assertEquals(step.getRequiredUsers(), 2);
}
@Test
public void scoreReview_EvaluationStep() throws WorkflowConfigurationException {
Step step = scoreReview.getStep("evaluationStep");
assertEquals(step.getUserSelectionMethod().getId(), "noUserSelectionAction");
List<WorkflowActionConfig> actions = step.getActions();
assert (this.containsActionNamed(actions, "evaluationaction"));
assertNull(step.getNextStep(0));
}
private boolean containsActionNamed(List<WorkflowActionConfig> actions, String actionName) {
for (WorkflowActionConfig workflowActionConfig : actions) {
if (workflowActionConfig.getId().equalsIgnoreCase(actionName)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,68 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.xmlworkflow.state;
import static junit.framework.TestCase.assertEquals;
import java.util.List;
import org.dspace.AbstractUnitTest;
import org.dspace.utils.DSpace;
import org.junit.Test;
/**
* Tests that check that the spring beans (of type {@link Workflow}) in workflow.xml get created correctly
*
* @author Maria Verdonck (Atmire) on 19/12/2019
*/
public class WorkflowTest extends AbstractUnitTest {
private Workflow defaultWorkflow
= new DSpace().getServiceManager().getServiceByName("defaultWorkflow", Workflow.class);
private Workflow selectSingleReviewer
= new DSpace().getServiceManager().getServiceByName("selectSingleReviewer", Workflow.class);
private Workflow scoreReview
= new DSpace().getServiceManager().getServiceByName("scoreReview", Workflow.class);
@Test
public void defaultWorkflow() {
assertEquals(defaultWorkflow.getFirstStep().getId(), "reviewstep");
List<Step> steps = defaultWorkflow.getSteps();
assertEquals(steps.size(), 3);
assert (this.containsStepNamed(steps, "reviewstep"));
assert (this.containsStepNamed(steps, "editstep"));
assert (this.containsStepNamed(steps, "finaleditstep"));
}
@Test
public void selectSingleReviewer() {
assertEquals(selectSingleReviewer.getFirstStep().getId(), "selectReviewerStep");
List<Step> steps = selectSingleReviewer.getSteps();
assertEquals(steps.size(), 2);
assert (this.containsStepNamed(steps, "selectReviewerStep"));
assert (this.containsStepNamed(steps, "singleUserReviewStep"));
}
@Test
public void scoreReview() {
assertEquals(scoreReview.getFirstStep().getId(), "scoreReviewStep");
List<Step> steps = scoreReview.getSteps();
assertEquals(steps.size(), 2);
assert (this.containsStepNamed(steps, "scoreReviewStep"));
assert (this.containsStepNamed(steps, "evaluationStep"));
}
private boolean containsStepNamed(List<Step> steps, String stepName) {
for (Step step : steps) {
if (step.getId().equalsIgnoreCase(stepName)) {
return true;
}
}
return false;
}
}

View File

@@ -115,7 +115,6 @@
<bean class="org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItemServiceImpl"/> <bean class="org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItemServiceImpl"/>
<bean class="org.dspace.xmlworkflow.XmlWorkflowServiceImpl"/> <bean class="org.dspace.xmlworkflow.XmlWorkflowServiceImpl"/>
<bean class="org.dspace.xmlworkflow.WorkflowRequirementsServiceImpl"/> <bean class="org.dspace.xmlworkflow.WorkflowRequirementsServiceImpl"/>
<bean class="org.dspace.xmlworkflow.XmlWorkflowFactoryImpl"/>
<!-- Discovery indexable object services --> <!-- Discovery indexable object services -->
<bean class="org.dspace.discovery.indexobject.ClaimedTaskIndexFactoryImpl" autowire-candidate="true"/> <bean class="org.dspace.discovery.indexobject.ClaimedTaskIndexFactoryImpl" autowire-candidate="true"/>

View File

@@ -13,7 +13,7 @@
<bean id="singleuserreviewactionAPI" class="org.dspace.xmlworkflow.state.actions.processingaction.SingleUserReviewAction" scope="prototype"/> <bean id="singleuserreviewactionAPI" class="org.dspace.xmlworkflow.state.actions.processingaction.SingleUserReviewAction" scope="prototype"/>
<bean id="selectrevieweractionAPI" class="org.dspace.xmlworkflow.state.actions.processingaction.SelectReviewerAction" scope="prototype"> <bean id="selectrevieweractionAPI" class="org.dspace.xmlworkflow.state.actions.processingaction.SelectReviewerAction" scope="prototype">
<property name="roleId" value="reviewer"/> <property name="role" ref="scoreAssignedReviewer"/>
</bean> </bean>
<bean id="scorereviewactionAPI" class="org.dspace.xmlworkflow.state.actions.processingaction.ScoreReviewAction" scope="prototype"/> <bean id="scorereviewactionAPI" class="org.dspace.xmlworkflow.state.actions.processingaction.ScoreReviewAction" scope="prototype"/>
<bean id="evaluationactionAPI" class="org.dspace.xmlworkflow.state.actions.processingaction.ScoreEvaluationAction" scope="prototype"> <bean id="evaluationactionAPI" class="org.dspace.xmlworkflow.state.actions.processingaction.ScoreEvaluationAction" scope="prototype">

View File

@@ -0,0 +1,190 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<bean class="org.dspace.xmlworkflow.XmlWorkflowFactoryImpl">
<property name="workflowMapping">
<util:map>
<entry key="defaultWorkflow"
value-ref="defaultWorkflow"/>
<!-- <entry key="123456789/4" value-ref="selectSingleReviewer"/>-->
<!-- <entry key="123456789/5" value-ref="scoreReview"/>-->
</util:map>
</property>
</bean>
<!--Standard DSpace workflow-->
<bean name="defaultWorkflow" class="org.dspace.xmlworkflow.state.Workflow">
<property name="firstStep" ref="reviewstep"/>
<property name="steps">
<util:list>
<ref bean="reviewstep"/>
<ref bean="editstep"/>
<ref bean="finaleditstep"/>
</util:list>
</property>
</bean>
<bean name="reviewstep" class="org.dspace.xmlworkflow.state.Step">
<property name="userSelectionMethod" ref="claimaction"/>
<property name="role" ref="reviewer"/>
<property name="outcomes">
<util:map>
<entry key="#{ T(org.dspace.xmlworkflow.state.actions.ActionResult).OUTCOME_COMPLETE}"
value-ref="editstep"/>
</util:map>
</property>
<property name="actions">
<util:list>
<ref bean="reviewaction"/>
</util:list>
</property>
</bean>
<bean name="reviewer" class="org.dspace.xmlworkflow.Role">
<property name="scope" value="#{ T(org.dspace.xmlworkflow.Role.Scope).COLLECTION}"/>
<property name="name" value="Reviewer"/>
<property name="description"
value="The people responsible for this step are able to edit the metadata of incoming submissions, and then accept or reject them."/>
</bean>
<bean name="editstep" class="org.dspace.xmlworkflow.state.Step">
<property name="userSelectionMethod" ref="claimaction"/>
<property name="role" ref="editor" />
<property name="outcomes">
<util:map>
<entry key="#{ T(org.dspace.xmlworkflow.state.actions.ActionResult).OUTCOME_COMPLETE}"
value-ref="finaleditstep"/>
</util:map>
</property>
<property name="actions">
<list>
<ref bean="editaction"/>
</list>
</property>
</bean>
<bean name="editor" class="org.dspace.xmlworkflow.Role">
<property name="scope" value="#{ T(org.dspace.xmlworkflow.Role.Scope).COLLECTION}"/>
<property name="name" value="Editor"/>
<property name="description"
value="The people responsible for this step are able to edit the metadata of incoming submissions, and then accept or reject them."/>
</bean>
<bean name="finaleditstep" class="org.dspace.xmlworkflow.state.Step">
<property name="userSelectionMethod" ref="claimaction"/>
<property name="role" ref="finaleditor" />
<property name="actions">
<list>
<ref bean="finaleditaction"/>
</list>
</property>
</bean>
<bean name="finaleditor" class="org.dspace.xmlworkflow.Role">
<property name="scope" value="#{ T(org.dspace.xmlworkflow.Role.Scope).COLLECTION}"/>
<property name="name" value="Final Editor"/>
<property name="description"
value="The people responsible for this step are able to edit the metadata of incoming submissions, but will not be able to reject them."/>
</bean>
<!--Workflow where a reviewManager can select a single review who will then either accept/reject the item-->
<bean name="selectSingleReviewer" class="org.dspace.xmlworkflow.state.Workflow">
<property name="firstStep" ref="selectReviewerStep"/>
<property name="steps">
<util:list>
<ref bean="selectReviewerStep"/>
<ref bean="singleUserReviewStep"/>
</util:list>
</property>
</bean>
<bean name="selectReviewerStep" class="org.dspace.xmlworkflow.state.Step">
<property name="userSelectionMethod" ref="claimaction"/>
<property name="role" ref="reviewmanagers">
</property>
<property name="actions">
<list>
<ref bean="selectrevieweraction" />
</list>
</property>
<property name="outcomes">
<util:map>
<entry key="#{ T(org.dspace.xmlworkflow.state.actions.ActionResult).OUTCOME_COMPLETE}"
value-ref="singleUserReviewStep"/>
</util:map>
</property>
</bean>
<bean name="reviewmanagers" class="org.dspace.xmlworkflow.Role">
<property name="scope" value="#{ T(org.dspace.xmlworkflow.Role.Scope).REPOSITORY}"/>
<property name="name" value="ReviewManagers"/>
</bean>
<bean name="singleUserReviewStep" class="org.dspace.xmlworkflow.state.Step">
<property name="userSelectionMethod" ref="autoassignAction"/>
<property name="role" ref="scoreAssignedReviewer">
</property>
<property name="actions">
<list>
<ref bean="singleuserreviewaction" />
</list>
</property>
<property name="outcomes">
<util:map>
<entry key="#{ T(org.dspace.xmlworkflow.state.actions.processingaction.SingleUserReviewAction).OUTCOME_REJECT}" value-ref="selectReviewerStep"/>
</util:map>
</property>
</bean>
<bean name="scoreAssignedReviewer" class="org.dspace.xmlworkflow.Role">
<property name="scope" value="#{ T(org.dspace.xmlworkflow.Role.Scope).ITEM}"/>
<property name="name" value="Reviewer"/>
</bean>
<!--Workflow where a number of users will perform reviews on an item and depending on the scores the item will be archived/rejected-->
<bean name="scoreReview" class="org.dspace.xmlworkflow.state.Workflow">
<property name="firstStep" ref="scoreReviewStep"/>
<property name="steps">
<util:list>
<ref bean="scoreReviewStep"/>
<ref bean="evaluationStep"/>
</util:list>
</property>
</bean>
<bean name="scoreReviewStep" class="org.dspace.xmlworkflow.state.Step">
<property name="userSelectionMethod" ref="claimaction"/>
<property name="role" ref="scoreReviewers"/>
<property name="outcomes">
<util:map>
<entry key="#{ T(org.dspace.xmlworkflow.state.actions.ActionResult).OUTCOME_COMPLETE}"
value-ref="evaluationStep"/>
</util:map>
</property>
<property name="actions">
<list>
<ref bean="scorereviewaction"/>
</list>
</property>
<property name="requiredUsers" value="2"/>
</bean>
<bean name="evaluationStep" class="org.dspace.xmlworkflow.state.Step">
<property name="userSelectionMethod" ref="noUserSelectionAction"/>
<property name="actions">
<list>
<ref bean="evaluationaction"/>
</list>
</property>
</bean>
<bean name="scoreReviewers" class="org.dspace.xmlworkflow.Role">
<property name="scope" value="#{ T(org.dspace.xmlworkflow.Role.Scope).COLLECTION}"/>
<property name="name" value="ScoreReviewers"/>
</bean>
</beans>

View File

@@ -1,89 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<wf-config>
<workflow-map>
<name-map collection="default" workflow="default"/>
<!--<name-map collection="123456789/4" workflow="selectSingleReviewer"/>-->
<!--<name-map collection="123456789/5" workflow="scoreReview"/>-->
</workflow-map>
<!--Standard workflow step-->
<workflow start="reviewstep" id="default">
<roles>
<role id="reviewer" name="Reviewer" description="The people responsible for this step are able to edit the metadata of incoming submissions, and then accept or reject them." />
<role id="editor" name="Editor" description="The people responsible for this step are able to edit the metadata of incoming submissions, and then accept or reject them."/>
<role id="finaleditor" name="Final Editor" description="The people responsible for this step are able to edit the metadata of incoming submissions, but will not be able to reject them."/>
</roles>
<step id="reviewstep" role="reviewer" userSelectionMethod="claimaction">
<outcomes>
<step status="0">editstep</step>
</outcomes>
<actions>
<action id="reviewaction"/>
</actions>
</step>
<step id="editstep" role="editor" userSelectionMethod="claimaction">
<outcomes>
<step status="0">finaleditstep</step>
</outcomes>
<actions>
<action id="editaction"/>
</actions>
</step>
<step id="finaleditstep" role="finaleditor" userSelectionMethod="claimaction">
<actions>
<action id="finaleditaction"/>
</actions>
</step>
</workflow>
<!--Workflow where a reviewManager can select a single review who will then either accept/reject the item-->
<workflow id="selectSingleReviewer" start="selectReviewerStep">
<roles>
<role id="reviewer" name="Reviewer" scope="item" />
<role id="reviewmanagers" name="ReviewManagers" scope="repository"/>
</roles>
<step id="selectReviewerStep" role="reviewmanagers" userSelectionMethod="claimaction">
<outcomes>
<step status="0">singleUserReviewStep</step>
</outcomes>
<actions>
<action id="selectrevieweraction"/>
</actions>
</step>
<step id="singleUserReviewStep" role="reviewer" userSelectionMethod="autoassignAction">
<outcomes>
<step status="1">selectReviewerStep</step>
</outcomes>
<actions>
<action id="singleuserreviewaction"/>
</actions>
</step>
</workflow>
<!--Workflow where a number of users will perform reviews on an item and depending on the scores the item will be archived/rejected-->
<workflow id="scoreReview" start="scoreReviewStep">
<roles>
<role id="scoreReviewers" name="ScoreReviewers" scope="collection" description="The people responsible to select a single reviewer for the submission"/>
</roles>
<step id="scoreReviewStep" role="scoreReviewers" userSelectionMethod="claimaction" requiredUsers="2">
<outcomes>
<step status="0">evaluationStep</step>
</outcomes>
<actions>
<action id="scorereviewaction"/>
</actions>
</step>
<step id="evaluationStep" userSelectionMethod="noUserSelectionAction">
<actions>
<action id="evaluationaction"/>
</actions>
</step>
</workflow>
</wf-config>