Skip to content

Instantly share code, notes, and snippets.

@aschweer
Last active June 15, 2017 03:55
Show Gist options
  • Save aschweer/02be15ca535ef1e83bf9da0b769c6b11 to your computer and use it in GitHub Desktop.
Save aschweer/02be15ca535ef1e83bf9da0b769c6b11 to your computer and use it in GitHub Desktop.
Enabling collaborative review with the DSpace configurable workflow

The files in this gist show how we achieved a collaborative review workflow in DSpace 5, using the configurable workflow functionality.

It accompanies the following poster presentation: http://hdl.handle.net/10289/11110

Schweer, A., Barr, J., Congdon, D., & Symes, M. (2017). Enabling collaborative review with the DSpace configurable workflow. Presented at the The Twelfth International Conference on Open Repositories, OR2017, Brisbane, QLD, Australia, June 27-30, 2017.

Paths within the DSpace source for the files below:

  • dspace/config/workflow.xml
  • dspace/config/spring/api/workflow-actions.xml
  • dspace/config/spring/xmlui/workflow-actions-xmlui.xml
  • dspace/modules/additions/src/main/java/nz/ac/waikato/its/irr/dspace/xmlworkflow/state/actions/processingaction/AgsciteReviewActionLogic.java
  • dspace/modules/xmlui/src/main/java/nz/ac/waikato/its/irr/dspace/app/xmlui/aspect/xmlworkflow/actions/userassignment/ClaimActionScreen.java
  • dspace/modules/xmlui/src/main/java/nz/ac/waikato/its/irr/dspace/app/xmlui/aspect/xmlworkflow/actions/processingaction/AgSciteReviewActionScreen.java
  • dspace/modules/xmlui/src/main/java/nz/ac/waikato/its/irr/dspace/app/xmlui/aspect/xmlworkflow/actions/AgSciteWorkflowScreenUtils.java

The configuration files may need further work -- they are condensed to minimal examples here. There is an accompanying JavaScript file that holds the pre-set state transition notes.

All code licenced under a BSD 3-Clause licence.

Work done by the IRR team at ITS, the University of Waikato http://uow-irrs.github.io/

