/* * WorkflowManager.java * * Version: $Revision: 3738 $ * * Date: $Date: 2009-04-24 04:32:12 +0000 (Fri, 24 Apr 2009) $ * * Copyright (c) 2002-2009, The DSpace Foundation. 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 DSpace Foundation nor the names of its * 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.workflow; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.MissingResourceException; import java.util.ResourceBundle; import javax.mail.MessagingException; import org.apache.log4j.Logger; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.AuthorizeManager; import org.dspace.content.Bitstream; import org.dspace.content.Collection; import org.dspace.content.DCDate; import org.dspace.content.DCValue; import org.dspace.content.InstallItem; import org.dspace.content.Item; import org.dspace.content.WorkspaceItem; import org.dspace.core.ConfigurationManager; import org.dspace.core.Context; import org.dspace.core.Email; import org.dspace.core.I18nUtil; import org.dspace.core.LogManager; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.dspace.handle.HandleManager; import org.dspace.storage.rdbms.DatabaseManager; import org.dspace.storage.rdbms.TableRow; import org.dspace.storage.rdbms.TableRowIterator; /** * Workflow state machine * * Notes: * * Determining item status from the database: * * When an item has not been submitted yet, it is in the user's personal * workspace (there is a row in PersonalWorkspace pointing to it.) * * When an item is submitted and is somewhere in a workflow, it has a row in the * WorkflowItem table pointing to it. The state of the workflow can be * determined by looking at WorkflowItem.getState() * * When a submission is complete, the WorkflowItem pointing to the item is * destroyed and the archive() method is called, which hooks the item up to the * archive. * * Notification: When an item enters a state that requires notification, * (WFSTATE_STEP1POOL, WFSTATE_STEP2POOL, WFSTATE_STEP3POOL,) the workflow needs * to notify the appropriate groups that they have a pending task to claim. * * Revealing lists of approvers, editors, and reviewers. A method could be added * to do this, but it isn't strictly necessary. (say public List * getStateEPeople( WorkflowItem wi, int state ) could return people affected by * the item's current state. */ public class WorkflowManager { // states to store in WorkflowItem for the GUI to report on // fits our current set of workflow states (stored in WorkflowItem.state) public static final int WFSTATE_SUBMIT = 0; // hmm, probably don't need public static final int WFSTATE_STEP1POOL = 1; // waiting for a reviewer to // claim it public static final int WFSTATE_STEP1 = 2; // task - reviewer has claimed it public static final int WFSTATE_STEP2POOL = 3; // waiting for an admin to // claim it public static final int WFSTATE_STEP2 = 4; // task - admin has claimed item public static final int WFSTATE_STEP3POOL = 5; // waiting for an editor to // claim it public static final int WFSTATE_STEP3 = 6; // task - editor has claimed the // item public static final int WFSTATE_ARCHIVE = 7; // probably don't need this one // either /** Symbolic names of workflow steps. */ public static final String workflowText[] = { "SUBMIT", // 0 "STEP1POOL", // 1 "STEP1", // 2 "STEP2POOL", // 3 "STEP2", // 4 "STEP3POOL", // 5 "STEP3", // 6 "ARCHIVE" // 7 }; /* support for 'no notification' */ private static Map noEMail = new HashMap(); /** log4j logger */ private static Logger log = Logger.getLogger(WorkflowManager.class); /** * Translate symbolic name of workflow state into number. * The name is case-insensitive. Returns -1 when name cannot * be matched. * @param state symbolic name of workflow state, must be one of * the elements of workflowText array. * @return numeric workflow state or -1 for error. */ public static int getWorkflowID(String state) { for (int i = 0; i < workflowText.length; ++i) if (state.equalsIgnoreCase(workflowText[i])) return i; return -1; } /** * startWorkflow() begins a workflow - in a single transaction do away with * the PersonalWorkspace entry and turn it into a WorkflowItem. * * @param c * Context * @param wsi * The WorkspaceItem to convert to a workflow item * @return The resulting workflow item */ public static WorkflowItem start(Context c, WorkspaceItem wsi) throws SQLException, AuthorizeException, IOException { // FIXME Check auth Item myitem = wsi.getItem(); Collection collection = wsi.getCollection(); log.info(LogManager.getHeader(c, "start_workflow", "workspace_item_id=" + wsi.getID() + "item_id=" + myitem.getID() + "collection_id=" + collection.getID())); // record the start of the workflow w/provenance message recordStart(c, myitem); // create the WorkflowItem TableRow row = DatabaseManager.create(c, "workflowitem"); row.setColumn("item_id", myitem.getID()); row.setColumn("collection_id", wsi.getCollection().getID()); WorkflowItem wfi = new WorkflowItem(c, row); wfi.setMultipleFiles(wsi.hasMultipleFiles()); wfi.setMultipleTitles(wsi.hasMultipleTitles()); wfi.setPublishedBefore(wsi.isPublishedBefore()); // remove the WorkspaceItem wsi.deleteWrapper(); // now get the worflow started doState(c, wfi, WFSTATE_STEP1POOL, null); // Return the workflow item return wfi; } /** * startWithoutNotify() starts the workflow normally, but disables * notifications (useful for large imports,) for the first workflow step - * subsequent notifications happen normally */ public static WorkflowItem startWithoutNotify(Context c, WorkspaceItem wsi) throws SQLException, AuthorizeException, IOException { // make a hash table entry with item ID for no notify // notify code checks no notify hash for item id noEMail.put(new Integer(wsi.getItem().getID()), new Boolean(true)); return start(c, wsi); } /** * getOwnedTasks() returns a List of WorkflowItems containing the tasks * claimed and owned by an EPerson. The GUI displays this info on the * MyDSpace page. * * @param e * The EPerson we want to fetch owned tasks for. */ public static List getOwnedTasks(Context c, EPerson e) throws java.sql.SQLException { ArrayList mylist = new ArrayList(); String myquery = "SELECT * FROM WorkflowItem WHERE owner= ? "; TableRowIterator tri = DatabaseManager.queryTable(c, "workflowitem", myquery,e.getID()); try { while (tri.hasNext()) { mylist.add(new WorkflowItem(c, tri.next())); } } finally { if (tri != null) tri.close(); } return mylist; } /** * getPooledTasks() returns a List of WorkflowItems an EPerson could claim * (as a reviewer, etc.) for display on a user's MyDSpace page. * * @param e * The Eperson we want to fetch the pooled tasks for. */ public static List getPooledTasks(Context c, EPerson e) throws SQLException { ArrayList mylist = new ArrayList(); String myquery = "SELECT workflowitem.* FROM workflowitem, TaskListItem" + " WHERE tasklistitem.eperson_id= ? " + " AND tasklistitem.workflow_id=workflowitem.workflow_id"; TableRowIterator tri = DatabaseManager .queryTable(c, "workflowitem", myquery, e.getID()); try { while (tri.hasNext()) { mylist.add(new WorkflowItem(c, tri.next())); } } finally { if (tri != null) tri.close(); } return mylist; } /** * claim() claims a workflow task for an EPerson * * @param wi * WorkflowItem to do the claim on * @param e * The EPerson doing the claim */ public static void claim(Context c, WorkflowItem wi, EPerson e) throws SQLException, IOException, AuthorizeException { int taskstate = wi.getState(); switch (taskstate) { case WFSTATE_STEP1POOL: // authorize DSpaceActions.SUBMIT_REVIEW doState(c, wi, WFSTATE_STEP1, e); break; case WFSTATE_STEP2POOL: // authorize DSpaceActions.SUBMIT_STEP2 doState(c, wi, WFSTATE_STEP2, e); break; case WFSTATE_STEP3POOL: // authorize DSpaceActions.SUBMIT_STEP3 doState(c, wi, WFSTATE_STEP3, e); break; // if we got here, we weren't pooled... error? // FIXME - log the error? } log.info(LogManager.getHeader(c, "claim_task", "workflow_item_id=" + wi.getID() + "item_id=" + wi.getItem().getID() + "collection_id=" + wi.getCollection().getID() + "newowner_id=" + wi.getOwner().getID() + "old_state=" + taskstate + "new_state=" + wi.getState())); } /** * approveAction() sends an item forward in the workflow (reviewers, * approvers, and editors all do an 'approve' to move the item forward) if * the item arrives at the submit state, then remove the WorkflowItem and * call the archive() method to put it in the archive, and email notify the * submitter of a successful submission * * @param c * Context * @param wi * WorkflowItem do do the approval on * @param e * EPerson doing the approval */ public static void advance(Context c, WorkflowItem wi, EPerson e) throws SQLException, IOException, AuthorizeException { int taskstate = wi.getState(); switch (taskstate) { case WFSTATE_STEP1: // authorize DSpaceActions.SUBMIT_REVIEW // Record provenance recordApproval(c, wi, e); doState(c, wi, WFSTATE_STEP2POOL, e); break; case WFSTATE_STEP2: // authorize DSpaceActions.SUBMIT_STEP2 // Record provenance recordApproval(c, wi, e); doState(c, wi, WFSTATE_STEP3POOL, e); break; case WFSTATE_STEP3: // authorize DSpaceActions.SUBMIT_STEP3 // We don't record approval for editors, since they can't reject, // and thus didn't actually make a decision doState(c, wi, WFSTATE_ARCHIVE, e); break; // error handling? shouldn't get here } log.info(LogManager.getHeader(c, "advance_workflow", "workflow_item_id=" + wi.getID() + ",item_id=" + wi.getItem().getID() + ",collection_id=" + wi.getCollection().getID() + ",old_state=" + taskstate + ",new_state=" + wi.getState())); } /** * unclaim() returns an owned task/item to the pool * * @param c * Context * @param wi * WorkflowItem to operate on * @param e * EPerson doing the operation */ public static void unclaim(Context c, WorkflowItem wi, EPerson e) throws SQLException, IOException, AuthorizeException { int taskstate = wi.getState(); switch (taskstate) { case WFSTATE_STEP1: // authorize DSpaceActions.STEP1 doState(c, wi, WFSTATE_STEP1POOL, e); break; case WFSTATE_STEP2: // authorize DSpaceActions.APPROVE doState(c, wi, WFSTATE_STEP2POOL, e); break; case WFSTATE_STEP3: // authorize DSpaceActions.STEP3 doState(c, wi, WFSTATE_STEP3POOL, e); break; // error handling? shouldn't get here // FIXME - what to do with error - log it? } log.info(LogManager.getHeader(c, "unclaim_workflow", "workflow_item_id=" + wi.getID() + ",item_id=" + wi.getItem().getID() + ",collection_id=" + wi.getCollection().getID() + ",old_state=" + taskstate + ",new_state=" + wi.getState())); } /** * abort() aborts a workflow, completely deleting it (administrator do this) * (it will basically do a reject from any state - the item ends up back in * the user's PersonalWorkspace * * @param c * Context * @param wi * WorkflowItem to operate on * @param e * EPerson doing the operation */ public static void abort(Context c, WorkflowItem wi, EPerson e) throws SQLException, AuthorizeException, IOException { // authorize a DSpaceActions.ABORT if (!AuthorizeManager.isAdmin(c)) { throw new AuthorizeException( "You must be an admin to abort a workflow"); } // stop workflow regardless of its state deleteTasks(c, wi); log.info(LogManager.getHeader(c, "abort_workflow", "workflow_item_id=" + wi.getID() + "item_id=" + wi.getItem().getID() + "collection_id=" + wi.getCollection().getID() + "eperson_id=" + e.getID())); // convert into personal workspace WorkspaceItem wsi = returnToWorkspace(c, wi); } // returns true if archived private static boolean doState(Context c, WorkflowItem wi, int newstate, EPerson newowner) throws SQLException, IOException, AuthorizeException { Collection mycollection = wi.getCollection(); Group mygroup = null; boolean archived = false; wi.setState(newstate); switch (newstate) { case WFSTATE_STEP1POOL: // any reviewers? // if so, add them to the tasklist wi.setOwner(null); // get reviewers (group 1 ) mygroup = mycollection.getWorkflowGroup(1); if ((mygroup != null) && !(mygroup.isEmpty())) { // get a list of all epeople in group (or any subgroups) EPerson[] epa = Group.allMembers(c, mygroup); // there were reviewers, change the state // and add them to the list createTasks(c, wi, epa); wi.update(); // email notification notifyGroupOfTask(c, wi, mygroup, epa); } else { // no reviewers, skip ahead archived = doState(c, wi, WFSTATE_STEP2POOL, null); } break; case WFSTATE_STEP1: // remove reviewers from tasklist // assign owner deleteTasks(c, wi); wi.setOwner(newowner); break; case WFSTATE_STEP2POOL: // clear owner // any approvers? // if so, add them to tasklist // if not, skip to next state wi.setOwner(null); // get approvers (group 2) mygroup = mycollection.getWorkflowGroup(2); if ((mygroup != null) && !(mygroup.isEmpty())) { //get a list of all epeople in group (or any subgroups) EPerson[] epa = Group.allMembers(c, mygroup); // there were approvers, change the state // timestamp, and add them to the list createTasks(c, wi, epa); // email notification notifyGroupOfTask(c, wi, mygroup, epa); } else { // no reviewers, skip ahead archived = doState(c, wi, WFSTATE_STEP3POOL, null); } break; case WFSTATE_STEP2: // remove admins from tasklist // assign owner deleteTasks(c, wi); wi.setOwner(newowner); break; case WFSTATE_STEP3POOL: // any editors? // if so, add them to tasklist wi.setOwner(null); mygroup = mycollection.getWorkflowGroup(3); if ((mygroup != null) && !(mygroup.isEmpty())) { // get a list of all epeople in group (or any subgroups) EPerson[] epa = Group.allMembers(c, mygroup); // there were editors, change the state // timestamp, and add them to the list createTasks(c, wi, epa); // email notification notifyGroupOfTask(c, wi, mygroup, epa); } else { // no editors, skip ahead archived = doState(c, wi, WFSTATE_ARCHIVE, newowner); } break; case WFSTATE_STEP3: // remove editors from tasklist // assign owner deleteTasks(c, wi); wi.setOwner(newowner); break; case WFSTATE_ARCHIVE: // put in archive in one transaction try { // remove workflow tasks deleteTasks(c, wi); mycollection = wi.getCollection(); Item myitem = archive(c, wi); // now email notification notifyOfArchive(c, myitem, mycollection); archived = true; } catch (IOException e) { // indexer causes this throw e; } catch (SQLException e) { // problem starting workflow throw e; } break; } if ((wi != null) && !archived) { wi.update(); } return archived; } /** * Commit the contained item to the main archive. The item is associated * with the relevant collection, added to the search index, and any other * tasks such as assigning dates are performed. * * @return the fully archived item. */ private static Item archive(Context c, WorkflowItem wfi) throws SQLException, IOException, AuthorizeException { // FIXME: Check auth Item item = wfi.getItem(); Collection collection = wfi.getCollection(); log.info(LogManager.getHeader(c, "archive_item", "workflow_item_id=" + wfi.getID() + "item_id=" + item.getID() + "collection_id=" + collection.getID())); InstallItem.installItem(c, wfi); // Log the event log.info(LogManager.getHeader(c, "install_item", "workflow_id=" + wfi.getID() + ", item_id=" + item.getID() + "handle=FIXME")); return item; } /** * notify the submitter that the item is archived */ private static void notifyOfArchive(Context c, Item i, Collection coll) throws SQLException, IOException { try { // Get submitter EPerson ep = i.getSubmitter(); // Get the Locale Locale supportedLocale = I18nUtil.getEPersonLocale(ep); Email email = ConfigurationManager.getEmail(I18nUtil.getEmailFilename(supportedLocale, "submit_archive")); // Get the item handle to email to user String handle = HandleManager.findHandle(c, i); // Get title DCValue[] titles = i.getDC("title", null, Item.ANY); String title = ""; try { title = I18nUtil.getMessage("org.dspace.workflow.WorkflowManager.untitled"); } catch (MissingResourceException e) { title = "Untitled"; } if (titles.length > 0) { title = titles[0].value; } email.addRecipient(ep.getEmail()); email.addArgument(title); email.addArgument(coll.getMetadata("name")); email.addArgument(HandleManager.getCanonicalForm(handle)); email.send(); } catch (MessagingException e) { log.warn(LogManager.getHeader(c, "notifyOfArchive", "cannot email user" + " item_id=" + i.getID())); } } /** * Return the workflow item to the workspace of the submitter. The workflow * item is removed, and a workspace item created. * * @param c * Context * @param wfi * WorkflowItem to be 'dismantled' * @return the workspace item */ private static WorkspaceItem returnToWorkspace(Context c, WorkflowItem wfi) throws SQLException, IOException, AuthorizeException { Item myitem = wfi.getItem(); Collection mycollection = wfi.getCollection(); // FIXME: How should this interact with the workflow system? // FIXME: Remove license // FIXME: Provenance statement? // Create the new workspace item row TableRow row = DatabaseManager.create(c, "workspaceitem"); row.setColumn("item_id", myitem.getID()); row.setColumn("collection_id", mycollection.getID()); DatabaseManager.update(c, row); int wsi_id = row.getIntColumn("workspace_item_id"); WorkspaceItem wi = WorkspaceItem.find(c, wsi_id); wi.setMultipleFiles(wfi.hasMultipleFiles()); wi.setMultipleTitles(wfi.hasMultipleTitles()); wi.setPublishedBefore(wfi.isPublishedBefore()); wi.update(); //myitem.update(); log.info(LogManager.getHeader(c, "return_to_workspace", "workflow_item_id=" + wfi.getID() + "workspace_item_id=" + wi.getID())); // Now remove the workflow object manually from the database DatabaseManager.updateQuery(c, "DELETE FROM WorkflowItem WHERE workflow_id=" + wfi.getID()); return wi; } /** * rejects an item - rejection means undoing a submit - WorkspaceItem is * created, and the WorkflowItem is removed, user is emailed * rejection_message. * * @param c * Context * @param wi * WorkflowItem to operate on * @param e * EPerson doing the operation * @param rejection_message * message to email to user */ public static WorkspaceItem reject(Context c, WorkflowItem wi, EPerson e, String rejection_message) throws SQLException, AuthorizeException, IOException { // authorize a DSpaceActions.REJECT // stop workflow deleteTasks(c, wi); // rejection provenance Item myitem = wi.getItem(); // Get current date String now = DCDate.getCurrent().toString(); // Get user's name + email address String usersName = getEPersonName(e); // Here's what happened String provDescription = "Rejected by " + usersName + ", reason: " + rejection_message + " on " + now + " (GMT) "; // Add to item as a DC field myitem.addDC("description", "provenance", "en", provDescription); myitem.update(); // convert into personal workspace WorkspaceItem wsi = returnToWorkspace(c, wi); // notify that it's been rejected notifyOfReject(c, wi, e, rejection_message); log.info(LogManager.getHeader(c, "reject_workflow", "workflow_item_id=" + wi.getID() + "item_id=" + wi.getItem().getID() + "collection_id=" + wi.getCollection().getID() + "eperson_id=" + e.getID())); return wsi; } // creates workflow tasklist entries for a workflow // for all the given EPeople private static void createTasks(Context c, WorkflowItem wi, EPerson[] epa) throws SQLException { // create a tasklist entry for each eperson for (int i = 0; i < epa.length; i++) { // can we get away without creating a tasklistitem class? // do we want to? TableRow tr = DatabaseManager.create(c, "tasklistitem"); tr.setColumn("eperson_id", epa[i].getID()); tr.setColumn("workflow_id", wi.getID()); DatabaseManager.update(c, tr); } } // deletes all tasks associated with a workflowitem static void deleteTasks(Context c, WorkflowItem wi) throws SQLException { String myrequest = "DELETE FROM TaskListItem WHERE workflow_id= ? "; DatabaseManager.updateQuery(c, myrequest, wi.getID()); } private static void notifyGroupOfTask(Context c, WorkflowItem wi, Group mygroup, EPerson[] epa) throws SQLException, IOException { // check to see if notification is turned off // and only do it once - delete key after notification has // been suppressed for the first time Integer myID = new Integer(wi.getItem().getID()); if (noEMail.containsKey(myID)) { // suppress email, and delete key noEMail.remove(myID); } else { try { // Get the item title String title = getItemTitle(wi); // Get the submitter's name String submitter = getSubmitterName(wi); // Get the collection Collection coll = wi.getCollection(); String message = ""; for (int i = 0; i < epa.length; i++) { Locale supportedLocale = I18nUtil.getEPersonLocale(epa[i]); Email email = ConfigurationManager.getEmail(I18nUtil.getEmailFilename(supportedLocale, "submit_task")); email.addArgument(title); email.addArgument(coll.getMetadata("name")); email.addArgument(submitter); ResourceBundle messages = ResourceBundle.getBundle("Messages", supportedLocale); switch (wi.getState()) { case WFSTATE_STEP1POOL: message = messages.getString("org.dspace.workflow.WorkflowManager.step1"); break; case WFSTATE_STEP2POOL: message = messages.getString("org.dspace.workflow.WorkflowManager.step2"); break; case WFSTATE_STEP3POOL: message = messages.getString("org.dspace.workflow.WorkflowManager.step3"); break; } email.addArgument(message); email.addArgument(getMyDSpaceLink()); email.addRecipient(epa[i].getEmail()); email.send(); } } catch (MessagingException e) { log.warn(LogManager.getHeader(c, "notifyGroupofTask", "cannot email user" + " group_id" + mygroup.getID() + " workflow_item_id" + wi.getID())); } } } /** * Add all the specified people to the list of email recipients, * and send it * * @param c * Context * @param epeople * Eperson[] of recipients * @param email * Email object containing the message */ private static void emailRecipients(Context c, EPerson[] epa, Email email) throws SQLException, MessagingException { for (int i = 0; i < epa.length; i++) { email.addRecipient(epa[i].getEmail()); } email.send(); } private static String getMyDSpaceLink() { return ConfigurationManager.getProperty("dspace.url") + "/mydspace"; } private static void notifyOfReject(Context c, WorkflowItem wi, EPerson e, String reason) { try { // Get the item title String title = getItemTitle(wi); // Get the collection Collection coll = wi.getCollection(); // Get rejector's name String rejector = getEPersonName(e); Locale supportedLocale = I18nUtil.getEPersonLocale(e); Email email = ConfigurationManager.getEmail(I18nUtil.getEmailFilename(supportedLocale,"submit_reject")); email.addRecipient(getSubmitterEPerson(wi).getEmail()); email.addArgument(title); email.addArgument(coll.getMetadata("name")); email.addArgument(rejector); email.addArgument(reason); email.addArgument(getMyDSpaceLink()); email.send(); } catch (Exception ex) { // log this email error log.warn(LogManager.getHeader(c, "notify_of_reject", "cannot email user" + " eperson_id" + e.getID() + " eperson_email" + e.getEmail() + " workflow_item_id" + wi.getID())); } } // FIXME - are the following methods still needed? private static EPerson getSubmitterEPerson(WorkflowItem wi) throws SQLException { EPerson e = wi.getSubmitter(); return e; } /** * get the title of the item in this workflow * * @param wi the workflow item object */ public static String getItemTitle(WorkflowItem wi) throws SQLException { Item myitem = wi.getItem(); DCValue[] titles = myitem.getDC("title", null, Item.ANY); // only return the first element, or "Untitled" if (titles.length > 0) { return titles[0].value; } else { return I18nUtil.getMessage("org.dspace.workflow.WorkflowManager.untitled "); } } /** * get the name of the eperson who started this workflow * * @param wi the workflow item */ public static String getSubmitterName(WorkflowItem wi) throws SQLException { EPerson e = wi.getSubmitter(); return getEPersonName(e); } private static String getEPersonName(EPerson e) throws SQLException { String submitter = e.getFullName(); submitter = submitter + "(" + e.getEmail() + ")"; return submitter; } // Record approval provenance statement private static void recordApproval(Context c, WorkflowItem wi, EPerson e) throws SQLException, IOException, AuthorizeException { Item item = wi.getItem(); // Get user's name + email address String usersName = getEPersonName(e); // Get current date String now = DCDate.getCurrent().toString(); // Here's what happened String provDescription = "Approved for entry into archive by " + usersName + " on " + now + " (GMT) "; // add bitstream descriptions (name, size, checksums) provDescription += InstallItem.getBitstreamProvenanceMessage(item); // Add to item as a DC field item.addDC("description", "provenance", "en", provDescription); item.update(); } // Create workflow start provenance message private static void recordStart(Context c, Item myitem) throws SQLException, IOException, AuthorizeException { // Get non-internal format bitstreams Bitstream[] bitstreams = myitem.getNonInternalBitstreams(); // get date DCDate now = DCDate.getCurrent(); // Create provenance description String provmessage = ""; if (myitem.getSubmitter() != null) { provmessage = "Submitted by " + myitem.getSubmitter().getFullName() + " (" + myitem.getSubmitter().getEmail() + ") on " + now.toString() + "\n"; } else // null submitter { provmessage = "Submitted by unknown (probably automated) on" + now.toString() + "\n"; } // add sizes and checksums of bitstreams provmessage += InstallItem.getBitstreamProvenanceMessage(myitem); // Add message to the DC myitem.addDC("description", "provenance", "en", provmessage); myitem.update(); } }