/** * 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.xmlui.aspect.submission; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import org.apache.cocoon.environment.ObjectModelHelper; import org.apache.cocoon.environment.Request; import org.apache.cocoon.environment.http.HttpEnvironment; import org.apache.log4j.Logger; import org.dspace.app.util.DCInput; import org.dspace.app.util.SubmissionConfig; import org.dspace.app.util.SubmissionConfigReader; import org.dspace.app.util.SubmissionInfo; import org.dspace.app.util.SubmissionStepConfig; import org.dspace.app.xmlui.utils.UIException; import org.dspace.app.xmlui.utils.ContextUtil; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Collection; import org.dspace.content.InProgressSubmission; import org.dspace.content.Item; import org.dspace.content.WorkspaceItem; import org.dspace.core.Context; import org.dspace.core.LogManager; import org.dspace.handle.HandleManager; import org.dspace.submit.AbstractProcessingStep; import org.dspace.workflow.WorkflowItem; import org.dspace.workflow.WorkflowManager; import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem; /** * This is a utility class to aid in the submission flow scripts. * Since data validation is cumbersome inside a flow script this * is a collection of methods to perform processing at each step * of the flow, the flow script will ties these operations * together in a meaningful order but all actually processing * is done through these various processes. * * @author Scott Phillips * @author Tim Donohue (modified for Configurable Submission) */ public class FlowUtils { private static final Logger log = Logger.getLogger(FlowUtils.class); /** Where the submissionInfo is stored on an HTTP Request object */ private static final String DSPACE_SUBMISSION_INFO = "dspace.submission.info"; /** * Return the InProgressSubmission, either workspaceItem or workflowItem, * depending on the id provided. If the id begins with an S then it is a * considered a workspaceItem. If the id begins with a W then it is * considered a workflowItem. * * @param context * @param inProgressSubmissionID * @return The InprogressSubmission or null if non found */ public static InProgressSubmission findSubmission(Context context, String inProgressSubmissionID) throws SQLException, AuthorizeException, IOException { char type = inProgressSubmissionID.charAt(0); int id = Integer.valueOf(inProgressSubmissionID.substring(1)); if (type == 'S') { return WorkspaceItem.find(context, id); } else if (type == 'W') { return WorkflowItem.find(context, id); } else if (type == 'X') { return XmlWorkflowItem.find(context, id); } return null; } /** * Return the workspace identified by the given id, the id should be * prepended with the character S to signify that it is a workspace * instead of a workflow. * * @param context * @param inProgressSubmissionID * @return The found workspaceitem or null if none found. */ public static WorkspaceItem findWorkspace(Context context, String inProgressSubmissionID) throws SQLException, AuthorizeException, IOException { InProgressSubmission submission = findSubmission(context, inProgressSubmissionID); if (submission instanceof WorkspaceItem) { return (WorkspaceItem) submission; } return null; } /** * Obtains the submission info for the current submission process. * If a submissionInfo object has already been created * for this HTTP request, it is re-used, otherwise it is created. * * @param objectModel * the cocoon Objectmodel * @param workspaceID * the workspaceID of the submission info to obtain * * @return a SubmissionInfo object */ public static SubmissionInfo obtainSubmissionInfo(Map objectModel, String workspaceID) throws SQLException, IOException, AuthorizeException { Request request = ObjectModelHelper.getRequest(objectModel); Context context = ContextUtil.obtainContext(objectModel); //try loading subInfo from HTTP request SubmissionInfo subInfo = (SubmissionInfo) request.getAttribute(DSPACE_SUBMISSION_INFO); //get the submission represented by the WorkspaceID InProgressSubmission submission = findSubmission(context, workspaceID); //if no submission info, or wrong submission info, reload it! if ((subInfo == null && submission!=null) || (subInfo!=null && submission!=null && subInfo.getSubmissionItem().getID()!=submission.getID())) { try { final HttpServletRequest httpRequest = (HttpServletRequest) objectModel .get(HttpEnvironment.HTTP_REQUEST_OBJECT); // load submission info subInfo = SubmissionInfo.load(httpRequest, submission); // Set the session ID context.setExtraLogInfo("session_id=" + request.getSession().getId()); // Store the submissionInfo in the request request.setAttribute(DSPACE_SUBMISSION_INFO, subInfo); } catch(Exception e) { throw new SQLException("Error loading Submission Info: " + e.getMessage(), e); } } else if(subInfo==null && submission==null) { throw new SQLException("Unable to load Submission Information, since WorkspaceID (ID:" + workspaceID + ") is not a valid in-process submission."); } return subInfo; } /** * Indicate the user has advanced to the given page within a given step. * This will only actually do anything when it's a user initially entering * a submission. It will increase the "stage reached" and "page reached" * columns - it will not "set back" where a user has reached. * * @param context The current DSpace content * @param id The unique ID of the current workflow/workspace * @param step the step the user has just reached * @param page the page (within the step) the user has just reached */ public static void setPageReached(Context context, String id, int step, int page) throws SQLException, AuthorizeException, IOException { InProgressSubmission submission = findSubmission(context, id); if (submission instanceof WorkspaceItem) { WorkspaceItem workspaceItem = (WorkspaceItem) submission; if (step > workspaceItem.getStageReached()) { workspaceItem.setStageReached(step); workspaceItem.setPageReached(1); //reset page to first page in new step workspaceItem.update(); context.commit(); } else if ((step == workspaceItem.getStageReached()) && (page > workspaceItem.getPageReached())) { workspaceItem.setPageReached(page); workspaceItem.update(); context.commit(); } } } /** * Set a specific step and page as reached. * It will also "set back" where a user has reached. * * @param context The current DSpace content * @param id The unique ID of the current workflow/workspace * @param step the step to set as reached, can be also a previous reached step * @param page the page (within the step) to set as reached, can be also a previous reached page */ public static void setBackPageReached(Context context, String id, int step, int page) throws SQLException, AuthorizeException, IOException { InProgressSubmission submission = findSubmission(context, id); if (submission instanceof WorkspaceItem) { WorkspaceItem workspaceItem = (WorkspaceItem) submission; workspaceItem.setStageReached(step); workspaceItem.setPageReached(page > 0 ? page : 1); workspaceItem.update(); context.commit(); } } /** * Find the maximum step the user has reached in the submission processes. * If this submission is a workflow then return max-int. * * @param context The current DSpace content * @param id The unique ID of the current workflow/workspace */ public static int getMaximumStepReached(Context context, String id) throws SQLException, AuthorizeException, IOException { InProgressSubmission submission = findSubmission(context, id); if (submission instanceof WorkspaceItem) { WorkspaceItem workspaceItem = (WorkspaceItem) submission; int stage = workspaceItem.getStageReached(); if (stage < 0) { stage = 0; } return stage; } // This is a workflow, return infinity. return Integer.MAX_VALUE; } /** * Find the maximum page (within the maximum step) that the user has * reached in the submission processes. * If this submission is a workflow then return max-int. * * @param context The current DSpace content * @param id The unique ID of the current workflow/workspace */ public static int getMaximumPageReached(Context context, String id) throws SQLException, AuthorizeException, IOException { InProgressSubmission submission = findSubmission(context, id); if (submission instanceof WorkspaceItem) { WorkspaceItem workspaceItem = (WorkspaceItem) submission; int page = workspaceItem.getPageReached(); if (page < 0) { page = 0; } return page; } // This is a workflow, return infinity. return Integer.MAX_VALUE; } /** * Get current step number * * @param stepAndPage * a double representing the current step and page * (e.g. 1.2 is page 2 of step 1) * @return step number */ public static int getStep(double stepAndPage) { //split step and page (e.g. 1.2 is page 2 of step 1) String[] fields = Double.toString(stepAndPage).split("\\."); // split on period return Integer.parseInt(fields[0]); } /** * Get number of the current page within the current step * *@param stepAndPage * a double representing the current step and page * (e.g. 1.2 is page 2 of step 1) * @return page number (within current step) */ public static int getPage(double stepAndPage) { //split step and page (e.g. 1.2 is page 2 of step 1) String[] fields = Double.toString(stepAndPage).split("\\."); // split on period return Integer.parseInt(fields[1]); } /** * Process the save or remove step. If the user has selected to * remove their submission then remove it. * * @param context The current DSpace content * @param id The unique ID of the current workspace/workflow * @param request The cocoon request object. */ public static void processSaveOrRemove(Context context, String id, Request request) throws SQLException, AuthorizeException, IOException { if (request.getParameter("submit_remove") != null) { // If they selected to remove the item then delete everything. WorkspaceItem workspace = findWorkspace(context,id); workspace.deleteAll(); context.commit(); } } /** * Return the HTML / DRI field name for the given input. * * @param input * @return field name as a String (e.g. dc_contributor_editor) */ public static String getFieldName(DCInput input) { String dcSchema = input.getSchema(); String dcElement = input.getElement(); String dcQualifier = input.getQualifier(); if (dcQualifier != null && ! dcQualifier.equals(Item.ANY)) { return dcSchema + "_" + dcElement + '_' + dcQualifier; } else { return dcSchema + "_" + dcElement; } } /** * Retrieves a list of all steps and pages within the * current submission process. * <P> * This list may differ from the list of steps in the * progress bar if the current submission process includes * non-interactive steps which do not appear in the progress bar! * <P> * This method is used by the Manakin submission flowscript * (submission.js) to step forward/backward between steps. * * @param request * The HTTP Servlet Request object * @param subInfo * the current SubmissionInfo object * */ public static StepAndPage[] getListOfAllSteps(HttpServletRequest request, SubmissionInfo subInfo) { ArrayList<StepAndPage> listStepNumbers = new ArrayList<StepAndPage>(); // loop through all steps for (int i = 0; i < subInfo.getSubmissionConfig().getNumberOfSteps(); i++) { // get the current step info SubmissionStepConfig currentStep = subInfo.getSubmissionConfig() .getStep(i); int stepNumber = currentStep.getStepNumber(); //Skip over the "Select Collection" step, since // a user is never allowed to return to that step or jump from that step if(currentStep.getId()!=null && currentStep.getId().equals(SubmissionStepConfig.SELECT_COLLECTION_STEP)) { continue; } // default to just one page in this step int numPages = 1; try { // load the processing class for this step ClassLoader loader = subInfo.getClass().getClassLoader(); Class<?> stepClass = loader.loadClass(currentStep.getProcessingClassName()); // call the "getNumberOfPages()" method of the class // to get it's number of pages AbstractProcessingStep step = (AbstractProcessingStep) stepClass .newInstance(); // get number of pages from servlet numPages = step.getNumberOfPages(request, subInfo); } catch (Exception e) { log.error("Error loading step information from Step Class '" + currentStep.getProcessingClassName() + "' Error:", e); } // save each of the step's pages to the progress bar for (int j = 1; j <= numPages; j++) { StepAndPage stepAndPageNum = new StepAndPage(stepNumber,j); listStepNumbers.add(stepAndPageNum); }// end for each page }// end for each step //convert into an array and return that return listStepNumbers.toArray(new StepAndPage[listStepNumbers.size()]); } }