package nz.ac.waikato.its.irr.dspace.xmlworkflow.state.actions.processingaction;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Item;
import org.dspace.content.Metadatum;
import org.dspace.core.*;
import org.dspace.curate.Curator;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.dspace.xmlworkflow.WorkflowConfigurationException;
import org.dspace.xmlworkflow.WorkflowException;
import org.dspace.xmlworkflow.WorkflowRequirementsManager;
import org.dspace.xmlworkflow.XmlWorkflowManager;
import org.dspace.xmlworkflow.state.Step;
import org.dspace.xmlworkflow.state.actions.ActionResult;
import org.dspace.xmlworkflow.state.actions.processingaction.ProcessingAction;
import org.dspace.xmlworkflow.storedcomponents.ClaimedTask;
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
import javax.mail.MessagingException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
/**
* @author Andrea Schweer schweer@waikato.ac.nz
* for the University of Waikato's Institutional Research Repositories
*/
public class AgsciteReviewActionLogic extends ProcessingAction {
public static final String SUBMIT_REJECT = "submit_reject";
public static final String SUBMIT_CONFIRM_REJECT = "submit_confirm_reject";
public static final String SUBMIT_CANCEL_REJECT = "submit_cancel_reject";
public static final java.lang.String SUBMIT_CURATE = "submit_curate";
public static final String SUBMIT_CONFIRM_CURATE = "submit_confirm_curate";
public static final String SUBMIT_CANCEL_CURATE = "submit_cancel_curate";
public static final String SUBMIT_EDIT_NOTES = "submit_edit-notes";
public static final String SUBMIT_CONFIRM_EDIT_NOTES = "submit_confirm-edit-notes";
public static final String SUBMIT_CANCEL_EDIT_NOTES = "submit_cancel-edit-notes";
public static final String SUBMIT_PARK = "submit_park";
public static final String SUBMIT_CONFIRM_PARK = "submit_confirm-park";
public static final String SUBMIT_CANCEL_PARK = "submit_cancel-park";
public static final String SUBMIT_EDIT = "submit_edit";
public static final String SUBMIT_ACCEPT = "submit_accept";
public static final String SUBMIT_UNCLAIM = "submit_unclaim";
public static final String SUBMIT_BACK = "submit_leave";
public enum STEP {
reviewstep,
lowprioritystep,
infoneededstep,
adminquestionstep,
outofscopestep
}
private final static Logger log = Logger.getLogger(AgsciteReviewActionLogic.class);
@Override
public void activate(Context c, XmlWorkflowItem wf) throws SQLException, IOException, AuthorizeException, WorkflowException {
}
@Override
public ActionResult execute(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request) throws SQLException, AuthorizeException, IOException, WorkflowException {
String action = getSubmitAction(request);
switch (action) {
case SUBMIT_EDIT_NOTES:
return showPage(request, "notes");
case SUBMIT_CONFIRM_EDIT_NOTES:
return processConfirmEditNotes(c, wfi, request);
case SUBMIT_CANCEL_EDIT_NOTES:
return showPage(request, "main");
case SUBMIT_BACK:
return new ActionResult(ActionResult.TYPE.TYPE_CANCEL);
case SUBMIT_ACCEPT:
return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE);
case SUBMIT_REJECT:
return showPage(request, "reject");
case SUBMIT_CONFIRM_REJECT:
return processConfirmReject(c, wfi, request);
case SUBMIT_CANCEL_REJECT:
return showPage(request, "main");
case SUBMIT_PARK:
return showPage(request, "park");
case SUBMIT_CONFIRM_PARK:
return processConfirmPark(c, wfi, request);
case SUBMIT_CANCEL_PARK:
return showPage(request, "main");
case SUBMIT_UNCLAIM:
return unclaimTask(c, wfi, step.getId());
case SUBMIT_CURATE:
return showPage(request, "curate");
case SUBMIT_CONFIRM_CURATE:
return processConfirmCurate(c, wfi, request);
case SUBMIT_CANCEL_CURATE:
return showPage(request, "main");
}
// unknown action
return new ActionResult(ActionResult.TYPE.TYPE_ERROR);
}
private static String getSubmitAction(HttpServletRequest request) {
Enumeration parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
Object name = parameterNames.nextElement();
if (Objects.toString(name).startsWith("submit_")) {
return name.toString();
}
}
return "";
}
private static ActionResult unclaimTask(Context context, XmlWorkflowItem workflowItem, String stepId) throws SQLException, AuthorizeException, IOException {
try {
ClaimedTask claimedTask = ClaimedTask.findByWorkflowIdAndEPerson(context, workflowItem.getID(), context.getCurrentUser().getID());
XmlWorkflowManager.deleteClaimedTask(context, workflowItem, claimedTask);
WorkflowRequirementsManager.removeClaimedUser(context, workflowItem, context.getCurrentUser(), stepId);
log.info(LogManager.getHeader(context, "unclaim_workflow", "workflow_id=" + workflowItem.getID()));
return new ActionResult(ActionResult.TYPE.TYPE_SUBMISSION_PAGE);
} catch (WorkflowConfigurationException e) {
log.error("Caught workflow configuration exception: " + e.getMessage(), e);
return new ActionResult(ActionResult.TYPE.TYPE_ERROR);
}
}
private static boolean hasNonPrefillContent(String value, String prefill) {
return StringUtils.isNotBlank(value) && (StringUtils.isBlank(prefill) || !value.equals(prefill));
}
private static ActionResult showPage(HttpServletRequest request, String pageName) {
request.setAttribute("page", pageName);
return new ActionResult(ActionResult.TYPE.TYPE_PAGE);
}
private static void onNoteAdded(Context context, XmlWorkflowItem wfi, String noteField, String note) {
try {
Group approvers = Group.findByName(context, "Approvers");
if (approvers == null) {
log.warn("Cannot find workflow notification group (name='Approvers'), not sending notification");
return;
}
Email mail;
try {
mail = Email.getEmail(I18nUtil.getEmailFilename(context.getCurrentLocale(), "agscite_workflow_" + noteField));
} catch (IOException e) {
mail = Email.getEmail(I18nUtil.getEmailFilename(context.getCurrentLocale(), "agscite_workflow_note"));
}
if (mail == null) {
log.warn("No e-mail template found for 'agscite_workflow_" + noteField + "' / 'agscite_workflow_note', not sending notification");
return;
}
mail.addArgument(wfi.getItem().getName());
mail.addArgument(noteField);
mail.addArgument(note);
mail.addArgument(ConfigurationManager.getProperty("dspace.url") + "/submissions");
EPerson[] recipients = approvers.getMembers();
for (EPerson recipient : recipients) {
mail.addRecipient(recipient.getEmail());
}
mail.send();
} catch (SQLException | IOException | MessagingException | RuntimeException e) {
log.error("Encountered problem while trying to send e-mail about added workflow note: " +e.getMessage(), e);
}
}
private ActionResult processConfirmPark(Context c, XmlWorkflowItem wfi, HttpServletRequest request) throws SQLException, AuthorizeException {
int outcomeCode;
String newStage = request.getParameter("stage");
try {
outcomeCode = STEP.valueOf(newStage).ordinal() + 1;
} catch (IllegalArgumentException | NullPointerException e) {
// unknown stage
addErrorField(request, "stage");
request.setAttribute("page", "park");
return new ActionResult(ActionResult.TYPE.TYPE_ERROR);
}
// add note, send e-mail if needed
String note = request.getParameter("note");
String prefill = request.getParameter("prefill");
if (hasNonPrefillContent(note, prefill)) {
Item item = wfi.getItem();
item.addMetadata("workflow", "agscite", "note", "en_NZ", note);
item.update();
onNoteAdded(c, wfi, "note", note);
}
return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, outcomeCode);
}
private ActionResult processConfirmEditNotes(Context c, XmlWorkflowItem wfi, HttpServletRequest request) throws SQLException, AuthorizeException {
Item item = wfi.getItem();
Metadatum[] currentNoteMetas = item.getMetadata("workflow", "agscite", "note", Item.ANY);
List<String> newNoteValues = new ArrayList<>();
for (int i = 0; i < currentNoteMetas.length; i++) {
boolean delete = Boolean.TRUE.toString().equals(request.getParameter("delete-note-" + i));
if (!delete) {
String newNoteValue = request.getParameter("note-value-" + i);
if (StringUtils.isNotBlank(newNoteValue)) {
newNoteValues.add(newNoteValue);
}
}
}
item.clearMetadata("workflow", "agscite", "note", Item.ANY);
if (!newNoteValues.isEmpty()) {
item.addMetadata("workflow", "agscite", "note", "en_NZ", newNoteValues.toArray(new String[0]));
}
item.update();
return showPage(request, "main");
}
private ActionResult processConfirmReject(Context c, XmlWorkflowItem wfi, HttpServletRequest request) throws SQLException, IOException, AuthorizeException {
String reason = request.getParameter("reason");
if (StringUtils.isBlank(reason)) {
addErrorField(request, "reason");
request.setAttribute("page", "reject");
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
XmlWorkflowManager.sendWorkflowItemBackSubmission(c, wfi, c.getCurrentUser(), this.getProvenanceStartId(), reason);
return new ActionResult(ActionResult.TYPE.TYPE_SUBMISSION_PAGE);
}
private ActionResult processConfirmCurate(Context c, XmlWorkflowItem wfi, HttpServletRequest request) {
String taskName = request.getParameter("task");
if (StringUtils.isBlank(taskName)) {
addErrorField(request, "task");
request.setAttribute("page", "curate");
return new ActionResult(ActionResult.TYPE.TYPE_ERROR);
}
Curator curator = new Curator();
curator.addTask(taskName);
curator.setInvoked(Curator.Invoked.INTERACTIVE);
try {
curator.curate(wfi.getItem());
} catch (IOException e) {
log.error("Caught exception while trying to curate workflow item workflowitem_id=" + wfi.getID(), e);
request.setAttribute("curationError", e.getMessage());
}
request.setAttribute("curationStatus", curator.getStatus(taskName));
request.setAttribute("curationResult", curator.getResult(taskName));
return showPage(request, "curate");
}
}
package nz.ac.waikato.its.irr.dspace.app.xmlui.aspect.xmlworkflow.actions.processingaction;
import com.google.common.collect.Collections2;
import nz.ac.waikato.its.irr.dspace.app.xmlui.aspect.xmlworkflow.actions.AgSciteWorkflowScreenUtils;
import nz.ac.waikato.its.irr.dspace.xmlworkflow.state.actions.processingaction.AgsciteReviewActionLogic;
import org.apache.cocoon.environment.ObjectModelHelper;
import org.apache.cocoon.environment.Request;
import org.apache.commons.lang.StringUtils;
import org.dspace.app.xmlui.aspect.administrative.CurateForm;
import org.dspace.app.xmlui.aspect.administrative.FlowCurationUtils;
import org.dspace.app.xmlui.aspect.xmlworkflow.AbstractXMLUIAction;
import org.dspace.app.xmlui.utils.UIException;
import org.dspace.app.xmlui.wing.WingException;
import org.dspace.app.xmlui.wing.element.*;
import org.dspace.app.xmlui.wing.element.List;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Collection;
import org.dspace.content.Item;
import org.dspace.content.Metadatum;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Context;
import org.dspace.curate.Curator;
import org.dspace.xmlworkflow.state.actions.Action;
import org.dspace.xmlworkflow.storedcomponents.ClaimedTask;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.net.URLDecoder;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @author Andrea Schweer schweer@waikato.ac.nz
* for the University of Waikato's Institutional Research Repositories
*/
public class AgSciteReviewActionScreen extends AbstractXMLUIAction {
private static final DateFormat DATE_FORMAT;
static {
DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
DATE_FORMAT.setTimeZone(TimeZone.getDefault()); // use local timestamps
}
@Override
protected void addWorkflowItemInformation(Division div, Item item, Request request) throws WingException {
AgSciteWorkflowScreenUtils.addWorkflowNotes(item, div, !"notes".equals(request.getAttribute("page")));
super.addWorkflowItemInformation(div, item, request);
}
@Override
public void addBody(Body body) throws SAXException, WingException, SQLException, IOException, AuthorizeException {
Item item = workflowItem.getItem();
Collection collection = workflowItem.getCollection();
Request request = ObjectModelHelper.getRequest(objectModel);
java.util.List<ClaimedTask> userTasks = ClaimedTask.findByEperson(context, context.getCurrentUser().getID());
ClaimedTask thisTask = null;
for (ClaimedTask userTask : userTasks) {
if (userTask.getWorkflowItemID() == workflowItem.getID()) {
thisTask = userTask;
break;
}
}
if (thisTask == null) {
throw new UIException("Could not find claimed task for user id=" + context.getCurrentUser().getID()
+ " and workflow item id=" + workflowItem.getID());
}
String actionURL = contextPath + "/handle/" + collection.getHandle() + "/xmlworkflow";
Division div = body.addInteractiveDivision("perform-task", actionURL, Division.METHOD_POST, "primary workflow");
div.setHead(message("xmlui.XMLWorkflow.agsciteWorkflow." + thisTask.getStepID()));
addWorkflowItemInformation(div, item, request);
Object page = request.getAttribute("page");
if ("notes".equals(page)) {
renderEditNotesScreen(context, item, div);
} else if ("park".equals(page)) {
renderParkingScreen(context, request, div, thisTask.getStepID());
} else if ("reject".equals(page)) {
renderRejectReasonScreen(request, div);
} else if ("curate".equals(page)) {
renderCurateScreen(request, div);
} else {
addOutcomeButtons(div);
}
div.addHidden("submission-continue").setValue(knot.getId());
}
private void renderParkingScreen(Context context, Request request, Division div, String stepID) throws WingException {
List list = div.addList("park", List.TYPE_FORM);
list.setHead("Change stage / add note");
Select stageSelect = list.addItem().addSelect("stage");
stageSelect.setLabel("Next stage");
stageSelect.setHelp("Select the next workflow stage for this task. You can leave it at the current stage (pre-selected) and just add a new workflow note if you like. Regardless of the new you choose, the task will be returned to the workflow task pool.");
stageSelect.setAutofocus("autofocus");
if (Action.getErrorFields(request).contains("stage")) {
stageSelect.addError("You must select one of the stages listed.");
}
for (AgsciteReviewActionLogic.STEP step : AgsciteReviewActionLogic.STEP.values()) {
String name = step.toString();
stageSelect.addOption(name, message("xmlui.XMLWorkflow.agsciteWorkflow." + name));
}
// pre-select the stage that is in the request or otherwise pre-select the current one
String stageRequestValue = Objects.toString(request.getAttribute("stage"), null);
try {
AgsciteReviewActionLogic.STEP requestStage = AgsciteReviewActionLogic.STEP.valueOf(stageRequestValue);
stageSelect.setOptionSelected(requestStage.toString());
} catch (IllegalArgumentException | NullPointerException e) {
stageSelect.setOptionSelected(stepID);
}
String prefill = makePrefill(context);
Select reasonPresets = list.addItem().addSelect("notePreset");
reasonPresets.setLabel("Note pre-sets");
reasonPresets.addOption(true, "", "None/other - see below");
TextArea reason = list.addItem().addTextArea("note");
reason.setLabel("Note");
reason.setRequired(false);
reason.setSize(5, 50);
reason.setAutofocus("autofocus");
reason.setValue(prefill);
reason.setHelp("Optionally, type in a note for your fellow AgScite administrators.");
org.dspace.app.xmlui.wing.element.Item buttonsPara = list.addItem();
buttonsPara.addButton(AgsciteReviewActionLogic.SUBMIT_CONFIRM_PARK).setValue("Confirm");
buttonsPara.addButton(AgsciteReviewActionLogic.SUBMIT_CANCEL_PARK).setValue("Cancel");
div.addHidden("prefill").setValue(prefill);
}
private void renderEditNotesScreen(Context context, Item item, Division div) throws WingException {
List list = div.addList("edit-notes", List.TYPE_FORM);
list.setHead("Edit notes");
list.addItem("Use this screen to edit/delete individual notes, for example to correct typos or to remove notes that are no longer relevant. When editing a note, please retain the timestamp and author name at the beginning of the note.");
Metadatum[] noteMetas = item.getMetadata("workflow", "agscite", "note", Item.ANY);
int i = 0;
for (Metadatum noteMeta : noteMetas) {
Composite entry = list.addItem().addComposite("composite");
entry.addCheckBox("delete-note-" + i).addOption(Boolean.TRUE.toString(), "Delete this note");
TextArea valueArea = entry.addTextArea("note-value-" + i);
valueArea.setValue(noteMeta.value);
i++;
}
list.addItem("Confirming the changes on this page will delete the notes whose check boxes are ticked and apply any changes you made to the text of the remaining notes.");
org.dspace.app.xmlui.wing.element.Item buttonsPara = list.addItem();
buttonsPara.addButton(AgsciteReviewActionLogic.SUBMIT_CONFIRM_EDIT_NOTES).setValue("Confirm changes");
buttonsPara.addButton(AgsciteReviewActionLogic.SUBMIT_CANCEL_EDIT_NOTES).setValue("Cancel");
}
private void renderRejectReasonScreen(Request request, Division div) throws WingException {
List list = div.addList("reason-list", List.TYPE_FORM);
list.setHead("Reject submission");
TextArea answerField = list.addItem().addTextArea("reason");
answerField.setLabel("Reason");
answerField.setRequired();
answerField.setSize(5, 50);
answerField.setAutofocus("autofocus");
if (Action.getErrorFields(request).contains("answer")) {
answerField.addError("Reason is required");
if (StringUtils.isNotBlank(request.getParameter("reason"))) {
answerField.setValue(request.getParameter("reason"));
}
}
answerField.setHelp("Type in your reason for rejecting this submission. The reason will be recorded and the submission will be returned to the original submitter.");
org.dspace.app.xmlui.wing.element.Item buttons = list.addItem();
buttons.addButton(AgsciteReviewActionLogic.SUBMIT_CONFIRM_REJECT).setValue("Reject");
buttons.addButton(AgsciteReviewActionLogic.SUBMIT_CANCEL_REJECT).setValue("Cancel");
}
private void renderCurateScreen(Request request, Division div) throws WingException {
List list = div.addList("edit-notes", List.TYPE_FORM);
list.setHead("Curate");
if (request.getAttribute("curationError") != null) {
String curationError = Objects.toString(request.getAttribute("curationError"), "(no message)");
list.addItem("curationOutcomeError", "alert alert-danger").addContent("The curation task has reported an error. " +
"Please contact dspace_help@waikato.ac.nz. There may be more information in the DSpace log file. " +
"The error message was: " + curationError);
} else if (request.getAttribute("curationStatus") != null && request.getAttribute("curationStatus") instanceof Integer) {
int curationStatus = (Integer)request.getAttribute("curationStatus");
String statusType = "info";
if (curationStatus == Curator.CURATE_ERROR || curationStatus == Curator.CURATE_FAIL) {
statusType = "danger";
} else if (curationStatus == Curator.CURATE_SKIP) {
statusType = "warning";
} else if (curationStatus == Curator.CURATE_SUCCESS) {
statusType = "success";
}
String curationResult = Objects.toString(request.getAttribute("curationResult"), "(no result information)");
list.addItem("curation-result", "alert alert-" + statusType).addContent("The task reported the following result: " + curationResult);
}
list.addItem("Choose a curation task to run on the submission, or click Go back to return to the submission page.");
Select taskSelect = list.addItem().addSelect("task");
populateCurationTaskOptions(taskSelect);
taskSelect.setLabel("Task");
taskSelect.setSize(1);
taskSelect.setRequired();
taskSelect.setAutofocus("autofocus");
if (Action.getErrorFields(request).contains("task")) {
taskSelect.addError("You must select a task.");
if (StringUtils.isNotBlank(request.getParameter("task"))) {
taskSelect.setOptionSelected(request.getParameter("task"));
}
}
org.dspace.app.xmlui.wing.element.Item buttonsPara = list.addItem();
buttonsPara.addButton(AgsciteReviewActionLogic.SUBMIT_CONFIRM_CURATE).setValue("Run curation task");
buttonsPara.addButton(AgsciteReviewActionLogic.SUBMIT_CANCEL_CURATE).setValue("Go back");
}
private void populateCurationTaskOptions(Select taskSelect) throws WingException {
String csvList = ConfigurationManager.getProperty("curate", "ui.tasknames");
if (StringUtils.isBlank(csvList)) {
log.warn("No curation tasks found");
return;
}
String[] properties = csvList.split(",");
Map<String, String> tasks = new HashMap<>();
java.util.List<String> taskNames = new ArrayList<>();
for (String property : properties)
{
String[] keyValuePair = property.split("=");
if (keyValuePair.length == 2) {
tasks.put(keyValuePair[1].trim(), keyValuePair[0].trim());
taskNames.add(keyValuePair[1].trim());
} else {
log.warn("Malformed key/value pair in curation task configuration: " + property);
}
}
// ensure citation generator is listed first here since it's used most often
Collections.sort(taskNames);
if (taskNames.contains("Generate citation from metadata")) {
taskNames.remove("Generate citation from metadata");
taskNames.add(0, "Generate citation from metadata");
}
for (String taskName : taskNames) {
taskSelect.addOption(tasks.get(taskName), taskName);
}
}
private void addOutcomeButtons(Division div) throws WingException {
Table table = div.addTable("workflow-actions", 6, 2);
table.setHead("Actions");
// Edit
Row row = table.addRow();
row.addCellContent("Edit the submission's metadata and files.");
row.addCell().addButton(AgsciteReviewActionLogic.SUBMIT_EDIT).setValue("Edit");
// Curate
row = table.addRow();
row.addCellContent("Run a curation task on the submission (e.g., generate citation from metadata).");
row.addCell().addButton(AgsciteReviewActionLogic.SUBMIT_CURATE).setValue("Curate");
// Accept
row = table.addRow();
row.addCellContent("Accept the submission into AgScite.");
row.addCell().addButton(AgsciteReviewActionLogic.SUBMIT_ACCEPT).setValue("Accept");
// Reject
row = table.addRow();
row.addCellContent("Reject the submission from AgScite. The submission will be returned to the original submitter for review and re-submission. You will be asked to enter a reason for the rejection on the next page.");
row.addCell().addButton(AgsciteReviewActionLogic.SUBMIT_REJECT).setValue("Reject");
// Park
row = table.addRow();
row.addCellContent("Change the workflow task's stage and/or add a note.");
row.addCell().addButton(AgsciteReviewActionLogic.SUBMIT_PARK).setValue("Change stage / add note");
// Return to pool
row = table.addRow();
row.addCellContent("Return the task to the workflow pool.");
row.addCell().addButton(AgsciteReviewActionLogic.SUBMIT_UNCLAIM).setValue("Return to pool");
// Everyone can just cancel
row = table.addRow();
row.addCell(0, 2).addButton(AgsciteReviewActionLogic.SUBMIT_BACK).setValue("Back to overview");
}
private static String makePrefill(Context context) {
synchronized (DATE_FORMAT) {
return "["
+ DATE_FORMAT.format(new Date())
+ ", "
+ context.getCurrentUser().getFullName()
+ "] ";
}
}
}
package nz.ac.waikato.its.irr.dspace.app.xmlui.aspect.xmlworkflow.actions;
import nz.ac.waikato.its.irr.dspace.xmlworkflow.state.actions.processingaction.AgsciteReviewActionLogic;
import org.apache.commons.lang.StringUtils;
import org.dspace.app.xmlui.wing.WingException;
import org.dspace.app.xmlui.wing.element.Button;
import org.dspace.app.xmlui.wing.element.Division;
import org.dspace.content.Item;
import org.dspace.content.Metadatum;
/**
* @author Andrea Schweer schweer@waikato.ac.nz
* for the University of Waikato's Institutional Research Repositories
*/
public class AgSciteWorkflowScreenUtils {
public static void addWorkflowNotes(Item item, Division div, boolean allowEdits) throws WingException {
Division questionDiv = div.addDivision("workflow-notes");
questionDiv.setHead("Workflow Notes");
Metadatum[] noteMetas = item.getMetadata("workflow", "agscite", "note", Item.ANY);
if (noteMetas != null && noteMetas.length > 0) {
int i = 0;
for (Metadatum noteMeta : noteMetas) {
if (noteMeta != null && StringUtils.isNotBlank(noteMeta.value)) {
questionDiv.addPara("note-" + i++, "blockquote workflow-note").addContent(noteMeta.value);
}
}
if (allowEdits) {
Button notesButton = questionDiv.addPara().addButton(AgsciteReviewActionLogic.SUBMIT_EDIT_NOTES);
notesButton.setValue("Edit notes");
}
} else {
questionDiv.addPara("This task has no workflow notes.");
}
}
}
package nz.ac.waikato.its.irr.dspace.app.xmlui.aspect.xmlworkflow.actions.userassignment;
import nz.ac.waikato.its.irr.dspace.app.xmlui.aspect.xmlworkflow.actions.AgSciteWorkflowScreenUtils;
import org.apache.cocoon.environment.Request;
import org.dspace.app.xmlui.aspect.xmlworkflow.actions.userassignment.ClaimAction;
import org.dspace.app.xmlui.wing.WingException;
import org.dspace.app.xmlui.wing.element.Body;
import org.dspace.app.xmlui.wing.element.Division;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Item;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.sql.SQLException;
/**
* @author Andrea Schweer schweer@waikato.ac.nz
* for the University of Waikato's Institutional Research Repositories
*/
public class ClaimActionScreen extends ClaimAction {
@Override
protected void addWorkflowItemInformation(Division div, Item item, Request request) throws WingException {
AgSciteWorkflowScreenUtils.addWorkflowNotes(item, div, false);
super.addWorkflowItemInformation(div, item, request);
}
@Override
public void addBody(Body body) throws SAXException, WingException, SQLException, IOException, AuthorizeException {
super.addBody(body);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd">
<bean id="claimaction_xmlui" class="nz.ac.waikato.its.irr.dspace.app.xmlui.aspect.xmlworkflow.actions.userassignment.ClaimActionScreen" scope="singleton"/>
<bean id="agscitereviewaction_xmlui" class="nz.ac.waikato.its.irr.dspace.app.xmlui.aspect.xmlworkflow.actions.processingaction.AgSciteReviewActionScreen" scope="singleton"/>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd">
<bean id="agscitereviewactionAPI" class="nz.ac.waikato.its.irr.dspace.xmlworkflow.state.actions.processingaction.AgsciteReviewActionLogic" scope="prototype"/>
<bean id="agscitereviewaction" class="org.dspace.xmlworkflow.state.actions.WorkflowActionConfig" scope="prototype">
<constructor-arg type="java.lang.String" value="agscitereviewaction"/>
<property name="processingAction" ref="agscitereviewactionAPI"/>
<property name="requiresUI" value="true"/>
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<wf-config>
<workflow-map>
<name-map collection="default" workflow="agsciteWorkflow"/>
</workflow-map>
<workflow start="reviewstep" id="agsciteWorkflow">
<roles>
<role id="editor" name="Editor" description="The people responsible for this step are able to process submissions at all stages of the workflow."/>
</roles>
<step id="reviewstep" role="editor" userSelectionMethod="claimaction">
<outcomes>
<step status="1">reviewstep</step>
<step status="2">lowprioritystep</step>
<step status="3">infoneededstep</step>
<step status="4">adminquestionstep</step>
<step status="5">outofscopestep</step>
</outcomes>
<actions>
<action id="agscitereviewaction"/>
</actions>
</step>
<step id="lowprioritystep" role="editor" userSelectionMethod="claimaction">
<outcomes>
<step status="1">reviewstep</step>
<step status="2">lowprioritystep</step>
<step status="3">infoneededstep</step>
<step status="4">adminquestionstep</step>
<step status="5">outofscopestep</step>
</outcomes>
<actions>
<action id="agscitereviewaction"/>
</actions>
</step>
<step id="infoneededstep" role="editor" userSelectionMethod="claimaction">
<outcomes>
<step status="1">reviewstep</step>
<step status="2">lowprioritystep</step>
<step status="3">infoneededstep</step>
<step status="4">adminquestionstep</step>
<step status="5">outofscopestep</step>
</outcomes>
<actions>
<action id="agscitereviewaction"/>
</actions>
</step>
<step id="adminquestionstep" role="editor" userSelectionMethod="claimaction">
<outcomes>
<step status="1">reviewstep</step>
<step status="2">lowprioritystep</step>
<step status="3">infoneededstep</step>
<step status="4">adminquestionstep</step>
<step status="5">outofscopestep</step>
</outcomes>
<actions>
<action id="agscitereviewaction"/>
</actions>
</step>
<step id="outofscopestep" role="editor" userSelectionMethod="claimaction">
<outcomes>
<step status="1">reviewstep</step>
<step status="2">lowprioritystep</step>
<step status="3">infoneededstep</step>
<step status="4">adminquestionstep</step>
<step status="5">outofscopestep</step>
</outcomes>
<actions>
<action id="agscitereviewaction"/>
</actions>
</step>
</workflow>
</wf-config>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment