Merge pull request #2115 from mwoodiupui/DS-3951

[DS-3951] Send Request Copy email to multiple recipients, and some new recipient strategies
This commit is contained in:
Andrea Bollini
2022-09-15 15:38:17 +02:00
committed by GitHub
14 changed files with 444 additions and 95 deletions

View File

@@ -0,0 +1,46 @@
/**
* 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.app.requestitem;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.dspace.content.Collection;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.springframework.lang.NonNull;
/**
* Derive request recipients from groups of the Collection which owns an Item.
* The list will include all members of the administrators group. If the
* resulting list is empty, delegates to {@link RequestItemHelpdeskStrategy}.
*
* @author Mark H. Wood <mwood@iupui.edu>
*/
public class CollectionAdministratorsRequestItemStrategy
extends RequestItemHelpdeskStrategy {
@Override
@NonNull
public List<RequestItemAuthor> getRequestItemAuthor(Context context,
Item item)
throws SQLException {
List<RequestItemAuthor> recipients = new ArrayList<>();
Collection collection = item.getOwningCollection();
for (EPerson admin : collection.getAdministrators().getMembers()) {
recipients.add(new RequestItemAuthor(admin));
}
if (recipients.isEmpty()) {
return super.getRequestItemAuthor(context, item);
} else {
return recipients;
}
}
}

View File

@@ -0,0 +1,61 @@
/**
* 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.app.requestitem;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
/**
* Assemble a list of recipients from the results of other strategies.
* The list of strategy classes is injected as the constructor argument
* {@code strategies}.
* If the strategy list is not configured, returns an empty List.
*
* @author Mark H. Wood <mwood@iupui.edu>
*/
public class CombiningRequestItemStrategy
implements RequestItemAuthorExtractor {
/** The strategies to combine. */
private final List<RequestItemAuthorExtractor> strategies;
/**
* Initialize a combination of strategies.
* @param strategies the author extraction strategies to combine.
*/
public CombiningRequestItemStrategy(@NonNull List<RequestItemAuthorExtractor> strategies) {
Assert.notNull(strategies, "Strategy list may not be null");
this.strategies = strategies;
}
/**
* Do not call.
* @throws IllegalArgumentException always
*/
private CombiningRequestItemStrategy() {
throw new IllegalArgumentException();
}
@Override
@NonNull
public List<RequestItemAuthor> getRequestItemAuthor(Context context, Item item)
throws SQLException {
List<RequestItemAuthor> recipients = new ArrayList<>();
for (RequestItemAuthorExtractor strategy : strategies) {
recipients.addAll(strategy.getRequestItemAuthor(context, item));
}
return recipients;
}
}

View File

@@ -27,7 +27,7 @@ import org.dspace.core.Context;
import org.dspace.core.ReloadableEntity; import org.dspace.core.ReloadableEntity;
/** /**
* Object representing an Item Request * Object representing an Item Request.
*/ */
@Entity @Entity
@Table(name = "requestitem") @Table(name = "requestitem")
@@ -94,6 +94,9 @@ public class RequestItem implements ReloadableEntity<Integer> {
this.allfiles = allfiles; this.allfiles = allfiles;
} }
/**
* @return {@code true} if all of the Item's files are requested.
*/
public boolean isAllfiles() { public boolean isAllfiles() {
return allfiles; return allfiles;
} }
@@ -102,6 +105,9 @@ public class RequestItem implements ReloadableEntity<Integer> {
this.reqMessage = reqMessage; this.reqMessage = reqMessage;
} }
/**
* @return a message from the requester.
*/
public String getReqMessage() { public String getReqMessage() {
return reqMessage; return reqMessage;
} }
@@ -110,6 +116,9 @@ public class RequestItem implements ReloadableEntity<Integer> {
this.reqName = reqName; this.reqName = reqName;
} }
/**
* @return Human-readable name of the user requesting access.
*/
public String getReqName() { public String getReqName() {
return reqName; return reqName;
} }
@@ -118,6 +127,9 @@ public class RequestItem implements ReloadableEntity<Integer> {
this.reqEmail = reqEmail; this.reqEmail = reqEmail;
} }
/**
* @return address of the user requesting access.
*/
public String getReqEmail() { public String getReqEmail() {
return reqEmail; return reqEmail;
} }
@@ -126,6 +138,9 @@ public class RequestItem implements ReloadableEntity<Integer> {
this.token = token; this.token = token;
} }
/**
* @return a unique request identifier which can be emailed.
*/
public String getToken() { public String getToken() {
return token; return token;
} }

View File

@@ -11,20 +11,31 @@ import org.dspace.eperson.EPerson;
/** /**
* Simple DTO to transfer data about the corresponding author for the Request * Simple DTO to transfer data about the corresponding author for the Request
* Copy feature * Copy feature.
* *
* @author Andrea Bollini * @author Andrea Bollini
*/ */
public class RequestItemAuthor { public class RequestItemAuthor {
private String fullName; private final String fullName;
private String email; private final String email;
/**
* Construct an author record from given data.
*
* @param fullName the author's full name.
* @param email the author's email address.
*/
public RequestItemAuthor(String fullName, String email) { public RequestItemAuthor(String fullName, String email) {
super(); super();
this.fullName = fullName; this.fullName = fullName;
this.email = email; this.email = email;
} }
/**
* Construct an author from an EPerson's metadata.
*
* @param ePerson the EPerson.
*/
public RequestItemAuthor(EPerson ePerson) { public RequestItemAuthor(EPerson ePerson) {
super(); super();
this.fullName = ePerson.getFullName(); this.fullName = ePerson.getFullName();

View File

@@ -8,26 +8,28 @@
package org.dspace.app.requestitem; package org.dspace.app.requestitem;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List;
import org.dspace.content.Item; import org.dspace.content.Item;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.springframework.lang.NonNull;
/** /**
* Interface to abstract the strategy for select the author to contact for * Interface to abstract the strategy for selecting the author to contact for
* request copy * request copy.
* *
* @author Andrea Bollini * @author Andrea Bollini
*/ */
public interface RequestItemAuthorExtractor { public interface RequestItemAuthorExtractor {
/** /**
* Retrieve the auhtor to contact for a request copy of the give item. * Retrieve the author to contact for requesting a copy of the given item.
* *
* @param context DSpace context object * @param context DSpace context object
* @param item item to request * @param item item to request
* @return An object containing name an email address to send the request to * @return Names and email addresses to send the request to.
* or null if no valid email address was found.
* @throws SQLException if database error * @throws SQLException if database error
*/ */
public RequestItemAuthor getRequestItemAuthor(Context context, Item item) throws SQLException; @NonNull
public List<RequestItemAuthor> getRequestItemAuthor(Context context, Item item)
throws SQLException;
} }

View File

@@ -72,28 +72,48 @@ public class RequestItemEmailNotifier {
static public void sendRequest(Context context, RequestItem ri, String responseLink) static public void sendRequest(Context context, RequestItem ri, String responseLink)
throws IOException, SQLException { throws IOException, SQLException {
// Who is making this request? // Who is making this request?
RequestItemAuthor author = requestItemAuthorExtractor List<RequestItemAuthor> authors = requestItemAuthorExtractor
.getRequestItemAuthor(context, ri.getItem()); .getRequestItemAuthor(context, ri.getItem());
String authorEmail = author.getEmail();
String authorName = author.getFullName();
// Build an email to the approver. // Build an email to the approver.
Email email = Email.getEmail(I18nUtil.getEmailFilename(context.getCurrentLocale(), Email email = Email.getEmail(I18nUtil.getEmailFilename(context.getCurrentLocale(),
"request_item.author")); "request_item.author"));
email.addRecipient(authorEmail); for (RequestItemAuthor author : authors) {
email.addRecipient(author.getEmail());
}
email.setReplyTo(ri.getReqEmail()); // Requester's address email.setReplyTo(ri.getReqEmail()); // Requester's address
email.addArgument(ri.getReqName()); // {0} Requester's name email.addArgument(ri.getReqName()); // {0} Requester's name
email.addArgument(ri.getReqEmail()); // {1} Requester's address email.addArgument(ri.getReqEmail()); // {1} Requester's address
email.addArgument(ri.isAllfiles() // {2} All bitstreams or just one? email.addArgument(ri.isAllfiles() // {2} All bitstreams or just one?
? I18nUtil.getMessage("itemRequest.all") : ri.getBitstream().getName()); ? I18nUtil.getMessage("itemRequest.all") : ri.getBitstream().getName());
email.addArgument(handleService.getCanonicalForm(ri.getItem().getHandle()));
email.addArgument(handleService.getCanonicalForm(ri.getItem().getHandle())); // {3}
email.addArgument(ri.getItem().getName()); // {4} requested item's title email.addArgument(ri.getItem().getName()); // {4} requested item's title
email.addArgument(ri.getReqMessage()); // {5} message from requester email.addArgument(ri.getReqMessage()); // {5} message from requester
email.addArgument(responseLink); // {6} Link back to DSpace for action email.addArgument(responseLink); // {6} Link back to DSpace for action
email.addArgument(authorName); // {7} corresponding author name
email.addArgument(authorEmail); // {8} corresponding author email StringBuilder names = new StringBuilder();
email.addArgument(configurationService.getProperty("dspace.name")); StringBuilder addresses = new StringBuilder();
email.addArgument(configurationService.getProperty("mail.helpdesk")); for (RequestItemAuthor author : authors) {
if (names.length() > 0) {
names.append("; ");
addresses.append("; ");
}
names.append(author.getFullName());
addresses.append(author.getEmail());
}
email.addArgument(names.toString()); // {7} corresponding author name
email.addArgument(addresses.toString()); // {8} corresponding author email
email.addArgument(configurationService.getProperty("dspace.name")); // {9}
email.addArgument(configurationService.getProperty("mail.helpdesk")); // {10}
// Send the email. // Send the email.
try { try {

View File

@@ -8,6 +8,8 @@
package org.dspace.app.requestitem; package org.dspace.app.requestitem;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.dspace.content.Item; import org.dspace.content.Item;
@@ -16,11 +18,11 @@ import org.dspace.core.I18nUtil;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.service.EPersonService; import org.dspace.eperson.service.EPersonService;
import org.dspace.services.ConfigurationService; import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
/** /**
* RequestItem strategy to allow DSpace support team's helpdesk to receive requestItem request * RequestItem strategy to allow DSpace support team's helpdesk to receive requestItem request.
* With this enabled, then the Item author/submitter doesn't receive the request, but the helpdesk instead does. * With this enabled, then the Item author/submitter doesn't receive the request, but the helpdesk instead does.
* *
* Failover to the RequestItemSubmitterStrategy, which means the submitter would get the request if there is no * Failover to the RequestItemSubmitterStrategy, which means the submitter would get the request if there is no
@@ -33,19 +35,24 @@ public class RequestItemHelpdeskStrategy extends RequestItemSubmitterStrategy {
@Autowired(required = true) @Autowired(required = true)
protected EPersonService ePersonService; protected EPersonService ePersonService;
@Autowired(required = true)
private ConfigurationService configuration;
public RequestItemHelpdeskStrategy() { public RequestItemHelpdeskStrategy() {
} }
@Override @Override
public RequestItemAuthor getRequestItemAuthor(Context context, Item item) throws SQLException { @NonNull
ConfigurationService configurationService public List<RequestItemAuthor> getRequestItemAuthor(Context context, Item item)
= DSpaceServicesFactory.getInstance().getConfigurationService(); throws SQLException {
boolean helpdeskOverridesSubmitter = configurationService boolean helpdeskOverridesSubmitter = configuration
.getBooleanProperty("request.item.helpdesk.override", false); .getBooleanProperty("request.item.helpdesk.override", false);
String helpDeskEmail = configurationService.getProperty("mail.helpdesk"); String helpDeskEmail = configuration.getProperty("mail.helpdesk");
if (helpdeskOverridesSubmitter && StringUtils.isNotBlank(helpDeskEmail)) { if (helpdeskOverridesSubmitter && StringUtils.isNotBlank(helpDeskEmail)) {
return getHelpDeskPerson(context, helpDeskEmail); List<RequestItemAuthor> authors = new ArrayList<>(1);
authors.add(getHelpDeskPerson(context, helpDeskEmail));
return authors;
} else { } else {
//Fallback to default logic (author of Item) if helpdesk isn't fully enabled or setup //Fallback to default logic (author of Item) if helpdesk isn't fully enabled or setup
return super.getRequestItemAuthor(context, item); return super.getRequestItemAuthor(context, item);

View File

@@ -8,6 +8,8 @@
package org.dspace.app.requestitem; package org.dspace.app.requestitem;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -16,12 +18,13 @@ import org.dspace.content.MetadataValue;
import org.dspace.content.service.ItemService; import org.dspace.content.service.ItemService;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.core.I18nUtil; import org.dspace.core.I18nUtil;
import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
/** /**
* Try to look to an item metadata for the corresponding author name and email. * Try to look to an item metadata for the corresponding author name and email.
* Failover to the RequestItemSubmitterStrategy * Failover to the RequestItemSubmitterStrategy.
* *
* @author Andrea Bollini * @author Andrea Bollini
*/ */
@@ -30,6 +33,9 @@ public class RequestItemMetadataStrategy extends RequestItemSubmitterStrategy {
protected String emailMetadata; protected String emailMetadata;
protected String fullNameMetadata; protected String fullNameMetadata;
@Autowired(required = true)
protected ConfigurationService configurationService;
@Autowired(required = true) @Autowired(required = true)
protected ItemService itemService; protected ItemService itemService;
@@ -37,59 +43,72 @@ public class RequestItemMetadataStrategy extends RequestItemSubmitterStrategy {
} }
@Override @Override
public RequestItemAuthor getRequestItemAuthor(Context context, Item item) @NonNull
public List<RequestItemAuthor> getRequestItemAuthor(Context context, Item item)
throws SQLException { throws SQLException {
RequestItemAuthor author = null; List<RequestItemAuthor> authors;
if (emailMetadata != null) { if (emailMetadata != null) {
List<MetadataValue> vals = itemService.getMetadataByMetadataString(item, emailMetadata); List<MetadataValue> vals = itemService.getMetadataByMetadataString(item, emailMetadata);
if (vals.size() > 0) { List<MetadataValue> nameVals;
String email = vals.iterator().next().getValue(); if (null != fullNameMetadata) {
String fullname = null; nameVals = itemService.getMetadataByMetadataString(item, fullNameMetadata);
if (fullNameMetadata != null) { } else {
List<MetadataValue> nameVals = itemService.getMetadataByMetadataString(item, fullNameMetadata); nameVals = Collections.EMPTY_LIST;
if (nameVals.size() > 0) { }
fullname = nameVals.iterator().next().getValue(); boolean useNames = vals.size() == nameVals.size();
if (!vals.isEmpty()) {
authors = new ArrayList<>(vals.size());
for (int authorIndex = 0; authorIndex < vals.size(); authorIndex++) {
String email = vals.get(authorIndex).getValue();
String fullname = null;
if (useNames) {
fullname = nameVals.get(authorIndex).getValue();
} }
if (StringUtils.isBlank(fullname)) {
fullname = I18nUtil.getMessage(
"org.dspace.app.requestitem.RequestItemMetadataStrategy.unnamed",
context);
}
RequestItemAuthor author = new RequestItemAuthor(
fullname, email);
authors.add(author);
} }
if (StringUtils.isBlank(fullname)) { return authors;
fullname = I18nUtil } else {
.getMessage( return Collections.EMPTY_LIST;
"org.dspace.app.requestitem.RequestItemMetadataStrategy.unnamed",
context);
}
author = new RequestItemAuthor(fullname, email);
return author;
} }
} else { } else {
// Uses the basic strategy to look for the original submitter // Uses the basic strategy to look for the original submitter
author = super.getRequestItemAuthor(context, item); authors = super.getRequestItemAuthor(context, item);
// Is the author or their email null. If so get the help desk or admin name and email
if (null == author || null == author.getEmail()) { // Remove from the list authors that do not have email addresses.
String email = null; for (RequestItemAuthor author : authors) {
String name = null; if (null == author.getEmail()) {
authors.remove(author);
}
}
if (authors.isEmpty()) { // No author email addresses! Fall back
//First get help desk name and email //First get help desk name and email
email = DSpaceServicesFactory.getInstance() String email = configurationService.getProperty("mail.helpdesk");
.getConfigurationService().getProperty("mail.helpdesk"); String name = configurationService.getProperty("mail.helpdesk.name");
name = DSpaceServicesFactory.getInstance()
.getConfigurationService().getProperty("mail.helpdesk.name");
// If help desk mail is null get the mail and name of admin // If help desk mail is null get the mail and name of admin
if (email == null) { if (email == null) {
email = DSpaceServicesFactory.getInstance() email = configurationService.getProperty("mail.admin");
.getConfigurationService().getProperty("mail.admin"); name = configurationService.getProperty("mail.admin.name");
name = DSpaceServicesFactory.getInstance()
.getConfigurationService().getProperty("mail.admin.name");
} }
author = new RequestItemAuthor(name, email); authors.add(new RequestItemAuthor(name, email));
} }
return authors;
} }
return author;
} }
public void setEmailMetadata(String emailMetadata) { public void setEmailMetadata(@NonNull String emailMetadata) {
this.emailMetadata = emailMetadata; this.emailMetadata = emailMetadata;
} }
public void setFullNameMetadata(String fullNameMetadata) { public void setFullNameMetadata(@NonNull String fullNameMetadata) {
this.fullNameMetadata = fullNameMetadata; this.fullNameMetadata = fullNameMetadata;
} }

View File

@@ -8,10 +8,13 @@
package org.dspace.app.requestitem; package org.dspace.app.requestitem;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.dspace.content.Item; import org.dspace.content.Item;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.springframework.lang.NonNull;
/** /**
* Basic strategy that looks to the original submitter. * Basic strategy that looks to the original submitter.
@@ -24,21 +27,23 @@ public class RequestItemSubmitterStrategy implements RequestItemAuthorExtractor
} }
/** /**
* Returns the submitter of an Item as RequestItemAuthor or null if the * Returns the submitter of an Item as RequestItemAuthor or an empty List if
* Submitter is deleted. * the Submitter is deleted.
* *
* @return The submitter of the item or null if the submitter is deleted * @return The submitter of the item or empty List if the submitter is deleted
* @throws SQLException if database error * @throws SQLException if database error
*/ */
@Override @Override
public RequestItemAuthor getRequestItemAuthor(Context context, Item item) @NonNull
public List<RequestItemAuthor> getRequestItemAuthor(Context context, Item item)
throws SQLException { throws SQLException {
EPerson submitter = item.getSubmitter(); EPerson submitter = item.getSubmitter();
RequestItemAuthor author = null; List<RequestItemAuthor> authors = new ArrayList<>(1);
if (null != submitter) { if (null != submitter) {
author = new RequestItemAuthor( RequestItemAuthor author = new RequestItemAuthor(
submitter.getFullName(), submitter.getEmail()); submitter.getFullName(), submitter.getEmail());
authors.add(author);
} }
return author; return authors;
} }
} }

View File

@@ -0,0 +1,62 @@
/**
* 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.app.requestitem;
import static org.junit.Assert.assertEquals;
import java.util.List;
import org.dspace.content.Collection;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.junit.Test;
import org.mockito.Mockito;
/**
*
* @author Mark H. Wood <mwood@iupui.edu>
*/
public class CollectionAdministratorsRequestItemStrategyTest {
private static final String NAME = "John Q. Public";
private static final String EMAIL = "jqpublic@example.com";
/**
* Test of getRequestItemAuthor method, of class CollectionAdministratorsRequestItemStrategy.
* @throws java.lang.Exception passed through.
*/
@Test
public void testGetRequestItemAuthor()
throws Exception {
System.out.println("getRequestItemAuthor");
Context context = Mockito.mock(Context.class);
EPerson eperson1 = Mockito.mock(EPerson.class);
Mockito.when(eperson1.getEmail()).thenReturn(EMAIL);
Mockito.when(eperson1.getFullName()).thenReturn(NAME);
Group group1 = Mockito.mock(Group.class);
Mockito.when(group1.getMembers()).thenReturn(List.of(eperson1));
Collection collection1 = Mockito.mock(Collection.class);
Mockito.when(collection1.getAdministrators()).thenReturn(group1);
Item item = Mockito.mock(Item.class);
Mockito.when(item.getOwningCollection()).thenReturn(collection1);
Mockito.when(item.getSubmitter()).thenReturn(eperson1);
CollectionAdministratorsRequestItemStrategy instance = new CollectionAdministratorsRequestItemStrategy();
List<RequestItemAuthor> result = instance.getRequestItemAuthor(context,
item);
assertEquals("Should be one author", 1, result.size());
assertEquals("Name should match " + NAME, NAME, result.get(0).getFullName());
assertEquals("Email should match " + EMAIL, EMAIL, result.get(0).getEmail());
}
}

View File

@@ -0,0 +1,53 @@
/**
* 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.app.requestitem;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
import java.util.List;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.junit.Test;
import org.mockito.Mockito;
/**
*
* @author Mark H. Wood <mwood@iupui.edu>
*/
public class CombiningRequestItemStrategyTest {
/**
* Test of getRequestItemAuthor method, of class CombiningRequestItemStrategy.
* @throws java.lang.Exception passed through.
*/
@Test
public void testGetRequestItemAuthor()
throws Exception {
System.out.println("getRequestItemAuthor");
Context context = null;
Item item = Mockito.mock(Item.class);
RequestItemAuthor author1 = new RequestItemAuthor("Pat Paulsen", "ppaulsen@example.com");
RequestItemAuthor author2 = new RequestItemAuthor("Alfred E. Neuman", "aeneuman@example.com");
RequestItemAuthor author3 = new RequestItemAuthor("Alias Undercover", "aundercover@example.com");
RequestItemAuthorExtractor strategy1 = Mockito.mock(RequestItemHelpdeskStrategy.class);
Mockito.when(strategy1.getRequestItemAuthor(context, item)).thenReturn(List.of(author1));
RequestItemAuthorExtractor strategy2 = Mockito.mock(RequestItemMetadataStrategy.class);
Mockito.when(strategy2.getRequestItemAuthor(context, item)).thenReturn(List.of(author2, author3));
List<RequestItemAuthorExtractor> strategies = List.of(strategy1, strategy2);
CombiningRequestItemStrategy instance = new CombiningRequestItemStrategy(strategies);
List<RequestItemAuthor> result = instance.getRequestItemAuthor(context,
item);
assertThat(result, containsInAnyOrder(author1, author2, author3));
}
}

View File

@@ -15,7 +15,9 @@ import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@@ -229,14 +231,20 @@ public class RequestItemRepository
} }
// Check for authorized user // Check for authorized user
RequestItemAuthor authorizer; List<RequestItemAuthor> authorizers;
try { try {
authorizer = requestItemAuthorExtractor.getRequestItemAuthor(context, ri.getItem()); authorizers = requestItemAuthorExtractor.getRequestItemAuthor(context, ri.getItem());
} catch (SQLException ex) { } catch (SQLException ex) {
LOG.warn("Failed to find an authorizer: {}", ex.getMessage()); LOG.warn("Failed to find an authorizer: {}", ex.getMessage());
authorizer = new RequestItemAuthor("", ""); authorizers = Collections.EMPTY_LIST;
} }
if (!authorizer.getEmail().equals(context.getCurrentUser().getEmail())) {
boolean authorized = false;
String requester = context.getCurrentUser().getEmail();
for (RequestItemAuthor authorizer : authorizers) {
authorized |= authorizer.getEmail().equals(requester);
}
if (!authorized) {
throw new AuthorizeException("Not authorized to approve this request"); throw new AuthorizeException("Not authorized to approve this request");
} }

View File

@@ -142,10 +142,10 @@ public class DeleteEPersonSubmitterIT extends AbstractControllerIntegrationTest
Item item = itemService.find(context, installItem.getID()); Item item = itemService.find(context, installItem.getID());
RequestItemAuthor requestItemAuthor = requestItemAuthorExtractor.getRequestItemAuthor(context, item); List<RequestItemAuthor> requestItemAuthor = requestItemAuthorExtractor.getRequestItemAuthor(context, item);
assertEquals("Help Desk", requestItemAuthor.getFullName()); assertEquals("Help Desk", requestItemAuthor.get(0).getFullName());
assertEquals("dspace-help@myu.edu", requestItemAuthor.getEmail()); assertEquals("dspace-help@myu.edu", requestItemAuthor.get(0).getEmail());
} }
/** /**
@@ -171,7 +171,7 @@ public class DeleteEPersonSubmitterIT extends AbstractControllerIntegrationTest
Item item = installItemService.installItem(context, wsi); Item item = installItemService.installItem(context, wsi);
List<Operation> opsToWithDraw = new ArrayList<Operation>(); List<Operation> opsToWithDraw = new ArrayList<>();
ReplaceOperation replaceOperationToWithDraw = new ReplaceOperation("/withdrawn", true); ReplaceOperation replaceOperationToWithDraw = new ReplaceOperation("/withdrawn", true);
opsToWithDraw.add(replaceOperationToWithDraw); opsToWithDraw.add(replaceOperationToWithDraw);
String patchBodyToWithdraw = getPatchContent(opsToWithDraw); String patchBodyToWithdraw = getPatchContent(opsToWithDraw);
@@ -191,7 +191,7 @@ public class DeleteEPersonSubmitterIT extends AbstractControllerIntegrationTest
assertNull(retrieveItemSubmitter(item.getID())); assertNull(retrieveItemSubmitter(item.getID()));
List<Operation> opsToReinstate = new ArrayList<Operation>(); List<Operation> opsToReinstate = new ArrayList<>();
ReplaceOperation replaceOperationToReinstate = new ReplaceOperation("/withdrawn", false); ReplaceOperation replaceOperationToReinstate = new ReplaceOperation("/withdrawn", false);
opsToReinstate.add(replaceOperationToReinstate); opsToReinstate.add(replaceOperationToReinstate);
String patchBodyToReinstate = getPatchContent(opsToReinstate); String patchBodyToReinstate = getPatchContent(opsToReinstate);

View File

@@ -8,25 +8,65 @@
http://www.springframework.org/schema/context/spring-context-2.5.xsd" http://www.springframework.org/schema/context/spring-context-2.5.xsd"
default-autowire-candidates="*Service,*DAO,javax.sql.DataSource"> default-autowire-candidates="*Service,*DAO,javax.sql.DataSource">
<description>
Strategies for determining who receives Request Copy emails.
A copy request "strategy" class produces a list of addresses to which a
request email should be sent. Each strategy gets its addresses from a
different source. Select the one that meets your need, or use the
CombiningRequestItemStrategy to meld the lists from two or more other
strategies.
</description>
<context:annotation-config /> <!-- allows us to use Spring annotations in beans --> <context:annotation-config /> <!-- allows us to use Spring annotations in beans -->
<bean class="org.dspace.app.requestitem.RequestItemMetadataStrategy" <!-- Select the implementation to be used. -->
id="org.dspace.app.requestitem.RequestItemAuthorExtractor" <alias alias='org.dspace.app.requestitem.RequestItemAuthorExtractor'
autowire-candidate='true'> name='org.dspace.app.requestitem.RequestItemMetadataStrategy'/>
<!--
Uncomment these properties if you want lookup in metadata the email and
the name of the author to contact for request copy.
If you don't configure that or if the requested item doesn't have these
metadata, the submitter data are used as fail over.
<property name="emailMetadata" value="schema.element.qualifier" /> <!-- Get recipients from an item metadata field. -->
<property name="fullNameMetadata" value="schema.element.qualifier" /> <bean class="org.dspace.app.requestitem.RequestItemMetadataStrategy"
--> id="org.dspace.app.requestitem.RequestItemMetadataStrategy"
autowire-candidate="true">
<!--
Uncomment these properties if you want lookup in metadata the email
and the name of the author to contact for request copy.
If you don't configure that or if the requested item doesn't have
these metadata, the submitter data are used as fail over.
-->
<!--
<property name="emailMetadata"
value="schema.element.qualifier" />
<property name="fullNameMetadata"
value="schema.element.qualifier" />
-->
</bean> </bean>
<!-- HelpDesk to instead get RequestItem emails--> <!-- HelpDesk to instead get RequestItem emails-->
<!--<bean class="org.dspace.app.requestitem.RequestItemHelpdeskStrategy" <!--
id="org.dspace.app.requestitem.RequestItemAuthorExtractor" <bean class="org.dspace.app.requestitem.RequestItemHelpdeskStrategy"
autowire-candidate='true'></bean>--> id="org.dspace.app.requestitem.RequestItemHelpdeskStrategy"/>
-->
<!-- Send request emails to administrators of an Item's owning Collection. -->
<!--
<bean class='org.dspace.app.requestitem.CollectionAdministratorsRequestItemStrategy'
id='org.dspace.app.requestitem.CollectionAdministratorsRequestItemStrategy'/>
-->
<!-- Execute multiple strategies and concatenate their lists of recipients.
Mail will go to all members of the combined list. -->
<!--
<bean class='org.dspace.app.requestitem.CombiningRequestItemStrategy'
id='org.dspace.app.requestitem.CombiningRequestItemStrategy'
autowire='no'>
<constructor-arg>
<description>A list of references to RequestItemAuthorExtractor beans</description>
<list>
<ref bean='org.dspace.app.requestitem.RequestItemMetadataStrategy'/>
<ref bean='org.dspace.app.requestitem.CollectionAdministratorsRequestItemStrategy'/>
</list>
</constructor-arg>
</bean>
-->
</beans> </beans>