/*
* SubmissionController.java
*
* Version: $Revision: 3864 $
*
* Date: $Date: 2009-06-06 12:30:05 +0000 (Sat, 06 Jun 2009) $
*
* Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts
* Institute of Technology. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the Hewlett-Packard Company nor the name of the
* Massachusetts Institute of Technology nor the names of their
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.dspace.app.webui.servlet;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.dspace.app.util.SubmissionInfo;
import org.dspace.app.util.SubmissionStepConfig;
import org.dspace.app.util.Util;
import org.dspace.app.webui.submit.JSPStepManager;
import org.dspace.app.webui.util.FileUploadRequest;
import org.dspace.app.webui.util.JSPManager;
import org.dspace.app.webui.util.UIUtil;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.dspace.content.WorkspaceItem;
import org.dspace.core.Context;
import org.dspace.core.LogManager;
import org.dspace.workflow.WorkflowItem;
import org.dspace.submit.AbstractProcessingStep;
import org.dspace.submit.step.UploadStep;
/**
* Submission Manager servlet for DSpace. Handles the initial submission of
* items, as well as the editing of items further down the line.
* <p>
* Whenever the submit servlet receives a GET request, this is taken to indicate
* the start of a fresh new submission, where no collection has been selected,
* and the submission process is started from scratch.
* <p>
* All other interactions happen via POSTs. Part of the post will normally be a
* (hidden) "step" parameter, which will correspond to the form that the user
* has just filled out. If this is absent, step 0 (select collection) is
* assumed, meaning that it's simple to place "Submit to this collection"
* buttons on collection home pages.
* <p>
* According to the step number of the incoming form, the values posted from the
* form are processed (using the process* methods), and the item updated as
* appropriate. The servlet then forwards control of the request to the
* appropriate JSP (from jsp/submit) to render the next stage of the process or
* an error if appropriate. Each of these JSPs may require that attributes be
* passed in. Check the comments at the top of a JSP to see which attributes are
* needed. All submit-related forms require a properly initialised
* SubmissionInfo object to be present in the the "submission.info" attribute.
* This holds the core information relevant to the submission, e.g. the item,
* personal workspace or workflow item, the submitting "e-person", and the
* target collection.
* <p>
* When control of the request reaches a JSP, it is assumed that all checks,
* interactions with the database and so on have been performed and that all
* necessary information to render the form is in memory. e.g. The
* SubmitFormInfo object passed in must be correctly filled out. Thus the JSPs
* do no error or integrity checking; it is the servlet's responsibility to
* ensure that everything is prepared. The servlet is fairly diligent about
* ensuring integrity at each step.
* <p>
* Each step has an integer constant defined below. The main sequence of the
* submission procedure always runs from 0 upwards, until SUBMISSION_COMPLETE.
* Other, not-in-sequence steps (such as the cancellation screen and the
* "previous version ID verification" screen) have numbers much higher than
* SUBMISSION_COMPLETE. These conventions allow the progress bar component of
* the submission forms to render the user's progress through the process.
*
* @see org.dspace.app.util.SubmissionInfo
* @see org.dspace.app.util.SubmissionConfig
* @see org.dspace.app.util.SubmissionStepConfig
* @see org.dspace.app.webui.submit.JSPStepManager
*
* @author Tim Donohue
* @version $Revision: 3864 $
*/
public class SubmissionController extends DSpaceServlet
{
// Steps in the submission process
/** Selection collection step */
public static final int SELECT_COLLECTION = 0;
/** First step after "select collection" */
public static final int FIRST_STEP = 1;
/** For workflows, first step is step #0 (since Select Collection is already filtered out) */
public static final int WORKFLOW_FIRST_STEP = 0;
/** path to the JSP shown once the submission is completed */
private static String COMPLETE_JSP = "/submit/complete.jsp";
/** log4j logger */
private static Logger log = Logger
.getLogger(SubmissionController.class);
protected void doDSGet(Context context, HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException,
SQLException, AuthorizeException
{
/*
* Possible GET parameters:
*
* resume= <workspace_item_id> - Resumes submitting the given workspace
* item
*
* workflow= <workflow_id> - Starts editing the given workflow item in
* workflow mode
*
* With no parameters, doDSGet() just calls doDSPost(), which continues
* the current submission (if one exists in the Request), or creates a
* new submission (if no existing submission can be found).
*/
// try to get a workspace ID or workflow ID
String workspaceID = request.getParameter("resume");
String workflowID = request.getParameter("workflow");
// If resuming a workspace item
if (workspaceID != null)
{
try
{
// load the workspace item
WorkspaceItem wi = WorkspaceItem.find(context, Integer
.parseInt(workspaceID));
//load submission information
SubmissionInfo si = SubmissionInfo.load(request, wi);
//TD: Special case - If a user is resuming a submission
//where the submission process now has less steps, then
//we will need to reset the stepReached in the database
//(Hopefully this will never happen, but just in case!)
if(getStepReached(si) >= si.getSubmissionConfig().getNumberOfSteps())
{
//update Stage Reached to the last step in the Process
int lastStep = si.getSubmissionConfig().getNumberOfSteps()-1;
wi.setStageReached(lastStep);
//flag that user is on last page of last step
wi.setPageReached(AbstractProcessingStep.LAST_PAGE_REACHED);
//commit all changes to database immediately
wi.update();
context.commit();
//update submission info
si.setSubmissionItem(wi);
}
// start over at beginning of first step
setBeginningOfStep(request, true);
doStep(context, request, response, si, FIRST_STEP);
}
catch (NumberFormatException nfe)
{
log.warn(LogManager.getHeader(context, "bad_workspace_id",
"bad_id=" + workspaceID));
JSPManager.showInvalidIDError(request, response, workspaceID,
-1);
}
}
else if (workflowID != null) // if resuming a workflow item
{
try
{
// load the workflow item
WorkflowItem wi = WorkflowItem.find(context, Integer
.parseInt(workflowID));
//load submission information
SubmissionInfo si = SubmissionInfo.load(request, wi);
// start over at beginning of first workflow step
setBeginningOfStep(request, true);
doStep(context, request, response, si, WORKFLOW_FIRST_STEP);
}
catch (NumberFormatException nfe)
{
log.warn(LogManager.getHeader(context, "bad_workflow_id",
"bad_id=" + workflowID));
JSPManager
.showInvalidIDError(request, response, workflowID, -1);
}
}
else
{
// otherwise, forward to doDSPost() to do usual processing
doDSPost(context, request, response);
}
}
protected void doDSPost(Context context, HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException,
SQLException, AuthorizeException
{
// Configuration of current step in Item Submission Process
SubmissionStepConfig currentStepConfig;
//need to find out what type of form we are dealing with
String contentType = request.getContentType();
// if multipart form, we have to wrap the multipart request
// in order to be able to retrieve request parameters, etc.
if ((contentType != null)
&& (contentType.indexOf("multipart/form-data") != -1))
{
request = wrapMultipartRequest(request);
//also, upload any files and save their contents to Request (for later processing by UploadStep)
uploadFiles(context, request);
}
// Reload submission info from request parameters
SubmissionInfo subInfo = getSubmissionInfo(context, request);
// a submission info object is necessary to continue
if (subInfo == null)
{
// Work around for problem where people select "is a thesis", see
// the error page, and then use their "back" button thinking they
// can start another submission - it's been removed so the ID in the
// form is invalid. If we detect the "removed_thesis" attribute we
// display a friendly message instead of an integrity error.
if (request.getSession().getAttribute("removed_thesis") != null)
{
request.getSession().removeAttribute("removed_thesis");
JSPManager.showJSP(request, response,
"/submit/thesis-removed-workaround.jsp");
return;
}
else
{
// If the submission info was invalid, throw an integrity error
log.warn(LogManager.getHeader(context, "integrity_error",
UIUtil.getRequestLogInfo(request)));
JSPManager.showIntegrityError(request, response);
return;
}
}
// First, check for a click on "Cancel/Save" button.
if (UIUtil.getSubmitButton(request, "").equals(AbstractProcessingStep.CANCEL_BUTTON))
{
// Get the current step
currentStepConfig = getCurrentStepConfig(request, subInfo);
// forward user to JSP which will confirm
// the cancel/save request.
doCancelOrSave(context, request, response, subInfo,
currentStepConfig);
}
// Special case - no InProgressSubmission yet
// If no submission, we assume we will be going
// to the "select collection" step.
else if (subInfo.getSubmissionItem() == null)
{
// we have just started this submission
// (or we have just resumed a saved submission)
// do the "Select Collection" step
doStep(context, request, response, subInfo, SELECT_COLLECTION);
}
else
// otherwise, figure out the next Step to call!
{
// Get the current step
currentStepConfig = getCurrentStepConfig(request, subInfo);
//if user already confirmed the cancel/save request
if (UIUtil.getBoolParameter(request, "cancellation"))
{
// user came from the cancel/save page,
// so we need to process that page before proceeding
processCancelOrSave(context, request, response, subInfo, currentStepConfig);
}
//check for click on "<- Previous" button
else if (UIUtil.getSubmitButton(request, "").startsWith(
AbstractProcessingStep.PREVIOUS_BUTTON))
{
// return to the previous step
doPreviousStep(context, request, response, subInfo, currentStepConfig);
}
//check for click on Progress Bar
else if (UIUtil.getSubmitButton(request, "").startsWith(
AbstractProcessingStep.PROGRESS_BAR_PREFIX))
{
// jumping to a particular step/page
doStepJump(context, request, response, subInfo, currentStepConfig);
}
else
{
// by default, load step class to start
// or continue its processing
doStep(context, request, response, subInfo, currentStepConfig.getStepNumber());
}
}
}
/**
* Forward processing to the specified step.
*
* @param context
* DSpace context
* @param request
* the request object
* @param response
* the response object
* @param subInfo
* SubmissionInfo pertaining to this submission
* @param stepNumber
* The number of the step to perform
*/
private void doStep(Context context, HttpServletRequest request,
HttpServletResponse response, SubmissionInfo subInfo, int stepNumber)
throws ServletException, IOException, SQLException,
AuthorizeException
{
SubmissionStepConfig currentStepConfig = null;
if (subInfo.getSubmissionConfig() != null)
{
// get step to perform
currentStepConfig = subInfo.getSubmissionConfig().getStep(stepNumber);
}
else
{
log.fatal(LogManager.getHeader(context, "no_submission_process",
"trying to load step=" + stepNumber
+ ", but submission process is null"));
JSPManager.showInternalError(request, response);
}
// if this is the furthest step the user has been to, save that info
if (!subInfo.isInWorkflow() && (currentStepConfig.getStepNumber() > getStepReached(subInfo)))
{
// update submission info
userHasReached(subInfo, currentStepConfig.getStepNumber());
// commit changes to database
context.commit();
// flag that we just started this step (for JSPStepManager class)
setBeginningOfStep(request, true);
}
// save current step to request attribute
saveCurrentStepConfig(request, currentStepConfig);
log.debug("Calling Step Class: '"
+ currentStepConfig.getProcessingClassName() + "'");
try
{
JSPStepManager stepManager = JSPStepManager.loadStep(currentStepConfig);
//tell the step class to do its processing
boolean stepFinished = stepManager.processStep(context, request, response, subInfo);
//if this step is finished, continue to next step
if(stepFinished)
{
// If we finished up an upload, then we need to change
// the FileUploadRequest object back to a normal HTTPServletRequest
if(request instanceof FileUploadRequest)
{
request = ((FileUploadRequest)request).getOriginalRequest();
}
//retrieve any changes to the SubmissionInfo object
subInfo = getSubmissionInfo(context, request);
//do the next step!
doNextStep(context, request, response, subInfo, currentStepConfig);
}
else
{
//commit & close context
context.complete();
}
}
catch (Exception e)
{
log.error("Error loading step class'" + currentStepConfig.getProcessingClassName() + "':", e);
JSPManager.showInternalError(request, response);
}
}
/**
* Forward processing to the next step.
*
* @param context
* DSpace context
* @param request
* the request object
* @param response
* the response object
* @param subInfo
* SubmissionInfo pertaining to this submission
*/
private void doNextStep(Context context, HttpServletRequest request,
HttpServletResponse response, SubmissionInfo subInfo, SubmissionStepConfig currentStepConfig)
throws ServletException, IOException, SQLException,
AuthorizeException
{
// find current Step number
int currentStepNum;
if (currentStepConfig == null)
{
currentStepNum = -1;
}
else
{
currentStepNum = currentStepConfig.getStepNumber();
}
// as long as there are more steps after the current step,
// do the next step in the current Submission Process
if (subInfo.getSubmissionConfig().hasMoreSteps(currentStepNum))
{
// update the current step & do this step
currentStepNum++;
//flag that we are going to the start of this next step (for JSPStepManager class)
setBeginningOfStep(request, true);
doStep(context, request, response, subInfo, currentStepNum);
}
else
{
//if this submission is in the workflow process,
//forward user back to relevant task page
if(subInfo.isInWorkflow())
{
request.setAttribute("workflow.item", subInfo.getSubmissionItem());
JSPManager.showJSP(request, response,
"/mydspace/perform-task.jsp");
}
else
{
// The Submission is COMPLETE!!
// save our current Submission information into the Request object
saveSubmissionInfo(request, subInfo);
// forward to completion JSP
showProgressAwareJSP(request, response, subInfo, COMPLETE_JSP);
}
}
}
/**
* Forward processing to the previous step. This method is called if it is
* determined that the "previous" button was pressed.
*
* @param context
* DSpace context
* @param request
* the request object
* @param response
* the response object
* @param subInfo
* SubmissionInfo pertaining to this submission
*/
private void doPreviousStep(Context context, HttpServletRequest request,
HttpServletResponse response, SubmissionInfo subInfo, SubmissionStepConfig currentStepConfig)
throws ServletException, IOException, SQLException,
AuthorizeException
{
int result = doSaveCurrentState(context, request, response, subInfo, currentStepConfig);
int currStep=currentStepConfig.getStepNumber();
int currPage=AbstractProcessingStep.getCurrentPage(request);
double currStepAndPage = Float.parseFloat(currStep+"."+currPage);
// default value if we are in workflow
double stepAndPageReached = -1;
if (!subInfo.isInWorkflow())
{
stepAndPageReached = Float.parseFloat(getStepReached(subInfo)+"."+JSPStepManager.getPageReached(subInfo));
}
if (result != AbstractProcessingStep.STATUS_COMPLETE && currStepAndPage != stepAndPageReached)
{
doStep(context, request, response, subInfo, currStep);
}
// find current Step number
int currentStepNum;
if (currentStepConfig == null)
{
currentStepNum = -1;
}
else
{
currentStepNum = currentStepConfig.getStepNumber();
}
//Check to see if we are actually just going to a
//previous PAGE within the same step.
int currentPageNum = AbstractProcessingStep.getCurrentPage(request);
boolean foundPrevious = false;
//since there are pages before this one in this current step
//just go backwards one page.
if(currentPageNum > 1)
{
//decrease current page number
AbstractProcessingStep.setCurrentPage(request, currentPageNum-1);
foundPrevious = true;
//send user back to the beginning of same step!
//NOTE: the step should handle going back one page
// in its doPreProcessing() method
setBeginningOfStep(request, true);
doStep(context, request, response, subInfo, currentStepNum);
}
// Since we cannot go back one page,
// check if there is a step before this step.
// If so, go backwards one step
else if (currentStepNum > FIRST_STEP)
{
currentStepConfig = getPreviousVisibleStep(request, subInfo);
if(currentStepConfig != null)
{
currentStepNum = currentStepConfig.getStepNumber();
foundPrevious = true;
}
if(foundPrevious)
{
//flag to JSPStepManager that we are going backwards
//an entire step
request.setAttribute("step.backwards", new Boolean(true));
// flag that we are going back to the start of this step (for JSPStepManager class)
setBeginningOfStep(request, true);
doStep(context, request, response, subInfo, currentStepNum);
}
}
//if there is no previous, visible step, throw an error!
if(!foundPrevious)
{
log.error(LogManager
.getHeader(context, "no_previous_visible_step",
"Attempting to go to previous step for step="
+ currentStepNum + "." +
"NO PREVIOUS VISIBLE STEP OR PAGE FOUND!"));
JSPManager.showIntegrityError(request, response);
}
}
/**
* Process a click on a button in the progress bar. This jumps to the step
* whose button was pressed.
*
* @param context
* DSpace context object
* @param request
* the request object
* @param response
* the response object
* @param subInfo
* SubmissionInfo pertaining to this submission
*/
private void doStepJump(Context context, HttpServletRequest request,
HttpServletResponse response, SubmissionInfo subInfo, SubmissionStepConfig currentStepConfig)
throws ServletException, IOException, SQLException,
AuthorizeException
{
// Find the button that was pressed. It would start with
// "submit_jump_".
String buttonPressed = UIUtil.getSubmitButton(request, "");
int nextStep = -1; // next step to load
int nextPage = -1; // page within the nextStep to load
if (buttonPressed.startsWith("submit_jump_"))
{
// Button on progress bar pressed
try
{
// get step & page info (in form: stepNum.pageNum) after
// "submit_jump_"
String stepAndPage = buttonPressed.substring(12);
// split into stepNum and pageNum
String[] fields = stepAndPage.split("\\."); // split on period
nextStep = Integer.parseInt(fields[0]);
nextPage = Integer.parseInt(fields[1]);
}
catch (NumberFormatException ne)
{
// mangled number
nextStep = -1;
nextPage = -1;
}
// Integrity check: make sure they aren't going
// forward or backward too far
if ((!subInfo.isInWorkflow() && nextStep < FIRST_STEP) ||
(subInfo.isInWorkflow() && nextStep < WORKFLOW_FIRST_STEP))
{
nextStep = -1;
nextPage = -1;
}
// if trying to jump to a step you haven't been to yet
if (!subInfo.isInWorkflow() && (nextStep > getStepReached(subInfo)))
{
nextStep = -1;
}
}
if (nextStep == -1)
{
// Either no button pressed, or an illegal stage
// reached. UI doesn't allow this, so something's
// wrong if that happens.
log.warn(LogManager.getHeader(context, "integrity_error", UIUtil
.getRequestLogInfo(request)));
JSPManager.showIntegrityError(request, response);
}
else
{
int result = doSaveCurrentState(context, request, response,
subInfo, currentStepConfig);
// Now, if the request was a multi-part (file upload), we need to
// get the original request back out, as the wrapper causes problems
// further down the line.
if (request instanceof FileUploadRequest)
{
FileUploadRequest fur = (FileUploadRequest) request;
request = fur.getOriginalRequest();
}
int currStep = currentStepConfig.getStepNumber();
int currPage = AbstractProcessingStep.getCurrentPage(request);
double currStepAndPage = Float
.parseFloat(currStep + "." + currPage);
// default value if we are in workflow
double stepAndPageReached = -1;
if (!subInfo.isInWorkflow())
{
stepAndPageReached = Float.parseFloat(getStepReached(subInfo)+"."+JSPStepManager.getPageReached(subInfo));
}
if (result != AbstractProcessingStep.STATUS_COMPLETE
&& currStepAndPage != stepAndPageReached)
{
doStep(context, request, response, subInfo, currStep);
}
else
{
// save page info to request (for the step to access)
AbstractProcessingStep.setCurrentPage(request, nextPage);
// flag that we are going back to the start of this step (for
// JSPStepManager class)
setBeginningOfStep(request, true);
log.debug("Jumping to Step " + nextStep + " and Page "
+ nextPage);
// do the step (the step should take care of going to
// the specified page)
doStep(context, request, response, subInfo, nextStep);
}
}
}
/**
* Respond to the user clicking "cancel/save"
* from any of the steps. This method first calls
* the "doPostProcessing()" method of the step, in
* order to ensure any inputs are saved.
*
* @param context
* DSpace context
* @param request
* current servlet request object
* @param response
* current servlet response object
* @param subInfo
* SubmissionInfo object
* @param stepConfig
* config of step who's page the user clicked "cancel" on.
*/
private void doCancelOrSave(Context context, HttpServletRequest request,
HttpServletResponse response, SubmissionInfo subInfo,
SubmissionStepConfig stepConfig) throws ServletException, IOException,
SQLException, AuthorizeException
{
// If this is a workflow item, we need to return the
// user to the "perform task" page
if (subInfo.isInWorkflow())
{
int result = doSaveCurrentState(context, request, response, subInfo, stepConfig);
if (result == AbstractProcessingStep.STATUS_COMPLETE)
{
request.setAttribute("workflow.item", subInfo.getSubmissionItem());
JSPManager.showJSP(request, response, "/mydspace/perform-task.jsp");
}
else
{
int currStep=stepConfig.getStepNumber();
doStep(context, request, response, subInfo, currStep);
}
}
else
{
// if no submission has been started,
if (subInfo.getSubmissionItem() == null)
{
// forward them to the 'cancelled' page,
// since we haven't created an item yet.
JSPManager.showJSP(request, response,
"/submit/cancelled-removed.jsp");
}
else
{
//tell the step class to do its processing (to save any inputs)
//but, send flag that this is a "cancellation"
setCancellationInProgress(request, true);
int result = doSaveCurrentState(context, request, response, subInfo,
stepConfig);
int currStep=stepConfig.getStepNumber();
int currPage=AbstractProcessingStep.getCurrentPage(request);
double currStepAndPage = Float.parseFloat(currStep+"."+currPage);
double stepAndPageReached = Float.parseFloat(getStepReached(subInfo)+"."+JSPStepManager.getPageReached(subInfo));
if (result != AbstractProcessingStep.STATUS_COMPLETE && currStepAndPage < stepAndPageReached){
setReachedStepAndPage(subInfo, currStep, currPage);
}
//commit & close context
context.complete();
// save changes to submission info & step info for JSP
saveSubmissionInfo(request, subInfo);
saveCurrentStepConfig(request, stepConfig);
// forward to cancellation confirmation JSP
showProgressAwareJSP(request, response, subInfo,
"/submit/cancel.jsp");
}
}
}
private int doSaveCurrentState(Context context,
HttpServletRequest request, HttpServletResponse response,
SubmissionInfo subInfo, SubmissionStepConfig stepConfig)
throws ServletException
{
int result = -1;
// As long as we're not uploading a file, go ahead and SAVE
// all of the user's inputs for later
try
{
// call post-processing on Step (to save any inputs from JSP)
log
.debug("Cancel/Save or Jump/Previous Request: calling processing for Step: '"
+ stepConfig.getProcessingClassName() + "'");
try
{
// load the step class (using the current class loader)
ClassLoader loader = this.getClass().getClassLoader();
Class stepClass = loader.loadClass(stepConfig
.getProcessingClassName());
// load the JSPStepManager object for this step
AbstractProcessingStep step = (AbstractProcessingStep) stepClass
.newInstance();
result = step.doProcessing(context, request, response, subInfo);
}
catch (Exception e)
{
log.error("Error loading step class'"
+ stepConfig.getProcessingClassName() + "':", e);
JSPManager.showInternalError(request, response);
}
}
catch(Exception e)
{
throw new ServletException(e);
}
return result;
}
/**
* Process information from "submission cancelled" page.
* This saves the item if the user decided to "cancel & save",
* or removes the item if the user decided to "cancel & remove".
*
* @param context
* current DSpace context
* @param request
* current servlet request object
* @param response
* current servlet response object
* @param subInfo
* submission info object
*/
private void processCancelOrSave(Context context,
HttpServletRequest request, HttpServletResponse response,
SubmissionInfo subInfo, SubmissionStepConfig currentStepConfig) throws ServletException, IOException,
SQLException, AuthorizeException
{
String buttonPressed = UIUtil.getSubmitButton(request, "submit_back");
if (buttonPressed.equals("submit_back"))
{
// re-load current step at beginning
setBeginningOfStep(request, true);
doStep(context, request, response, subInfo, currentStepConfig
.getStepNumber());
}
else if (buttonPressed.equals("submit_remove"))
{
// User wants to cancel and remove
// Cancellation page only applies to workspace items
WorkspaceItem wi = (WorkspaceItem) subInfo.getSubmissionItem();
wi.deleteAll();
JSPManager.showJSP(request, response,
"/submit/cancelled-removed.jsp");
context.complete();
}
else if (buttonPressed.equals("submit_keep"))
{
// Save submission for later - just show message
JSPManager.showJSP(request, response, "/submit/saved.jsp");
}
else
{
doStepJump(context, request, response, subInfo, currentStepConfig);
}
}
// ****************************************************************
// ****************************************************************
// MISCELLANEOUS CONVENIENCE METHODS
// ****************************************************************
// ****************************************************************
/**
* Show a JSP after setting attributes needed by progress bar
*
* @param request
* the request object
* @param response
* the response object
* @param subInfo
* the SubmissionInfo object
* @param jspPath
* relative path to JSP
*/
private static void showProgressAwareJSP(HttpServletRequest request,
HttpServletResponse response, SubmissionInfo subInfo, String jspPath)
throws ServletException, IOException
{
saveSubmissionInfo(request, subInfo);
JSPManager.showJSP(request, response, jspPath);
}
/**
* Reloads a filled-out submission info object from the parameters in the
* current request. If there is a problem, <code>null</code> is returned.
*
* @param context
* DSpace context
* @param request
* HTTP request
*
* @return filled-out submission info, or null
*/
public static SubmissionInfo getSubmissionInfo(Context context,
HttpServletRequest request) throws SQLException, ServletException
{
SubmissionInfo info = null;
// Is full Submission Info in Request Attribute?
if (request.getAttribute("submission.info") != null)
{
// load from cache
info = (SubmissionInfo) request.getAttribute("submission.info");
}
else
{
// Need to rebuild Submission Info from Request Parameters
if (request.getParameter("workflow_id") != null)
{
int workflowID = UIUtil.getIntParameter(request, "workflow_id");
info = SubmissionInfo.load(request, WorkflowItem.find(context, workflowID));
}
else if(request.getParameter("workspace_item_id") != null)
{
int workspaceID = UIUtil.getIntParameter(request,
"workspace_item_id");
info = SubmissionInfo.load(request, WorkspaceItem.find(context, workspaceID));
}
else
{
//by default, initialize Submission Info with no item
info = SubmissionInfo.load(request, null);
}
// We must have a submission object if after the first step,
// otherwise something is wrong!
if ((getStepReached(info) > FIRST_STEP)
&& (info.getSubmissionItem() == null))
{
log.warn(LogManager.getHeader(context,
"cannot_load_submission_info",
"InProgressSubmission is null!"));
return null;
}
if (request.getParameter("bundle_id") != null)
{
int bundleID = UIUtil.getIntParameter(request, "bundle_id");
info.setBundle(Bundle.find(context, bundleID));
}
if (request.getParameter("bitstream_id") != null)
{
int bitstreamID = UIUtil.getIntParameter(request,
"bitstream_id");
info.setBitstream(Bitstream.find(context, bitstreamID));
}
// save to Request Attribute
saveSubmissionInfo(request, info);
}// end if unable to load SubInfo from Request Attribute
return info;
}
/**
* Saves the submission info object to the current request.
*
* @param request
* HTTP request
* @param si
* the current submission info
*
*/
public static void saveSubmissionInfo(HttpServletRequest request,
SubmissionInfo si)
{
// save to request
request.setAttribute("submission.info", si);
}
/**
* Get the configuration of the current step from parameters in the request,
* along with the current SubmissionInfo object.
* If there is a problem, <code>null</code> is returned.
*
* @param request
* HTTP request
* @param si
* The current SubmissionInfo object
*
* @return the current SubmissionStepConfig
*/
public static SubmissionStepConfig getCurrentStepConfig(
HttpServletRequest request, SubmissionInfo si)
{
int stepNum = -1;
SubmissionStepConfig step = (SubmissionStepConfig) request
.getAttribute("step");
if (step == null)
{
// try and get it as a parameter
stepNum = UIUtil.getIntParameter(request, "step");
// if something is wrong, return null
if (stepNum < 0 || si == null || si.getSubmissionConfig() == null)
{
return null;
}
else
{
return si.getSubmissionConfig().getStep(stepNum);
}
}
else
{
return step;
}
}
/**
* Saves the current step configuration into the request.
*
* @param request
* HTTP request
* @param step
* The current SubmissionStepConfig
*/
public static void saveCurrentStepConfig(HttpServletRequest request,
SubmissionStepConfig step)
{
// save to request
request.setAttribute("step", step);
}
/**
* Checks if the current step is also the first "visibile" step in the item submission
* process.
*
* @param request
* HTTP request
* @param si
* The current Submission Info
*
* @return whether or not the current step is the first step
*/
public static boolean isFirstStep(HttpServletRequest request,
SubmissionInfo si)
{
SubmissionStepConfig step = getCurrentStepConfig(request, si);
if ((step != null) && (getPreviousVisibleStep(request, si) == null))
{
return true;
}
else
{
return false;
}
}
/**
* Return the previous "visibile" step in the item submission
* process if any, <code>null</code> otherwise.
*
* @param request
* HTTP request
* @param si
* The current Submission Info
*
* @return the previous step in the item submission process if any
*/
public static SubmissionStepConfig getPreviousVisibleStep(HttpServletRequest request,
SubmissionInfo si)
{
SubmissionStepConfig step = getCurrentStepConfig(request, si);
SubmissionStepConfig currentStepConfig, previousStep = null;
int currentStepNum = step.getStepNumber();
//need to find a previous step that is VISIBLE to the user!
while(currentStepNum>FIRST_STEP)
{
// update the current step & do this previous step
currentStepNum--;
//get previous step
currentStepConfig = si.getSubmissionConfig().getStep(currentStepNum);
if(currentStepConfig.isVisible())
{
previousStep = currentStepConfig;
break;
}
}
return previousStep;
}
/**
* Get whether or not the current step has just begun. This helps determine
* if we've done any pre-processing yet. If the step is just started, we
* need to do pre-processing, otherwise we should be doing post-processing.
* If there is a problem, <code>false</code> is returned.
*
* @param request
* HTTP request
*
* @return true if the step has just started (and JSP has not been loaded
* for this step), false otherwise.
*/
public static boolean isBeginningOfStep(HttpServletRequest request)
{
Boolean stepStart = (Boolean) request.getAttribute("step.start");
if (stepStart != null)
{
return stepStart.booleanValue();
}
else
{
return false;
}
}
/**
* Get whether or not the current step has just begun. This helps determine
* if we've done any pre-processing yet. If the step is just started, we
* need to do pre-processing, otherwise we should be doing post-processing.
* If there is a problem, <code>false</code> is returned.
*
* @param request
* HTTP request
* @param beginningOfStep
* true if step just began
*/
public static void setBeginningOfStep(HttpServletRequest request,
boolean beginningOfStep)
{
request.setAttribute("step.start", new Boolean(beginningOfStep));
}
/**
* Get whether or not a cancellation is in progress (i.e. the
* user clicked on the "Cancel/Save" button from any submission
* page).
*
* @param request
* HTTP request
*
* @return true if a cancellation is in progress
*/
public static boolean isCancellationInProgress(HttpServletRequest request)
{
Boolean cancellation = (Boolean) request.getAttribute("submission.cancellation");
if (cancellation != null)
{
return cancellation.booleanValue();
}
else
{
return false;
}
}
/**
* Sets whether or not a cancellation is in progress (i.e. the
* user clicked on the "Cancel/Save" button from any submission
* page).
*
* @param request
* HTTP request
* @param cancellationInProgress
* true if cancellation is in progress
*/
private static void setCancellationInProgress(HttpServletRequest request, boolean cancellationInProgress)
{
request.setAttribute("submission.cancellation", new Boolean(cancellationInProgress));
}
/**
* Return the submission info as hidden parameters for an HTML form on a JSP
* page.
*
* @param context
* DSpace context
* @param request
* HTTP request
* @return HTML hidden parameters
*/
public static String getSubmissionParameters(Context context,
HttpServletRequest request) throws SQLException, ServletException
{
SubmissionInfo si = getSubmissionInfo(context, request);
SubmissionStepConfig step = getCurrentStepConfig(request, si);
String info = "";
if ((si.getSubmissionItem() != null) && si.isInWorkflow())
{
info = info
+ "<input type=\"hidden\" name=\"workflow_id\" value=\""
+ si.getSubmissionItem().getID() + "\"/>";
}
else if (si.getSubmissionItem() != null)
{
info = info
+ "<input type=\"hidden\" name=\"workspace_item_id\" value=\""
+ si.getSubmissionItem().getID() + "\"/>";
}
if (si.getBundle() != null)
{
info = info + "<input type=\"hidden\" name=\"bundle_id\" value=\""
+ si.getBundle().getID() + "\"/>";
}
if (si.getBitstream() != null)
{
info = info
+ "<input type=\"hidden\" name=\"bitstream_id\" value=\""
+ si.getBitstream().getID() + "\"/>";
}
if (step != null)
{
info = info + "<input type=\"hidden\" name=\"step\" value=\""
+ step.getStepNumber() + "\"/>";
}
// save the current page from the current Step Servlet
int page = AbstractProcessingStep.getCurrentPage(request);
info = info + "<input type=\"hidden\" name=\"page\" value=\"" + page
+ "\"/>";
// save the current JSP name to a hidden variable
String jspDisplayed = JSPStepManager.getLastJSPDisplayed(request);
info = info + "<input type=\"hidden\" name=\"jsp\" value=\""
+ jspDisplayed + "\"/>";
return info;
}
/**
* Indicate the user has advanced to the given stage. This will only
* actually do anything when it's a user initially entering a submission. It
* will only increase the "stage reached" column - it will not "set back"
* where a user has reached. Whenever the "stage reached" column is
* increased, the "page reached" column is reset to 1, since you've now
* reached page #1 of the next stage.
*
* @param subInfo
* the SubmissionInfo object pertaining to the current submission
* @param step
* the step the user has just reached
*/
private void userHasReached(SubmissionInfo subInfo, int step)
throws SQLException, AuthorizeException, IOException
{
if (!subInfo.isInWorkflow() && subInfo.getSubmissionItem() != null)
{
WorkspaceItem wi = (WorkspaceItem) subInfo.getSubmissionItem();
if (step > wi.getStageReached())
{
wi.setStageReached(step);
wi.setPageReached(1); // reset page reached back to 1 (since
// it's page 1 of the new step)
wi.update();
}
}
}
/**
* Set a specific step and page as reached.
* It will also "set back" where a user has reached.
*
* @param subInfo
* the SubmissionInfo object pertaining to the current submission
* @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
*/
private void setReachedStepAndPage(SubmissionInfo subInfo, int step,
int page) throws SQLException, AuthorizeException, IOException
{
if (!subInfo.isInWorkflow() && subInfo.getSubmissionItem() != null)
{
WorkspaceItem wi = (WorkspaceItem) subInfo.getSubmissionItem();
wi.setStageReached(step);
wi.setPageReached(page);
wi.update();
}
}
/**
* Find out which step a user has reached in the submission process. If the
* submission is in the workflow process, this returns -1.
*
* @param subInfo
* submission info object
*
* @return step reached
*/
public static int getStepReached(SubmissionInfo subInfo)
{
if (subInfo == null || subInfo.isInWorkflow() || subInfo.getSubmissionItem() == null)
{
return -1;
}
else
{
WorkspaceItem wi = (WorkspaceItem) subInfo.getSubmissionItem();
int i = wi.getStageReached();
// Uninitialised workspace items give "-1" as the stage reached
// this is a special value used by the progress bar, so we change
// it to "FIRST_STEP"
if (i == -1)
{
i = FIRST_STEP;
}
return i;
}
}
/**
* Wraps a multipart form request, so that its attributes and parameters can
* still be accessed as normal.
*
* @return wrapped multipart request object
*
* @throws ServletException
* if there are no more pages in this step
*/
private HttpServletRequest wrapMultipartRequest(HttpServletRequest request)
throws ServletException
{
HttpServletRequest wrappedRequest;
try
{
// if not already wrapped
if (!Class.forName("org.dspace.app.webui.util.FileUploadRequest")
.isInstance(request))
{
// Wrap multipart request
wrappedRequest = new FileUploadRequest(request);
return (HttpServletRequest) wrappedRequest;
}
else
{ // already wrapped
return request;
}
}
catch (Exception e)
{
throw new ServletException(e);
}
}
/**
* Upload any files found on the Request, and save them back as
* Request attributes, for further processing by the appropriate user interface.
*
* @param context
* current DSpace context
* @param request
* current servlet request object
*/
public void uploadFiles(Context context, HttpServletRequest request)
throws ServletException
{
FileUploadRequest wrapper = null;
String filePath = null;
InputStream fileInputStream = null;
try
{
// if we already have a FileUploadRequest, use it
if (Class.forName("org.dspace.app.webui.util.FileUploadRequest")
.isInstance(request))
{
wrapper = (FileUploadRequest) request;
}
else
{
// Wrap multipart request to get the submission info
wrapper = new FileUploadRequest(request);
}
Enumeration fileParams = wrapper.getFileParameterNames();
while(fileParams.hasMoreElements())
{
String fileName = (String) fileParams.nextElement();
File temp = wrapper.getFile(fileName);
//if file exists and has a size greater than zero
if (temp != null && temp.length() > 0)
{
// Read the temp file into an inputstream
fileInputStream = new BufferedInputStream(
new FileInputStream(temp));
filePath = wrapper.getFilesystemName(fileName);
// cleanup our temp file
temp.delete();
//save this file's info to request (for UploadStep class)
request.setAttribute(fileName + "-path", filePath);
request.setAttribute(fileName + "-inputstream", fileInputStream);
request.setAttribute(fileName + "-description", wrapper.getParameter("description"));
}
}
}
catch (Exception e)
{
// Problem with uploading
log.warn(LogManager.getHeader(context, "upload_error", ""), e);
throw new ServletException(e);
}
}
}