/** * 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.workflowbasic; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.Collection; import org.dspace.content.*; import org.dspace.content.service.CollectionService; import org.dspace.content.service.InstallItemService; import org.dspace.content.service.ItemService; import org.dspace.content.service.WorkspaceItemService; import org.dspace.core.*; import org.dspace.curate.service.WorkflowCuratorService; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.dspace.eperson.service.GroupService; import org.dspace.handle.service.HandleService; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.usage.UsageWorkflowEvent; import org.dspace.workflowbasic.service.BasicWorkflowItemService; import org.dspace.workflowbasic.service.BasicWorkflowService; import org.dspace.workflowbasic.service.TaskListItemService; import org.springframework.beans.factory.annotation.Autowired; import javax.mail.MessagingException; import java.io.IOException; import java.sql.SQLException; import java.util.*; public class BasicWorkflowServiceImpl implements BasicWorkflowService { @Autowired(required = true) protected AuthorizeService authorizeService; @Autowired(required = true) protected CollectionService collectionService; @Autowired(required = true) protected GroupService groupService; @Autowired(required = true) protected HandleService handleService; @Autowired(required = true) protected InstallItemService installItemService; @Autowired(required = true) protected ItemService itemService; @Autowired(required = true) protected TaskListItemService taskListItemService; @Autowired(required = true) protected WorkflowCuratorService workflowCuratorService; @Autowired(required = true) protected BasicWorkflowItemService workflowItemService; @Autowired(required = true) protected WorkspaceItemService workspaceItemService; protected BasicWorkflowServiceImpl() { } /** Symbolic names of workflow steps. */ protected final String workflowText[] = { "SUBMIT", // 0 "STEP1POOL", // 1 "STEP1", // 2 "STEP2POOL", // 3 "STEP2", // 4 "STEP3POOL", // 5 "STEP3", // 6 "ARCHIVE" // 7 }; /* support for 'no notification' */ protected Map<UUID, Boolean> noEMail = new HashMap<>(); /** log4j logger */ private final Logger log = Logger.getLogger(BasicWorkflowServiceImpl.class); @Override public int getWorkflowID(String state) { for (int i = 0; i < workflowText.length; ++i) { if (state.equalsIgnoreCase(workflowText[i])) { return i; } } return -1; } @Override public void addInitialWorkspaceItemPolicies(Context context, WorkspaceItem workspaceItem) throws SQLException, AuthorizeException { // Now create the policies for the submitter and workflow // users to modify item and contents // contents = bitstreams, bundles // FIXME: icky hardcoded workflow steps Collection collection = workspaceItem.getCollection(); Group step1group = collectionService.getWorkflowGroup(collection, 1); Group step2group = collectionService.getWorkflowGroup(collection, 2); Group step3group = collectionService.getWorkflowGroup(collection, 3); Item item = workspaceItem.getItem(); if (step1group != null) { authorizeService.addPolicy(context, item, Constants.READ, step1group, ResourcePolicy.TYPE_WORKFLOW); } if (step2group != null) { authorizeService.addPolicy(context, item, Constants.READ, step2group, ResourcePolicy.TYPE_WORKFLOW); } if (step3group != null) { authorizeService.addPolicy(context, item, Constants.READ, step3group, ResourcePolicy.TYPE_WORKFLOW); } if (step1group != null) { authorizeService.addPolicy(context, item, Constants.WRITE, step1group, ResourcePolicy.TYPE_WORKFLOW); } if (step2group != null) { authorizeService.addPolicy(context, item, Constants.WRITE, step2group, ResourcePolicy.TYPE_WORKFLOW); } if (step3group != null) { authorizeService.addPolicy(context, item, Constants.WRITE, step3group, ResourcePolicy.TYPE_WORKFLOW); } if (step1group != null) { authorizeService.addPolicy(context, item, Constants.ADD, step1group, ResourcePolicy.TYPE_WORKFLOW); } if (step2group != null) { authorizeService.addPolicy(context, item, Constants.ADD, step2group, ResourcePolicy.TYPE_WORKFLOW); } if (step3group != null) { authorizeService.addPolicy(context, item, Constants.ADD, step3group, ResourcePolicy.TYPE_WORKFLOW); } if (step1group != null) { authorizeService.addPolicy(context, item, Constants.REMOVE, step1group, ResourcePolicy.TYPE_WORKFLOW); } if (step2group != null) { authorizeService.addPolicy(context, item, Constants.REMOVE, step2group, ResourcePolicy.TYPE_WORKFLOW); } if (step3group != null) { authorizeService.addPolicy(context, item, Constants.REMOVE, step3group, ResourcePolicy.TYPE_WORKFLOW); } if (step1group != null) { authorizeService.addPolicy(context, item, Constants.DELETE, step1group, ResourcePolicy.TYPE_WORKFLOW); } if (step2group != null) { authorizeService.addPolicy(context, item, Constants.DELETE, step2group, ResourcePolicy.TYPE_WORKFLOW); } if (step3group != null) { authorizeService.addPolicy(context, item, Constants.DELETE, step3group, ResourcePolicy.TYPE_WORKFLOW); } } @Override public BasicWorkflowItem start(Context context, WorkspaceItem wsi) throws SQLException, AuthorizeException, IOException { // FIXME Check auth Item myitem = wsi.getItem(); Collection collection = wsi.getCollection(); log.info(LogManager.getHeader(context, "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(context, myitem); // create the WorkflowItem BasicWorkflowItem wfi = workflowItemService.create(context, myitem, collection); wfi.setMultipleFiles(wsi.hasMultipleFiles()); wfi.setMultipleTitles(wsi.hasMultipleTitles()); wfi.setPublishedBefore(wsi.isPublishedBefore()); // remove the WorkspaceItem workspaceItemService.deleteWrapper(context, wsi); // now get the workflow started wfi.setState(WFSTATE_SUBMIT); advance(context, wfi, null); // Return the workflow item return wfi; } @Override public BasicWorkflowItem 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(wsi.getItem().getID(), Boolean.TRUE); return start(c, wsi); } @Override public List<BasicWorkflowItem> getOwnedTasks(Context context, EPerson e) throws java.sql.SQLException { return workflowItemService.findByOwner(context, e); } @Override public List<BasicWorkflowItem> getPooledTasks(Context context, EPerson e) throws SQLException { return workflowItemService.findPooledTasks(context, e); } @Override public void claim(Context context, BasicWorkflowItem workflowItem, EPerson e) throws SQLException, IOException, AuthorizeException { int taskstate = workflowItem.getState(); switch (taskstate) { case WFSTATE_STEP1POOL: // authorize DSpaceActions.SUBMIT_REVIEW doState(context, workflowItem, WFSTATE_STEP1, e); break; case WFSTATE_STEP2POOL: // authorize DSpaceActions.SUBMIT_STEP2 doState(context, workflowItem, WFSTATE_STEP2, e); break; case WFSTATE_STEP3POOL: // authorize DSpaceActions.SUBMIT_STEP3 doState(context, workflowItem, WFSTATE_STEP3, e); break; // if we got here, we weren't pooled... error? // FIXME - log the error? } log.info(LogManager.getHeader(context, "claim_task", "workflow_item_id=" + workflowItem.getID() + "item_id=" + workflowItem.getItem().getID() + "collection_id=" + workflowItem.getCollection().getID() + "newowner_id=" + workflowItem.getOwner().getID() + "old_state=" + taskstate + "new_state=" + workflowItem.getState())); } @Override public void advance(Context context, BasicWorkflowItem workflowItem, EPerson e) throws SQLException, IOException, AuthorizeException { advance(context, workflowItem, e, true, true); } @Override public boolean advance(Context context, BasicWorkflowItem workflowItem, EPerson e, boolean curate, boolean record) throws SQLException, IOException, AuthorizeException { int taskstate = workflowItem.getState(); boolean archived = false; // perform curation tasks if needed if (curate && workflowCuratorService.needsCuration(workflowItem)) { if (! workflowCuratorService.doCuration(context, workflowItem)) { // don't proceed - either curation tasks queued, or item rejected log.info(LogManager.getHeader(context, "advance_workflow", "workflow_item_id=" + workflowItem.getID() + ",item_id=" + workflowItem.getItem().getID() + ",collection_id=" + workflowItem.getCollection().getID() + ",old_state=" + taskstate + ",doCuration=false")); return false; } } switch (taskstate) { case WFSTATE_SUBMIT: archived = doState(context, workflowItem, WFSTATE_STEP1POOL, e); break; case WFSTATE_STEP1: // authorize DSpaceActions.SUBMIT_REVIEW // Record provenance if (record) { recordApproval(context, workflowItem, e); } archived = doState(context, workflowItem, WFSTATE_STEP2POOL, e); break; case WFSTATE_STEP2: // authorize DSpaceActions.SUBMIT_STEP2 // Record provenance if (record) { recordApproval(context, workflowItem, e); } archived = doState(context, workflowItem, 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 archived = doState(context, workflowItem, WFSTATE_ARCHIVE, e); break; // error handling? shouldn't get here } log.info(LogManager.getHeader(context, "advance_workflow", "workflow_item_id=" + workflowItem.getID() + ",item_id=" + workflowItem.getItem().getID() + ",collection_id=" + workflowItem.getCollection().getID() + ",old_state=" + taskstate + ",new_state=" + workflowItem.getState())); return archived; } @Override public void unclaim(Context context, BasicWorkflowItem workflowItem, EPerson e) throws SQLException, IOException, AuthorizeException { int taskstate = workflowItem.getState(); switch (taskstate) { case WFSTATE_STEP1: // authorize DSpaceActions.STEP1 doState(context, workflowItem, WFSTATE_STEP1POOL, e); break; case WFSTATE_STEP2: // authorize DSpaceActions.APPROVE doState(context, workflowItem, WFSTATE_STEP2POOL, e); break; case WFSTATE_STEP3: // authorize DSpaceActions.STEP3 doState(context, workflowItem, WFSTATE_STEP3POOL, e); break; // error handling? shouldn't get here // FIXME - what to do with error - log it? } log.info(LogManager.getHeader(context, "unclaim_workflow", "workflow_item_id=" + workflowItem.getID() + ",item_id=" + workflowItem.getItem().getID() + ",collection_id=" + workflowItem.getCollection().getID() + ",old_state=" + taskstate + ",new_state=" + workflowItem.getState())); } @Override public WorkspaceItem abort(Context context, BasicWorkflowItem workflowItem, EPerson e) throws SQLException, AuthorizeException, IOException { // authorize a DSpaceActions.ABORT if (!authorizeService.isAdmin(context)) { throw new AuthorizeException( "You must be an admin to abort a workflow"); } // stop workflow regardless of its state taskListItemService.deleteByWorkflowItem(context, workflowItem); log.info(LogManager.getHeader(context, "abort_workflow", "workflow_item_id=" + workflowItem.getID() + "item_id=" + workflowItem.getItem().getID() + "collection_id=" + workflowItem.getCollection().getID() + "eperson_id=" + e.getID())); // convert into personal workspace return returnToWorkspace(context, workflowItem); } // returns true if archived protected boolean doState(Context context, BasicWorkflowItem workflowItem, int newstate, EPerson newowner) throws SQLException, IOException, AuthorizeException { Collection mycollection = workflowItem.getCollection(); Group mygroup = null; boolean archived = false; //Gather our old data for launching the workflow event int oldState = workflowItem.getState(); // in case we don't want to inform reviewers about tasks returned to // the pool by other reviewers, we'll ne to know whether they were owned // before. => keep this information before setting the new owner. EPerson oldOwner = workflowItem.getOwner(); workflowItem.setState(newstate); switch (newstate) { case WFSTATE_STEP1POOL: // any reviewers? // if so, add them to the tasklist workflowItem.setOwner(null); // get reviewers (group 1 ) mygroup = collectionService.getWorkflowGroup(mycollection, 1); if ((mygroup != null) && !(groupService.isEmpty(mygroup))) { // get a list of all epeople in group (or any subgroups) List<EPerson> epa = groupService.allMembers(context, mygroup); // there were reviewers, change the state // and add them to the list createTasks(context, workflowItem, epa); workflowItemService.update(context, workflowItem); if (ConfigurationManager.getBooleanProperty("workflow", "notify.returned.tasks", true) || oldState != WFSTATE_STEP1 || oldOwner == null) { // email notification notifyGroupOfTask(context, workflowItem, mygroup, epa); } } else { // no reviewers, skip ahead workflowItem.setState(WFSTATE_STEP1); archived = advance(context, workflowItem, null, true, false); } break; case WFSTATE_STEP1: // remove reviewers from tasklist // assign owner taskListItemService.deleteByWorkflowItem(context, workflowItem); workflowItem.setOwner(newowner); break; case WFSTATE_STEP2POOL: // clear owner // any approvers? // if so, add them to tasklist // if not, skip to next state workflowItem.setOwner(null); // get approvers (group 2) mygroup = collectionService.getWorkflowGroup(mycollection, 2); if ((mygroup != null) && !(groupService.isEmpty(mygroup))) { //get a list of all epeople in group (or any subgroups) List<EPerson> epa = groupService.allMembers(context, mygroup); // there were approvers, change the state // timestamp, and add them to the list createTasks(context, workflowItem, epa); if (ConfigurationManager.getBooleanProperty("workflow", "notify.returned.tasks", true) || oldState != WFSTATE_STEP2 || oldOwner == null) { // email notification notifyGroupOfTask(context, workflowItem, mygroup, epa); } } else { // no reviewers, skip ahead workflowItem.setState(WFSTATE_STEP2); archived = advance(context, workflowItem, null, true, false); } break; case WFSTATE_STEP2: // remove admins from tasklist // assign owner taskListItemService.deleteByWorkflowItem(context, workflowItem); workflowItem.setOwner(newowner); break; case WFSTATE_STEP3POOL: // any editors? // if so, add them to tasklist workflowItem.setOwner(null); mygroup = collectionService.getWorkflowGroup(mycollection, 3); if ((mygroup != null) && !(groupService.isEmpty(mygroup))) { // get a list of all epeople in group (or any subgroups) List<EPerson> epa = groupService.allMembers(context, mygroup); // there were editors, change the state // timestamp, and add them to the list createTasks(context, workflowItem, epa); if (ConfigurationManager.getBooleanProperty("workflow", "notify.returned.tasks", true) || oldState != WFSTATE_STEP3 || oldOwner == null) { // email notification notifyGroupOfTask(context, workflowItem, mygroup, epa); } } else { // no editors, skip ahead workflowItem.setState(WFSTATE_STEP3); archived = advance(context, workflowItem, null, true, false); } break; case WFSTATE_STEP3: // remove editors from tasklist // assign owner taskListItemService.deleteByWorkflowItem(context, workflowItem); workflowItem.setOwner(newowner); break; case WFSTATE_ARCHIVE: // put in archive in one transaction // remove workflow tasks taskListItemService.deleteByWorkflowItem(context, workflowItem); mycollection = workflowItem.getCollection(); Item myitem = archive(context, workflowItem); // now email notification notifyOfArchive(context, myitem, mycollection); archived = true; break; } logWorkflowEvent(context, workflowItem.getItem(), workflowItem, context.getCurrentUser(), newstate, newowner, mycollection, oldState, mygroup); if (!archived) { workflowItemService.update(context, workflowItem); } return archived; } protected void logWorkflowEvent(Context context, Item item, BasicWorkflowItem workflowItem, EPerson actor, int newstate, EPerson newOwner, Collection mycollection, int oldState, Group newOwnerGroup) { if(newstate == WFSTATE_ARCHIVE || newstate == WFSTATE_STEP1POOL || newstate == WFSTATE_STEP2POOL || newstate == WFSTATE_STEP3POOL){ //Clear the newowner variable since this one isn't owned anymore ! newOwner = null; } UsageWorkflowEvent usageWorkflowEvent = new UsageWorkflowEvent(context, item, workflowItem, workflowText[newstate], workflowText[oldState], mycollection, actor); if(newOwner != null){ usageWorkflowEvent.setEpersonOwners(newOwner); } if(newOwnerGroup != null){ usageWorkflowEvent.setGroupOwners(newOwnerGroup); } DSpaceServicesFactory.getInstance().getEventService().fireEvent(usageWorkflowEvent); } @Override public String getWorkflowText(int state) { if (state > -1 && state < workflowText.length) { return workflowText[state]; } throw new IllegalArgumentException("Invalid workflow state passed"); } /** * 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. * * @param context * The relevant DSpace Context. * @param workflowItem * which workflow item to archive * @return the fully archived item. * @throws IOException * A general class of exceptions produced by failed or interrupted I/O operations. * @throws SQLException * An exception that provides information on a database access error or other errors. * @throws AuthorizeException * Exception indicating the current user of the context does not have permission * to perform a particular action. */ @Override public Item archive(Context context, BasicWorkflowItem workflowItem) throws SQLException, IOException, AuthorizeException { // FIXME: Check auth Item item = workflowItem.getItem(); Collection collection = workflowItem.getCollection(); log.info(LogManager.getHeader(context, "archive_item", "workflow_item_id=" + workflowItem.getID() + "item_id=" + item.getID() + "collection_id=" + collection.getID())); installItemService.installItem(context, workflowItem); // Log the event log.info(LogManager.getHeader(context, "install_item", "workflow_id=" + workflowItem.getID() + ", item_id=" + item.getID() + "handle=FIXME")); return item; } /** * notify the submitter that the item is archived * * @param context * The relevant DSpace Context. * @param item * which item was archived * @param coll * collection name to display in template * @throws SQLException * An exception that provides information on a database access error or other errors. * @throws IOException * A general class of exceptions produced by failed or interrupted I/O operations. */ protected void notifyOfArchive(Context context, Item item, Collection coll) throws SQLException, IOException { try { // Get submitter EPerson ep = item.getSubmitter(); // Get the Locale Locale supportedLocale = I18nUtil.getEPersonLocale(ep); Email email = Email.getEmail(I18nUtil.getEmailFilename(supportedLocale, "submit_archive")); // Get the item handle to email to user String handle = handleService.findHandle(context, item); // Get title String title = item.getName(); if (StringUtils.isBlank(title)) { try { title = I18nUtil.getMessage("org.dspace.workflow.WorkflowManager.untitled"); } catch (MissingResourceException e) { title = "Untitled"; } } email.addRecipient(ep.getEmail()); email.addArgument(title); email.addArgument(coll.getName()); email.addArgument(handleService.getCanonicalForm(handle)); email.send(); } catch (MessagingException e) { log.warn(LogManager.getHeader(context, "notifyOfArchive", "cannot email user; item_id=" + item.getID() + ": " + e.getMessage())); } } /** * 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 * @throws SQLException * An exception that provides information on a database access error or other errors. * @throws IOException * A general class of exceptions produced by failed or interrupted I/O operations. * @throws AuthorizeException * Exception indicating the current user of the context does not have permission * to perform a particular action. */ protected WorkspaceItem returnToWorkspace(Context c, BasicWorkflowItem wfi) throws SQLException, IOException, AuthorizeException { // FIXME: How should this interact with the workflow system? // FIXME: Remove license // FIXME: Provenance statement? // Create the new workspace item row WorkspaceItem workspaceItem = workspaceItemService.create(c, wfi); workspaceItem.setMultipleFiles(wfi.hasMultipleFiles()); workspaceItem.setMultipleTitles(wfi.hasMultipleTitles()); workspaceItem.setPublishedBefore(wfi.isPublishedBefore()); workspaceItemService.update(c, workspaceItem); //myitem.update(); log.info(LogManager.getHeader(c, "return_to_workspace", "workflow_item_id=" + wfi.getID() + "workspace_item_id=" + workspaceItem.getID())); // Now remove the workflow object manually from the database workflowItemService.deleteWrapper(c, wfi); return workspaceItem; } @Override public WorkspaceItem sendWorkflowItemBackSubmission(Context context, BasicWorkflowItem workflowItem, EPerson ePerson, String provenancePrefix, String rejection_message) throws SQLException, AuthorizeException, IOException { int oldState = workflowItem.getState(); // authorize a DSpaceActions.REJECT // stop workflow taskListItemService.deleteByWorkflowItem(context, workflowItem); // rejection provenance Item myitem = workflowItem.getItem(); // Get current date String now = DCDate.getCurrent().toString(); // Get user's name + email address String usersName = getEPersonName(ePerson); // Here's what happened String provDescription = "Rejected by " + usersName + ", reason: " + rejection_message + " on " + now + " (GMT) "; // Add to item as a DC field itemService.addMetadata(context, myitem, MetadataSchema.DC_SCHEMA, "description", "provenance", "en", provDescription); itemService.update(context, myitem); // convert into personal workspace WorkspaceItem wsi = returnToWorkspace(context, workflowItem); // notify that it's been rejected notifyOfReject(context, workflowItem, ePerson, rejection_message); log.info(LogManager.getHeader(context, "reject_workflow", "workflow_item_id=" + workflowItem.getID() + "item_id=" + workflowItem.getItem().getID() + "collection_id=" + workflowItem.getCollection().getID() + "eperson_id=" + ePerson.getID())); logWorkflowEvent(context, wsi.getItem(), workflowItem, ePerson, WFSTATE_SUBMIT, null, wsi.getCollection(), oldState, null); return wsi; } // creates workflow tasklist entries for a workflow // for all the given EPeople protected void createTasks(Context c, BasicWorkflowItem wi, List<EPerson> epa) throws SQLException { // create a tasklist entry for each eperson for (EPerson anEpa : epa) { // can we get away without creating a tasklistitem class? // do we want to? taskListItemService.create(c, wi, anEpa); } } // send notices of curation activity @Override public void notifyOfCuration(Context c, BasicWorkflowItem wi, List<EPerson> ePeople, String taskName, String action, String message) throws SQLException, IOException { 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(); for (EPerson epa : ePeople) { Locale supportedLocale = I18nUtil.getEPersonLocale(epa); Email email = Email.getEmail(I18nUtil.getEmailFilename(supportedLocale, "flowtask_notify")); email.addArgument(title); email.addArgument(coll.getName()); email.addArgument(submitter); email.addArgument(taskName); email.addArgument(message); email.addArgument(action); email.addRecipient(epa.getEmail()); email.send(); } } catch (MessagingException e) { log.warn(LogManager.getHeader(c, "notifyOfCuration", "cannot email users of workflow_item_id " + wi.getID() + ": " + e.getMessage())); } } protected void notifyGroupOfTask(Context c, BasicWorkflowItem wi, Group mygroup, List<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 UUID myID = 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 (EPerson anEpa : epa) { Locale supportedLocale = I18nUtil.getEPersonLocale(anEpa); Email email = Email.getEmail(I18nUtil.getEmailFilename(supportedLocale, "submit_task")); email.addArgument(title); email.addArgument(coll.getName()); 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(anEpa.getEmail()); email.send(); } } catch (MessagingException e) { String gid = (mygroup != null) ? String.valueOf(mygroup.getID()) : "none"; log.warn(LogManager.getHeader(c, "notifyGroupofTask", "cannot email user group_id=" + gid + " workflow_item_id=" + wi.getID() + ": " + e.getMessage())); } } } @Override public String getMyDSpaceLink() { return ConfigurationManager.getProperty("dspace.url") + "/mydspace"; } protected void notifyOfReject(Context context, BasicWorkflowItem workflowItem, EPerson e, String reason) { try { // Get the item title String title = getItemTitle(workflowItem); // Get the collection Collection coll = workflowItem.getCollection(); // Get rejector's name String rejector = getEPersonName(e); Locale supportedLocale = I18nUtil.getEPersonLocale(e); Email email = Email.getEmail(I18nUtil.getEmailFilename(supportedLocale,"submit_reject")); email.addRecipient(workflowItem.getSubmitter().getEmail()); email.addArgument(title); email.addArgument(coll.getName()); email.addArgument(rejector); email.addArgument(reason); email.addArgument(getMyDSpaceLink()); email.send(); } catch (RuntimeException re) { // log this email error log.warn(LogManager.getHeader(context, "notify_of_reject", "cannot email user eperson_id=" + e.getID() + " eperson_email=" + e.getEmail() + " workflow_item_id=" + workflowItem.getID() + ": " + re.getMessage())); throw re; } catch (Exception ex) { // log this email error log.warn(LogManager.getHeader(context, "notify_of_reject", "cannot email user eperson_id=" + e.getID() + " eperson_email=" + e.getEmail() + " workflow_item_id=" + workflowItem.getID() + ": " + ex.getMessage())); } } @Override public String getItemTitle(BasicWorkflowItem wi) throws SQLException { Item myitem = wi.getItem(); String title = myitem.getName(); // only return the first element, or "Untitled" if (StringUtils.isNotBlank(title)) { return title; } else { return I18nUtil.getMessage("org.dspace.workflow.WorkflowManager.untitled "); } } @Override public String getSubmitterName(BasicWorkflowItem wi) throws SQLException { EPerson e = wi.getSubmitter(); return getEPersonName(e); } protected String getEPersonName(EPerson e) throws SQLException { String submitter = e.getFullName(); submitter = submitter + " (" + e.getEmail() + ")"; return submitter; } // Record approval provenance statement protected void recordApproval(Context context, BasicWorkflowItem workflowItem, EPerson e) throws SQLException, IOException, AuthorizeException { Item item = workflowItem.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 += installItemService.getBitstreamProvenanceMessage(context, item); // Add to item as a DC field itemService.addMetadata(context, item, MetadataSchema.DC_SCHEMA, "description", "provenance", "en", provDescription); itemService.update(context, item); } // Create workflow start provenance message protected void recordStart(Context context, Item myitem) throws SQLException, IOException, AuthorizeException { // 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 += installItemService.getBitstreamProvenanceMessage(context, myitem); // Add message to the DC itemService.addMetadata(context, myitem, MetadataSchema.DC_SCHEMA, "description", "provenance", "en", provmessage); itemService.update(context, myitem); } @Override public void deleteCollection(Context context, Collection collection) throws SQLException, IOException, AuthorizeException { collection.setWorkflowGroup(1, null); collection.setWorkflowGroup(2, null); collection.setWorkflowGroup(3, null); workflowItemService.deleteByCollection(context, collection); } @Override public List<String> getEPersonDeleteConstraints(Context context, EPerson ePerson) throws SQLException { List<String> resultList = new ArrayList<>(); List<BasicWorkflowItem> workflowItems = workflowItemService.findByOwner(context, ePerson); if(CollectionUtils.isNotEmpty(workflowItems)) { resultList.add("workflowitem"); } List<TaskListItem> taskListItems = taskListItemService.findByEPerson(context, ePerson); if(CollectionUtils.isNotEmpty(taskListItems)) { resultList.add("tasklistitem"); } return resultList; } @Override public Group getWorkflowRoleGroup(Context context, Collection collection, String roleName, Group roleGroup) throws SQLException, AuthorizeException { if ("WF_STEP1".equals(roleName)) { roleGroup = collection.getWorkflowStep1(); if (roleGroup == null) roleGroup = collectionService.createWorkflowGroup(context, collection, 1); } else if ("WF_STEP2".equals(roleName)) { roleGroup = collection.getWorkflowStep2(); if (roleGroup == null) roleGroup = collectionService.createWorkflowGroup(context, collection, 2); } else if ("WF_STEP3".equals(roleName)) { roleGroup = collection.getWorkflowStep3(); if (roleGroup == null) roleGroup = collectionService.createWorkflowGroup(context, collection, 3); } return roleGroup; } @Override public List<String> getFlywayMigrationLocations() { return Collections.emptyList(); } }