/* * SubmissionInfo.java * * Version: $Revision: 3705 $ * * Date: $Date: 2009-04-11 18:02:24 +0100 (Sat, 11 Apr 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.util; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.HashMap; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.apache.log4j.Logger; import org.dspace.content.Bitstream; import org.dspace.content.Bundle; import org.dspace.content.InProgressSubmission; import org.dspace.submit.AbstractProcessingStep; import org.dspace.workflow.WorkflowItem; /** * Information about an item being editing with the submission UI * * @author Robert Tansley * @version $Revision: 3705 $ */ public class SubmissionInfo { /** log4j logger */ private static Logger log = Logger.getLogger(SubmissionInfo.class); /** The item which is being submitted */ private InProgressSubmission submissionItem = null; /** * The Submission process config, which holds all info about the submission * process that this item is going through (including all steps, etc) */ private SubmissionConfig submissionConfig = null; /** * Handle of the collection where this item is being submitted */ private String collectionHandle = null; /*************************************************************************** * Holds all information used to build the Progress Bar in a key,value set. * Keys are the number of the step, followed by the number of the page * within the step (e.g. "2.1" = The first page of Step 2) (e.g. "5.2" = The * second page of Step 5) Values are the Headings to display for each step * (e.g. "Describe") **************************************************************************/ private HashMap progressBar = null; /** The element or element_qualifier to show more input boxes for */ private String moreBoxesFor; /** The element or element_qualifier to scroll to initially using anchor */ private String jumpToField; /** If non-empty, form-relative indices of missing fields */ private List missingFields; /** Specific bundle we're dealing with */ private Bundle bundle; /** Specific bitstream we're dealing with */ private Bitstream bitstream; /** Reader for submission process configuration file * */ private static SubmissionConfigReader submissionConfigReader; /** * Default Constructor - PRIVATE * <p> * Create a SubmissionInfo object * using the load() method! * */ private SubmissionInfo() { } /** * Loads all known submission information based on the given in progress * submission and request object. * <P> * If subItem is null, then just loads the default submission information * for a new submission. * * @param request * The HTTP Servlet Request object * @param subItem * The in-progress submission we are loading information for * * @return a SubmissionInfo object * * @throws ServletException * if an error occurs */ public static SubmissionInfo load(HttpServletRequest request, InProgressSubmission subItem) throws ServletException { boolean forceReload = false; SubmissionInfo subInfo = new SubmissionInfo(); // load SubmissionConfigReader only the first time // or if we're using a different UI now. if (submissionConfigReader == null) { submissionConfigReader = new SubmissionConfigReader(); forceReload=true; } // save the item which is going through the submission process subInfo.setSubmissionItem(subItem); // Only if the submission item is created can we set its collection String collectionHandle = SubmissionConfigReader.DEFAULT_COLLECTION; if (subItem != null) { collectionHandle = subItem.getCollection().getHandle(); } // save this collection handle to this submission info object subInfo.setCollectionHandle(collectionHandle); // load Submission Process config for this item's collection // (Note: this also loads the Progress Bar info, since it is // dependent on the Submission config) loadSubmissionConfig(request, subInfo, forceReload); return subInfo; } /** * Is this submission in the workflow process? * * @return true if the current submission is in the workflow process */ public boolean isInWorkflow() { return ((this.submissionItem != null) && this.submissionItem instanceof WorkflowItem); } /** * Return the current in progress submission * * @return the InProgressSubmission object representing the current * submission */ public InProgressSubmission getSubmissionItem() { return this.submissionItem; } /** * Updates the current in progress submission item * * @param subItem * the new InProgressSubmission object */ public void setSubmissionItem(InProgressSubmission subItem) { this.submissionItem = subItem; } /** * Return the current submission process config (which includes all steps * which need to be completed for the submission to be successful) * * @return the SubmissionConfig object, which contains info on all the steps * in the current submission process */ public SubmissionConfig getSubmissionConfig() { return this.submissionConfig; } /** * Causes the SubmissionConfig to be completely reloaded from the XML * configuration file (item-submission.xml). * <P> * Note: This also reloads the progress bar info, since the progress bar * depends entirely on the submission process (and its steps). * * @param request * The HTTP Servlet Request object * * @throws ServletException * if an error occurs */ public void reloadSubmissionConfig(HttpServletRequest request) throws ServletException { // Only if the submission item is created can we set its collection String collectionHandle = SubmissionConfigReader.DEFAULT_COLLECTION; if (this.submissionItem != null) { collectionHandle = submissionItem.getCollection().getHandle(); } this.setCollectionHandle(collectionHandle); // force a reload of the submission process configuration loadSubmissionConfig(request, this, true); } /** * Returns a particular global step definition based on its ID. * <P> * Global step definitions are those defined in the <step-definitions> * section of the configuration file. * * @param stepID * step's identifier * * @return the SubmissionStepConfig representing the step * * @throws ServletException * if no default submission process configuration defined */ public SubmissionStepConfig getStepConfig(String stepID) throws ServletException { return submissionConfigReader.getStepConfig(stepID); } /** * Return text information suitable for logging. * <p> * This method is used by several of the Step classes * to log major events during the submission process (e.g. when * license agreement was accepted, when item was submitted, * when it was available in DSpace, etc.) * * @return the type and ID of the submission, bundle and/or bitstream for * logging */ public String getSubmissionLogInfo() { String info = ""; if (isInWorkflow()) { info = info + "workflow_id=" + getSubmissionItem().getID(); } else { info = info + "workspace_item_id" + getSubmissionItem().getID(); } if (getBundle() != null) { info = info + ",bundle_id=" + getBundle().getID(); } if (getBitstream() != null) { info = info + ",bitstream_id=" + getBitstream().getID(); } return info; } /** * Gets the handle of the collection to which this item is being submitted * * @return the collection handle */ public String getCollectionHandle() { return this.collectionHandle; } /** * Sets the handle of the collection to which this item is being submitted * * @param handle * the new collection handle */ public void setCollectionHandle(String handle) { this.collectionHandle = handle; } /** * Return the information used to build the progress bar (this includes all * the steps in this submission, as well as the ordering and names of the * steps). * <p> * Returns a Hashmap, with the following specifics: * <p> * Keys are the number of the step, followed by the number of the page * within the step * <p> * (e.g. "2.1" = The first page of Step 2) * <p> * (e.g. "5.2" = The second page of Step 5) * <P> * Values are the Headings to display for each step (e.g. "Describe") * * @return a Hashmap of Progress Bar information. */ public Map getProgressBarInfo() { return this.progressBar; } /** * Return the current bitstream we're working with (This is used during * upload processes, or user interfaces that are dealing with bitstreams) * * @return the Bitstream object for the bitstream */ public Bitstream getBitstream() { return this.bitstream; } /** * Sets the current bitstream we're working with (This is used during upload * processes, or user interfaces that are dealing with bitstreams) * * @param bits * the bitstream */ public void setBitstream(Bitstream bits) { this.bitstream = bits; } /** * Return the current bundle we're working with (This is used during upload * processes, or user interfaces that are dealing with bundles/bitstreams) * * @return the Bundle object for the bundle */ public Bundle getBundle() { return this.bundle; } /** * Sets the current bundle we're working with (This is used during upload * processes, or user interfaces that are dealing with bundles/bitstreams) * * @param bund * the bundle */ public void setBundle(Bundle bund) { this.bundle = bund; } /** * Return form related indices of the required fields which were not filled * out by the user. * * @return a List of empty fields which are required */ public List getMissingFields() { return this.missingFields; } /** * Sets the form related indices of the required fields which were not * filled out by the user. * * @param missing * the List of empty fields which are required */ public void setMissingFields(List missing) { this.missingFields = missing; } /** * Return metadata field which user has requested more input boxes be * displayed (by pressing "Add More" on one of the "Describe" pages) * * @return the String name of the field element */ public String getMoreBoxesFor() { return this.moreBoxesFor; } /** * Sets the metadata field which user has requested more input boxes be * displayed (by pressing "Add More" on one of the "Describe" pages) * * @param fieldname * the name of the field element on the page */ public void setMoreBoxesFor(String fieldname) { this.moreBoxesFor = fieldname; } /** * Return metadata field which JSP should "jump to" (i.e. set focus on) when * the JSP next loads. This is used during the Describe step. * * @return the String name of the field element */ public String getJumpToField() { return this.jumpToField; } /** * Sets metadata field which JSP should "jump to" (i.e. set focus on) when * the JSP next loads. This is used during the Describe step. * * @param fieldname * the name of the field on the page */ public void setJumpToField(String fieldname) { this.jumpToField = fieldname; } /** * Load necessary information to build the Progress Bar for the Item * Submission Progress. * * This information is returned in the form of a HashMap (which is then * stored as a part of the SubmissionInfo). The HashMap takes the following * form: * * Keys - the number of the step, followed by the number of the page within * the step (e.g. "2.1" = The first page of Step 2) (e.g. "5.2" = The second * page of Step 5) * * Values - the headings to display for each step (e.g. "Describe", * "Verify") * * @param request * The HTTP Servlet Request object * @param subInfo * the SubmissionInfo object we are loading into * @param forceReload * If true, this method reloads from stratch (and overwrites * cached progress bar info) * */ private static void loadProgressBar(HttpServletRequest request, SubmissionInfo subInfo, boolean forceReload) { LinkedHashMap progressBarInfo = null; log.debug("Loading Progress Bar Info"); if (!forceReload) { // first, attempt to load from cache progressBarInfo = (LinkedHashMap) loadProgressBarFromCache(request .getSession()); } if (progressBarInfo != null && log.isDebugEnabled()) { log.debug("Found Progress Bar Info in cache: " + progressBarInfo.size() + " pages to display in progress bar"); } // if unable to load from cache, must load from scratch else { progressBarInfo = new LinkedHashMap(); // loop through all steps for (int i = 0; i < subInfo.submissionConfig.getNumberOfSteps(); i++) { // get the current step info SubmissionStepConfig currentStep = subInfo.submissionConfig .getStep(i); String stepNumber = Integer.toString(currentStep .getStepNumber()); String stepHeading = currentStep.getHeading(); String processingClass = currentStep.getProcessingClassName(); // as long as this step is visible, include it in // the Progress Bar if (currentStep.isVisible()) { // 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(processingClass); // 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 progress bar 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++) { String pageNumber = Integer.toString(j); /***Added by CG ****/ //Nasty hack to rename the progress buttons for licence steps if(processingClass.equals("org.dspace.submit.step.jorum.JorumLicenseStep")){ if(j==1){ stepHeading=currentStep.getCCHeading(); }else{ stepHeading=currentStep.getTermsHeading(); } } /*******/ // store ("stepNumber.pageNumber", Heading) for each // page in the step progressBarInfo.put(stepNumber + "." + pageNumber, stepHeading); }// end for each page } }// end for each step log.debug("Loaded Progress Bar Info from scratch: " + progressBarInfo.size() + " pages to display in progress bar"); // cache this new progress bar saveProgressBarToCache(request.getSession(), progressBarInfo); }// end if null // save progressBarInfo to submission Info subInfo.progressBar = progressBarInfo; } /** * Saves all progress bar information into session cache. This saves us from * having to reload this same progress bar over and over again. * * @param session * The HTTP Session object * @param progressBarInfo * The progress bar info to cache * */ private static void saveProgressBarToCache(HttpSession session, HashMap progressBarInfo) { // cache progress bar info to Session session.setAttribute("submission.progressbar", progressBarInfo); } /** * Attempts to retrieve progress bar information (for a particular * collection) from session cache. * * If the progress bar info cannot be found, returns null * * @param session * The HTTP Session object * * @return progressBarInfo HashMap (if found), or null (if not) * */ private static HashMap loadProgressBarFromCache(HttpSession session) { return (HashMap) session.getAttribute("submission.progressbar"); } /** * Loads SubmissionConfig object for the given submission info object. If a * SubmissionConfig object cannot be loaded, a Servlet Error is thrown. * <p> * This method just loads this SubmissionConfig object internally, so that * it is available via a call to "getSubmissionConfig()" * * @param request * The HTTP Servlet Request object * @param subInfo * the SubmissionInfo object we are loading into * @param forceReload * If true, this method reloads from stratch (and overwrites * cached SubmissionConfig) * */ private static void loadSubmissionConfig(HttpServletRequest request, SubmissionInfo subInfo, boolean forceReload) throws ServletException { log.debug("Loading Submission Config information"); if (!forceReload) { // first, try to load from cache subInfo.submissionConfig = loadSubmissionConfigFromCache(request .getSession(), subInfo.getCollectionHandle(), subInfo .isInWorkflow()); } if (subInfo.submissionConfig == null || forceReload) { // reload the proper Submission process config // (by reading the XML config file) subInfo.submissionConfig = submissionConfigReader .getSubmissionConfig(subInfo.getCollectionHandle(), subInfo .isInWorkflow()); // cache this new submission process configuration saveSubmissionConfigToCache(request.getSession(), subInfo.submissionConfig, subInfo.getCollectionHandle(), subInfo.isInWorkflow()); // also must force reload Progress Bar info, // since it's based on the Submission config loadProgressBar(request, subInfo, true); } else { log.debug("Found Submission Config in session cache!"); // try and reload progress bar from cache loadProgressBar(request, subInfo, false); } } /** * Saves SubmissionConfig object into session cache. This saves us from * having to reload this object during every "Step". * * @param session * The HTTP Session object * @param subConfig * The SubmissionConfig to cache * @param collectionHandle * The Collection handle this SubmissionConfig corresponds to * @param isWorkflow * Whether this SubmissionConfig corresponds to a workflow * * */ private static void saveSubmissionConfigToCache(HttpSession session, SubmissionConfig subConfig, String collectionHandle, boolean isWorkflow) { // cache the submission process config // and the collection it corresponds to session.setAttribute("submission.config", subConfig); session.setAttribute("submission.config.collection", collectionHandle); session.setAttribute("submission.config.isWorkflow", new Boolean( isWorkflow)); } /** * Loads SubmissionConfig object from session cache for the given * Collection. If a SubmissionConfig object cannot be found, null is * returned. * * @param session * The HTTP Session object * @param collectionHandle * The Collection handle of the SubmissionConfig to load * @param isWorkflow * whether or not we loading the Submission process for a * workflow item * * @return The cached SubmissionConfig for this collection */ private static SubmissionConfig loadSubmissionConfigFromCache( HttpSession session, String collectionHandle, boolean isWorkflow) { // attempt to load submission process config // from cache for the current collection String cachedHandle = (String) session .getAttribute("submission.config.collection"); Boolean cachedIsWorkflow = (Boolean) session .getAttribute("submission.config.isWorkflow"); // only load from cache if the collection handle and // workflow item status both match! if (collectionHandle.equals(cachedHandle) && isWorkflow == cachedIsWorkflow.booleanValue()) { return (SubmissionConfig) session.getAttribute("submission.config"); } else { return null; } } }