/**
* 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.submit.step;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Date;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.time.DateUtils;
import org.apache.log4j.Logger;
import org.dspace.app.util.SubmissionInfo;
import org.dspace.app.util.Util;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.AuthorizeManager;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.content.*;
import org.dspace.core.Context;
import org.dspace.core.ConfigurationManager;
import org.dspace.curate.Curator;
import org.dspace.handle.HandleManager;
/**
* Upload step with the advanced embargo system for DSpace. Processes the actual
* upload of files for an item being submitted into DSpace.
* <P>
* This class performs all the behind-the-scenes processing that
* this particular step requires. This class's methods are utilized
* by both the JSP-UI and the Manakin XML-UI
*
* @see org.dspace.app.util.SubmissionConfig
* @see org.dspace.app.util.SubmissionStepConfig
* @see org.dspace.submit.step.UploadStep
* @see org.dspace.submit.AbstractProcessingStep
*
* @author Tim Donohue
* @author Keiji Suzuki
* @version $Revision$
*/
public class UploadWithEmbargoStep extends UploadStep
{
public static final int STATUS_EDIT_POLICIES = 30;
public static final int STATUS_EDIT_POLICIES_ERROR_SELECT_GROUP = 31;
public static final int STATUS_EDIT_POLICIES_DUPLICATED_POLICY = 32;
public static final int STATUS_EDIT_POLICY_ERROR_SELECT_GROUP = 33;
public static final int STATUS_EDIT_POLICY_DUPLICATED_POLICY = 34;
/** log4j logger */
private static Logger log = Logger.getLogger(UploadWithEmbargoStep.class);
/**
* Do any processing of the information input by the user, and/or perform
* step processing (if no user interaction required)
* <P>
* It is this method's job to save any data to the underlying database, as
* necessary, and return error messages (if any) which can then be processed
* by the appropriate user interface (JSP-UI or XML-UI)
* <P>
* NOTE: If this step is a non-interactive step (i.e. requires no UI), then
* it should perform *all* of its processing in this method!
*
* @param context
* current DSpace context
* @param request
* current servlet request object
* @param response
* current servlet response object
* @param subInfo
* submission info object
* @return Status or error flag which will be processed by
* doPostProcessing() below! (if STATUS_COMPLETE or 0 is returned,
* no errors occurred!)
*/
public int doProcessing(Context context, HttpServletRequest request,
HttpServletResponse response, SubmissionInfo subInfo)
throws ServletException, IOException, SQLException,
AuthorizeException
{
// get button user pressed
String buttonPressed = Util.getSubmitButton(request, NEXT_BUTTON);
// get reference to item
Item item = subInfo.getSubmissionItem().getItem();
// -----------------------------------
// Step #0: Upload new files (if any)
// -----------------------------------
String contentType = request.getContentType();
// if multipart form, then we are uploading a file
if ((contentType != null)
&& (contentType.indexOf("multipart/form-data") != -1))
{
// This is a multipart request, so it's a file upload
// (return any status messages or errors reported)
int status = processUploadFile(context, request, response, subInfo);
// if error occurred, return immediately
if (status != STATUS_COMPLETE)
{
return status;
}
}
// if user pressed jump-to button in process bar,
// return success (so that jump will occur)
if (buttonPressed.startsWith(PROGRESS_BAR_PREFIX) ||
buttonPressed.startsWith(PREVIOUS_BUTTON))
{
// check if a file is required to be uploaded
if (fileRequired && !item.hasUploadedFiles())
{
return STATUS_NO_FILES_ERROR;
}
else
{
return STATUS_COMPLETE;
}
}
// POLICIES FORM MANAGEMENT
int result = editBitstreamPolicies(request, context, subInfo, buttonPressed);
if(result != -1) return result;
// ---------------------------------------------
// Step #1: Check if this was just a request to
// edit file information.
// (or canceled editing information)
// ---------------------------------------------
// check if we're already editing a specific bitstream
if (request.getParameter("bitstream_id") != null)
{
if (buttonPressed.equals(CANCEL_EDIT_BUTTON))
{
// canceled an edit bitstream request
subInfo.setBitstream(null);
// this flag will just return us to the normal upload screen
return STATUS_EDIT_COMPLETE;
}
else
{
// load info for bitstream we are editing
Bitstream b = Bitstream.find(context, Integer.parseInt(request
.getParameter("bitstream_id")));
// save bitstream to submission info
subInfo.setBitstream(b);
}
}
else if (buttonPressed.startsWith("submit_edit_"))
{
// get ID of bitstream that was requested for editing
String bitstreamID = buttonPressed.substring("submit_edit_"
.length());
Bitstream b = Bitstream
.find(context, Integer.parseInt(bitstreamID));
// save bitstream to submission info
subInfo.setBitstream(b);
// return appropriate status flag to say we are now editing the
// bitstream
return STATUS_EDIT_BITSTREAM;
}
// ---------------------------------------------
// Step #2: Process any remove file request(s)
// ---------------------------------------------
// Remove-selected requests come from Manakin
if (buttonPressed.equalsIgnoreCase("submit_remove_selected"))
{
// this is a remove multiple request!
if (request.getParameter("remove") != null)
{
// get all files to be removed
String[] removeIDs = request.getParameterValues("remove");
// remove each file in the list
for (int i = 0; i < removeIDs.length; i++)
{
int id = Integer.parseInt(removeIDs[i]);
int status = processRemoveFile(context, item, id);
// if error occurred, return immediately
if (status != STATUS_COMPLETE)
{
return status;
}
}
// remove current bitstream from Submission Info
subInfo.setBitstream(null);
}
}
else if (buttonPressed.startsWith("submit_remove_"))
{
// A single file "remove" button must have been pressed
int id = Integer.parseInt(buttonPressed.substring(14));
int status = processRemoveFile(context, item, id);
// if error occurred, return immediately
if (status != STATUS_COMPLETE)
{
return status;
}
// remove current bitstream from Submission Info
subInfo.setBitstream(null);
}
// -------------------------------------------------
// Step #3: Check for a change in file description
// -------------------------------------------------
String fileDescription = request.getParameter("description");
if (fileDescription != null && fileDescription.length() > 0)
{
// save this file description
int status = processSaveFileDescription(context, request, response,
subInfo);
// if error occurred, return immediately
if (status != STATUS_COMPLETE)
{
return status;
}
}
// ------------------------------------------
// Step #4: Check for a file format change
// (if user had to manually specify format)
// ------------------------------------------
int formatTypeID = Util.getIntParameter(request, "format");
String formatDesc = request.getParameter("format_description");
// if a format id or description was found, then save this format!
if (formatTypeID >= 0
|| (formatDesc != null && formatDesc.length() > 0))
{
// save this specified format
int status = processSaveFileFormat(context, request, response,
subInfo);
// if error occurred, return immediately
if (status != STATUS_COMPLETE)
{
return status;
}
}
// execute only if comes from EditBitstreamStep
if(buttonPressed.equals("submit_save")){
processAccessFields(context, request, subInfo, subInfo.getBitstream());
}
// ---------------------------------------------------
// Step #5: Check if primary bitstream has changed
// -------------------------------------------------
if (request.getParameter("primary_bitstream_id") != null)
{
Bundle[] bundles = item.getBundles("ORIGINAL");
if (bundles.length > 0)
{
bundles[0].setPrimaryBitstreamID(Integer.valueOf(request
.getParameter("primary_bitstream_id")).intValue());
bundles[0].update();
}
}
// ---------------------------------------------------
// Step #6: Determine if there is an error because no
// files have been uploaded.
// ---------------------------------------------------
//check if a file is required to be uploaded
if (fileRequired && !item.hasUploadedFiles())
{
return STATUS_NO_FILES_ERROR;
}
return STATUS_COMPLETE;
}
/**
* Process the upload of a new file!
*
* @param context
* current DSpace context
* @param request
* current servlet request object
* @param response
* current servlet response object
* @param subInfo
* submission info object
*
* @return Status or error flag which will be processed by
* UI-related code! (if STATUS_COMPLETE or 0 is returned,
* no errors occurred!)
*/
public int processUploadFile(Context context, HttpServletRequest request,
HttpServletResponse response, SubmissionInfo subInfo)
throws ServletException, IOException, SQLException,
AuthorizeException
{
boolean formatKnown = true;
boolean fileOK = false;
BitstreamFormat bf = null;
Bitstream b = null;
//NOTE: File should already be uploaded.
//Manakin does this automatically via Cocoon.
//For JSP-UI, the SubmissionController.uploadFiles() does the actual upload
Enumeration attNames = request.getAttributeNames();
//loop through our request attributes
while(attNames.hasMoreElements())
{
String attr = (String) attNames.nextElement();
//if this ends with "-path", this attribute
//represents a newly uploaded file
if(attr.endsWith("-path"))
{
//strip off the -path to get the actual parameter
//that the file was uploaded as
String param = attr.replace("-path", "");
// Load the file's path and input stream and description
String filePath = (String) request.getAttribute(param + "-path");
InputStream fileInputStream = (InputStream) request.getAttribute(param + "-inputstream");
//attempt to get description from attribute first, then direct from a parameter
String fileDescription = (String) request.getAttribute(param + "-description");
if(fileDescription==null ||fileDescription.length()==0)
{
request.getParameter("description");
}
// if information wasn't passed by User Interface, we had a problem
// with the upload
if (filePath == null || fileInputStream == null)
{
return STATUS_UPLOAD_ERROR;
}
if (subInfo == null)
{
// In any event, if we don't have the submission info, the request
// was malformed
return STATUS_INTEGRITY_ERROR;
}
// Create the bitstream
Item item = subInfo.getSubmissionItem().getItem();
// do we already have a bundle?
Bundle[] bundles = item.getBundles("ORIGINAL");
if (bundles.length < 1)
{
// set bundle's name to ORIGINAL
b = item.createSingleBitstream(fileInputStream, "ORIGINAL");
}
else
{
// we have a bundle already, just add bitstream
b = bundles[0].createBitstream(fileInputStream);
}
// Strip all but the last filename. It would be nice
// to know which OS the file came from.
String noPath = filePath;
while (noPath.indexOf('/') > -1)
{
noPath = noPath.substring(noPath.indexOf('/') + 1);
}
while (noPath.indexOf('\\') > -1)
{
noPath = noPath.substring(noPath.indexOf('\\') + 1);
}
b.setName(noPath);
b.setSource(filePath);
b.setDescription(fileDescription);
// Identify the format
bf = FormatIdentifier.guessFormat(context, b);
b.setFormat(bf);
// Update to DB
b.update();
item.update();
processAccessFields(context, request, subInfo, b);
// commit all changes to database
context.commit();
if ((bf != null) && (bf.isInternal()))
{
log.warn("Attempt to upload file format marked as internal system use only");
backoutBitstream(subInfo, b, item);
return STATUS_UPLOAD_ERROR;
}
// Check for virus
if (ConfigurationManager.getBooleanProperty("submission-curation", "virus-scan"))
{
Curator curator = new Curator();
curator.addTask("vscan").curate(item);
int status = curator.getStatus("vscan");
if (status == Curator.CURATE_ERROR)
{
backoutBitstream(subInfo, b, item);
return STATUS_VIRUS_CHECKER_UNAVAILABLE;
}
else if (status == Curator.CURATE_FAIL)
{
backoutBitstream(subInfo, b, item);
return STATUS_CONTAINS_VIRUS;
}
}
// If we got this far then everything is more or less ok.
// Comment - not sure if this is the right place for a commit here
// but I'm not brave enough to remove it - Robin.
context.commit();
// save this bitstream to the submission info, as the
// bitstream we're currently working with
subInfo.setBitstream(b);
//if format was not identified
if (bf == null)
{
return STATUS_UNKNOWN_FORMAT;
}
}//end if attribute ends with "-path"
}//end while
return STATUS_COMPLETE;
}
private void processAccessFields(Context context, HttpServletRequest request, SubmissionInfo subInfo, Bitstream b) throws SQLException, AuthorizeException {
// ResourcePolicy Management
boolean isAdvancedFormEnabled= ConfigurationManager.getBooleanProperty("webui.submission.restrictstep.enableAdvancedForm", false);
// if it is a simple form we should create the policy for Anonymous
// if Anonymous does not have right on this collection, create policies for any other groups with
// DEFAULT_ITEM_READ specified.
if(!isAdvancedFormEnabled){
Date startDate = null;
try {
startDate = DateUtils.parseDate(request.getParameter("embargo_until_date"), new String[]{"yyyy-MM-dd", "yyyy-MM", "yyyy"});
} catch (Exception e) {
//Ignore start date already null
}
String reason = request.getParameter("reason");
AuthorizeManager.generateAutomaticPolicies(context, startDate, reason, b, (Collection) HandleManager.resolveToObject(context, subInfo.getCollectionHandle()));
}
}
private int editBitstreamPolicies(HttpServletRequest request, Context context, SubmissionInfo subInfo, String buttonPressed)
throws SQLException, AuthorizeException {
// FORM: EditBitstreamPolicies SELECTED OPERATION: Return
if (buttonPressed.equals("bitstream_list_submit_return")){
return STATUS_COMPLETE;
}
// FORM: UploadStep SELECTED OPERATION: go to EditBitstreamPolicies
else if (buttonPressed.startsWith("submit_editPolicy_")){
String bitstreamID = buttonPressed.substring("submit_editPolicy_".length());
Bitstream b = Bitstream.find(context, Integer.parseInt(bitstreamID));
subInfo.setBitstream(b);
return STATUS_EDIT_POLICIES;
}
// FORM: EditBitstreamPolicies SELECTED OPERATION: Add New Policy.
else if (buttonPressed.startsWith(AccessStep.FORM_ACCESS_BUTTON_ADD)){
Bitstream b = Bitstream.find(context, Integer.parseInt(request.getParameter("bitstream_id")));
subInfo.setBitstream(b);
int result=-1;
if( (result = AccessStep.checkForm(request))!=0){
return result;
}
Date dateStartDate = AccessStep.getEmbargoUntil(request);
String reason = request.getParameter("reason");
String name = request.getParameter("name");
int groupID = 0;
if(request.getParameter("group_id")!=null){
try{
groupID=Integer.parseInt(request.getParameter("group_id"));
}catch (NumberFormatException nfe){
return STATUS_EDIT_POLICIES_ERROR_SELECT_GROUP;
}
}
ResourcePolicy rp = null;
if( (rp= AuthorizeManager.createOrModifyPolicy(null, context, name, groupID, null, dateStartDate, org.dspace.core.Constants.READ, reason, b))==null){
return STATUS_EDIT_POLICIES_DUPLICATED_POLICY;
}
rp.update();
context.commit();
return STATUS_EDIT_POLICIES;
}
// FORM: EditBitstreamPolicies SELECTED OPERATION: go to EditPolicyForm
else if(org.dspace.submit.step.AccessStep.wasEditPolicyPressed(context, buttonPressed, subInfo)){
Bitstream b = Bitstream.find(context, Integer.parseInt(request.getParameter("bitstream_id")));
subInfo.setBitstream(b);
return org.dspace.submit.step.AccessStep.STATUS_EDIT_POLICY;
}
// FORM: EditPolicy SELECTED OPERATION: Save or Cancel.
else if(org.dspace.submit.step.AccessStep.comeFromEditPolicy(request)) {
Bitstream b = Bitstream.find(context, Integer.parseInt(request.getParameter("bitstream_id")));
subInfo.setBitstream(b);
String reason = request.getParameter("reason");
String name = request.getParameter("name");
int groupID = 0;
if(request.getParameter("group_id")!=null){
try{
groupID=Integer.parseInt(request.getParameter("group_id"));
}catch (NumberFormatException nfe){
return STATUS_EDIT_POLICIES_ERROR_SELECT_GROUP;
}
}
if(org.dspace.submit.step.AccessStep.saveOrCancelEditPolicy(context, request,
subInfo, buttonPressed, b, name, groupID, reason)==org.dspace.submit.step.AccessStep.EDIT_POLICY_STATUS_DUPLICATED_POLICY)
return STATUS_EDIT_POLICY_DUPLICATED_POLICY;
return STATUS_EDIT_POLICIES;
}
// FORM: EditBitstreamPolicies SELECTED OPERATION: Remove Policies
if(org.dspace.submit.step.AccessStep.wasRemovePolicyPressed(buttonPressed)){
Bitstream b = Bitstream.find(context, Integer.parseInt(request.getParameter("bitstream_id")));
subInfo.setBitstream(b);
org.dspace.submit.step.AccessStep.removePolicy(context, buttonPressed);
context.commit();
return STATUS_EDIT_POLICIES;
}
return -1;
}
}