|
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 [email protected] |
|
* 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 [email protected]. 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() |
|
+ "] "; |
|
} |
|
} |
|
} |