mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 01:54:22 +00:00
Merge branch 'main' into CST-7754
This commit is contained in:
@@ -303,6 +303,10 @@ public class BitstreamStorageServiceImpl implements BitstreamStorageService, Ini
|
||||
commitCounter++;
|
||||
if (commitCounter % 100 == 0) {
|
||||
context.dispatchEvents();
|
||||
// Commit actual changes to DB after dispatch events
|
||||
System.out.print("Performing incremental commit to the database...");
|
||||
context.commit();
|
||||
System.out.println(" Incremental commit done!");
|
||||
}
|
||||
|
||||
context.uncacheEntity(bitstream);
|
||||
|
@@ -18,6 +18,7 @@ import org.dspace.core.Context;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.eperson.Group;
|
||||
import org.dspace.xmlworkflow.WorkflowConfigurationException;
|
||||
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
|
||||
|
||||
/**
|
||||
* Service interface class for the WorkflowService framework.
|
||||
@@ -100,6 +101,9 @@ public interface WorkflowService<T extends WorkflowItem> {
|
||||
String rejection_message)
|
||||
throws SQLException, AuthorizeException, IOException;
|
||||
|
||||
public void restartWorkflow(Context context, XmlWorkflowItem wi, EPerson decliner, String provenance)
|
||||
throws SQLException, AuthorizeException, IOException, WorkflowException;
|
||||
|
||||
public String getMyDSpaceLink();
|
||||
|
||||
public void deleteCollection(Context context, Collection collection)
|
||||
|
@@ -41,6 +41,9 @@ public class Role implements BeanNameAware {
|
||||
@Autowired
|
||||
private WorkflowItemRoleService workflowItemRoleService;
|
||||
|
||||
// Whether or not to delete temporary group made attached to the WorkflowItemRole for this role in AutoAssignAction
|
||||
private boolean deleteTemporaryGroup = false;
|
||||
|
||||
private String id;
|
||||
private String name;
|
||||
private String description;
|
||||
@@ -153,4 +156,17 @@ public class Role implements BeanNameAware {
|
||||
public void setInternal(boolean internal) {
|
||||
isInternal = internal;
|
||||
}
|
||||
|
||||
public boolean isDeleteTemporaryGroup() {
|
||||
return deleteTemporaryGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for config that indicated whether or not to delete temporary group made attached to the
|
||||
* WorkflowItemRole for this role in AutoAssignAction
|
||||
* @param deleteTemporaryGroup
|
||||
*/
|
||||
public void setDeleteTemporaryGroup(boolean deleteTemporaryGroup) {
|
||||
this.deleteTemporaryGroup = deleteTemporaryGroup;
|
||||
}
|
||||
}
|
||||
|
@@ -1076,6 +1076,53 @@ public class XmlWorkflowServiceImpl implements XmlWorkflowService {
|
||||
return wsi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restartWorkflow(Context context, XmlWorkflowItem wi, EPerson decliner, String provenance)
|
||||
throws SQLException, AuthorizeException, IOException, WorkflowException {
|
||||
if (!authorizeService.isAdmin(context)) {
|
||||
throw new AuthorizeException("You must be an admin to restart a workflow");
|
||||
}
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
// rejection provenance
|
||||
Item myitem = wi.getItem();
|
||||
|
||||
// Here's what happened
|
||||
String provDescription =
|
||||
provenance + " Declined by " + getEPersonName(decliner) + " on " + DCDate.getCurrent().toString() +
|
||||
" (GMT) ";
|
||||
|
||||
// Add to item as a DC field
|
||||
itemService
|
||||
.addMetadata(context, myitem, MetadataSchemaEnum.DC.getName(),
|
||||
"description", "provenance", "en", provDescription);
|
||||
|
||||
//Clear any workflow schema related metadata
|
||||
itemService
|
||||
.clearMetadata(context, myitem, WorkflowRequirementsService.WORKFLOW_SCHEMA, Item.ANY, Item.ANY, Item.ANY);
|
||||
|
||||
itemService.update(context, myitem);
|
||||
|
||||
// remove policy for controller
|
||||
removeUserItemPolicies(context, myitem, decliner);
|
||||
revokeReviewerPolicies(context, myitem);
|
||||
|
||||
// convert into personal workspace
|
||||
WorkspaceItem wsi = returnToWorkspace(context, wi);
|
||||
|
||||
// Because of issue of xmlWorkflowItemService not realising wfi wrapper has been deleted
|
||||
context.commit();
|
||||
wsi = context.reloadEntity(wsi);
|
||||
|
||||
log.info(LogHelper.getHeader(context, "decline_workflow", "workflow_item_id="
|
||||
+ wi.getID() + "item_id=" + wi.getItem().getID() + "collection_id=" + wi.getCollection().getID() +
|
||||
"eperson_id=" + decliner.getID()));
|
||||
|
||||
// Restart workflow
|
||||
this.startWithoutNotify(context, wsi);
|
||||
context.restoreAuthSystemState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the workflow item to the workspace of the submitter. The workflow
|
||||
* item is removed, and a workspace item created.
|
||||
|
@@ -14,10 +14,15 @@ import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.DCDate;
|
||||
import org.dspace.content.MetadataSchemaEnum;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.workflow.WorkflowException;
|
||||
import org.dspace.xmlworkflow.RoleMembers;
|
||||
import org.dspace.xmlworkflow.WorkflowConfigurationException;
|
||||
import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory;
|
||||
import org.dspace.xmlworkflow.state.Step;
|
||||
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
|
||||
|
||||
@@ -37,6 +42,8 @@ public abstract class Action {
|
||||
|
||||
private WorkflowActionConfig parent;
|
||||
private static final String ERROR_FIELDS_ATTRIBUTE = "dspace.workflow.error_fields";
|
||||
private List<String> advancedOptions = new ArrayList<>();
|
||||
private List<ActionAdvancedInfo> advancedInfo = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Called when a workflow item becomes eligible for this Action.
|
||||
@@ -192,4 +199,58 @@ public abstract class Action {
|
||||
//save updated list
|
||||
setErrorFields(request, errorFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of advanced options that the user can select at this action
|
||||
* @return A list of advanced options of this action, resulting in the next step of the workflow
|
||||
*/
|
||||
protected List<String> getAdvancedOptions() {
|
||||
return advancedOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this Action has advanced options, false if it doesn't
|
||||
* @return true if there are advanced options, false otherwise
|
||||
*/
|
||||
protected boolean isAdvanced() {
|
||||
return !getAdvancedOptions().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of advanced info required by the advanced options
|
||||
* @return A list of advanced info required by the advanced options
|
||||
*/
|
||||
protected List<ActionAdvancedInfo> getAdvancedInfo() {
|
||||
return advancedInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds info in the metadata field dc.description.provenance about item being approved containing in which step
|
||||
* it was approved, which user approved it and the time
|
||||
*
|
||||
* @param c DSpace contect
|
||||
* @param wfi Workflow item we're adding workflow accept provenance on
|
||||
*/
|
||||
public void addApprovedProvenance(Context c, XmlWorkflowItem wfi) throws SQLException, AuthorizeException {
|
||||
ItemService itemService = ContentServiceFactory.getInstance().getItemService();
|
||||
|
||||
//Add the provenance for the accept
|
||||
String now = DCDate.getCurrent().toString();
|
||||
|
||||
// Get user's name + email address
|
||||
String usersName =
|
||||
XmlWorkflowServiceFactory.getInstance().getXmlWorkflowService().getEPersonName(c.getCurrentUser());
|
||||
|
||||
String provDescription = getProvenanceStartId() + " Approved for entry into archive by " + usersName + " on "
|
||||
+ now + " (GMT) ";
|
||||
|
||||
// Add to item as a DC field
|
||||
c.turnOffAuthorisationSystem();
|
||||
itemService.addMetadata(c, wfi.getItem(), MetadataSchemaEnum.DC.getName(), "description", "provenance", "en",
|
||||
provDescription);
|
||||
itemService.update(c, wfi.getItem());
|
||||
c.restoreAuthSystemState();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.actions;
|
||||
|
||||
/**
|
||||
* Interface for the shared properties of an 'advancedInfo' section of an advanced workflow {@link Action}
|
||||
* Implementations of this class will define the specific fields per action that will need to be defined/configured
|
||||
* to pass along this info to REST endpoint
|
||||
*/
|
||||
public abstract class ActionAdvancedInfo {
|
||||
|
||||
protected String type;
|
||||
protected String id;
|
||||
|
||||
protected final static String TYPE_PREFIX = "action_info_";
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = TYPE_PREFIX + type;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the Action id to be set.
|
||||
* This is an MD5 hash of the type and the stringified properties of the advanced info
|
||||
*
|
||||
* @param type The type of this Action to be included in the MD5 hash
|
||||
*/
|
||||
protected abstract void generateId(String type);
|
||||
|
||||
}
|
@@ -69,4 +69,28 @@ public class WorkflowActionConfig {
|
||||
return this.processingAction.getOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of advanced options this user has on this action, resulting in the next step of the workflow
|
||||
* @return A list of advanced options of this action, resulting in the next step of the workflow
|
||||
*/
|
||||
public List<String> getAdvancedOptions() {
|
||||
return this.processingAction.getAdvancedOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean depending on whether this action has advanced options
|
||||
* @return The boolean indicating whether this action has advanced options
|
||||
*/
|
||||
public boolean isAdvanced() {
|
||||
return this.processingAction.isAdvanced();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Map of info for the advanced options this user has on this action
|
||||
* @return a Map of info for the advanced options this user has on this action
|
||||
*/
|
||||
public List<ActionAdvancedInfo> getAdvancedInfo() {
|
||||
return this.processingAction.getAdvancedInfo();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -15,8 +15,6 @@ import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.dspace.app.util.Util;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.DCDate;
|
||||
import org.dspace.content.MetadataSchemaEnum;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory;
|
||||
import org.dspace.xmlworkflow.state.Step;
|
||||
@@ -34,8 +32,6 @@ import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
|
||||
*/
|
||||
public class AcceptEditRejectAction extends ProcessingAction {
|
||||
|
||||
private static final String SUBMIT_APPROVE = "submit_approve";
|
||||
private static final String SUBMIT_REJECT = "submit_reject";
|
||||
private static final String SUBMITTER_IS_DELETED_PAGE = "submitter_deleted";
|
||||
|
||||
//TODO: rename to AcceptAndEditMetadataAction
|
||||
@@ -53,7 +49,7 @@ public class AcceptEditRejectAction extends ProcessingAction {
|
||||
case SUBMIT_APPROVE:
|
||||
return processAccept(c, wfi);
|
||||
case SUBMIT_REJECT:
|
||||
return processRejectPage(c, wfi, request);
|
||||
return super.processRejectPage(c, wfi, request);
|
||||
case SUBMITTER_IS_DELETED_PAGE:
|
||||
return processSubmitterIsDeletedPage(c, wfi, request);
|
||||
default:
|
||||
@@ -69,33 +65,18 @@ public class AcceptEditRejectAction extends ProcessingAction {
|
||||
options.add(SUBMIT_APPROVE);
|
||||
options.add(SUBMIT_REJECT);
|
||||
options.add(ProcessingAction.SUBMIT_EDIT_METADATA);
|
||||
options.add(RETURN_TO_POOL);
|
||||
return options;
|
||||
}
|
||||
|
||||
public ActionResult processAccept(Context c, XmlWorkflowItem wfi)
|
||||
throws SQLException, AuthorizeException {
|
||||
//Delete the tasks
|
||||
addApprovedProvenance(c, wfi);
|
||||
super.addApprovedProvenance(c, wfi);
|
||||
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE);
|
||||
}
|
||||
|
||||
public ActionResult processRejectPage(Context c, XmlWorkflowItem wfi, HttpServletRequest request)
|
||||
throws SQLException, AuthorizeException, IOException {
|
||||
String reason = request.getParameter("reason");
|
||||
if (reason == null || 0 == reason.trim().length()) {
|
||||
addErrorField(request, "reason");
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_ERROR);
|
||||
}
|
||||
|
||||
// We have pressed reject, so remove the task the user has & put it back
|
||||
// to a workspace item
|
||||
XmlWorkflowServiceFactory.getInstance().getXmlWorkflowService().sendWorkflowItemBackSubmission(c, wfi,
|
||||
c.getCurrentUser(), this.getProvenanceStartId(), reason);
|
||||
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_SUBMISSION_PAGE);
|
||||
}
|
||||
|
||||
public ActionResult processSubmitterIsDeletedPage(Context c, XmlWorkflowItem wfi, HttpServletRequest request)
|
||||
throws SQLException, AuthorizeException, IOException {
|
||||
if (request.getParameter("submit_delete") != null) {
|
||||
@@ -111,21 +92,4 @@ public class AcceptEditRejectAction extends ProcessingAction {
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_PAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private void addApprovedProvenance(Context c, XmlWorkflowItem wfi) throws SQLException, AuthorizeException {
|
||||
//Add the provenance for the accept
|
||||
String now = DCDate.getCurrent().toString();
|
||||
|
||||
// Get user's name + email address
|
||||
String usersName = XmlWorkflowServiceFactory.getInstance().getXmlWorkflowService()
|
||||
.getEPersonName(c.getCurrentUser());
|
||||
|
||||
String provDescription = getProvenanceStartId() + " Approved for entry into archive by "
|
||||
+ usersName + " on " + now + " (GMT) ";
|
||||
|
||||
// Add to item as a DC field
|
||||
itemService.addMetadata(c, wfi.getItem(), MetadataSchemaEnum.DC.getName(), "description", "provenance", "en",
|
||||
provDescription);
|
||||
itemService.update(c, wfi.getItem());
|
||||
}
|
||||
}
|
||||
|
@@ -14,10 +14,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.dspace.app.util.Util;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.DCDate;
|
||||
import org.dspace.content.MetadataSchemaEnum;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory;
|
||||
import org.dspace.xmlworkflow.state.Step;
|
||||
import org.dspace.xmlworkflow.state.actions.ActionResult;
|
||||
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
|
||||
@@ -52,7 +49,7 @@ public class FinalEditAction extends ProcessingAction {
|
||||
switch (Util.getSubmitButton(request, SUBMIT_CANCEL)) {
|
||||
case SUBMIT_APPROVE:
|
||||
//Delete the tasks
|
||||
addApprovedProvenance(c, wfi);
|
||||
super.addApprovedProvenance(c, wfi);
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE);
|
||||
default:
|
||||
//We pressed the leave button so return to our submissions page
|
||||
@@ -67,25 +64,8 @@ public class FinalEditAction extends ProcessingAction {
|
||||
List<String> options = new ArrayList<>();
|
||||
options.add(SUBMIT_APPROVE);
|
||||
options.add(ProcessingAction.SUBMIT_EDIT_METADATA);
|
||||
options.add(RETURN_TO_POOL);
|
||||
return options;
|
||||
}
|
||||
|
||||
private void addApprovedProvenance(Context c, XmlWorkflowItem wfi) throws SQLException, AuthorizeException {
|
||||
//Add the provenance for the accept
|
||||
String now = DCDate.getCurrent().toString();
|
||||
|
||||
// Get user's name + email address
|
||||
String usersName = XmlWorkflowServiceFactory.getInstance().getXmlWorkflowService()
|
||||
.getEPersonName(c.getCurrentUser());
|
||||
|
||||
String provDescription = getProvenanceStartId() + " Approved for entry into archive by "
|
||||
+ usersName + " on " + now + " (GMT) ";
|
||||
|
||||
// Add to item as a DC field
|
||||
itemService.addMetadata(c, wfi.getItem(), MetadataSchemaEnum.DC.getName(), "description", "provenance", "en",
|
||||
provDescription);
|
||||
itemService.update(c, wfi.getItem());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -7,12 +7,16 @@
|
||||
*/
|
||||
package org.dspace.xmlworkflow.state.actions.processingaction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.xmlworkflow.service.XmlWorkflowService;
|
||||
import org.dspace.xmlworkflow.state.actions.Action;
|
||||
import org.dspace.xmlworkflow.state.actions.ActionResult;
|
||||
import org.dspace.xmlworkflow.storedcomponents.ClaimedTask;
|
||||
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
|
||||
import org.dspace.xmlworkflow.storedcomponents.service.ClaimedTaskService;
|
||||
@@ -32,9 +36,15 @@ public abstract class ProcessingAction extends Action {
|
||||
protected ClaimedTaskService claimedTaskService;
|
||||
@Autowired(required = true)
|
||||
protected ItemService itemService;
|
||||
@Autowired
|
||||
protected XmlWorkflowService xmlWorkflowService;
|
||||
|
||||
public static final String SUBMIT_EDIT_METADATA = "submit_edit_metadata";
|
||||
public static final String SUBMIT_CANCEL = "submit_cancel";
|
||||
protected static final String SUBMIT_APPROVE = "submit_approve";
|
||||
protected static final String SUBMIT_REJECT = "submit_reject";
|
||||
protected static final String RETURN_TO_POOL = "return_to_pool";
|
||||
protected static final String REJECT_REASON = "reason";
|
||||
|
||||
@Override
|
||||
public boolean isAuthorized(Context context, HttpServletRequest request, XmlWorkflowItem wfi) throws SQLException {
|
||||
@@ -48,4 +58,31 @@ public abstract class ProcessingAction extends Action {
|
||||
task.getStepID().equals(getParent().getStep().getId()) &&
|
||||
task.getActionID().equals(getParent().getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Process result when option {@link this#SUBMIT_REJECT} is selected.
|
||||
* - Sets the reason and workflow step responsible on item in dc.description.provenance
|
||||
* - Send workflow back to the submission
|
||||
* If reason is not given => error
|
||||
*/
|
||||
public ActionResult processRejectPage(Context c, XmlWorkflowItem wfi, HttpServletRequest request)
|
||||
throws SQLException, AuthorizeException, IOException {
|
||||
String reason = request.getParameter(REJECT_REASON);
|
||||
if (reason == null || 0 == reason.trim().length()) {
|
||||
addErrorField(request, REJECT_REASON);
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_ERROR);
|
||||
}
|
||||
|
||||
// We have pressed reject, so remove the task the user has & put it back
|
||||
// to a workspace item
|
||||
xmlWorkflowService.sendWorkflowItemBackSubmission(c, wfi, c.getCurrentUser(), this.getProvenanceStartId(),
|
||||
reason);
|
||||
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_SUBMISSION_PAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isAdvanced() {
|
||||
return !getAdvancedOptions().isEmpty();
|
||||
}
|
||||
}
|
||||
|
@@ -15,8 +15,6 @@ import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.dspace.app.util.Util;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.DCDate;
|
||||
import org.dspace.content.MetadataSchemaEnum;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory;
|
||||
import org.dspace.xmlworkflow.state.Step;
|
||||
@@ -36,11 +34,8 @@ public class ReviewAction extends ProcessingAction {
|
||||
public static final int MAIN_PAGE = 0;
|
||||
public static final int REJECT_PAGE = 1;
|
||||
|
||||
private static final String SUBMIT_APPROVE = "submit_approve";
|
||||
private static final String SUBMIT_REJECT = "submit_reject";
|
||||
private static final String SUBMITTER_IS_DELETED_PAGE = "submitter_deleted";
|
||||
|
||||
|
||||
@Override
|
||||
public void activate(Context c, XmlWorkflowItem wfItem) {
|
||||
|
||||
@@ -54,7 +49,7 @@ public class ReviewAction extends ProcessingAction {
|
||||
case SUBMIT_APPROVE:
|
||||
return processAccept(c, wfi);
|
||||
case SUBMIT_REJECT:
|
||||
return processRejectPage(c, wfi, step, request);
|
||||
return super.processRejectPage(c, wfi, request);
|
||||
case SUBMITTER_IS_DELETED_PAGE:
|
||||
return processSubmitterIsDeletedPage(c, wfi, request);
|
||||
default:
|
||||
@@ -69,50 +64,15 @@ public class ReviewAction extends ProcessingAction {
|
||||
List<String> options = new ArrayList<>();
|
||||
options.add(SUBMIT_APPROVE);
|
||||
options.add(SUBMIT_REJECT);
|
||||
options.add(RETURN_TO_POOL);
|
||||
return options;
|
||||
}
|
||||
|
||||
public ActionResult processAccept(Context c, XmlWorkflowItem wfi) throws SQLException, AuthorizeException {
|
||||
//Delete the tasks
|
||||
addApprovedProvenance(c, wfi);
|
||||
super.addApprovedProvenance(c, wfi);
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE);
|
||||
}
|
||||
|
||||
private void addApprovedProvenance(Context c, XmlWorkflowItem wfi) throws SQLException, AuthorizeException {
|
||||
//Add the provenance for the accept
|
||||
String now = DCDate.getCurrent().toString();
|
||||
|
||||
// Get user's name + email address
|
||||
String usersName = XmlWorkflowServiceFactory.getInstance().getXmlWorkflowService()
|
||||
.getEPersonName(c.getCurrentUser());
|
||||
|
||||
String provDescription = getProvenanceStartId() + " Approved for entry into archive by "
|
||||
+ usersName + " on " + now + " (GMT) ";
|
||||
|
||||
// Add to item as a DC field
|
||||
itemService.addMetadata(c, wfi.getItem(), MetadataSchemaEnum.DC.getName(), "description", "provenance", "en",
|
||||
provDescription);
|
||||
itemService.update(c, wfi.getItem());
|
||||
}
|
||||
|
||||
public ActionResult processRejectPage(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request)
|
||||
throws SQLException, AuthorizeException, IOException {
|
||||
String reason = request.getParameter("reason");
|
||||
if (reason == null || 0 == reason.trim().length()) {
|
||||
request.setAttribute("page", REJECT_PAGE);
|
||||
addErrorField(request, "reason");
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_ERROR);
|
||||
}
|
||||
|
||||
//We have pressed reject, so remove the task the user has & put it back to a workspace item
|
||||
XmlWorkflowServiceFactory.getInstance().getXmlWorkflowService()
|
||||
.sendWorkflowItemBackSubmission(c, wfi, c.getCurrentUser(),
|
||||
this.getProvenanceStartId(), reason);
|
||||
|
||||
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_SUBMISSION_PAGE);
|
||||
}
|
||||
|
||||
public ActionResult processSubmitterIsDeletedPage(Context c, XmlWorkflowItem wfi, HttpServletRequest request)
|
||||
throws SQLException, AuthorizeException, IOException {
|
||||
if (request.getParameter("submit_delete") != null) {
|
||||
|
@@ -7,6 +7,9 @@
|
||||
*/
|
||||
package org.dspace.xmlworkflow.state.actions.processingaction;
|
||||
|
||||
import static org.dspace.xmlworkflow.state.actions.processingaction.ScoreReviewAction.REVIEW_FIELD;
|
||||
import static org.dspace.xmlworkflow.state.actions.processingaction.ScoreReviewAction.SCORE_FIELD;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
@@ -19,7 +22,6 @@ import org.dspace.content.MetadataSchemaEnum;
|
||||
import org.dspace.content.MetadataValue;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory;
|
||||
import org.dspace.xmlworkflow.service.WorkflowRequirementsService;
|
||||
import org.dspace.xmlworkflow.state.Step;
|
||||
import org.dspace.xmlworkflow.state.actions.ActionResult;
|
||||
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
|
||||
@@ -37,6 +39,7 @@ import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
|
||||
*/
|
||||
public class ScoreEvaluationAction extends ProcessingAction {
|
||||
|
||||
// Minimum aggregate of scores
|
||||
private int minimumAcceptanceScore;
|
||||
|
||||
@Override
|
||||
@@ -47,43 +50,64 @@ public class ScoreEvaluationAction extends ProcessingAction {
|
||||
@Override
|
||||
public ActionResult execute(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request)
|
||||
throws SQLException, AuthorizeException, IOException {
|
||||
boolean hasPassed = false;
|
||||
//Retrieve all our scores from the metadata & add em up
|
||||
// Retrieve all our scores from the metadata & add em up
|
||||
int scoreMean = getMeanScore(wfi);
|
||||
//We have passed if we have at least gained our minimum score
|
||||
boolean hasPassed = getMinimumAcceptanceScore() <= scoreMean;
|
||||
//Whether or not we have passed, clear our score information
|
||||
itemService.clearMetadata(c, wfi.getItem(), SCORE_FIELD.schema, SCORE_FIELD.element, SCORE_FIELD.qualifier,
|
||||
Item.ANY);
|
||||
if (hasPassed) {
|
||||
this.addRatingInfoToProv(c, wfi, scoreMean);
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE);
|
||||
} else {
|
||||
//We haven't passed, reject our item
|
||||
XmlWorkflowServiceFactory.getInstance().getXmlWorkflowService()
|
||||
.sendWorkflowItemBackSubmission(c, wfi, c.getCurrentUser(), this.getProvenanceStartId(),
|
||||
"The item was reject due to a bad review score.");
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_SUBMISSION_PAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private int getMeanScore(XmlWorkflowItem wfi) {
|
||||
List<MetadataValue> scores = itemService
|
||||
.getMetadata(wfi.getItem(), WorkflowRequirementsService.WORKFLOW_SCHEMA, "score", null, Item.ANY);
|
||||
.getMetadata(wfi.getItem(), SCORE_FIELD.schema, SCORE_FIELD.element, SCORE_FIELD.qualifier, Item.ANY);
|
||||
int scoreMean = 0;
|
||||
if (0 < scores.size()) {
|
||||
int totalScoreCount = 0;
|
||||
for (MetadataValue score : scores) {
|
||||
totalScoreCount += Integer.parseInt(score.getValue());
|
||||
}
|
||||
int scoreMean = totalScoreCount / scores.size();
|
||||
//We have passed if we have at least gained our minimum score
|
||||
hasPassed = getMinimumAcceptanceScore() <= scoreMean;
|
||||
//Wether or not we have passed, clear our score information
|
||||
itemService
|
||||
.clearMetadata(c, wfi.getItem(), WorkflowRequirementsService.WORKFLOW_SCHEMA, "score", null, Item.ANY);
|
||||
scoreMean = totalScoreCount / scores.size();
|
||||
}
|
||||
return scoreMean;
|
||||
}
|
||||
|
||||
String provDescription = getProvenanceStartId() + " Approved for entry into archive with a score of: " +
|
||||
scoreMean;
|
||||
itemService.addMetadata(c, wfi.getItem(), MetadataSchemaEnum.DC.getName(),
|
||||
"description", "provenance", "en", provDescription);
|
||||
itemService.update(c, wfi.getItem());
|
||||
private void addRatingInfoToProv(Context c, XmlWorkflowItem wfi, int scoreMean)
|
||||
throws SQLException, AuthorizeException {
|
||||
StringBuilder provDescription = new StringBuilder();
|
||||
provDescription.append(String.format("%s Approved for entry into archive with a score of: %s",
|
||||
getProvenanceStartId(), scoreMean));
|
||||
List<MetadataValue> reviews = itemService
|
||||
.getMetadata(wfi.getItem(), REVIEW_FIELD.schema, REVIEW_FIELD.element, REVIEW_FIELD.qualifier, Item.ANY);
|
||||
if (!reviews.isEmpty()) {
|
||||
provDescription.append(" | Reviews: ");
|
||||
}
|
||||
if (hasPassed) {
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE);
|
||||
} else {
|
||||
//We haven't passed, reject our item
|
||||
XmlWorkflowServiceFactory.getInstance().getXmlWorkflowService()
|
||||
.sendWorkflowItemBackSubmission(c, wfi, c.getCurrentUser(),
|
||||
this.getProvenanceStartId(),
|
||||
"The item was reject due to a bad review score.");
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_SUBMISSION_PAGE);
|
||||
for (MetadataValue review : reviews) {
|
||||
provDescription.append(String.format("; %s", review.getValue()));
|
||||
}
|
||||
c.turnOffAuthorisationSystem();
|
||||
itemService.addMetadata(c, wfi.getItem(), MetadataSchemaEnum.DC.getName(),
|
||||
"description", "provenance", "en", provDescription.toString());
|
||||
itemService.update(c, wfi.getItem());
|
||||
c.restoreAuthSystemState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getOptions() {
|
||||
return new ArrayList<>();
|
||||
List<String> options = new ArrayList<>();
|
||||
options.add(RETURN_TO_POOL);
|
||||
return options;
|
||||
}
|
||||
|
||||
public int getMinimumAcceptanceScore() {
|
||||
|
@@ -9,14 +9,20 @@ package org.dspace.xmlworkflow.state.actions.processingaction;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.util.Util;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.MetadataFieldName;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.xmlworkflow.service.WorkflowRequirementsService;
|
||||
import org.dspace.xmlworkflow.state.Step;
|
||||
import org.dspace.xmlworkflow.state.actions.ActionAdvancedInfo;
|
||||
import org.dspace.xmlworkflow.state.actions.ActionResult;
|
||||
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
|
||||
|
||||
@@ -24,40 +30,121 @@ import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
|
||||
* This action will allow multiple users to rate a certain item
|
||||
* if the mean of this score is higher then the minimum score the
|
||||
* item will be sent to the next action/step else it will be rejected
|
||||
*
|
||||
* @author Bram De Schouwer (bram.deschouwer at dot com)
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
* @author Ben Bosman (ben at atmire dot com)
|
||||
* @author Mark Diggory (markd at atmire dot com)
|
||||
*/
|
||||
public class ScoreReviewAction extends ProcessingAction {
|
||||
private static final Logger log = LogManager.getLogger(ScoreReviewAction.class);
|
||||
|
||||
private static final String SUBMIT_SCORE = "submit_score";
|
||||
// Option(s)
|
||||
public static final String SUBMIT_SCORE = "submit_score";
|
||||
|
||||
// Response param(s)
|
||||
private static final String SCORE = "score";
|
||||
private static final String REVIEW = "review";
|
||||
|
||||
// Metadata fields to save params in
|
||||
public static final MetadataFieldName SCORE_FIELD =
|
||||
new MetadataFieldName(WorkflowRequirementsService.WORKFLOW_SCHEMA, SCORE, null);
|
||||
public static final MetadataFieldName REVIEW_FIELD =
|
||||
new MetadataFieldName(WorkflowRequirementsService.WORKFLOW_SCHEMA, REVIEW, null);
|
||||
|
||||
// Whether or not it is required that a text review is added to the rating
|
||||
private boolean descriptionRequired;
|
||||
// Maximum value rating is allowed to be
|
||||
private int maxValue;
|
||||
|
||||
@Override
|
||||
public void activate(Context c, XmlWorkflowItem wf) {
|
||||
|
||||
// empty
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult execute(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request)
|
||||
throws SQLException, AuthorizeException {
|
||||
if (request.getParameter(SUBMIT_SCORE) != null) {
|
||||
int score = Util.getIntParameter(request, "score");
|
||||
//Add our score to the metadata
|
||||
itemService.addMetadata(c, wfi.getItem(), WorkflowRequirementsService.WORKFLOW_SCHEMA, "score", null, null,
|
||||
String.valueOf(score));
|
||||
itemService.update(c, wfi.getItem());
|
||||
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE);
|
||||
} else {
|
||||
//We have pressed the leave button so return to our submission page
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_SUBMISSION_PAGE);
|
||||
throws SQLException, AuthorizeException {
|
||||
if (super.isOptionInParam(request) &&
|
||||
StringUtils.equalsIgnoreCase(Util.getSubmitButton(request, SUBMIT_CANCEL), SUBMIT_SCORE)) {
|
||||
return processSetRating(c, wfi, request);
|
||||
}
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_CANCEL);
|
||||
}
|
||||
|
||||
private ActionResult processSetRating(Context c, XmlWorkflowItem wfi, HttpServletRequest request)
|
||||
throws SQLException, AuthorizeException {
|
||||
|
||||
int score = Util.getIntParameter(request, SCORE);
|
||||
String review = request.getParameter(REVIEW);
|
||||
if (!this.checkRequestValid(score, review)) {
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_ERROR);
|
||||
}
|
||||
//Add our rating and review to the metadata
|
||||
itemService.addMetadata(c, wfi.getItem(), SCORE_FIELD.schema, SCORE_FIELD.element, SCORE_FIELD.qualifier, null,
|
||||
String.valueOf(score));
|
||||
if (StringUtils.isNotBlank(review)) {
|
||||
itemService.addMetadata(c, wfi.getItem(), REVIEW_FIELD.schema, REVIEW_FIELD.element,
|
||||
REVIEW_FIELD.qualifier, null, String.format("%s - %s", score, review));
|
||||
}
|
||||
itemService.update(c, wfi.getItem());
|
||||
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Request is not valid if:
|
||||
* - Given score is higher than configured maxValue
|
||||
* - There is no review given and description is configured to be required
|
||||
* Config in workflow-actions.xml
|
||||
*
|
||||
* @param score Given score rating from request
|
||||
* @param review Given review/description from request
|
||||
* @return True if valid request params with config, otherwise false
|
||||
*/
|
||||
private boolean checkRequestValid(int score, String review) {
|
||||
if (score > this.maxValue) {
|
||||
log.error("{} only allows max rating {} (config workflow-actions.xml), given rating of " +
|
||||
"{} not allowed.", this.getClass().toString(), this.maxValue, score);
|
||||
return false;
|
||||
}
|
||||
if (StringUtils.isBlank(review) && this.descriptionRequired) {
|
||||
log.error("{} has config descriptionRequired=true (workflow-actions.xml), so rating " +
|
||||
"requests without 'review' query param containing description are not allowed",
|
||||
this.getClass().toString());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getOptions() {
|
||||
return List.of(SUBMIT_SCORE, RETURN_TO_POOL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getAdvancedOptions() {
|
||||
return Arrays.asList(SUBMIT_SCORE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ActionAdvancedInfo> getAdvancedInfo() {
|
||||
ScoreReviewActionAdvancedInfo scoreReviewActionAdvancedInfo = new ScoreReviewActionAdvancedInfo();
|
||||
scoreReviewActionAdvancedInfo.setDescriptionRequired(descriptionRequired);
|
||||
scoreReviewActionAdvancedInfo.setMaxValue(maxValue);
|
||||
scoreReviewActionAdvancedInfo.setType(SUBMIT_SCORE);
|
||||
scoreReviewActionAdvancedInfo.generateId(SUBMIT_SCORE);
|
||||
return Collections.singletonList(scoreReviewActionAdvancedInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter that sets the descriptionRequired property from workflow-actions.xml
|
||||
* @param descriptionRequired boolean whether a description is required
|
||||
*/
|
||||
public void setDescriptionRequired(boolean descriptionRequired) {
|
||||
this.descriptionRequired = descriptionRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter that sets the maxValue property from workflow-actions.xml
|
||||
* @param maxValue integer of the maximum allowed value
|
||||
*/
|
||||
public void setMaxValue(int maxValue) {
|
||||
this.maxValue = maxValue;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 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.actions.processingaction;
|
||||
|
||||
import org.dspace.xmlworkflow.state.actions.ActionAdvancedInfo;
|
||||
import org.springframework.util.DigestUtils;
|
||||
|
||||
/**
|
||||
* Class that holds the advanced information needed for the
|
||||
* {@link org.dspace.xmlworkflow.state.actions.processingaction.ScoreReviewAction}
|
||||
* See config {@code workflow-actions.cfg}
|
||||
*/
|
||||
public class ScoreReviewActionAdvancedInfo extends ActionAdvancedInfo {
|
||||
private boolean descriptionRequired;
|
||||
private int maxValue;
|
||||
|
||||
public boolean isDescriptionRequired() {
|
||||
return descriptionRequired;
|
||||
}
|
||||
|
||||
public void setDescriptionRequired(boolean descriptionRequired) {
|
||||
this.descriptionRequired = descriptionRequired;
|
||||
}
|
||||
|
||||
public int getMaxValue() {
|
||||
return maxValue;
|
||||
}
|
||||
|
||||
public void setMaxValue(int maxValue) {
|
||||
this.maxValue = maxValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateId(String type) {
|
||||
String idString = type
|
||||
+ ";descriptionRequired," + descriptionRequired
|
||||
+ ";maxValue," + maxValue;
|
||||
super.id = DigestUtils.md5DigestAsHex(idString.getBytes());
|
||||
}
|
||||
}
|
@@ -9,17 +9,27 @@ package org.dspace.xmlworkflow.state.actions.processingaction;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.util.Util;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.eperson.Group;
|
||||
import org.dspace.eperson.service.EPersonService;
|
||||
import org.dspace.eperson.service.GroupService;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.xmlworkflow.Role;
|
||||
import org.dspace.xmlworkflow.state.Step;
|
||||
import org.dspace.xmlworkflow.state.actions.ActionAdvancedInfo;
|
||||
import org.dspace.xmlworkflow.state.actions.ActionResult;
|
||||
import org.dspace.xmlworkflow.storedcomponents.WorkflowItemRole;
|
||||
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
|
||||
@@ -37,13 +47,13 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
*/
|
||||
public class SelectReviewerAction extends ProcessingAction {
|
||||
|
||||
public static final int SEARCH_RESULTS_PAGE = 1;
|
||||
|
||||
public static final int RESULTS_PER_PAGE = 5;
|
||||
private static final Logger log = LogManager.getLogger(SelectReviewerAction.class);
|
||||
|
||||
private static final String SUBMIT_CANCEL = "submit_cancel";
|
||||
private static final String SUBMIT_SEARCH = "submit_search";
|
||||
private static final String SUBMIT_SELECT_REVIEWER = "submit_select_reviewer_";
|
||||
private static final String SUBMIT_SELECT_REVIEWER = "submit_select_reviewer";
|
||||
private static final String PARAM_REVIEWER = "eperson";
|
||||
|
||||
private static final String CONFIG_REVIEWER_GROUP = "action.selectrevieweraction.group";
|
||||
|
||||
private Role role;
|
||||
|
||||
@@ -53,6 +63,15 @@ public class SelectReviewerAction extends ProcessingAction {
|
||||
@Autowired(required = true)
|
||||
private WorkflowItemRoleService workflowItemRoleService;
|
||||
|
||||
@Autowired
|
||||
private ConfigurationService configurationService;
|
||||
|
||||
@Autowired
|
||||
private GroupService groupService;
|
||||
|
||||
private static Group selectFromReviewsGroup;
|
||||
private static boolean selectFromReviewsGroupInitialised = false;
|
||||
|
||||
@Override
|
||||
public void activate(Context c, XmlWorkflowItem wf) {
|
||||
|
||||
@@ -60,56 +79,128 @@ public class SelectReviewerAction extends ProcessingAction {
|
||||
|
||||
@Override
|
||||
public ActionResult execute(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request)
|
||||
throws SQLException, AuthorizeException {
|
||||
throws SQLException, AuthorizeException {
|
||||
String submitButton = Util.getSubmitButton(request, SUBMIT_CANCEL);
|
||||
|
||||
//Check if our user has pressed cancel
|
||||
if (submitButton.equals(SUBMIT_CANCEL)) {
|
||||
//Send us back to the submissions page
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_CANCEL);
|
||||
|
||||
} else if (submitButton.equals(SUBMIT_SEARCH)) {
|
||||
//Perform the search
|
||||
String query = request.getParameter("query");
|
||||
int page = Util.getIntParameter(request, "result-page");
|
||||
if (page == -1) {
|
||||
page = 0;
|
||||
}
|
||||
|
||||
int resultCount = ePersonService.searchResultCount(c, query);
|
||||
List<EPerson> epeople = ePersonService.search(c, query, page * RESULTS_PER_PAGE, RESULTS_PER_PAGE);
|
||||
|
||||
|
||||
request.setAttribute("eperson-result-count", resultCount);
|
||||
request.setAttribute("eperson-results", epeople);
|
||||
request.setAttribute("result-page", page);
|
||||
request.setAttribute("page", SEARCH_RESULTS_PAGE);
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_PAGE, SEARCH_RESULTS_PAGE);
|
||||
} else if (submitButton.startsWith(SUBMIT_SELECT_REVIEWER)) {
|
||||
//Retrieve the identifier of the eperson which will do the reviewing
|
||||
UUID reviewerId = UUID.fromString(submitButton.substring(submitButton.lastIndexOf("_") + 1));
|
||||
EPerson reviewer = ePersonService.find(c, reviewerId);
|
||||
//Assign the reviewer. The workflowitemrole will be translated into a task in the autoassign
|
||||
WorkflowItemRole workflowItemRole = workflowItemRoleService.create(c);
|
||||
workflowItemRole.setEPerson(reviewer);
|
||||
workflowItemRole.setRoleId(getRole().getId());
|
||||
workflowItemRole.setWorkflowItem(wfi);
|
||||
workflowItemRoleService.update(c, workflowItemRole);
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE);
|
||||
return processSelectReviewers(c, wfi, request);
|
||||
}
|
||||
|
||||
//There are only 2 active buttons on this page, so if anything else happens just return an error
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to handle the {@link this#SUBMIT_SELECT_REVIEWER} action:
|
||||
* - will retrieve the reviewer(s) uuid from request (param {@link this#PARAM_REVIEWER})
|
||||
* - assign them to a {@link WorkflowItemRole}
|
||||
* - In {@link org.dspace.xmlworkflow.state.actions.userassignment.AutoAssignAction} these reviewer(s) will get
|
||||
* claimed task for this {@link XmlWorkflowItem}
|
||||
* Will result in error if:
|
||||
* - No reviewer(s) uuid in request (param {@link this#PARAM_REVIEWER})
|
||||
* - If none of the reviewer(s) uuid passed along result in valid EPerson
|
||||
* - If the reviewer(s) passed along are not in {@link this#selectFromReviewsGroup} when it is set
|
||||
*
|
||||
* @param c current DSpace session
|
||||
* @param wfi the item on which the action is to be performed
|
||||
* @param request the current client request
|
||||
* @return the result of performing the action
|
||||
*/
|
||||
private ActionResult processSelectReviewers(Context c, XmlWorkflowItem wfi, HttpServletRequest request)
|
||||
throws SQLException, AuthorizeException {
|
||||
//Retrieve the identifier of the eperson which will do the reviewing
|
||||
String[] reviewerIds = request.getParameterValues(PARAM_REVIEWER);
|
||||
if (ArrayUtils.isEmpty(reviewerIds)) {
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_ERROR);
|
||||
}
|
||||
List<EPerson> reviewers = new ArrayList<>();
|
||||
for (String reviewerId : reviewerIds) {
|
||||
EPerson reviewer = ePersonService.find(c, UUID.fromString(reviewerId));
|
||||
if (reviewer == null) {
|
||||
log.warn("No EPerson found with uuid {}", reviewerId);
|
||||
} else {
|
||||
reviewers.add(reviewer);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.checkReviewersValid(c, reviewers)) {
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_ERROR);
|
||||
}
|
||||
|
||||
createWorkflowItemRole(c, wfi, reviewers);
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE);
|
||||
}
|
||||
|
||||
private boolean checkReviewersValid(Context c, List<EPerson> reviewers) throws SQLException {
|
||||
if (reviewers.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
Group group = this.getGroup(c);
|
||||
if (group != null) {
|
||||
for (EPerson reviewer: reviewers) {
|
||||
if (!groupService.isMember(c, reviewer, group)) {
|
||||
log.error("Reviewers selected must be member of group {}", group.getID());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private WorkflowItemRole createWorkflowItemRole(Context c, XmlWorkflowItem wfi, List<EPerson> reviewers)
|
||||
throws SQLException, AuthorizeException {
|
||||
WorkflowItemRole workflowItemRole = workflowItemRoleService.create(c);
|
||||
workflowItemRole.setRoleId(getRole().getId());
|
||||
workflowItemRole.setWorkflowItem(wfi);
|
||||
if (reviewers.size() == 1) {
|
||||
// 1 reviewer in workflowitemrole => will be translated into a claimed task in the autoassign
|
||||
workflowItemRole.setEPerson(reviewers.get(0));
|
||||
} else {
|
||||
// multiple reviewers, create a temporary group and assign this group, the workflowitemrole will be
|
||||
// translated into a claimed task for reviewers in the autoassign, where group will be deleted
|
||||
c.turnOffAuthorisationSystem();
|
||||
Group selectedReviewsGroup = groupService.create(c);
|
||||
groupService.setName(selectedReviewsGroup, "selectedReviewsGroup_" + wfi.getID());
|
||||
for (EPerson reviewer : reviewers) {
|
||||
groupService.addMember(c, selectedReviewsGroup, reviewer);
|
||||
}
|
||||
workflowItemRole.setGroup(selectedReviewsGroup);
|
||||
c.restoreAuthSystemState();
|
||||
}
|
||||
workflowItemRoleService.update(c, workflowItemRole);
|
||||
return workflowItemRole;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getOptions() {
|
||||
List<String> options = new ArrayList<>();
|
||||
options.add(SUBMIT_SEARCH);
|
||||
options.add(SUBMIT_SELECT_REVIEWER);
|
||||
options.add(RETURN_TO_POOL);
|
||||
return options;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getAdvancedOptions() {
|
||||
return Arrays.asList(SUBMIT_SELECT_REVIEWER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ActionAdvancedInfo> getAdvancedInfo() {
|
||||
List<ActionAdvancedInfo> advancedInfo = new ArrayList<>();
|
||||
SelectReviewerActionAdvancedInfo selectReviewerActionAdvancedInfo = new SelectReviewerActionAdvancedInfo();
|
||||
if (getGroup(null) != null) {
|
||||
selectReviewerActionAdvancedInfo.setGroup(getGroup(null).getID().toString());
|
||||
}
|
||||
selectReviewerActionAdvancedInfo.setType(SUBMIT_SELECT_REVIEWER);
|
||||
selectReviewerActionAdvancedInfo.generateId(SUBMIT_SELECT_REVIEWER);
|
||||
advancedInfo.add(selectReviewerActionAdvancedInfo);
|
||||
return advancedInfo;
|
||||
}
|
||||
|
||||
public Role getRole() {
|
||||
return role;
|
||||
}
|
||||
@@ -118,4 +209,49 @@ public class SelectReviewerAction extends ProcessingAction {
|
||||
public void setRole(Role role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Reviewer group from the "action.selectrevieweraction.group" property in actions.cfg by its UUID or name
|
||||
* Returns null if no (valid) group configured
|
||||
*
|
||||
* @return configured reviewers Group from property or null if none
|
||||
*/
|
||||
private Group getGroup(@Nullable Context context) {
|
||||
if (selectFromReviewsGroupInitialised) {
|
||||
return this.selectFromReviewsGroup;
|
||||
}
|
||||
if (context == null) {
|
||||
context = new Context();
|
||||
}
|
||||
String groupIdOrName = configurationService.getProperty(CONFIG_REVIEWER_GROUP);
|
||||
|
||||
if (StringUtils.isNotBlank(groupIdOrName)) {
|
||||
Group group = null;
|
||||
try {
|
||||
// try to get group by name
|
||||
group = groupService.findByName(context, groupIdOrName);
|
||||
if (group == null) {
|
||||
// try to get group by uuid if not a name
|
||||
group = groupService.find(context, UUID.fromString(groupIdOrName));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// There is an issue with the reviewer group that is set; if it is not set then can be chosen
|
||||
// from all epeople
|
||||
log.error("Issue with determining matching group for config {}={} for reviewer group of " +
|
||||
"select reviewers workflow", CONFIG_REVIEWER_GROUP, groupIdOrName);
|
||||
}
|
||||
|
||||
this.selectFromReviewsGroup = group;
|
||||
}
|
||||
selectFromReviewsGroupInitialised = true;
|
||||
return this.selectFromReviewsGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used by IT, e.g. {@code XmlWorkflowServiceIT}, when defining new 'Reviewers' group
|
||||
*/
|
||||
static public void resetGroup() {
|
||||
selectFromReviewsGroup = null;
|
||||
selectFromReviewsGroupInitialised = false;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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.actions.processingaction;
|
||||
|
||||
import org.dspace.xmlworkflow.state.actions.ActionAdvancedInfo;
|
||||
import org.springframework.util.DigestUtils;
|
||||
|
||||
/**
|
||||
* Class that holds the advanced information needed for the
|
||||
* {@link org.dspace.xmlworkflow.state.actions.processingaction.SelectReviewerAction}
|
||||
* See config {@code workflow-actions.cfg}
|
||||
*/
|
||||
public class SelectReviewerActionAdvancedInfo extends ActionAdvancedInfo {
|
||||
private String group;
|
||||
|
||||
public String getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public void setGroup(String group) {
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateId(String type) {
|
||||
String idString = type
|
||||
+ ";group," + group;
|
||||
super.id = DigestUtils.md5DigestAsHex(idString.getBytes());
|
||||
}
|
||||
}
|
||||
|
@@ -13,11 +13,15 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.util.Util;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.DCDate;
|
||||
import org.dspace.content.MetadataSchemaEnum;
|
||||
import org.dspace.content.WorkspaceItem;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.workflow.WorkflowException;
|
||||
import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory;
|
||||
import org.dspace.xmlworkflow.state.Step;
|
||||
import org.dspace.xmlworkflow.state.actions.ActionResult;
|
||||
@@ -34,39 +38,59 @@ import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
|
||||
* @author Mark Diggory (markd at atmire dot com)
|
||||
*/
|
||||
public class SingleUserReviewAction extends ProcessingAction {
|
||||
|
||||
public static final int MAIN_PAGE = 0;
|
||||
public static final int REJECT_PAGE = 1;
|
||||
public static final int SUBMITTER_IS_DELETED_PAGE = 2;
|
||||
private static final Logger log = LogManager.getLogger(SingleUserReviewAction.class);
|
||||
|
||||
public static final int OUTCOME_REJECT = 1;
|
||||
|
||||
protected static final String SUBMIT_APPROVE = "submit_approve";
|
||||
protected static final String SUBMIT_REJECT = "submit_reject";
|
||||
protected static final String SUBMIT_DECLINE_TASK = "submit_decline_task";
|
||||
|
||||
@Override
|
||||
public void activate(Context c, XmlWorkflowItem wfItem) {
|
||||
|
||||
// empty
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult execute(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request)
|
||||
throws SQLException, AuthorizeException, IOException {
|
||||
int page = Util.getIntParameter(request, "page");
|
||||
|
||||
switch (page) {
|
||||
case MAIN_PAGE:
|
||||
return processMainPage(c, wfi, step, request);
|
||||
case REJECT_PAGE:
|
||||
return processRejectPage(c, wfi, step, request);
|
||||
case SUBMITTER_IS_DELETED_PAGE:
|
||||
return processSubmitterIsDeletedPage(c, wfi, request);
|
||||
throws SQLException, AuthorizeException, IOException, WorkflowException {
|
||||
if (!super.isOptionInParam(request)) {
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_CANCEL);
|
||||
}
|
||||
switch (Util.getSubmitButton(request, SUBMIT_CANCEL)) {
|
||||
case SUBMIT_APPROVE:
|
||||
return processAccept(c, wfi);
|
||||
case SUBMIT_REJECT:
|
||||
return processReject(c, wfi, request);
|
||||
case SUBMIT_DECLINE_TASK:
|
||||
return processDecline(c, wfi);
|
||||
default:
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_CANCEL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process {@link super#SUBMIT_REJECT} on this action, will either:
|
||||
* - If submitter of item no longer exists => Permanently delete corresponding item (no wfi/wsi remaining)
|
||||
* - Otherwise: reject item back to submission => becomes wsi of submitter again
|
||||
*/
|
||||
private ActionResult processReject(Context c, XmlWorkflowItem wfi, HttpServletRequest request)
|
||||
throws SQLException, IOException, AuthorizeException {
|
||||
if (wfi.getSubmitter() == null) {
|
||||
// If the original submitter is no longer there, delete the task
|
||||
return processDelete(c, wfi);
|
||||
} else {
|
||||
return super.processRejectPage(c, wfi, request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept the workflow item => last step in workflow so will be archived
|
||||
* Info on step & reviewer will be added on metadata dc.description.provenance of resulting item
|
||||
*/
|
||||
public ActionResult processAccept(Context c, XmlWorkflowItem wfi) throws SQLException, AuthorizeException {
|
||||
super.addApprovedProvenance(c, wfi);
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getOptions() {
|
||||
List<String> options = new ArrayList<>();
|
||||
@@ -76,87 +100,29 @@ public class SingleUserReviewAction extends ProcessingAction {
|
||||
return options;
|
||||
}
|
||||
|
||||
public ActionResult processMainPage(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request)
|
||||
throws SQLException, AuthorizeException {
|
||||
if (request.getParameter(SUBMIT_APPROVE) != null) {
|
||||
//Delete the tasks
|
||||
addApprovedProvenance(c, wfi);
|
||||
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE);
|
||||
} else if (request.getParameter(SUBMIT_REJECT) != null) {
|
||||
// Make sure we indicate which page we want to process
|
||||
if (wfi.getSubmitter() == null) {
|
||||
request.setAttribute("page", SUBMITTER_IS_DELETED_PAGE);
|
||||
} else {
|
||||
request.setAttribute("page", REJECT_PAGE);
|
||||
}
|
||||
// We have pressed reject item, so take the user to a page where they can reject
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_PAGE);
|
||||
} else if (request.getParameter(SUBMIT_DECLINE_TASK) != null) {
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, OUTCOME_REJECT);
|
||||
|
||||
} else {
|
||||
//We pressed the leave button so return to our submissions page
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_SUBMISSION_PAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private void addApprovedProvenance(Context c, XmlWorkflowItem wfi) throws SQLException, AuthorizeException {
|
||||
//Add the provenance for the accept
|
||||
String now = DCDate.getCurrent().toString();
|
||||
|
||||
// Get user's name + email address
|
||||
String usersName = XmlWorkflowServiceFactory.getInstance().getXmlWorkflowService()
|
||||
.getEPersonName(c.getCurrentUser());
|
||||
|
||||
String provDescription = getProvenanceStartId() + " Approved for entry into archive by "
|
||||
+ usersName + " on " + now + " (GMT) ";
|
||||
|
||||
// Add to item as a DC field
|
||||
itemService.addMetadata(c, wfi.getItem(), MetadataSchemaEnum.DC.getName(), "description", "provenance", "en",
|
||||
provDescription);
|
||||
itemService.update(c, wfi.getItem());
|
||||
}
|
||||
|
||||
public ActionResult processRejectPage(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request)
|
||||
/**
|
||||
* Since original submitter no longer exists, workflow item is permanently deleted
|
||||
*/
|
||||
private ActionResult processDelete(Context c, XmlWorkflowItem wfi)
|
||||
throws SQLException, AuthorizeException, IOException {
|
||||
if (request.getParameter("submit_reject") != null) {
|
||||
String reason = request.getParameter("reason");
|
||||
if (reason == null || 0 == reason.trim().length()) {
|
||||
request.setAttribute("page", REJECT_PAGE);
|
||||
addErrorField(request, "reason");
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_ERROR);
|
||||
}
|
||||
|
||||
//We have pressed reject, so remove the task the user has & put it back to a workspace item
|
||||
XmlWorkflowServiceFactory.getInstance().getXmlWorkflowService()
|
||||
.sendWorkflowItemBackSubmission(c, wfi, c.getCurrentUser(),
|
||||
this.getProvenanceStartId(), reason);
|
||||
|
||||
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_SUBMISSION_PAGE);
|
||||
} else {
|
||||
//Cancel, go back to the main task page
|
||||
request.setAttribute("page", MAIN_PAGE);
|
||||
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_PAGE);
|
||||
}
|
||||
EPerson user = c.getCurrentUser();
|
||||
c.turnOffAuthorisationSystem();
|
||||
WorkspaceItem workspaceItem = XmlWorkflowServiceFactory.getInstance().getXmlWorkflowService()
|
||||
.abort(c, wfi, user);
|
||||
ContentServiceFactory.getInstance().getWorkspaceItemService().deleteAll(c, workspaceItem);
|
||||
c.restoreAuthSystemState();
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_SUBMISSION_PAGE);
|
||||
}
|
||||
|
||||
public ActionResult processSubmitterIsDeletedPage(Context c, XmlWorkflowItem wfi, HttpServletRequest request)
|
||||
throws SQLException, AuthorizeException, IOException {
|
||||
if (request.getParameter("submit_delete") != null) {
|
||||
XmlWorkflowServiceFactory.getInstance().getXmlWorkflowService()
|
||||
.deleteWorkflowByWorkflowItem(c, wfi, c.getCurrentUser());
|
||||
// Delete and send user back to myDspace page
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_SUBMISSION_PAGE);
|
||||
} else if (request.getParameter("submit_keep_it") != null) {
|
||||
// Do nothing, just send it back to myDspace page
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_SUBMISSION_PAGE);
|
||||
} else {
|
||||
//Cancel, go back to the main task page
|
||||
request.setAttribute("page", MAIN_PAGE);
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_PAGE);
|
||||
}
|
||||
/**
|
||||
* Selected reviewer declines to review task, then the workflow is aborted and restarted
|
||||
*/
|
||||
private ActionResult processDecline(Context c, XmlWorkflowItem wfi)
|
||||
throws SQLException, IOException, AuthorizeException, WorkflowException {
|
||||
c.turnOffAuthorisationSystem();
|
||||
xmlWorkflowService.restartWorkflow(c, wfi, c.getCurrentUser(), this.getProvenanceStartId());
|
||||
c.restoreAuthSystemState();
|
||||
return new ActionResult(ActionResult.TYPE.TYPE_SUBMISSION_PAGE);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -80,6 +80,10 @@ public class AutoAssignAction extends UserSelectionAction {
|
||||
}
|
||||
//Delete our workflow item role since the users have been assigned
|
||||
workflowItemRoleService.delete(c, workflowItemRole);
|
||||
if (role.isDeleteTemporaryGroup() && workflowItemRole.getGroup() != null) {
|
||||
// Delete temporary groups created after members have workflow task assigned
|
||||
groupService.delete(c, workflowItemRole.getGroup());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warn(LogHelper.getHeader(c, "Error while executing auto assign action",
|
||||
@@ -127,7 +131,7 @@ public class AutoAssignAction extends UserSelectionAction {
|
||||
protected void createTaskForEPerson(Context c, XmlWorkflowItem wfi, Step step, WorkflowActionConfig actionConfig,
|
||||
EPerson user) throws SQLException, AuthorizeException, IOException {
|
||||
if (claimedTaskService.find(c, wfi, step.getId(), actionConfig.getId()) != null) {
|
||||
workflowRequirementsService.addClaimedUser(c, wfi, step, c.getCurrentUser());
|
||||
workflowRequirementsService.addClaimedUser(c, wfi, step, user);
|
||||
XmlWorkflowServiceFactory.getInstance().getXmlWorkflowService()
|
||||
.createOwnedTask(c, wfi, step, actionConfig, user);
|
||||
}
|
||||
|
@@ -138,6 +138,10 @@ public class ClaimAction extends UserSelectionAction {
|
||||
RoleMembers roleMembers = role.getMembers(context, wfi);
|
||||
|
||||
ArrayList<EPerson> epersons = roleMembers.getAllUniqueMembers(context);
|
||||
if (epersons.isEmpty() || step.getRequiredUsers() > epersons.size()) {
|
||||
log.warn(String.format("There must be at least %s ePerson(s) in the group",
|
||||
step.getRequiredUsers()));
|
||||
}
|
||||
return !(epersons.isEmpty() || step.getRequiredUsers() > epersons.size());
|
||||
} else {
|
||||
// We don't have a role and do have a UI so throw a workflow exception
|
||||
|
@@ -15,9 +15,12 @@
|
||||
<bean id="selectrevieweractionAPI" class="org.dspace.xmlworkflow.state.actions.processingaction.SelectReviewerAction" scope="prototype">
|
||||
<property name="role" ref="scoreassignedreviewer"/>
|
||||
</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">
|
||||
<property name="descriptionRequired" value="true"/>
|
||||
<property name="maxValue" value="5"/>
|
||||
</bean>
|
||||
<bean id="evaluationactionAPI" class="org.dspace.xmlworkflow.state.actions.processingaction.ScoreEvaluationAction" scope="prototype">
|
||||
<property name="minimumAcceptanceScore" value="50" />
|
||||
<property name="minimumAcceptanceScore" value="3" />
|
||||
</bean>
|
||||
|
||||
|
||||
@@ -63,6 +66,12 @@
|
||||
<property name="requiresUI" value="true"/>
|
||||
</bean>
|
||||
|
||||
<bean id="ratingreviewaction" class="org.dspace.xmlworkflow.state.actions.WorkflowActionConfig" scope="prototype">
|
||||
<constructor-arg type="java.lang.String" value="ratingreviewaction"/>
|
||||
<property name="processingAction" ref="ratingreviewactionAPI" />
|
||||
<property name="requiresUI" value="true"/>
|
||||
</bean>
|
||||
|
||||
<!--Autmatic step that evaluates scores (workflow.score) and checks if they match the configured minimum for archiving -->
|
||||
<bean id="evaluationaction" class="org.dspace.xmlworkflow.state.actions.WorkflowActionConfig" scope="prototype">
|
||||
<constructor-arg type="java.lang.String" value="evaluationaction"/>
|
||||
|
@@ -153,6 +153,7 @@
|
||||
<bean id="scoreassignedreviewer" class="org.dspace.xmlworkflow.Role">
|
||||
<property name="scope" value="#{ T(org.dspace.xmlworkflow.Role.Scope).ITEM}"/>
|
||||
<property name="name" value="Reviewer"/>
|
||||
<property name="deleteTemporaryGroup" value="true"/>
|
||||
</bean>
|
||||
|
||||
|
||||
|
@@ -9,11 +9,13 @@ package org.dspace.xmlworkflow;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.dspace.AbstractIntegrationTestWithDatabase;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.authorize.ResourcePolicy;
|
||||
import org.dspace.authorize.factory.AuthorizeServiceFactory;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
@@ -21,17 +23,24 @@ import org.dspace.builder.ClaimedTaskBuilder;
|
||||
import org.dspace.builder.CollectionBuilder;
|
||||
import org.dspace.builder.CommunityBuilder;
|
||||
import org.dspace.builder.EPersonBuilder;
|
||||
import org.dspace.builder.GroupBuilder;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.discovery.IndexingService;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.eperson.Group;
|
||||
import org.dspace.eperson.factory.EPersonServiceFactory;
|
||||
import org.dspace.eperson.service.GroupService;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory;
|
||||
import org.dspace.xmlworkflow.service.XmlWorkflowService;
|
||||
import org.dspace.xmlworkflow.state.Workflow;
|
||||
import org.dspace.xmlworkflow.state.actions.processingaction.SelectReviewerAction;
|
||||
import org.dspace.xmlworkflow.storedcomponents.ClaimedTask;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
|
||||
@@ -47,6 +56,22 @@ public class XmlWorkflowServiceIT extends AbstractIntegrationTestWithDatabase {
|
||||
.getServiceByName(IndexingService.class.getName(),
|
||||
IndexingService.class);
|
||||
protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService();
|
||||
protected GroupService groupService = EPersonServiceFactory.getInstance().getGroupService();
|
||||
protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
|
||||
|
||||
/**
|
||||
* Cleans up the created workflow role groups after each test
|
||||
* @throws SQLException
|
||||
* @throws AuthorizeException
|
||||
* @throws IOException
|
||||
*/
|
||||
@After
|
||||
public void cleanup() throws SQLException, AuthorizeException, IOException {
|
||||
Group reviewManagers = groupService.findByName(context, "ReviewManagers");
|
||||
if (reviewManagers != null) {
|
||||
groupService.delete(context, reviewManagers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to verify that if a user submits an item into the workflow, then it gets rejected that the submitter gets
|
||||
@@ -85,6 +110,93 @@ public class XmlWorkflowServiceIT extends AbstractIntegrationTestWithDatabase {
|
||||
assertTrue(this.containsRPForUser(taskToReject.getWorkflowItem().getItem(), submitter, Constants.WRITE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to verify that if a user submits an item into the workflow, a reviewmanager can select a single reviewer
|
||||
* eperson
|
||||
*/
|
||||
@Test
|
||||
public void workflowUserSingleSelectedReviewer_ItemShouldBeEditable() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
EPerson submitter = EPersonBuilder.createEPerson(context).withEmail("submitter@example.org").build();
|
||||
context.setCurrentUser(submitter);
|
||||
EPerson reviewManager =
|
||||
EPersonBuilder.createEPerson(context).withEmail("reviewmanager-test@example.org").build();
|
||||
Community community = CommunityBuilder.createCommunity(context)
|
||||
.withName("Parent Community")
|
||||
.build();
|
||||
Collection colWithWorkflow = CollectionBuilder.createCollection(context, community, "123456789/workflow-test-1")
|
||||
.withName("Collection WITH workflow")
|
||||
.withWorkflowGroup("reviewmanagers", reviewManager)
|
||||
.build();
|
||||
Workflow workflow = XmlWorkflowServiceFactory.getInstance().getWorkflowFactory().getWorkflow(colWithWorkflow);
|
||||
ClaimedTask task = ClaimedTaskBuilder.createClaimedTask(context, colWithWorkflow, reviewManager)
|
||||
.withTitle("Test workflow item to reject").build();
|
||||
// Set reviewer group property and add reviewer to group
|
||||
SelectReviewerAction.resetGroup();
|
||||
configurationService.setProperty("action.selectrevieweraction.group", "Reviewers");
|
||||
Group reviewerGroup = GroupBuilder.createGroup(context).withName("Reviewers").build();
|
||||
EPerson reviewer = EPersonBuilder.createEPerson(context).withEmail("reviewer@example.org").build();
|
||||
groupService.addMember(context, reviewerGroup, reviewer);
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
// Review Manager should have access to workflow item
|
||||
assertTrue(this.containsRPForUser(task.getWorkflowItem().getItem(), reviewManager, Constants.WRITE));
|
||||
|
||||
// select 1 reviewer
|
||||
MockHttpServletRequest httpSelectReviewerRequest = new MockHttpServletRequest();
|
||||
httpSelectReviewerRequest.setParameter("submit_select_reviewer", "true");
|
||||
httpSelectReviewerRequest.setParameter("eperson", reviewer.getID().toString());
|
||||
executeWorkflowAction(httpSelectReviewerRequest, workflow, task);
|
||||
|
||||
// Reviewer should have access to workflow item
|
||||
assertTrue(this.containsRPForUser(task.getWorkflowItem().getItem(), reviewer, Constants.WRITE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to verify that if a user submits an item into the workflow, a reviewmanager can select a multiple reviewer
|
||||
* epersons
|
||||
*/
|
||||
@Test
|
||||
public void workflowUserMultipleSelectedReviewer_ItemShouldBeEditable() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
EPerson submitter = EPersonBuilder.createEPerson(context).withEmail("submitter@example.org").build();
|
||||
context.setCurrentUser(submitter);
|
||||
EPerson reviewManager =
|
||||
EPersonBuilder.createEPerson(context).withEmail("reviewmanager-test@example.org").build();
|
||||
Community community = CommunityBuilder.createCommunity(context)
|
||||
.withName("Parent Community")
|
||||
.build();
|
||||
Collection colWithWorkflow = CollectionBuilder.createCollection(context, community, "123456789/workflow-test-1")
|
||||
.withName("Collection WITH workflow")
|
||||
.withWorkflowGroup("reviewmanagers", reviewManager)
|
||||
.build();
|
||||
Workflow workflow = XmlWorkflowServiceFactory.getInstance().getWorkflowFactory().getWorkflow(colWithWorkflow);
|
||||
ClaimedTask task = ClaimedTaskBuilder.createClaimedTask(context, colWithWorkflow, reviewManager)
|
||||
.withTitle("Test workflow item to reject").build();
|
||||
// Set reviewer group property and add reviewer to group
|
||||
SelectReviewerAction.resetGroup();
|
||||
configurationService.setProperty("action.selectrevieweraction.group", "Reviewers");
|
||||
Group reviewerGroup = GroupBuilder.createGroup(context).withName("Reviewers").build();
|
||||
EPerson reviewer1 = EPersonBuilder.createEPerson(context).withEmail("reviewer1@example.org").build();
|
||||
EPerson reviewer2 = EPersonBuilder.createEPerson(context).withEmail("reviewer2@example.org").build();
|
||||
groupService.addMember(context, reviewerGroup, reviewer1);
|
||||
groupService.addMember(context, reviewerGroup, reviewer2);
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
// Review Manager should have access to workflow item
|
||||
assertTrue(this.containsRPForUser(task.getWorkflowItem().getItem(), reviewManager, Constants.WRITE));
|
||||
|
||||
// Select multiple reviewers
|
||||
MockHttpServletRequest httpSelectMultipleReviewers = new MockHttpServletRequest();
|
||||
httpSelectMultipleReviewers.setParameter("submit_select_reviewer", "true");
|
||||
httpSelectMultipleReviewers.setParameter("eperson", reviewer1.getID().toString(), reviewer2.getID().toString());
|
||||
executeWorkflowAction(httpSelectMultipleReviewers, workflow, task);
|
||||
|
||||
// Reviewers should have access to workflow item
|
||||
assertTrue(this.containsRPForUser(task.getWorkflowItem().getItem(), reviewer1, Constants.WRITE));
|
||||
assertTrue(this.containsRPForUser(task.getWorkflowItem().getItem(), reviewer2, Constants.WRITE));
|
||||
}
|
||||
|
||||
private boolean containsRPForUser(Item item, EPerson user, int action) throws SQLException {
|
||||
List<ResourcePolicy> rps = authorizeService.getPolicies(context, item);
|
||||
for (ResourcePolicy rp : rps) {
|
||||
|
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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.app.rest.converter;
|
||||
|
||||
import org.dspace.app.rest.model.ScoreReviewActionAdvancedInfoRest;
|
||||
import org.dspace.app.rest.projection.Projection;
|
||||
import org.dspace.xmlworkflow.state.actions.processingaction.ScoreReviewActionAdvancedInfo;
|
||||
|
||||
/**
|
||||
* This converter is responsible for transforming the model representation of a ScoreReviewActionAdvancedInfo to
|
||||
* the REST representation of a ScoreReviewActionAdvancedInfo
|
||||
*/
|
||||
public class ScoreReviewActionAdvancedInfoConverter
|
||||
implements DSpaceConverter<ScoreReviewActionAdvancedInfo, ScoreReviewActionAdvancedInfoRest> {
|
||||
|
||||
@Override
|
||||
public ScoreReviewActionAdvancedInfoRest convert(ScoreReviewActionAdvancedInfo modelObject,
|
||||
Projection projection) {
|
||||
ScoreReviewActionAdvancedInfoRest restModel = new ScoreReviewActionAdvancedInfoRest();
|
||||
restModel.setDescriptionRequired(modelObject.isDescriptionRequired());
|
||||
restModel.setMaxValue(modelObject.getMaxValue());
|
||||
restModel.setType(modelObject.getType());
|
||||
restModel.setId(modelObject.getId());
|
||||
return restModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<ScoreReviewActionAdvancedInfo> getModelClass() {
|
||||
return ScoreReviewActionAdvancedInfo.class;
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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.app.rest.converter;
|
||||
|
||||
import org.dspace.app.rest.model.SelectReviewerActionAdvancedInfoRest;
|
||||
import org.dspace.app.rest.projection.Projection;
|
||||
import org.dspace.xmlworkflow.state.actions.processingaction.SelectReviewerActionAdvancedInfo;
|
||||
|
||||
/**
|
||||
* This converter is responsible for transforming the model representation of a SelectReviewerActionAdvancedInfo to
|
||||
* the REST representation of a SelectReviewerActionAdvancedInfo
|
||||
*/
|
||||
public class SelectReviewerActionAdvancedInfoConverter
|
||||
implements DSpaceConverter<SelectReviewerActionAdvancedInfo, SelectReviewerActionAdvancedInfoRest> {
|
||||
|
||||
@Override
|
||||
public SelectReviewerActionAdvancedInfoRest convert(SelectReviewerActionAdvancedInfo modelObject,
|
||||
Projection projection) {
|
||||
SelectReviewerActionAdvancedInfoRest restModel = new SelectReviewerActionAdvancedInfoRest();
|
||||
restModel.setGroup(modelObject.getGroup());
|
||||
restModel.setType(modelObject.getType());
|
||||
restModel.setId(modelObject.getId());
|
||||
return restModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<SelectReviewerActionAdvancedInfo> getModelClass() {
|
||||
return SelectReviewerActionAdvancedInfo.class;
|
||||
}
|
||||
}
|
@@ -26,6 +26,10 @@ public class WorkflowActionConverter implements DSpaceConverter<WorkflowActionCo
|
||||
restModel.setProjection(projection);
|
||||
restModel.setId(modelObject.getId());
|
||||
restModel.setOptions(modelObject.getOptions());
|
||||
if (modelObject.isAdvanced()) {
|
||||
restModel.setAdvancedOptions(modelObject.getAdvancedOptions());
|
||||
restModel.setAdvancedInfo(modelObject.getAdvancedInfo());
|
||||
}
|
||||
return restModel;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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.app.rest.model;
|
||||
|
||||
import org.dspace.xmlworkflow.state.actions.ActionAdvancedInfo;
|
||||
|
||||
/**
|
||||
* Abstract class for {@link ActionAdvancedInfo}
|
||||
*
|
||||
* @author Marie Verdonck (Atmire) on 03/02/23
|
||||
*/
|
||||
public abstract class AdvancedInfoRest {
|
||||
|
||||
String id;
|
||||
String type;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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.app.rest.model;
|
||||
|
||||
/**
|
||||
* The ScoreReviewActionAdvancedInfo REST Resource,
|
||||
* see {@link org.dspace.xmlworkflow.state.actions.processingaction.ScoreReviewActionAdvancedInfo}
|
||||
*/
|
||||
public class ScoreReviewActionAdvancedInfoRest extends AdvancedInfoRest {
|
||||
|
||||
private boolean descriptionRequired;
|
||||
private int maxValue;
|
||||
|
||||
public boolean isDescriptionRequired() {
|
||||
return descriptionRequired;
|
||||
}
|
||||
|
||||
public void setDescriptionRequired(boolean descriptionRequired) {
|
||||
this.descriptionRequired = descriptionRequired;
|
||||
}
|
||||
|
||||
public int getMaxValue() {
|
||||
return maxValue;
|
||||
}
|
||||
|
||||
public void setMaxValue(int maxValue) {
|
||||
this.maxValue = maxValue;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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.app.rest.model;
|
||||
|
||||
/**
|
||||
* The SelectReviewerActionAdvancedInfoRest REST Resource,
|
||||
* see {@link org.dspace.xmlworkflow.state.actions.processingaction.SelectReviewerActionAdvancedInfo}
|
||||
*/
|
||||
public class SelectReviewerActionAdvancedInfoRest extends AdvancedInfoRest {
|
||||
|
||||
private String groupId;
|
||||
|
||||
public String getGroup() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public void setGroup(String groupId) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
}
|
@@ -9,7 +9,10 @@ package org.dspace.app.rest.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.dspace.app.rest.RestResourceController;
|
||||
import org.dspace.xmlworkflow.state.actions.ActionAdvancedInfo;
|
||||
|
||||
/**
|
||||
* The rest resource used for workflow actions
|
||||
@@ -23,6 +26,8 @@ public class WorkflowActionRest extends BaseObjectRest<String> {
|
||||
public static final String NAME_PLURAL = "workflowactions";
|
||||
|
||||
private List<String> options;
|
||||
private List<String> advancedOptions;
|
||||
private List<ActionAdvancedInfo> advancedInfo;
|
||||
|
||||
@Override
|
||||
public String getCategory() {
|
||||
@@ -39,21 +44,33 @@ public class WorkflowActionRest extends BaseObjectRest<String> {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic getter for the options
|
||||
*
|
||||
* @return the options value of this WorkflowActionRest
|
||||
*/
|
||||
public List<String> getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic setter for the options
|
||||
*
|
||||
* @param options The options to be set on this WorkflowActionRest
|
||||
*/
|
||||
public void setOptions(List<String> options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public List<String> getAdvancedOptions() {
|
||||
return advancedOptions;
|
||||
}
|
||||
|
||||
public void setAdvancedOptions(List<String> advancedOptions) {
|
||||
this.advancedOptions = advancedOptions;
|
||||
}
|
||||
|
||||
public boolean getAdvanced() {
|
||||
return CollectionUtils.isNotEmpty(getAdvancedOptions());
|
||||
}
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public List<ActionAdvancedInfo> getAdvancedInfo() {
|
||||
return advancedInfo;
|
||||
}
|
||||
|
||||
public void setAdvancedInfo(List<ActionAdvancedInfo> advancedInfo) {
|
||||
this.advancedInfo = advancedInfo;
|
||||
}
|
||||
}
|
||||
|
@@ -44,6 +44,7 @@ import org.dspace.builder.ClaimedTaskBuilder;
|
||||
import org.dspace.builder.CollectionBuilder;
|
||||
import org.dspace.builder.CommunityBuilder;
|
||||
import org.dspace.builder.EPersonBuilder;
|
||||
import org.dspace.builder.GroupBuilder;
|
||||
import org.dspace.builder.ItemBuilder;
|
||||
import org.dspace.builder.PoolTaskBuilder;
|
||||
import org.dspace.builder.WorkflowItemBuilder;
|
||||
@@ -52,11 +53,11 @@ import org.dspace.content.Community;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.eperson.Group;
|
||||
import org.dspace.eperson.factory.EPersonServiceFactory;
|
||||
import org.dspace.eperson.service.GroupService;
|
||||
import org.dspace.xmlworkflow.factory.XmlWorkflowFactory;
|
||||
import org.dspace.xmlworkflow.state.Step;
|
||||
import org.dspace.xmlworkflow.state.actions.WorkflowActionConfig;
|
||||
import org.dspace.xmlworkflow.state.actions.processingaction.SelectReviewerAction;
|
||||
import org.dspace.xmlworkflow.storedcomponents.ClaimedTask;
|
||||
import org.dspace.xmlworkflow.storedcomponents.PoolTask;
|
||||
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
|
||||
@@ -78,6 +79,9 @@ public class TaskRestRepositoriesIT extends AbstractControllerIntegrationTest {
|
||||
@Autowired
|
||||
private XmlWorkflowFactory xmlWorkflowFactory;
|
||||
|
||||
@Autowired
|
||||
GroupService groupService;
|
||||
|
||||
@Test
|
||||
/**
|
||||
* Retrieve a specific pooltask
|
||||
@@ -4174,9 +4178,6 @@ public class TaskRestRepositoriesIT extends AbstractControllerIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void addReviewerToRunningWorkflowTest() throws Exception {
|
||||
|
||||
GroupService groupService = EPersonServiceFactory.getInstance().getGroupService();
|
||||
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
EPerson reviewer1 = EPersonBuilder.createEPerson(context)
|
||||
@@ -4329,4 +4330,384 @@ public class TaskRestRepositoriesIT extends AbstractControllerIntegrationTest {
|
||||
.andDo(result -> idRef.set(read(result.getResponse().getContentAsString(), "$.id")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the run of the selectSingleReviewer workflow
|
||||
* - Creates ‘ReviewManagers’ and ‘Reviewers’, each with some members
|
||||
* - Creates a normal user, not member of either group, this user is set on context
|
||||
* - Tests selecting a single reviewer, multiple reviewers and selecting a non-reviewer
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void selectReviewerWorkflowTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
// Create normal user, not member of "ReviewManagers" or "Reviewers" and set as current user
|
||||
EPerson user = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("user@example.com")
|
||||
.withPassword(password).build();
|
||||
context.setCurrentUser(user);
|
||||
|
||||
// Create creator as this user is member of "ReviewManagers" for this item
|
||||
EPerson creator = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("creator@example.com")
|
||||
.withPassword(password).build();
|
||||
|
||||
// Create with some members to be added to "ReviewManagers"
|
||||
EPerson reviewManager1 = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("reviewManager1@example.com")
|
||||
.withPassword(password).build();
|
||||
|
||||
EPerson reviewManager2 = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("reviewManager2@example.com")
|
||||
.withPassword(password).build();
|
||||
|
||||
EPerson reviewManager3 = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("reviewManager3@example.com")
|
||||
.withPassword(password).build();
|
||||
|
||||
// The "selectSingleReviewer" requires the "ReviewManagers" repository group to be present with at least 1
|
||||
// member
|
||||
GroupBuilder.createGroup(context)
|
||||
.withName("ReviewManagers")
|
||||
.addMember(reviewManager1)
|
||||
.addMember(reviewManager2)
|
||||
.addMember(reviewManager3)
|
||||
.build();
|
||||
|
||||
// Create "Reviewers" with some members
|
||||
Group reviewerGroup = GroupBuilder.createGroup(context).withName("Reviewers").build();
|
||||
|
||||
EPerson reviewer1 = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("reviewer1@example.com")
|
||||
.withPassword(password)
|
||||
.withGroupMembership(reviewerGroup).build();
|
||||
|
||||
EPerson reviewer2 = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("reviewer2@example.com")
|
||||
.withPassword(password)
|
||||
.withGroupMembership(reviewerGroup).build();
|
||||
|
||||
EPerson reviewer3 = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("reviewer3@example.com")
|
||||
.withPassword(password)
|
||||
.withGroupMembership(reviewerGroup).build();
|
||||
|
||||
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||
.withName("Parent Community").build();
|
||||
|
||||
// Create collection with handle "123456789/workflow-test-1" to use "selectSingleReviewer"
|
||||
Collection collection =
|
||||
CollectionBuilder.createCollection(context, parentCommunity, "123456789/workflow-test-1")
|
||||
.withName("Collection 1")
|
||||
.build();
|
||||
|
||||
// Create 3 pool tasks
|
||||
// First one for selecting a single reviewer
|
||||
PoolTask poolTask1 = PoolTaskBuilder.createPoolTask(context, collection, reviewManager1)
|
||||
.withTitle("Workflow Item 1")
|
||||
.withIssueDate("2017-10-17")
|
||||
.withAuthor("Smith, Donald")
|
||||
.withAuthor("Doe, John")
|
||||
.withSubject("ExtraEntry").build();
|
||||
XmlWorkflowItem witem1 = poolTask1.getWorkflowItem();
|
||||
// Second one for selecting multiple reviewers
|
||||
PoolTask poolTask2 = PoolTaskBuilder.createPoolTask(context, collection, reviewManager2)
|
||||
.withTitle("Workflow Item 2")
|
||||
.withIssueDate("2017-10-17")
|
||||
.withAuthor("Smith, Donald")
|
||||
.withAuthor("Doe, John")
|
||||
.withSubject("ExtraEntry").build();
|
||||
XmlWorkflowItem witem2 = poolTask2.getWorkflowItem();
|
||||
// Third one for trying to add user not in "Reviewers" group
|
||||
PoolTask poolTask3 = PoolTaskBuilder.createPoolTask(context, collection, reviewManager3)
|
||||
.withTitle("Workflow Item 3")
|
||||
.withIssueDate("2017-10-17")
|
||||
.withAuthor("Smith, Donald")
|
||||
.withAuthor("Doe, John")
|
||||
.withSubject("ExtraEntry").build();
|
||||
XmlWorkflowItem witem3 = poolTask3.getWorkflowItem();
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
String reviewManager1Token = getAuthToken(reviewManager1.getEmail(), password);
|
||||
String reviewManager2Token = getAuthToken(reviewManager2.getEmail(), password);
|
||||
String reviewManager3Token = getAuthToken(reviewManager3.getEmail(), password);
|
||||
|
||||
String reviewer1Token = getAuthToken(reviewer1.getEmail(), password);
|
||||
String reviewer2Token = getAuthToken(reviewer2.getEmail(), password);
|
||||
String reviewer3Token = getAuthToken(reviewer3.getEmail(), password);
|
||||
|
||||
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||
String userToken = getAuthToken(user.getEmail(), password);
|
||||
|
||||
AtomicReference<Integer> idRef = new AtomicReference<>();
|
||||
|
||||
// Verify as member of "ReviewManagers" you can find these pool tasks
|
||||
getClient(reviewManager1Token).perform(get("/api/workflow/pooltasks/" + poolTask1.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(PoolTaskMatcher.matchPoolTask(poolTask1, "selectReviewerStep"))));
|
||||
|
||||
// Verify as member of "Reviewers" you can not find these pool tasks
|
||||
getClient(reviewer1Token).perform(get("/api/workflow/pooltasks/" + poolTask1.getID()))
|
||||
.andExpect(status().isForbidden());
|
||||
|
||||
// Verify as member of "ReviewManagers" you can claim in this tasks
|
||||
getClient(reviewManager1Token).perform(post("/api/workflow/claimedtasks")
|
||||
.contentType(
|
||||
MediaType.parseMediaType(RestMediaTypes.TEXT_URI_LIST_VALUE))
|
||||
.content("/api/workflow/pooltasks/" + poolTask1.getID()))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$", Matchers.allOf(hasJsonPath("$.type", is("claimedtask")))))
|
||||
.andDo(result -> idRef.set(read(result.getResponse().getContentAsString(), "$.id")));
|
||||
|
||||
// Verify that pool task 1 no longer exists
|
||||
getClient(reviewManager1Token).perform(get("/api/workflow/pooltasks/" + poolTask1.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
// Verify items now in claimed tasks /api/workflow/claimedtasks for user reviewManager1
|
||||
getClient(reviewManager1Token).perform(get("/api/workflow/claimedtasks/search/findByUser")
|
||||
.param("uuid", reviewManager1.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._embedded.claimedtasks", Matchers.contains(
|
||||
Matchers.allOf(
|
||||
hasJsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks/")),
|
||||
hasJsonPath("$.type", Matchers.is("claimedtask")),
|
||||
hasJsonPath("$._embedded.owner",
|
||||
Matchers.is(EPersonMatcher.matchEPersonOnEmail(reviewManager1.getEmail()))),
|
||||
hasJsonPath("$._embedded.action.id", Matchers.is("selectrevieweraction")),
|
||||
hasJsonPath("$._embedded.workflowitem",
|
||||
Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(
|
||||
witem1, "Workflow Item 1", "2017-10-17", "ExtraEntry")))
|
||||
))))
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
|
||||
.andExpect(jsonPath("$.page.size", is(20)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(1)));
|
||||
|
||||
// Verify items now not in claimed tasks /api/workflow/claimedtasks for user reviewer1
|
||||
getClient(reviewer1Token).perform(get("/api/workflow/claimedtasks/search/findByUser")
|
||||
.param("uuid", reviewer1.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
|
||||
.andExpect(jsonPath("$.page.size", is(20)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||
|
||||
// Test for single reviewer
|
||||
SelectReviewerAction.resetGroup();
|
||||
// Select reviewer1 as a reviewer, wf step 1
|
||||
getClient(reviewManager1Token).perform(post("/api/workflow/claimedtasks/" + idRef.get())
|
||||
.param("submit_select_reviewer", "true")
|
||||
.param("eperson", reviewer1.getID().toString())
|
||||
.contentType(MediaType.APPLICATION_FORM_URLENCODED))
|
||||
.andExpect(status().isNoContent());
|
||||
|
||||
// Verify reviewer1 has the claimed task
|
||||
getClient(reviewer1Token).perform(get("/api/workflow/claimedtasks/search/findByUser")
|
||||
.param("uuid", reviewer1.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._embedded.claimedtasks", Matchers.contains(
|
||||
Matchers.allOf(
|
||||
hasJsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks/")),
|
||||
hasJsonPath("$.type", Matchers.is("claimedtask")),
|
||||
hasJsonPath("$._embedded.owner",
|
||||
Matchers.is(EPersonMatcher.matchEPersonOnEmail(reviewer1.getEmail()))),
|
||||
hasJsonPath("$._embedded.action.id", Matchers.is("singleuserreviewaction")),
|
||||
hasJsonPath("$._embedded.workflowitem",
|
||||
Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(
|
||||
witem1, "Workflow Item 1", "2017-10-17", "ExtraEntry")))
|
||||
))))
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
|
||||
.andExpect(jsonPath("$.page.size", is(20)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(1)));
|
||||
|
||||
// Verify other members of "Reviewers" don't have this task claimed
|
||||
getClient(reviewer2Token).perform(get("/api/workflow/claimedtasks/search/findByUser")
|
||||
.param("uuid", reviewer2.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
|
||||
.andExpect(jsonPath("$.page.size", is(20)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||
getClient(reviewer3Token).perform(get("/api/workflow/claimedtasks/search/findByUser")
|
||||
.param("uuid", reviewer3.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
|
||||
.andExpect(jsonPath("$.page.size", is(20)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||
// Verify members of "ReviewManagers" don't have this task claimed
|
||||
getClient(reviewManager1Token).perform(get("/api/workflow/claimedtasks/search/findByUser")
|
||||
.param("uuid", reviewManager1.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
|
||||
.andExpect(jsonPath("$.page.size", is(20)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||
getClient(reviewManager2Token).perform(get("/api/workflow/claimedtasks/search/findByUser")
|
||||
.param("uuid", reviewManager2.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
|
||||
.andExpect(jsonPath("$.page.size", is(20)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||
getClient(reviewManager3Token).perform(get("/api/workflow/claimedtasks/search/findByUser")
|
||||
.param("uuid", reviewManager3.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
|
||||
.andExpect(jsonPath("$.page.size", is(20)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||
// Verify other users don't have this task claimed
|
||||
getClient(userToken).perform(get("/api/workflow/claimedtasks/search/findByUser")
|
||||
.param("uuid", user.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
|
||||
.andExpect(jsonPath("$.page.size", is(20)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||
|
||||
// Test for multiple reviewers
|
||||
|
||||
// Claim pooltask2 as member of "ReviewManagers"
|
||||
getClient(reviewManager2Token).perform(post("/api/workflow/claimedtasks")
|
||||
.contentType(
|
||||
MediaType.parseMediaType(RestMediaTypes.TEXT_URI_LIST_VALUE))
|
||||
.content("/api/workflow/pooltasks/" + poolTask2.getID()))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$", Matchers.allOf(hasJsonPath("$.type", is("claimedtask")))))
|
||||
.andDo(result -> idRef.set(read(result.getResponse().getContentAsString(), "$.id")));
|
||||
|
||||
// Select reviewer2 and reviewer3 as reviewers, wf step 1
|
||||
getClient(reviewManager2Token).perform(post("/api/workflow/claimedtasks/" + idRef.get())
|
||||
.param("submit_select_reviewer", "true")
|
||||
.param("eperson", reviewer2.getID().toString())
|
||||
.param("eperson", reviewer3.getID().toString())
|
||||
.contentType(MediaType.APPLICATION_FORM_URLENCODED))
|
||||
.andExpect(status().isNoContent());
|
||||
|
||||
// Verify reviewer2 has the claimed task
|
||||
getClient(reviewer2Token).perform(get("/api/workflow/claimedtasks/search/findByUser")
|
||||
.param("uuid", reviewer2.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._embedded.claimedtasks", Matchers.contains(
|
||||
Matchers.allOf(
|
||||
hasJsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks/")),
|
||||
hasJsonPath("$.type", Matchers.is("claimedtask")),
|
||||
hasJsonPath("$._embedded.owner",
|
||||
Matchers.is(EPersonMatcher.matchEPersonOnEmail(reviewer2.getEmail()))),
|
||||
hasJsonPath("$._embedded.action.id", Matchers.is("singleuserreviewaction")),
|
||||
hasJsonPath("$._embedded.workflowitem",
|
||||
Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(
|
||||
witem2, "Workflow Item 2", "2017-10-17", "ExtraEntry")))
|
||||
))))
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
|
||||
.andExpect(jsonPath("$.page.size", is(20)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(1)));
|
||||
|
||||
// Verify reviewer3 has the claimed task too
|
||||
getClient(reviewer3Token).perform(get("/api/workflow/claimedtasks/search/findByUser")
|
||||
.param("uuid", reviewer3.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._embedded.claimedtasks", Matchers.contains(
|
||||
Matchers.allOf(
|
||||
hasJsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks/")),
|
||||
hasJsonPath("$.type", Matchers.is("claimedtask")),
|
||||
hasJsonPath("$._embedded.owner",
|
||||
Matchers.is(EPersonMatcher.matchEPersonOnEmail(reviewer3.getEmail()))),
|
||||
hasJsonPath("$._embedded.action.id", Matchers.is("singleuserreviewaction")),
|
||||
hasJsonPath("$._embedded.workflowitem",
|
||||
Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(
|
||||
witem2, "Workflow Item 2", "2017-10-17", "ExtraEntry")))
|
||||
))))
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
|
||||
.andExpect(jsonPath("$.page.size", is(20)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(1)));
|
||||
|
||||
// Verify reviewer1 of "Reviewers" doesn't have this task claimed, only the first task
|
||||
getClient(reviewer1Token).perform(get("/api/workflow/claimedtasks/search/findByUser")
|
||||
.param("uuid", reviewer1.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._embedded.claimedtasks", Matchers.contains(
|
||||
Matchers.allOf(
|
||||
hasJsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks/")),
|
||||
hasJsonPath("$.type", Matchers.is("claimedtask")),
|
||||
hasJsonPath("$._embedded.owner",
|
||||
Matchers.is(EPersonMatcher.matchEPersonOnEmail(reviewer1.getEmail()))),
|
||||
hasJsonPath("$._embedded.action.id", Matchers.is("singleuserreviewaction")),
|
||||
hasJsonPath("$._embedded.workflowitem",
|
||||
Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(
|
||||
witem1, "Workflow Item 1", "2017-10-17", "ExtraEntry"))),
|
||||
hasJsonPath("$._embedded.workflowitem",
|
||||
Matchers.not(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(
|
||||
witem2, "Workflow Item 2", "2017-10-17", "ExtraEntry")))
|
||||
))))
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
|
||||
.andExpect(jsonPath("$.page.size", is(20)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(1)));
|
||||
// Verify members of "ReviewManagers" don't have this task claimed
|
||||
getClient(reviewManager1Token).perform(get("/api/workflow/claimedtasks/search/findByUser")
|
||||
.param("uuid", reviewManager1.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
|
||||
.andExpect(jsonPath("$.page.size", is(20)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||
getClient(reviewManager2Token).perform(get("/api/workflow/claimedtasks/search/findByUser")
|
||||
.param("uuid", reviewManager2.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
|
||||
.andExpect(jsonPath("$.page.size", is(20)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||
getClient(reviewManager3Token).perform(get("/api/workflow/claimedtasks/search/findByUser")
|
||||
.param("uuid", reviewManager3.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
|
||||
.andExpect(jsonPath("$.page.size", is(20)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||
// Verify other users don't have this task claimed
|
||||
getClient(userToken).perform(get("/api/workflow/claimedtasks/search/findByUser")
|
||||
.param("uuid", user.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
|
||||
.andExpect(jsonPath("$.page.size", is(20)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||
|
||||
// Test to assign non-reviewer
|
||||
|
||||
// Claim pooltask3 as member of "ReviewManagers"
|
||||
getClient(reviewManager3Token).perform(post("/api/workflow/claimedtasks")
|
||||
.contentType(
|
||||
MediaType.parseMediaType(RestMediaTypes.TEXT_URI_LIST_VALUE))
|
||||
.content("/api/workflow/pooltasks/" + poolTask3.getID()))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$", Matchers.allOf(hasJsonPath("$.type", is("claimedtask")))))
|
||||
.andDo(result -> idRef.set(read(result.getResponse().getContentAsString(), "$.id")));
|
||||
|
||||
// Select (non-reviewer) user as a reviewer, wf step 1
|
||||
getClient(reviewManager3Token).perform(post("/api/workflow/claimedtasks/" + idRef.get())
|
||||
.param("submit_select_reviewer", "true")
|
||||
.param("eperson", user.getID().toString())
|
||||
.contentType(MediaType.APPLICATION_FORM_URLENCODED))
|
||||
.andExpect(status().isNoContent());
|
||||
|
||||
// Verify user does not have this task claimed
|
||||
getClient(userToken).perform(get("/api/workflow/claimedtasks/search/findByUser")
|
||||
.param("uuid", user.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
|
||||
.andExpect(jsonPath("$.page.size", is(20)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||
|
||||
// Verify item still in claimed tasks for user reviewManager3 on step "selectrevieweraction"
|
||||
getClient(reviewManager3Token).perform(get("/api/workflow/claimedtasks/search/findByUser")
|
||||
.param("uuid", reviewManager3.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._embedded.claimedtasks", Matchers.contains(
|
||||
Matchers.allOf(
|
||||
hasJsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks/")),
|
||||
hasJsonPath("$.type", Matchers.is("claimedtask")),
|
||||
hasJsonPath("$._embedded.owner",
|
||||
Matchers.is(EPersonMatcher.matchEPersonOnEmail(reviewManager3.getEmail()))),
|
||||
hasJsonPath("$._embedded.action.id", Matchers.is("selectrevieweraction")),
|
||||
hasJsonPath("$._embedded.workflowitem",
|
||||
Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(
|
||||
witem3, "Workflow Item 3", "2017-10-17", "ExtraEntry")))
|
||||
))))
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
|
||||
.andExpect(jsonPath("$.page.size", is(20)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(1)));
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,9 @@
|
||||
*/
|
||||
package org.dspace.app.rest;
|
||||
|
||||
import static org.dspace.xmlworkflow.state.actions.processingaction.ScoreReviewAction.SUBMIT_SCORE;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
@@ -17,9 +19,18 @@ import org.dspace.app.rest.matcher.WorkflowActionMatcher;
|
||||
import org.dspace.app.rest.model.WorkflowActionRest;
|
||||
import org.dspace.app.rest.repository.WorkflowActionRestRepository;
|
||||
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||
import org.dspace.builder.GroupBuilder;
|
||||
import org.dspace.eperson.Group;
|
||||
import org.dspace.eperson.factory.EPersonServiceFactory;
|
||||
import org.dspace.eperson.service.GroupService;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
import org.dspace.xmlworkflow.factory.XmlWorkflowFactory;
|
||||
import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory;
|
||||
import org.dspace.xmlworkflow.state.actions.WorkflowActionConfig;
|
||||
import org.dspace.xmlworkflow.state.actions.processingaction.ScoreReviewActionAdvancedInfo;
|
||||
import org.dspace.xmlworkflow.state.actions.processingaction.SelectReviewerAction;
|
||||
import org.dspace.xmlworkflow.state.actions.processingaction.SelectReviewerActionAdvancedInfo;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -31,6 +42,8 @@ import org.junit.Test;
|
||||
public class WorkflowActionRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||
|
||||
private XmlWorkflowFactory xmlWorkflowFactory = XmlWorkflowServiceFactory.getInstance().getWorkflowFactory();
|
||||
private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
|
||||
private GroupService groupService = EPersonServiceFactory.getInstance().getGroupService();
|
||||
|
||||
private static final String WORKFLOW_ACTIONS_ENDPOINT
|
||||
= "/api/" + WorkflowActionRest.CATEGORY + "/" + WorkflowActionRest.NAME_PLURAL;
|
||||
@@ -82,6 +95,7 @@ public class WorkflowActionRestRepositoryIT extends AbstractControllerIntegratio
|
||||
.andExpect(status().isOk())
|
||||
// has options
|
||||
.andExpect(jsonPath("$.options", not(empty())))
|
||||
.andExpect(jsonPath("$.advanced", is(false)))
|
||||
//Matches expected corresponding rest action values
|
||||
.andExpect(jsonPath("$", Matchers.is(
|
||||
WorkflowActionMatcher.matchWorkflowActionEntry(existentWorkflow)
|
||||
@@ -99,6 +113,7 @@ public class WorkflowActionRestRepositoryIT extends AbstractControllerIntegratio
|
||||
.andExpect(status().isOk())
|
||||
// has no options
|
||||
.andExpect(jsonPath("$.options", empty()))
|
||||
.andExpect(jsonPath("$.advanced", is(false)))
|
||||
//Matches expected corresponding rest action values
|
||||
.andExpect(jsonPath("$", Matchers.is(
|
||||
WorkflowActionMatcher.matchWorkflowActionEntry(existentWorkflowNoOptions)
|
||||
@@ -125,4 +140,68 @@ public class WorkflowActionRestRepositoryIT extends AbstractControllerIntegratio
|
||||
//We expect a 401 Unauthorized
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWorkflowActionByName_ExistentWithOptions_ratingreviewaction() throws Exception {
|
||||
String token = getAuthToken(eperson.getEmail(), password);
|
||||
String nameActionWithOptions = "scorereviewaction";
|
||||
WorkflowActionConfig existentWorkflow = xmlWorkflowFactory.getActionByName(nameActionWithOptions);
|
||||
|
||||
// create ScoreReviewActionAdvancedInfo to compare with output
|
||||
ScoreReviewActionAdvancedInfo scoreReviewActionAdvancedInfo = new ScoreReviewActionAdvancedInfo();
|
||||
scoreReviewActionAdvancedInfo.setDescriptionRequired(true);
|
||||
scoreReviewActionAdvancedInfo.setMaxValue(5);
|
||||
scoreReviewActionAdvancedInfo.setType(SUBMIT_SCORE);
|
||||
scoreReviewActionAdvancedInfo.generateId(SUBMIT_SCORE);
|
||||
|
||||
//When we call this facets endpoint
|
||||
getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT + "/" + nameActionWithOptions))
|
||||
//We expect a 200 is ok status
|
||||
.andExpect(status().isOk())
|
||||
// has options
|
||||
.andExpect(jsonPath("$.options", not(empty())))
|
||||
.andExpect(jsonPath("$.advancedOptions", not(empty())))
|
||||
.andExpect(jsonPath("$.advanced", is(true)))
|
||||
.andExpect(jsonPath("$.advancedInfo", Matchers.contains(
|
||||
WorkflowActionMatcher.matchScoreReviewActionAdvancedInfo(scoreReviewActionAdvancedInfo))))
|
||||
//Matches expected corresponding rest action values
|
||||
.andExpect(jsonPath("$", Matchers.is(
|
||||
WorkflowActionMatcher.matchWorkflowActionEntry(existentWorkflow)
|
||||
)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWorkflowActionByName_ExistentWithOptions_selectrevieweraction() throws Exception {
|
||||
String token = getAuthToken(eperson.getEmail(), password);
|
||||
String nameActionWithOptions = "selectrevieweraction";
|
||||
// create reviewers group
|
||||
SelectReviewerAction.resetGroup();
|
||||
context.turnOffAuthorisationSystem();
|
||||
Group group = GroupBuilder.createGroup(context).withName("ReviewersUUIDConfig").build();
|
||||
configurationService.setProperty("action.selectrevieweraction.group", group.getID());
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
// create SelectReviewerActionAdvancedInfo to compare with output
|
||||
SelectReviewerActionAdvancedInfo selectReviewerActionAdvancedInfo = new SelectReviewerActionAdvancedInfo();
|
||||
selectReviewerActionAdvancedInfo.setGroup(group.getID().toString());
|
||||
selectReviewerActionAdvancedInfo.setType("submit_select_reviewer");
|
||||
selectReviewerActionAdvancedInfo.generateId("submit_select_reviewer");
|
||||
|
||||
WorkflowActionConfig existentWorkflow = xmlWorkflowFactory.getActionByName(nameActionWithOptions);
|
||||
//When we call this facets endpoint
|
||||
getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT + "/" + nameActionWithOptions))
|
||||
//We expect a 200 is ok status
|
||||
.andExpect(status().isOk())
|
||||
// has options
|
||||
.andExpect(jsonPath("$.options", not(empty())))
|
||||
.andExpect(jsonPath("$.advancedOptions", not(empty())))
|
||||
.andExpect(jsonPath("$.advanced", is(true)))
|
||||
.andExpect(jsonPath("$.advancedInfo", Matchers.contains(
|
||||
WorkflowActionMatcher.matchSelectReviewerActionAdvancedInfo(selectReviewerActionAdvancedInfo))))
|
||||
//Matches expected corresponding rest action values
|
||||
.andExpect(jsonPath("$", Matchers.is(
|
||||
WorkflowActionMatcher.matchWorkflowActionEntry(existentWorkflow)
|
||||
)));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -14,7 +14,10 @@ import static org.hamcrest.Matchers.is;
|
||||
|
||||
import org.dspace.app.rest.model.WorkflowActionRest;
|
||||
import org.dspace.xmlworkflow.state.actions.WorkflowActionConfig;
|
||||
import org.dspace.xmlworkflow.state.actions.processingaction.ScoreReviewActionAdvancedInfo;
|
||||
import org.dspace.xmlworkflow.state.actions.processingaction.SelectReviewerActionAdvancedInfo;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.Matchers;
|
||||
|
||||
/**
|
||||
* @author Maria Verdonck (Atmire) on 06/01/2020
|
||||
@@ -32,7 +35,35 @@ public class WorkflowActionMatcher {
|
||||
return allOf(
|
||||
hasJsonPath("$.id", is(workflowAction.getId())),
|
||||
hasJsonPath("$.options", is(workflowAction.getOptions())),
|
||||
hasJsonPath("$.advanced", is(workflowAction.isAdvanced())),
|
||||
hasJsonPath("$._links.self.href", containsString(WORKFLOW_ACTIONS_ENDPOINT + workflowAction.getId()))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Matcher to check the contents of the advancedInfo for "ratingreviewaction"
|
||||
* @param scoreReviewActionAdvancedInfo identical ScoreReviewActionAdvancedInfo object
|
||||
*/
|
||||
public static Matcher<? super Object> matchScoreReviewActionAdvancedInfo(
|
||||
ScoreReviewActionAdvancedInfo scoreReviewActionAdvancedInfo) {
|
||||
return Matchers.allOf(
|
||||
hasJsonPath("$.descriptionRequired", is(scoreReviewActionAdvancedInfo.isDescriptionRequired())),
|
||||
hasJsonPath("$.maxValue", is(scoreReviewActionAdvancedInfo.getMaxValue())),
|
||||
hasJsonPath("$.type", is(scoreReviewActionAdvancedInfo.getType())),
|
||||
hasJsonPath("$.id", is(scoreReviewActionAdvancedInfo.getId()))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Matcher to check the contents of the advancedInfo for "selectrevieweraction"
|
||||
* @param selectReviewerActionAdvancedInfo identical SelectReviewerActionAdvancedInfo object
|
||||
*/
|
||||
public static Matcher<? super Object> matchSelectReviewerActionAdvancedInfo(
|
||||
SelectReviewerActionAdvancedInfo selectReviewerActionAdvancedInfo) {
|
||||
return Matchers.allOf(
|
||||
hasJsonPath("$.group", is(selectReviewerActionAdvancedInfo.getGroup())),
|
||||
hasJsonPath("$.type", is(selectReviewerActionAdvancedInfo.getType())),
|
||||
hasJsonPath("$.id", is(selectReviewerActionAdvancedInfo.getId()))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -16,4 +16,9 @@
|
||||
workflow.reviewer.file-edit=false
|
||||
|
||||
# Notify reviewers about tasks returned to the pool
|
||||
#workflow.notify.returned.tasks = true
|
||||
#workflow.notify.returned.tasks = true
|
||||
|
||||
# Reviewer group for the select reviewer workflow (can be UUID or group name)
|
||||
# This determines the group from which reviewers can be chosen
|
||||
# If this is not set, the review manager can choose reviewers from all e-people instead of this selected group
|
||||
action.selectrevieweraction.group = Reviewers
|
||||
|
@@ -17,7 +17,13 @@
|
||||
<dc-type>
|
||||
<schema>workflow</schema>
|
||||
<element>score</element>
|
||||
<scope_note>Metadata field used for the score review</scope_note>
|
||||
<scope_note>Metadata field used for the score review rating</scope_note>
|
||||
</dc-type>
|
||||
|
||||
<dc-type>
|
||||
<schema>workflow</schema>
|
||||
<element>review</element>
|
||||
<scope_note>Metadata field used for the score review description</scope_note>
|
||||
</dc-type>
|
||||
|
||||
</dspace-dc-types>
|
||||
|
@@ -13,9 +13,12 @@
|
||||
<bean id="selectrevieweractionAPI" class="org.dspace.xmlworkflow.state.actions.processingaction.SelectReviewerAction" scope="prototype">
|
||||
<property name="role" ref="scoreassignedreviewer"/>
|
||||
</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">
|
||||
<property name="descriptionRequired" value="true"/>
|
||||
<property name="maxValue" value="5"/>
|
||||
</bean>
|
||||
<bean id="evaluationactionAPI" class="org.dspace.xmlworkflow.state.actions.processingaction.ScoreEvaluationAction" scope="prototype">
|
||||
<property name="minimumAcceptanceScore" value="50" />
|
||||
<property name="minimumAcceptanceScore" value="3" />
|
||||
</bean>
|
||||
|
||||
|
||||
@@ -61,6 +64,12 @@
|
||||
<property name="requiresUI" value="true"/>
|
||||
</bean>
|
||||
|
||||
<bean id="ratingreviewaction" class="org.dspace.xmlworkflow.state.actions.WorkflowActionConfig" scope="prototype">
|
||||
<constructor-arg type="java.lang.String" value="ratingreviewaction"/>
|
||||
<property name="processingAction" ref="ratingreviewactionAPI" />
|
||||
<property name="requiresUI" value="true"/>
|
||||
</bean>
|
||||
|
||||
<!--Autmatic step that evaluates scores (workflow.score) and checks if they match the configured minimum for archiving -->
|
||||
<bean id="evaluationaction" class="org.dspace.xmlworkflow.state.actions.WorkflowActionConfig" scope="prototype">
|
||||
<constructor-arg type="java.lang.String" value="evaluationaction"/>
|
||||
|
@@ -151,6 +151,7 @@
|
||||
<bean id="scoreassignedreviewer" class="org.dspace.xmlworkflow.Role">
|
||||
<property name="scope" value="#{ T(org.dspace.xmlworkflow.Role.Scope).ITEM}"/>
|
||||
<property name="name" value="Reviewer"/>
|
||||
<property name="deleteTemporaryGroup" value="true"/>
|
||||
</bean>
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user