Merge branch 'main' of https://github.com/DSpace/DSpace into DURACOM-86

This commit is contained in:
Andrea Bollini
2022-09-20 22:35:12 +02:00
43 changed files with 1857 additions and 753 deletions

View File

@@ -548,17 +548,11 @@
<groupId>com.ibm.icu</groupId>
<artifactId>icu4j</artifactId>
</dependency>
<!-- Codebase at https://github.com/OCLC-Research/oaiharvester2/ -->
<!-- Codebase at https://github.com/DSpace/oclc-harvester2 -->
<dependency>
<groupId>org.dspace</groupId>
<artifactId>oclc-harvester2</artifactId>
</dependency>
<!-- Xalan is REQUIRED by 'oclc-harvester2' listed above (OAI harvesting fails without it).
Please do NOT use Xalan in DSpace codebase as it is not well maintained. -->
<dependency>
<groupId>xalan</groupId>
<artifactId>xalan</artifactId>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-services</artifactId>

View File

@@ -14,6 +14,7 @@ import java.util.Locale;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.lang3.StringUtils;
import org.dspace.core.Context;
@@ -54,14 +55,14 @@ public final class CreateAdministrator {
protected GroupService groupService;
/**
* For invoking via the command line. If called with no command line arguments,
* For invoking via the command line. If called with no command line arguments,
* it will negotiate with the user for the administrator details
*
* @param argv the command line arguments given
* @throws Exception if error
*/
public static void main(String[] argv)
throws Exception {
throws Exception {
CommandLineParser parser = new DefaultParser();
Options options = new Options();
@@ -69,19 +70,41 @@ public final class CreateAdministrator {
options.addOption("e", "email", true, "administrator email address");
options.addOption("f", "first", true, "administrator first name");
options.addOption("h", "help", false, "explain create-administrator options");
options.addOption("l", "last", true, "administrator last name");
options.addOption("c", "language", true, "administrator language");
options.addOption("p", "password", true, "administrator password");
CommandLine line = parser.parse(options, argv);
CommandLine line = null;
try {
line = parser.parse(options, argv);
} catch (Exception e) {
System.out.println(e.getMessage() + "\nTry \"dspace create-administrator -h\" to print help information.");
System.exit(1);
}
if (line.hasOption("e") && line.hasOption("f") && line.hasOption("l") &&
line.hasOption("c") && line.hasOption("p")) {
line.hasOption("c") && line.hasOption("p")) {
ca.createAdministrator(line.getOptionValue("e"),
line.getOptionValue("f"), line.getOptionValue("l"),
line.getOptionValue("c"), line.getOptionValue("p"));
line.getOptionValue("f"), line.getOptionValue("l"),
line.getOptionValue("c"), line.getOptionValue("p"));
} else if (line.hasOption("h")) {
String header = "\nA command-line tool for creating an initial administrator for setting up a" +
" DSpace site. Unless all the required parameters are passed it will" +
" prompt for an e-mail address, last name, first name and password from" +
" standard input.. An administrator group is then created and the data passed" +
" in used to create an e-person in that group.\n\n";
String footer = "\n";
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("dspace create-administrator", header, options, footer, true);
return;
} else {
ca.negotiateAdministratorDetails();
ca.negotiateAdministratorDetails(line);
}
}
@@ -91,7 +114,7 @@ public final class CreateAdministrator {
* @throws Exception if error
*/
protected CreateAdministrator()
throws Exception {
throws Exception {
context = new Context();
groupService = EPersonServiceFactory.getInstance().getGroupService();
ePersonService = EPersonServiceFactory.getInstance().getEPersonService();
@@ -103,20 +126,20 @@ public final class CreateAdministrator {
*
* @throws Exception if error
*/
protected void negotiateAdministratorDetails()
throws Exception {
protected void negotiateAdministratorDetails(CommandLine line)
throws Exception {
Console console = System.console();
System.out.println("Creating an initial administrator account");
boolean dataOK = false;
String email = null;
String firstName = null;
String lastName = null;
char[] password1 = null;
char[] password2 = null;
String email = line.getOptionValue('e');
String firstName = line.getOptionValue('f');
String lastName = line.getOptionValue('l');
String language = I18nUtil.getDefaultLocale().getLanguage();
ConfigurationService cfg = DSpaceServicesFactory.getInstance().getConfigurationService();
boolean flag = line.hasOption('p');
char[] password = null;
boolean dataOK = line.hasOption('f') && line.hasOption('e') && line.hasOption('l');
while (!dataOK) {
System.out.print("E-mail address: ");
@@ -147,8 +170,6 @@ public final class CreateAdministrator {
if (lastName != null) {
lastName = lastName.trim();
}
ConfigurationService cfg = DSpaceServicesFactory.getInstance().getConfigurationService();
if (cfg.hasProperty("webui.supported.locales")) {
System.out.println("Select one of the following languages: "
+ cfg.getProperty("webui.supported.locales"));
@@ -163,46 +184,59 @@ public final class CreateAdministrator {
}
}
System.out.println("Password will not display on screen.");
System.out.print("Password: ");
System.out.print("Is the above data correct? (y or n): ");
System.out.flush();
password1 = console.readPassword();
String s = console.readLine();
System.out.print("Again to confirm: ");
System.out.flush();
password2 = console.readPassword();
//TODO real password validation
if (password1.length > 1 && Arrays.equals(password1, password2)) {
// password OK
System.out.print("Is the above data correct? (y or n): ");
System.out.flush();
String s = console.readLine();
if (s != null) {
s = s.trim();
if (s.toLowerCase().startsWith("y")) {
dataOK = true;
}
if (s != null) {
s = s.trim();
if (s.toLowerCase().startsWith("y")) {
dataOK = true;
}
} else {
System.out.println("Passwords don't match");
}
}
if (!flag) {
password = getPassword(console);
if (password == null) {
return;
}
} else {
password = line.getOptionValue("p").toCharArray();
}
// if we make it to here, we are ready to create an administrator
createAdministrator(email, firstName, lastName, language, String.valueOf(password1));
createAdministrator(email, firstName, lastName, language, String.valueOf(password));
//Cleaning arrays that held password
Arrays.fill(password1, ' ');
Arrays.fill(password2, ' ');
}
private char[] getPassword(Console console) {
char[] password1 = null;
char[] password2 = null;
System.out.println("Password will not display on screen.");
System.out.print("Password: ");
System.out.flush();
password1 = console.readPassword();
System.out.print("Again to confirm: ");
System.out.flush();
password2 = console.readPassword();
// TODO real password validation
if (password1.length > 1 && Arrays.equals(password1, password2)) {
// password OK
Arrays.fill(password2, ' ');
return password1;
} else {
System.out.println("Passwords don't match");
return null;
}
}
/**
* Create the administrator with the given details. If the user
* Create the administrator with the given details. If the user
* already exists then they are simply upped to administrator status
*
* @param email the email for the user
@@ -213,8 +247,8 @@ public final class CreateAdministrator {
* @throws Exception if error
*/
protected void createAdministrator(String email, String first, String last,
String language, String pw)
throws Exception {
String language, String pw)
throws Exception {
// Of course we aren't an administrator yet so we need to
// circumvent authorisation
context.turnOffAuthorisationSystem();

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;
/**
* Object representing an Item Request
* Object representing an Item Request.
*/
@Entity
@Table(name = "requestitem")
@@ -94,6 +94,9 @@ public class RequestItem implements ReloadableEntity<Integer> {
this.allfiles = allfiles;
}
/**
* @return {@code true} if all of the Item's files are requested.
*/
public boolean isAllfiles() {
return allfiles;
}
@@ -102,6 +105,9 @@ public class RequestItem implements ReloadableEntity<Integer> {
this.reqMessage = reqMessage;
}
/**
* @return a message from the requester.
*/
public String getReqMessage() {
return reqMessage;
}
@@ -110,6 +116,9 @@ public class RequestItem implements ReloadableEntity<Integer> {
this.reqName = reqName;
}
/**
* @return Human-readable name of the user requesting access.
*/
public String getReqName() {
return reqName;
}
@@ -118,6 +127,9 @@ public class RequestItem implements ReloadableEntity<Integer> {
this.reqEmail = reqEmail;
}
/**
* @return address of the user requesting access.
*/
public String getReqEmail() {
return reqEmail;
}
@@ -126,6 +138,9 @@ public class RequestItem implements ReloadableEntity<Integer> {
this.token = token;
}
/**
* @return a unique request identifier which can be emailed.
*/
public String getToken() {
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
* Copy feature
* Copy feature.
*
* @author Andrea Bollini
*/
public class RequestItemAuthor {
private String fullName;
private String email;
private final String fullName;
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) {
super();
this.fullName = fullName;
this.email = email;
}
/**
* Construct an author from an EPerson's metadata.
*
* @param ePerson the EPerson.
*/
public RequestItemAuthor(EPerson ePerson) {
super();
this.fullName = ePerson.getFullName();

View File

@@ -8,26 +8,28 @@
package org.dspace.app.requestitem;
import java.sql.SQLException;
import java.util.List;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.springframework.lang.NonNull;
/**
* Interface to abstract the strategy for select the author to contact for
* request copy
* Interface to abstract the strategy for selecting the author to contact for
* request copy.
*
* @author Andrea Bollini
*/
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 item item to request
* @return An object containing name an email address to send the request to
* or null if no valid email address was found.
* @return Names and email addresses to send the request to.
* @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)
throws IOException, SQLException {
// Who is making this request?
RequestItemAuthor author = requestItemAuthorExtractor
List<RequestItemAuthor> authors = requestItemAuthorExtractor
.getRequestItemAuthor(context, ri.getItem());
String authorEmail = author.getEmail();
String authorName = author.getFullName();
// Build an email to the approver.
Email email = Email.getEmail(I18nUtil.getEmailFilename(context.getCurrentLocale(),
"request_item.author"));
email.addRecipient(authorEmail);
for (RequestItemAuthor author : authors) {
email.addRecipient(author.getEmail());
}
email.setReplyTo(ri.getReqEmail()); // Requester's address
email.addArgument(ri.getReqName()); // {0} Requester's name
email.addArgument(ri.getReqEmail()); // {1} Requester's address
email.addArgument(ri.isAllfiles() // {2} All bitstreams or just one?
? 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.getReqMessage()); // {5} message from requester
email.addArgument(responseLink); // {6} Link back to DSpace for action
email.addArgument(authorName); // {7} corresponding author name
email.addArgument(authorEmail); // {8} corresponding author email
email.addArgument(configurationService.getProperty("dspace.name"));
email.addArgument(configurationService.getProperty("mail.helpdesk"));
StringBuilder names = new StringBuilder();
StringBuilder addresses = new StringBuilder();
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.
try {

View File

@@ -8,6 +8,8 @@
package org.dspace.app.requestitem;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.dspace.content.Item;
@@ -16,11 +18,11 @@ import org.dspace.core.I18nUtil;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.service.EPersonService;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
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.
*
* 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)
protected EPersonService ePersonService;
@Autowired(required = true)
private ConfigurationService configuration;
public RequestItemHelpdeskStrategy() {
}
@Override
public RequestItemAuthor getRequestItemAuthor(Context context, Item item) throws SQLException {
ConfigurationService configurationService
= DSpaceServicesFactory.getInstance().getConfigurationService();
boolean helpdeskOverridesSubmitter = configurationService
@NonNull
public List<RequestItemAuthor> getRequestItemAuthor(Context context, Item item)
throws SQLException {
boolean helpdeskOverridesSubmitter = configuration
.getBooleanProperty("request.item.helpdesk.override", false);
String helpDeskEmail = configurationService.getProperty("mail.helpdesk");
String helpDeskEmail = configuration.getProperty("mail.helpdesk");
if (helpdeskOverridesSubmitter && StringUtils.isNotBlank(helpDeskEmail)) {
return getHelpDeskPerson(context, helpDeskEmail);
List<RequestItemAuthor> authors = new ArrayList<>(1);
authors.add(getHelpDeskPerson(context, helpDeskEmail));
return authors;
} else {
//Fallback to default logic (author of Item) if helpdesk isn't fully enabled or setup
return super.getRequestItemAuthor(context, item);

View File

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

View File

@@ -8,10 +8,13 @@
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.dspace.eperson.EPerson;
import org.springframework.lang.NonNull;
/**
* 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
* Submitter is deleted.
* Returns the submitter of an Item as RequestItemAuthor or an empty List if
* 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
*/
@Override
public RequestItemAuthor getRequestItemAuthor(Context context, Item item)
@NonNull
public List<RequestItemAuthor> getRequestItemAuthor(Context context, Item item)
throws SQLException {
EPerson submitter = item.getSubmitter();
RequestItemAuthor author = null;
List<RequestItemAuthor> authors = new ArrayList<>(1);
if (null != submitter) {
author = new RequestItemAuthor(
submitter.getFullName(), submitter.getEmail());
RequestItemAuthor author = new RequestItemAuthor(
submitter.getFullName(), submitter.getEmail());
authors.add(author);
}
return author;
return authors;
}
}

View File

@@ -14,9 +14,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.net.URLEncoder;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
@@ -465,17 +463,17 @@ public abstract class AbstractMETSDisseminator
Utils.copy(input, zip);
input.close();
} else {
log.warn("Adding zero-length file for Bitstream, SID="
+ String.valueOf(bitstream.getSequenceID())
log.warn("Adding zero-length file for Bitstream, uuid="
+ String.valueOf(bitstream.getID())
+ ", not authorized for READ.");
}
zip.closeEntry();
} else if (unauth != null && unauth.equalsIgnoreCase("skip")) {
log.warn("Skipping Bitstream, SID=" + String
.valueOf(bitstream.getSequenceID()) + ", not authorized for READ.");
log.warn("Skipping Bitstream, uuid=" + String
.valueOf(bitstream.getID()) + ", not authorized for READ.");
} else {
throw new AuthorizeException(
"Not authorized to read Bitstream, SID=" + String.valueOf(bitstream.getSequenceID()));
"Not authorized to read Bitstream, uuid=" + String.valueOf(bitstream.getID()));
}
}
}
@@ -896,12 +894,12 @@ public abstract class AbstractMETSDisseminator
continue;
} else if (!(unauth != null && unauth.equalsIgnoreCase("zero"))) {
throw new AuthorizeException(
"Not authorized to read Bitstream, SID=" + String.valueOf(bitstream.getSequenceID()));
"Not authorized to read Bitstream, uuid=" + String.valueOf(bitstream.getID()));
}
}
String sid = String.valueOf(bitstream.getSequenceID());
String fileID = bitstreamIDstart + sid;
String uuid = String.valueOf(bitstream.getID());
String fileID = bitstreamIDstart + uuid;
edu.harvard.hul.ois.mets.File file = new edu.harvard.hul.ois.mets.File();
file.setID(fileID);
file.setSEQ(bitstream.getSequenceID());
@@ -924,7 +922,7 @@ public abstract class AbstractMETSDisseminator
* extracted text or a thumbnail, so we use the name to work
* out which bitstream to be in the same group as
*/
String groupID = "GROUP_" + bitstreamIDstart + sid;
String groupID = "GROUP_" + bitstreamIDstart + uuid;
if ((bundle.getName() != null)
&& (bundle.getName().equals("THUMBNAIL") ||
bundle.getName().startsWith("TEXT"))) {
@@ -934,7 +932,7 @@ public abstract class AbstractMETSDisseminator
bitstream);
if (original != null) {
groupID = "GROUP_" + bitstreamIDstart
+ original.getSequenceID();
+ String.valueOf(original.getID());
}
}
file.setGROUPID(groupID);
@@ -1403,7 +1401,7 @@ public abstract class AbstractMETSDisseminator
// if bare manifest, use external "persistent" URI for bitstreams
if (params != null && (params.getBooleanProperty("manifestOnly", false))) {
// Try to build a persistent(-ish) URI for bitstream
// Format: {site-base-url}/bitstream/{item-handle}/{sequence-id}/{bitstream-name}
// Format: {site-ui-url}/bitstreams/{bitstream-uuid}
try {
// get handle of parent Item of this bitstream, if there is one:
String handle = null;
@@ -1414,26 +1412,13 @@ public abstract class AbstractMETSDisseminator
handle = bi.get(0).getHandle();
}
}
if (handle != null) {
return configurationService
.getProperty("dspace.ui.url")
+ "/bitstream/"
+ handle
+ "/"
+ String.valueOf(bitstream.getSequenceID())
+ "/"
+ URLEncoder.encode(bitstream.getName(), "UTF-8");
} else { //no Handle assigned, so persistent(-ish) URI for bitstream is
// Format: {site-base-url}/retrieve/{bitstream-internal-id}
return configurationService
.getProperty("dspace.ui.url")
+ "/retrieve/"
+ String.valueOf(bitstream.getID());
}
return configurationService
.getProperty("dspace.ui.url")
+ "/bitstreams/"
+ String.valueOf(bitstream.getID())
+ "/download";
} catch (SQLException e) {
log.error("Database problem", e);
} catch (UnsupportedEncodingException e) {
log.error("Unknown character set", e);
}
// We should only get here if we failed to build a nice URL above

View File

@@ -0,0 +1,94 @@
/**
* 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.ctask.general;
import java.io.IOException;
import java.sql.SQLException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.curate.AbstractCurationTask;
import org.dspace.curate.Curator;
import org.dspace.identifier.IdentifierException;
import org.dspace.identifier.IdentifierProvider;
import org.dspace.identifier.VersionedHandleIdentifierProviderWithCanonicalHandles;
import org.dspace.identifier.factory.IdentifierServiceFactory;
import org.dspace.identifier.service.IdentifierService;
import org.dspace.services.factory.DSpaceServicesFactory;
/**
* Ensure that an object has all of the identifiers that it should, minting them
* as necessary.
*
* @author Mark H. Wood {@literal <mwood@iupui.edu>}
*/
public class CreateMissingIdentifiers
extends AbstractCurationTask {
private static final Logger LOG = LogManager.getLogger();
@Override
public int perform(DSpaceObject dso)
throws IOException {
// Only some kinds of model objects get identifiers
if (!(dso instanceof Item)) {
return Curator.CURATE_SKIP;
}
// XXX Temporary escape when an incompatible provider is configured.
// XXX Remove this when the provider is fixed.
boolean compatible = DSpaceServicesFactory
.getInstance()
.getServiceManager()
.getServiceByName(
VersionedHandleIdentifierProviderWithCanonicalHandles.class.getCanonicalName(),
IdentifierProvider.class) == null;
if (!compatible) {
setResult("This task is not compatible with VersionedHandleIdentifierProviderWithCanonicalHandles");
return Curator.CURATE_ERROR;
}
// XXX End of escape
String typeText = Constants.typeText[dso.getType()];
// Get a Context
Context context;
try {
context = Curator.curationContext();
} catch (SQLException ex) {
report("Could not get the curation Context: " + ex.getMessage());
return Curator.CURATE_ERROR;
}
// Find the IdentifierService implementation
IdentifierService identifierService = IdentifierServiceFactory
.getInstance()
.getIdentifierService();
// Register any missing identifiers.
try {
identifierService.register(context, dso);
} catch (AuthorizeException | IdentifierException | SQLException ex) {
String message = ex.getMessage();
report(String.format("Identifier(s) not minted for %s %s: %s%n",
typeText, dso.getID().toString(), message));
LOG.error("Identifier(s) not minted: {}", message);
return Curator.CURATE_ERROR;
}
// Success!
report(String.format("%s %s registered.%n",
typeText, dso.getID().toString()));
return Curator.CURATE_SUCCESS;
}
}

View File

@@ -21,7 +21,6 @@ import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.dspace.discovery.indexobject.IndexableItem;
import org.dspace.service.impl.HttpConnectionPoolService;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.storage.rdbms.DatabaseUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -75,14 +74,13 @@ public class SolrSearchCore {
*/
protected void initSolr() {
if (solr == null) {
String solrService = DSpaceServicesFactory.getInstance().getConfigurationService()
.getProperty("discovery.search.server");
String solrService = configurationService.getProperty("discovery.search.server");
UrlValidator urlValidator = new UrlValidator(UrlValidator.ALLOW_LOCAL_URLS);
if (urlValidator.isValid(solrService) || configurationService
.getBooleanProperty("discovery.solr.url.validation.enabled", true)) {
try {
log.debug("Solr URL: " + solrService);
log.debug("Solr URL: {}", solrService);
HttpSolrClient solrServer = new HttpSolrClient.Builder(solrService)
.withHttpClient(httpConnectionPoolService.getClient())
.build();
@@ -103,10 +101,13 @@ public class SolrSearchCore {
solr = solrServer;
} catch (SolrServerException | IOException e) {
log.error("Error while initializing solr server", e);
log.error("Error while initializing solr server {}",
solrService, e);
throw new RuntimeException("Failed to contact Solr at " + solrService
+ " : " + e.getMessage());
}
} else {
log.error("Error while initializing solr, invalid url: " + solrService);
log.error("Error while initializing solr, invalid url: {}", solrService);
}
}
}

View File

@@ -15,6 +15,7 @@ import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -33,6 +34,7 @@ import org.dspace.content.dto.MetadataValueDTO;
import org.dspace.external.OpenAIRERestConnector;
import org.dspace.external.model.ExternalDataObject;
import org.dspace.external.provider.AbstractExternalDataProvider;
import org.dspace.importer.external.metadatamapping.MetadataFieldConfig;
import org.springframework.beans.factory.annotation.Autowired;
/**
@@ -40,13 +42,9 @@ import org.springframework.beans.factory.annotation.Autowired;
* will deal with the OpenAIRE External Data lookup
*
* @author paulo-graca
*
*/
public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
/**
* log4j logger
*/
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(OpenAIREFundingDataProvider.class);
/**
@@ -54,6 +52,16 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
*/
protected static final String PREFIX = "info:eu-repo/grantAgreement";
private static final String TITLE = "dcTitle";
private static final String SUBJECT = "dcSubject";
private static final String AWARD_URI = "awardURI";
private static final String FUNDER_NAME = "funderName";
private static final String SPATIAL = "coverageSpatial";
private static final String AWARD_NUMBER = "awardNumber";
private static final String FUNDER_ID = "funderIdentifier";
private static final String FUNDING_STREAM = "fundingStream";
private static final String TITLE_ALTERNATIVE = "titleAlternative";
/**
* rows default limit
*/
@@ -69,11 +77,9 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
*/
protected OpenAIRERestConnector connector;
/**
* required method
*/
public void init() throws IOException {
}
protected Map<String, MetadataFieldConfig> metadataFields;
public void init() throws IOException {}
@Override
public String getSourceIdentifier() {
@@ -266,14 +272,22 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
}
}
public Map<String, MetadataFieldConfig> getMetadataFields() {
return metadataFields;
}
public void setMetadataFields(Map<String, MetadataFieldConfig> metadataFields) {
this.metadataFields = metadataFields;
}
/**
* OpenAIRE Funding External Data Builder Class
*
* @author pgraca
*
*/
public static class ExternalDataObjectBuilder {
ExternalDataObject externalDataObject;
public class ExternalDataObjectBuilder {
private ExternalDataObject externalDataObject;
public ExternalDataObjectBuilder(Project project) {
String funderIdPrefix = "urn:openaire:";
@@ -283,46 +297,42 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
for (FundingTreeType fundingTree : projectHelper.getFundingTreeTypes()) {
FunderType funder = fundingTree.getFunder();
// Funder name
this.addFunderName(funder.getName());
this.addMetadata(metadataFields.get(FUNDER_NAME), funder.getName());
// Funder Id - convert it to an urn
this.addFunderID(funderIdPrefix + funder.getId());
this.addMetadata(metadataFields.get(FUNDER_ID), funderIdPrefix + funder.getId());
// Jurisdiction
this.addFunderJuristiction(funder.getJurisdiction());
this.addMetadata(metadataFields.get(SPATIAL), funder.getJurisdiction());
FundingHelper fundingHelper = new FundingHelper(
fundingTree.getFundingLevel2OrFundingLevel1OrFundingLevel0());
fundingTree.getFundingLevel2OrFundingLevel1OrFundingLevel0());
// Funding description
for (FundingType funding : fundingHelper.getFirstAvailableFunding()) {
this.addFundingStream(funding.getDescription());
this.addMetadata(metadataFields.get(FUNDING_STREAM), funding.getDescription());
}
}
// Title
for (String title : projectHelper.getTitles()) {
this.addAwardTitle(title);
this.addMetadata(metadataFields.get(TITLE), title);
this.setDisplayValue(title);
this.setValue(title);
}
// Code
for (String code : projectHelper.getCodes()) {
this.addAwardNumber(code);
this.addMetadata(metadataFields.get(AWARD_NUMBER), code);
}
// Website url
for (String url : projectHelper.getWebsiteUrls()) {
this.addAwardURI(url);
this.addMetadata(metadataFields.get(AWARD_URI), url);
}
// Acronyms
for (String acronym : projectHelper.getAcronyms()) {
this.addFundingItemAcronym(acronym);
this.addMetadata(metadataFields.get(TITLE_ALTERNATIVE), acronym);
}
// Keywords
for (String keyword : projectHelper.getKeywords()) {
this.addSubject(keyword);
this.addMetadata(metadataFields.get(SUBJECT), keyword);
}
}
@@ -366,7 +376,6 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder setId(String id) {
// we use base64 encoding in order to use slashes / and other
// characters that must be escaped for the <:entry-id>
String base64Id = Base64.getEncoder().encodeToString(id.getBytes());
@@ -374,128 +383,10 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
return this;
}
/**
* Add metadata dc.identifier
*
* @param metadata identifier
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addIdentifier(String identifier) {
this.externalDataObject.addMetadata(new MetadataValueDTO("dc", "identifier", null, null, identifier));
return this;
}
/**
* Add metadata project.funder.name
*
* @param metadata funderName
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addFunderName(String funderName) {
this.externalDataObject.addMetadata(new MetadataValueDTO("project", "funder", "name", null, funderName));
return this;
}
/**
* Add metadata project.funder.identifier
*
* @param metadata funderId
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addFunderID(String funderID) {
this.externalDataObject
.addMetadata(new MetadataValueDTO("project", "funder", "identifier", null, funderID));
return this;
}
/**
* Add metadata dc.title
*
* @param metadata awardTitle
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addAwardTitle(String awardTitle) {
this.externalDataObject.addMetadata(new MetadataValueDTO("dc", "title", null, null, awardTitle));
return this;
}
/**
* Add metadata oaire.fundingStream
*
* @param metadata fundingStream
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addFundingStream(String fundingStream) {
this.externalDataObject
.addMetadata(new MetadataValueDTO("oaire", "fundingStream", null, null, fundingStream));
return this;
}
/**
* Add metadata oaire.awardNumber
*
* @param metadata awardNumber
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addAwardNumber(String awardNumber) {
this.externalDataObject.addMetadata(new MetadataValueDTO("oaire", "awardNumber", null, null, awardNumber));
return this;
}
/**
* Add metadata oaire.awardURI
*
* @param metadata websiteUrl
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addAwardURI(String websiteUrl) {
this.externalDataObject.addMetadata(new MetadataValueDTO("oaire", "awardURI", null, null, websiteUrl));
return this;
}
/**
* Add metadata dc.title.alternative
*
* @param metadata fundingItemAcronym
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addFundingItemAcronym(String fundingItemAcronym) {
this.externalDataObject
.addMetadata(new MetadataValueDTO("dc", "title", "alternative", null, fundingItemAcronym));
return this;
}
/**
* Add metadata dc.coverage.spatial
*
* @param metadata funderJuristiction
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addFunderJuristiction(String funderJuristiction) {
this.externalDataObject
.addMetadata(new MetadataValueDTO("dc", "coverage", "spatial", null, funderJuristiction));
return this;
}
/**
* Add metadata dc.description
*
* @param metadata description
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addDescription(String description) {
this.externalDataObject.addMetadata(new MetadataValueDTO("dc", "description", null, null, description));
return this;
}
/**
* Add metadata dc.subject
*
* @param metadata subject
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addSubject(String subject) {
this.externalDataObject.addMetadata(new MetadataValueDTO("dc", "subject", null, null, subject));
public ExternalDataObjectBuilder addMetadata(MetadataFieldConfig metadataField, String value) {
this.externalDataObject.addMetadata(new MetadataValueDTO(metadataField.getSchema(),
metadataField.getElement(),
metadataField.getQualifier(), null, value));
return this;
}
@@ -508,4 +399,5 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
return this.externalDataObject;
}
}
}
}

View File

@@ -19,10 +19,8 @@ import java.util.Calendar;
import java.util.Date;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPathExpressionException;
import ORG.oclc.oai.harvester2.verb.Identify;
import ORG.oclc.oai.harvester2.verb.ListIdentifiers;
import org.dspace.content.Collection;
import org.dspace.core.Context;
import org.dspace.harvest.dao.HarvestedCollectionDAO;
@@ -33,6 +31,8 @@ import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.jdom2.input.DOMBuilder;
import org.oclc.oai.harvester2.verb.Identify;
import org.oclc.oai.harvester2.verb.ListIdentifiers;
import org.springframework.beans.factory.annotation.Autowired;
import org.w3c.dom.DOMException;
import org.xml.sax.SAXException;
@@ -198,7 +198,7 @@ public class HarvestedCollectionServiceImpl implements HarvestedCollectionServic
// First, see if we can contact the target server at all.
try {
new Identify(oaiSource);
} catch (IOException | ParserConfigurationException | TransformerException | SAXException ex) {
} catch (IOException | ParserConfigurationException | XPathExpressionException | SAXException ex) {
errorSet.add(OAI_ADDRESS_ERROR + ": OAI server could not be reached.");
return errorSet;
}
@@ -216,7 +216,7 @@ public class HarvestedCollectionServiceImpl implements HarvestedCollectionServic
try {
OREOAIPrefix = OAIHarvester.oaiResolveNamespaceToPrefix(oaiSource, OAIHarvester.getORENamespace().getURI());
DMDOAIPrefix = OAIHarvester.oaiResolveNamespaceToPrefix(oaiSource, DMD_NS.getURI());
} catch (IOException | ParserConfigurationException | TransformerException | SAXException ex) {
} catch (IOException | ParserConfigurationException | XPathExpressionException | SAXException ex) {
errorSet.add(OAI_ADDRESS_ERROR
+ ": OAI did not respond to ListMetadataFormats query ("
+ ORE_NS.getPrefix() + ":" + OREOAIPrefix + " ; "
@@ -260,7 +260,8 @@ public class HarvestedCollectionServiceImpl implements HarvestedCollectionServic
}
}
}
} catch (IOException | ParserConfigurationException | TransformerException | DOMException | SAXException e) {
} catch (IOException | ParserConfigurationException | XPathExpressionException | DOMException |
SAXException e) {
errorSet.add(OAI_ADDRESS_ERROR + ": OAI server could not be reached");
return errorSet;
} catch (RuntimeException re) {

View File

@@ -28,12 +28,8 @@ import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPathExpressionException;
import ORG.oclc.oai.harvester2.verb.GetRecord;
import ORG.oclc.oai.harvester2.verb.Identify;
import ORG.oclc.oai.harvester2.verb.ListMetadataFormats;
import ORG.oclc.oai.harvester2.verb.ListRecords;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -76,6 +72,10 @@ import org.jdom2.Element;
import org.jdom2.Namespace;
import org.jdom2.input.DOMBuilder;
import org.jdom2.output.XMLOutputter;
import org.oclc.oai.harvester2.verb.GetRecord;
import org.oclc.oai.harvester2.verb.Identify;
import org.oclc.oai.harvester2.verb.ListMetadataFormats;
import org.oclc.oai.harvester2.verb.ListRecords;
import org.xml.sax.SAXException;
@@ -493,11 +493,11 @@ public class OAIHarvester {
* @throws HarvestingException if harvesting error
* @throws ParserConfigurationException XML parsing error
* @throws SAXException if XML processing error
* @throws TransformerException if XML transformer error
* @throws XPathExpressionException if XPath error
*/
protected void processRecord(Element record, String OREPrefix, final long currentRecord, long totalListSize)
throws SQLException, AuthorizeException, IOException, CrosswalkException, HarvestingException,
ParserConfigurationException, SAXException, TransformerException {
ParserConfigurationException, SAXException, XPathExpressionException {
WorkspaceItem wi = null;
Date timeStart = new Date();
@@ -769,10 +769,10 @@ public class OAIHarvester {
* @throws IOException if IO error
* @throws SAXException if XML processing error
* @throws ParserConfigurationException XML parsing error
* @throws TransformerException if XML transformer error
* @throws XPathExpressionException if XPath error
*/
private String oaiGetDateGranularity(String oaiSource)
throws IOException, ParserConfigurationException, SAXException, TransformerException {
throws IOException, ParserConfigurationException, SAXException, XPathExpressionException {
Identify iden = new Identify(oaiSource);
return iden.getDocument().getElementsByTagNameNS(OAI_NS.getURI(), "granularity").item(0).getTextContent();
}
@@ -789,11 +789,11 @@ public class OAIHarvester {
* operations.
* @throws ParserConfigurationException XML parsing error
* @throws SAXException if XML processing error
* @throws TransformerException if XML transformer error
* @throws XPathExpressionException if XPath error
* @throws ConnectException if could not connect to OAI server
*/
public static String oaiResolveNamespaceToPrefix(String oaiSource, String MDNamespace)
throws IOException, ParserConfigurationException, SAXException, TransformerException, ConnectException {
throws IOException, ParserConfigurationException, SAXException, XPathExpressionException, ConnectException {
String metaPrefix = null;
// Query the OAI server for the metadata
@@ -866,11 +866,11 @@ public class OAIHarvester {
* operations.
* @throws ParserConfigurationException XML parsing error
* @throws SAXException if XML processing error
* @throws TransformerException if XML transformer error
* @throws XPathExpressionException if XPath error
* @throws HarvestingException if harvesting error
*/
protected List<Element> getMDrecord(String oaiSource, String itemOaiId, String metadataPrefix)
throws IOException, ParserConfigurationException, SAXException, TransformerException, HarvestingException {
throws IOException, ParserConfigurationException, SAXException, XPathExpressionException, HarvestingException {
GetRecord getRecord = new GetRecord(oaiSource, itemOaiId, metadataPrefix);
Set<String> errorSet = new HashSet<>();
// If the metadata is not available for this item, can the whole thing

View File

@@ -92,6 +92,9 @@ public interface IdentifierService {
throws AuthorizeException, SQLException, IdentifierException;
/**
* Used to register newly-minted identifiers. Each provider is responsible
* for creating the appropriate identifier. All providers are interrogated.
*
* @param context The relevant DSpace Context.
* @param dso DSpace object to be registered
* @throws AuthorizeException if authorization error
@@ -101,7 +104,7 @@ public interface IdentifierService {
void register(Context context, DSpaceObject dso) throws AuthorizeException, SQLException, IdentifierException;
/**
* Used to Register a specific Identifier (for example a Handle, hdl:1234.5/6)
* Used to Register a specific Identifier (for example a Handle, hdl:1234.5/6).
* The provider is responsible for detecting and processing the appropriate
* identifier. All Providers are interrogated. Multiple providers
* can process the same identifier.

View File

@@ -70,11 +70,24 @@ public class BibtexImportMetadataSourceServiceImpl extends AbstractPlainMetadata
keyValueItem.setKey(entry.getValue().getType().getValue());
keyValueItem.setValue(entry.getKey().getValue());
keyValues.add(keyValueItem);
PlainMetadataKeyValueItem typeItem = new PlainMetadataKeyValueItem();
typeItem.setKey("type");
typeItem.setValue(entry.getValue().getType().getValue());
keyValues.add(typeItem);
if (entry.getValue().getFields() != null) {
for (Entry<Key,Value> subentry : entry.getValue().getFields().entrySet()) {
PlainMetadataKeyValueItem innerItem = new PlainMetadataKeyValueItem();
innerItem.setKey(subentry.getKey().getValue());
innerItem.setValue(subentry.getValue().toUserString());
innerItem.setKey(subentry.getKey().getValue().toLowerCase());
String latexString = subentry.getValue().toUserString();
try {
org.jbibtex.LaTeXParser laTeXParser = new org.jbibtex.LaTeXParser();
List<org.jbibtex.LaTeXObject> latexObjects = laTeXParser.parse(latexString);
org.jbibtex.LaTeXPrinter laTeXPrinter = new org.jbibtex.LaTeXPrinter();
String plainTextString = laTeXPrinter.print(latexObjects);
innerItem.setValue(plainTextString.replaceAll("\n", " "));
} catch (ParseException e) {
innerItem.setValue(latexString);
}
keyValues.add(innerItem);
}
}

View File

@@ -0,0 +1,65 @@
/**
* 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.importer.external.metadatamapping.contributor;
import java.util.ArrayList;
import java.util.Collection;
import org.dspace.importer.external.metadatamapping.MetadataFieldMapping;
import org.dspace.importer.external.metadatamapping.MetadatumDTO;
/**
* Wrapper class used to split another MetadataContributor's output into distinct values.
* The split is performed by matching a regular expression against the wrapped MetadataContributor's output.
*
* @author Philipp Rumpf (philipp.rumpf@uni-bamberg.de)
*/
public class SplitMetadataContributor<T> implements MetadataContributor<T> {
private final MetadataContributor<T> innerContributor;
private final String regex;
/**
* @param innerContributor The MetadataContributor whose output is split
* @param regex A regular expression matching the separator between different values
*/
public SplitMetadataContributor(MetadataContributor<T> innerContributor, String regex) {
this.innerContributor = innerContributor;
this.regex = regex;
}
@Override
public void setMetadataFieldMapping(MetadataFieldMapping<T, MetadataContributor<T>> rt) {
}
/**
* Each metadatum returned by the wrapped MetadataContributor is split into one or more metadata values
* based on the provided regular expression.
*
* @param t The recordType object to retrieve metadata from
* @return The collection of processed metadata values
*/
@Override
public Collection<MetadatumDTO> contributeMetadata(T t) {
Collection<MetadatumDTO> metadata = innerContributor.contributeMetadata(t);
ArrayList<MetadatumDTO> splitMetadata = new ArrayList<>();
for (MetadatumDTO metadatumDTO : metadata) {
String[] split = metadatumDTO.getValue().split(regex);
for (String splitItem : split) {
MetadatumDTO splitMetadatumDTO = new MetadatumDTO();
splitMetadatumDTO.setSchema(metadatumDTO.getSchema());
splitMetadatumDTO.setElement(metadatumDTO.getElement());
splitMetadatumDTO.setQualifier(metadatumDTO.getQualifier());
splitMetadatumDTO.setValue(splitItem);
splitMetadata.add(splitMetadatumDTO);
}
}
return splitMetadata;
}
}

View File

@@ -621,6 +621,10 @@ public class StatisticsDataVisits extends StatisticsData {
}
if (dsoId != null && query.dsoType != -1) {
// Store the UUID of the DSO as an attribute. Needed in particular for Bitstream download usage reports,
// as the Bitstream itself won't be available when converting points to their REST representation
attrs.put("id", dsoId);
switch (query.dsoType) {
case Constants.BITSTREAM:
Bitstream bit = bitstreamService.findByIdOrLegacyId(context, dsoId);

View File

@@ -8,15 +8,17 @@
package org.dspace.submit.model;
import java.util.List;
import javax.inject.Inject;
import org.dspace.services.ConfigurationService;
/**
* A collection of conditions to be met when uploading Bitstreams.
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
*/
public class UploadConfiguration {
private ConfigurationService configurationService;
private final ConfigurationService configurationService;
private String metadataDefinition;
private List<AccessConditionOption> options;
@@ -24,22 +26,52 @@ public class UploadConfiguration {
private Boolean required;
private String name;
/**
* Construct a bitstream uploading configuration.
* @param configurationService DSpace configuration provided by the DI container.
*/
@Inject
public UploadConfiguration(ConfigurationService configurationService) {
this.configurationService = configurationService;
}
/**
* The list of access restriction types from which a submitter may choose.
* @return choices for restricting access to Bitstreams.
*/
public List<AccessConditionOption> getOptions() {
return options;
}
/**
* Set the list of access restriction types from which to choose.
* Required. May be empty.
* @param options choices for restricting access to Bitstreams.
*/
public void setOptions(List<AccessConditionOption> options) {
this.options = options;
}
/**
* Name of the submission form to which these conditions are attached.
* @return the form's name.
*/
public String getMetadata() {
return metadataDefinition;
}
/**
* Name the submission form to which these conditions are attached.
* @param metadata the form's name.
*/
public void setMetadata(String metadata) {
this.metadataDefinition = metadata;
}
/**
* Limit on the maximum size of an uploaded Bitstream.
* @return maximum upload size in bytes.
*/
public Long getMaxSize() {
if (maxSize == null) {
maxSize = configurationService.getLongProperty("upload.max");
@@ -47,10 +79,18 @@ public class UploadConfiguration {
return maxSize;
}
/**
* Limit the maximum size of an uploaded Bitstream.
* @param maxSize maximum upload size in bytes.
*/
public void setMaxSize(Long maxSize) {
this.maxSize = maxSize;
}
/**
* Is at least one Bitstream required when submitting a new Item?
* @return true if a Bitstream is required.
*/
public Boolean isRequired() {
if (required == null) {
//defaults to true
@@ -60,25 +100,27 @@ public class UploadConfiguration {
return required;
}
/**
* Is at least one Bitstream required when submitting a new Item?
* @param required true if a Bitstream is required.
*/
public void setRequired(Boolean required) {
this.required = required;
}
public ConfigurationService getConfigurationService() {
return configurationService;
}
public void setConfigurationService(ConfigurationService configurationService) {
this.configurationService = configurationService;
}
/**
* The unique name of this configuration.
* @return configuration's name.
*/
public String getName() {
return name;
}
/**
* Give this configuration a unique name. Required.
* @param name configuration's name.
*/
public void setName(String name) {
this.name = name;
}
}

View File

@@ -3,10 +3,9 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="uploadConfigurationDefault" class="org.dspace.submit.model.UploadConfiguration">
<property name="name" value="upload"></property>
<property name="configurationService" ref="org.dspace.services.ConfigurationService"/>
<property name="name" value="upload"/>
<property name="metadata" value="bitstream-metadata" />
<property name="options">
<list>
@@ -18,7 +17,7 @@
</list>
</property>
</bean>
<bean id="openAccess" class="org.dspace.submit.model.AccessConditionOption">
<property name="groupName" value="Anonymous"/>
<property name="name" value="openaccess"/>
@@ -31,7 +30,7 @@
<property name="hasStartDate" value="false"/>
<property name="hasEndDate" value="true"/>
<property name="endDateLimit" value="+6MONTHS"/>
</bean>
</bean>
<bean id="embargoed" class="org.dspace.submit.model.AccessConditionOption">
<property name="groupName" value="Anonymous"/>
<property name="name" value="embargo"/>
@@ -43,13 +42,13 @@
<property name="groupName" value="Administrator"/>
<property name="name" value="administrator"/>
<property name="hasStartDate" value="false"/>
<property name="hasEndDate" value="false"/>
<property name="hasEndDate" value="false"/>
</bean>
<!-- <bean id="networkAdministration" class="org.dspace.submit.model.AccessConditionOption">
<property name="groupName" value="INSTITUTIONAL_NETWORK"/>
<property name="name" value="networkAdministration"/>
<property name="hasStartDate" value="false"/>
<property name="hasEndDate" value="false"/>
<property name="hasEndDate" value="false"/>
</bean> -->
<bean id="uploadConfigurationService" class="org.dspace.submit.model.UploadConfigurationService">

View File

@@ -1,6 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd"
default-lazy-init="true">
<bean id="mockOpenAIRERestConnector" class="org.dspace.external.MockOpenAIRERestConnector">
@@ -15,11 +19,71 @@
init-method="init">
<property name="sourceIdentifier" value="openAIREFunding" />
<property name="connector" ref="mockOpenAIRERestConnector" />
<property name="metadataFields" ref="mapOfmetadata"/>
<property name="supportedEntityTypes">
<list>
<value>Project</value>
</list>
</property>
</bean>
<util:map id="mapOfmetadata"
map-class="java.util.HashMap" key-type="java.lang.String" value-type="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<entry key="awardNumber" value-ref="openaireAwardNumber" />
<entry key="fundingStream" value-ref="openaireFundingStream" />
<entry key="awardURI" value-ref="openaireAwardURI" />
<entry key="funderName" value-ref="openaireFunderName" />
<entry key="funderIdentifier" value-ref="openaireFunderIdentifier" />
<entry key="dcTitle" value-ref="openaireTitle" />
<entry key="titleAlternative" value-ref="openaireTitleAlternative" />
<entry key="dcIdentifier" value-ref="openaireIdentifier" />
<entry key="coverageSpatial" value-ref="openaireSpatial" />
<entry key="dcDescription" value-ref="openaireDescription" />
<entry key="dcSubject" value-ref="openaireSubject" />
</util:map>
<bean id="openaireAwardNumber" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="oaire.awardNumber"/>
</bean>
<bean id="openaireFundingStream" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="oaire.fundingStream"/>
</bean>
<bean id="openaireAwardURI" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="oaire.awardURI"/>
</bean>
<bean id="openaireFunderName" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="project.funder.name"/>
</bean>
<bean id="openaireFunderIdentifier" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="project.funder.identifier"/>
</bean>
<bean id="openaireTitle" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.title"/>
</bean>
<bean id="openaireTitleAlternative" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.title.alternative"/>
</bean>
<bean id="openaireIdentifier" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.identifier"/>
</bean>
<bean id="openaireSpatial" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.coverage.spatial"/>
</bean>
<bean id="openaireDescription" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.description"/>
</bean>
<bean id="openaireSubject" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.subject"/>
</bean>
</beans>

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

@@ -0,0 +1,79 @@
/**
* 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.ctask.general;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import org.dspace.AbstractIntegrationTestWithDatabase;
import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.ItemBuilder;
import org.dspace.content.Collection;
import org.dspace.content.Item;
import org.dspace.curate.Curator;
import org.dspace.identifier.VersionedHandleIdentifierProviderWithCanonicalHandles;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.junit.Test;
/**
* Rudimentary test of the curation task.
*
* @author mwood
*/
public class CreateMissingIdentifiersIT
extends AbstractIntegrationTestWithDatabase {
private static final String P_TASK_DEF
= "plugin.named.org.dspace.curate.CurationTask";
private static final String TASK_NAME = "test";
@Test
public void testPerform()
throws IOException {
ConfigurationService configurationService
= DSpaceServicesFactory.getInstance().getConfigurationService();
configurationService.setProperty(P_TASK_DEF, null);
configurationService.addPropertyValue(P_TASK_DEF,
CreateMissingIdentifiers.class.getCanonicalName() + " = " + TASK_NAME);
Curator curator = new Curator();
curator.addTask(TASK_NAME);
context.setCurrentUser(admin);
parentCommunity = CommunityBuilder.createCommunity(context)
.build();
Collection collection = CollectionBuilder.createCollection(context, parentCommunity)
.build();
Item item = ItemBuilder.createItem(context, collection)
.build();
/*
* Curate with regular test configuration -- should succeed.
*/
curator.curate(context, item);
int status = curator.getStatus(TASK_NAME);
assertEquals("Curation should succeed", Curator.CURATE_SUCCESS, status);
/*
* Now install an incompatible provider to make the task fail.
*/
DSpaceServicesFactory.getInstance()
.getServiceManager()
.registerServiceClass(
VersionedHandleIdentifierProviderWithCanonicalHandles.class.getCanonicalName(),
VersionedHandleIdentifierProviderWithCanonicalHandles.class);
curator.curate(context, item);
System.out.format("With incompatible provider, result is '%s'.\n",
curator.getResult(TASK_NAME));
assertEquals("Curation should fail", Curator.CURATE_ERROR,
curator.getStatus(TASK_NAME));
}
}

View File

@@ -15,7 +15,9 @@ import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.sql.SQLException;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
@@ -229,14 +231,20 @@ public class RequestItemRepository
}
// Check for authorized user
RequestItemAuthor authorizer;
List<RequestItemAuthor> authorizers;
try {
authorizer = requestItemAuthorExtractor.getRequestItemAuthor(context, ri.getItem());
authorizers = requestItemAuthorExtractor.getRequestItemAuthor(context, ri.getItem());
} catch (SQLException ex) {
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");
}

View File

@@ -251,7 +251,10 @@ public class UsageReportUtils {
for (int i = 0; i < dataset.getColLabels().size(); i++) {
UsageReportPointDsoTotalVisitsRest totalDownloadsPoint = new UsageReportPointDsoTotalVisitsRest();
totalDownloadsPoint.setType("bitstream");
totalDownloadsPoint.setId(dataset.getColLabels().get(i));
totalDownloadsPoint.setId(dataset.getColLabelsAttrs().get(i).get("id"));
totalDownloadsPoint.setLabel(dataset.getColLabels().get(i));
totalDownloadsPoint.addValue("views", Integer.valueOf(dataset.getMatrix()[0][i]));
usageReportRest.addPoint(totalDownloadsPoint);
}

View File

@@ -995,6 +995,305 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
bibtex.close();
}
@Test
/**
* Test the creation of workspaceitems POSTing to the resource collection endpoint a bibtex file
*
* @throws Exception
*/
public void createSingleWorkspaceItemFromBibtexArticleFileWithOneEntryTest() throws Exception {
context.turnOffAuthorisationSystem();
//** GIVEN **
//1. A community-collection structure with one parent community with sub-community and two collections.
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1)
.withName("Collection 1")
.withSubmitterGroup(eperson)
.build();
Collection col2 = CollectionBuilder.createCollection(context, child1)
.withName("Collection 2")
.withSubmitterGroup(eperson)
.build();
InputStream bibtex = getClass().getResourceAsStream("bibtex-test-article.bib");
final MockMultipartFile bibtexFile = new MockMultipartFile("file", "/local/path/bibtex-test-article.bib",
"application/x-bibtex", bibtex);
context.restoreAuthSystemState();
AtomicReference<List<Integer>> idRef = new AtomicReference<>();
String authToken = getAuthToken(eperson.getEmail(), password);
try {
// create a workspaceitem from a single bibliographic entry file explicitly in the default collection (col1)
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(bibtexFile))
// create should return 200, 201 (created) is better for single resource
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.workspaceitems[0]" +
".sections.traditionalpageone['dc.title'][0].value",
is("My Article")))
.andExpect(jsonPath("$._embedded.workspaceitems[0]" +
".sections.traditionalpageone['dc.type'][0].value",
is("article")))
.andExpect(
jsonPath("$._embedded.workspaceitems[0]._embedded.collection.id",
is(col1.getID().toString())))
.andExpect(jsonPath("$._embedded.workspaceitems[0].sections.upload.files[0]"
+ ".metadata['dc.source'][0].value",
is("/local/path/bibtex-test-article.bib")))
.andExpect(jsonPath("$._embedded.workspaceitems[0].sections.upload.files[0]"
+ ".metadata['dc.title'][0].value",
is("bibtex-test-article.bib")))
.andExpect(
jsonPath("$._embedded.workspaceitems[*]._embedded.upload").doesNotExist())
.andDo(result -> idRef.set(read(result.getResponse().getContentAsString(),
"$._embedded.workspaceitems[*].id")));
} finally {
if (idRef != null && idRef.get() != null) {
for (int i : idRef.get()) {
WorkspaceItemBuilder.deleteWorkspaceItem(i);
}
}
}
// create a workspaceitem from a single bibliographic entry file explicitly in the col2
try {
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(bibtexFile)
.param("owningCollection", col2.getID().toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.workspaceitems[0]" +
".sections.traditionalpageone['dc.title'][0].value",
is("My Article")))
.andExpect(jsonPath("$._embedded.workspaceitems[0]" +
".sections.traditionalpageone['dc.type'][0].value",
is("article")))
.andExpect(
jsonPath("$._embedded.workspaceitems[0]._embedded.collection.id",
is(col2.getID().toString())))
.andExpect(jsonPath("$._embedded.workspaceitems[0].sections.upload.files[0]"
+ ".metadata['dc.source'][0].value",
is("/local/path/bibtex-test-article.bib")))
.andExpect(jsonPath("$._embedded.workspaceitems[0].sections.upload"
+ ".files[0].metadata['dc.title'][0].value",
is("bibtex-test-article.bib")))
.andExpect(
jsonPath("$._embedded.workspaceitems[*]._embedded.upload").doesNotExist())
.andDo(result -> idRef.set(read(result.getResponse().getContentAsString(),
"$._embedded.workspaceitems[*].id")));
} finally {
if (idRef != null && idRef.get() != null) {
for (int i : idRef.get()) {
WorkspaceItemBuilder.deleteWorkspaceItem(i);
}
}
}
bibtex.close();
}
@Test
public void createSingleWorkspaceItemFromBibtexFileWithDiacriticsTest() throws Exception {
context.turnOffAuthorisationSystem();
//** GIVEN **
//1. A community-collection structure with one parent community with sub-community and two collections.
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1)
.withName("Collection 1")
.withSubmitterGroup(eperson)
.build();
Collection col2 = CollectionBuilder.createCollection(context, child1)
.withName("Collection 2")
.withSubmitterGroup(eperson)
.build();
InputStream bibtex = getClass().getResourceAsStream("bibtex-test-diacritics.bib");
final MockMultipartFile bibtexFile = new MockMultipartFile("file", "/local/path/bibtex-test-diacritics.bib",
"application/x-bibtex", bibtex);
context.restoreAuthSystemState();
AtomicReference<List<Integer>> idRef = new AtomicReference<>();
String authToken = getAuthToken(eperson.getEmail(), password);
try {
// create a workspaceitem from a single bibliographic entry file explicitly in the default collection (col1)
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(bibtexFile))
// create should return 200, 201 (created) is better for single resource
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.workspaceitems[0].sections." +
"traditionalpageone['dc.title'][0].value",
is("The German umlauts: ÄÖüß")))
.andExpect(
jsonPath("$._embedded.workspaceitems[0]._embedded.collection.id",
is(col1.getID().toString())))
.andExpect(jsonPath("$._embedded.workspaceitems[0].sections.upload.files[0]"
+ ".metadata['dc.source'][0].value",
is("/local/path/bibtex-test-diacritics.bib")))
.andExpect(jsonPath("$._embedded.workspaceitems[0].sections.upload.files[0]"
+ ".metadata['dc.title'][0].value",
is("bibtex-test-diacritics.bib")))
.andExpect(
jsonPath("$._embedded.workspaceitems[*]._embedded.upload").doesNotExist())
.andDo(result -> idRef.set(read(result.getResponse().getContentAsString(),
"$._embedded.workspaceitems[*].id")));
} finally {
if (idRef != null && idRef.get() != null) {
for (int i : idRef.get()) {
WorkspaceItemBuilder.deleteWorkspaceItem(i);
}
}
}
// create a workspaceitem from a single bibliographic entry file explicitly in the col2
try {
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(bibtexFile)
.param("owningCollection", col2.getID().toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.workspaceitems[0].sections." +
"traditionalpageone['dc.title'][0].value",
is("The German umlauts: ÄÖüß")))
.andExpect(
jsonPath("$._embedded.workspaceitems[0]._embedded.collection.id",
is(col2.getID().toString())))
.andExpect(jsonPath("$._embedded.workspaceitems[0].sections.upload.files[0]"
+ ".metadata['dc.source'][0].value",
is("/local/path/bibtex-test-diacritics.bib")))
.andExpect(jsonPath("$._embedded.workspaceitems[0].sections.upload"
+ ".files[0].metadata['dc.title'][0].value",
is("bibtex-test-diacritics.bib")))
.andExpect(
jsonPath("$._embedded.workspaceitems[*]._embedded.upload").doesNotExist())
.andDo(result -> idRef.set(read(result.getResponse().getContentAsString(),
"$._embedded.workspaceitems[*].id")));
} finally {
if (idRef != null && idRef.get() != null) {
for (int i : idRef.get()) {
WorkspaceItemBuilder.deleteWorkspaceItem(i);
}
}
}
bibtex.close();
}
@Test
/**
* Test the creation of workspaceitems POSTing to the resource collection endpoint a bibtex file
*
* @throws Exception
*/
public void createSingleWorkspaceItemFromBibtexFileWithMultipleAuthorsTest() throws Exception {
context.turnOffAuthorisationSystem();
//** GIVEN **
//1. A community-collection structure with one parent community with sub-community and two collections.
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1)
.withName("Collection 1")
.withSubmitterGroup(eperson)
.build();
Collection col2 = CollectionBuilder.createCollection(context, child1)
.withName("Collection 2")
.withSubmitterGroup(eperson)
.build();
InputStream bibtex = getClass().getResourceAsStream("bibtex-test-multiple-authors.bib");
final MockMultipartFile bibtexFile = new MockMultipartFile("file",
"/local/path/bibtex-test-multiple-authors.bib",
"application/x-bibtex", bibtex);
context.restoreAuthSystemState();
AtomicReference<List<Integer>> idRef = new AtomicReference<>();
String authToken = getAuthToken(eperson.getEmail(), password);
try {
// create a workspaceitem from a single bibliographic entry file explicitly in the default collection (col1)
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(bibtexFile))
// create should return 200, 201 (created) is better for single resource
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.workspaceitems[0]" +
".sections.traditionalpageone['dc.title'][0].value",
is("My Article")))
.andExpect(jsonPath("$._embedded.workspaceitems[0]" +
".sections.traditionalpageone['dc.contributor.author'][0].value",
is("A. Nauthor")))
.andExpect(jsonPath("$._embedded.workspaceitems[0]" +
".sections.traditionalpageone['dc.contributor.author'][1].value",
is("A. Nother")))
.andExpect(jsonPath("$._embedded.workspaceitems[0]" +
".sections.traditionalpageone['dc.contributor.author'][2].value",
is("A. Third")))
.andExpect(
jsonPath("$._embedded.workspaceitems[0]._embedded.collection.id",
is(col1.getID().toString())))
.andExpect(jsonPath("$._embedded.workspaceitems[0].sections.upload.files[0]"
+ ".metadata['dc.source'][0].value",
is("/local/path/bibtex-test-multiple-authors.bib")))
.andExpect(jsonPath("$._embedded.workspaceitems[0].sections.upload.files[0]"
+ ".metadata['dc.title'][0].value",
is("bibtex-test-multiple-authors.bib")))
.andExpect(
jsonPath("$._embedded.workspaceitems[*]._embedded.upload").doesNotExist())
.andDo(result -> idRef.set(read(result.getResponse().getContentAsString(),
"$._embedded.workspaceitems[*].id")));
} finally {
if (idRef != null && idRef.get() != null) {
for (int i : idRef.get()) {
WorkspaceItemBuilder.deleteWorkspaceItem(i);
}
}
}
// create a workspaceitem from a single bibliographic entry file explicitly in the col2
try {
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(bibtexFile)
.param("owningCollection", col2.getID().toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.workspaceitems[0]" +
".sections.traditionalpageone['dc.title'][0].value",
is("My Article")))
.andExpect(
jsonPath("$._embedded.workspaceitems[0]._embedded.collection.id",
is(col2.getID().toString())))
.andExpect(jsonPath("$._embedded.workspaceitems[0].sections.upload.files[0]"
+ ".metadata['dc.source'][0].value",
is("/local/path/bibtex-test-multiple-authors.bib")))
.andExpect(jsonPath("$._embedded.workspaceitems[0].sections.upload"
+ ".files[0].metadata['dc.title'][0].value",
is("bibtex-test-multiple-authors.bib")))
.andExpect(
jsonPath("$._embedded.workspaceitems[*]._embedded.upload").doesNotExist())
.andDo(result -> idRef.set(read(result.getResponse().getContentAsString(),
"$._embedded.workspaceitems[*].id")));
} finally {
if (idRef != null && idRef.get() != null) {
for (int i : idRef.get()) {
WorkspaceItemBuilder.deleteWorkspaceItem(i);
}
}
}
bibtex.close();
}
@Test
/**
* Test the creation of workspaceitems POSTing to the resource collection endpoint a csv file

View File

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

View File

@@ -49,13 +49,18 @@ public class UsageReportMatcher {
* @param points List of points to match to the json of UsageReport's list of points
* @return The matcher
*/
public static Matcher<? super Object> matchUsageReport(String id, String reportType,
List<UsageReportPointRest> points) {
public static Matcher<? super Object> matchUsageReport(
String id, String reportType, List<UsageReportPointRest> points
) {
return allOf(
matchUsageReport(id, reportType),
hasJsonPath("$.points", Matchers.containsInAnyOrder(
points.stream().map(point -> UsageReportPointMatcher
.matchUsageReportPoint(point.getId(), point.getType(), point.getValues().get("views")))
.collect(Collectors.toList()))));
points.stream()
.map(point -> UsageReportPointMatcher.matchUsageReportPoint(
point.getId(), point.getLabel(), point.getType(), point.getValues().get("views")
))
.collect(Collectors.toList()))
)
);
}
}

View File

@@ -28,15 +28,17 @@ public class UsageReportPointMatcher {
* Matcher for the usage report points (see {@link UsageReportPointRest})
*
* @param id Id to match if of json of UsageReportPoint
* @param label Label to match if of json of UsageReportPoint
* @param type Type to match if of json of UsageReportPoint
* @param views Nr of views, is in the values key-value pair of values of UsageReportPoint with key "views"
* @return The matcher
*/
public static Matcher<? super Object> matchUsageReportPoint(String id, String type, int views) {
public static Matcher<? super Object> matchUsageReportPoint(String id, String label, String type, int views) {
return allOf(
hasJsonPath("$.id", is(id)),
hasJsonPath("$.label", is(label)),
hasJsonPath("$.type", is(type)),
hasJsonPath("$.values.views", is(views))
);
);
}
}

View File

@@ -0,0 +1,4 @@
@article{ Nobody01,
author = "Nobody Jr",
title = "My Article",
year = "2006" }

View File

@@ -0,0 +1,4 @@
@misc{ Nobody01,
author = "Mo\ss",
title = { The German umlauts: \"A{\"O}{\"{u}}\ss },
year = "2006" }

View File

@@ -0,0 +1,4 @@
@misc{ Nobody01,
author = "A. Nauthor and A. Nother and A. Third",
title = "My Article",
year = "2006" }

View File

@@ -3,10 +3,9 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="uploadConfigurationDefault" class="org.dspace.submit.model.UploadConfiguration">
<property name="name" value="upload"></property>
<property name="configurationService" ref="org.dspace.services.ConfigurationService"/>
<property name="name" value="upload"/>
<property name="metadata" value="bitstream-metadata" />
<property name="options">
<list>
@@ -18,7 +17,7 @@
</list>
</property>
</bean>
<bean id="openAccess" class="org.dspace.submit.model.AccessConditionOption">
<property name="groupName" value="Anonymous"/>
<property name="name" value="openaccess"/>
@@ -31,26 +30,25 @@
<property name="hasStartDate" value="false"/>
<property name="hasEndDate" value="true"/>
<property name="endDateLimit" value="+6MONTHS"/>
</bean>
</bean>
<bean id="embargoed" class="org.dspace.submit.model.AccessConditionOption">
<property name="groupName" value="Anonymous"/>
<property name="name" value="embargo"/>
<property name="hasStartDate" value="true"/>
<property name="startDateLimit" value="+36MONTHS"/>
<property name="hasEndDate" value="false"/>
</bean>
</bean>
<bean id="administrator" class="org.dspace.submit.model.AccessConditionOption">
<property name="groupName" value="Administrator"/>
<property name="name" value="administrator"/>
<property name="hasStartDate" value="false"/>
<property name="hasEndDate" value="false"/>
<property name="hasEndDate" value="false"/>
</bean>
<!-- <bean id="networkAdministration" class="org.dspace.submit.model.AccessConditionOption">
<property name="groupName" value="INSTITUTIONAL_NETWORK"/>
<property name="name" value="networkAdministration"/>
<property name="hasStartDate" value="false"/>
<property name="hasEndDate" value="false"/>
<property name="hasEndDate" value="false"/>
</bean> -->
<bean id="uploadConfigurationService" class="org.dspace.submit.model.UploadConfigurationService">

View File

@@ -17,6 +17,7 @@
only matters here for postprocessing of the value. The mapped MetadatumContributor has full control over
what metadatafield is generated.
</description>
<entry key-ref="dcType" value-ref="bibtexTypeContrib" />
<entry key-ref="dcTitle" value-ref="bibtexTitleContrib" />
<entry key-ref="dcAuthors" value-ref="bibtexAuthorsContrib" />
<entry key-ref="dcJournal" value-ref="bibtexJournalContrib" />
@@ -39,14 +40,24 @@
<property name="key" value="journal" />
</bean>
<bean id="bibtexAuthorsContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleMetadataContributor">
<property name="field" ref="dcAuthors"/>
<property name="key" value="author" />
<bean id="bibtexAuthorsContrib" class="org.dspace.importer.external.metadatamapping.contributor.SplitMetadataContributor">
<constructor-arg name="innerContributor">
<bean class="org.dspace.importer.external.metadatamapping.contributor.SimpleMetadataContributor">
<property name="field" ref="dcAuthors"/>
<property name="key" value="author" />
</bean>
</constructor-arg>
<constructor-arg name="regex" value="\sand\s"/>
</bean>
<bean id="bibtexTitleContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleMetadataContributor">
<property name="field" ref="dcTitle"/>
<property name="key" value="title" />
</bean>
</beans>
<bean id="bibtexTypeContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleMetadataContributor">
<property name="field" ref="dcType"/>
<property name="key" value="type" />
</bean>
</beans>

View File

@@ -25,6 +25,9 @@
</bean>
<bean id="dcTitle" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.title"/>
</bean>
<bean id="dcType" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.type"/>
</bean>
<bean id="dcAuthors" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.contributor.author"/>

View File

@@ -1,6 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd"
default-lazy-init="true">
<bean id="openAIRERestConnector" class="org.dspace.external.OpenAIRERestConnector">
@@ -10,17 +14,65 @@
<property name="clientId" value="${openaire.token.clientId}"/>
<property name="clientSecret" value="${openaire.token.clientSecret}"/>
</bean>
<bean
class="org.dspace.external.provider.impl.OpenAIREFundingDataProvider"
init-method="init">
<bean class="org.dspace.external.provider.impl.OpenAIREFundingDataProvider" init-method="init">
<property name="sourceIdentifier" value="openAIREFunding" />
<property name="connector" ref="openAIRERestConnector" />
<property name="metadataFields" ref="mapOfmetadata"/>
<property name="supportedEntityTypes">
<list>
<value>Project</value>
</list>
</property>
</bean>
</beans>
<util:map id="mapOfmetadata"
map-class="java.util.HashMap" key-type="java.lang.String" value-type="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<entry key="awardNumber" value-ref="openaireAwardNumber" />
<entry key="fundingStream" value-ref="openaireFundingStream" />
<entry key="awardURI" value-ref="openaireAwardURI" />
<entry key="funderName" value-ref="openaireFunderName" />
<entry key="funderIdentifier" value-ref="openaireFunderIdentifier" />
<entry key="dcTitle" value-ref="openaireTitle" />
<entry key="titleAlternative" value-ref="openaireTitleAlternative" />
<entry key="coverageSpatial" value-ref="openaireSpatial" />
<entry key="dcSubject" value-ref="openaireSubject" />
</util:map>
<bean id="openaireAwardNumber" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="oaire.awardNumber"/>
</bean>
<bean id="openaireFundingStream" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="oaire.fundingStream"/>
</bean>
<bean id="openaireAwardURI" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="oaire.awardURI"/>
</bean>
<bean id="openaireFunderName" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="project.funder.name"/>
</bean>
<bean id="openaireFunderIdentifier" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="project.funder.identifier"/>
</bean>
<bean id="openaireTitle" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.title"/>
</bean>
<bean id="openaireTitleAlternative" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.title.alternative"/>
</bean>
<bean id="openaireSpatial" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.coverage.spatial"/>
</bean>
<bean id="openaireSubject" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.subject"/>
</bean>
</beans>

View File

@@ -8,25 +8,65 @@
http://www.springframework.org/schema/context/spring-context-2.5.xsd"
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 -->
<bean class="org.dspace.app.requestitem.RequestItemMetadataStrategy"
id="org.dspace.app.requestitem.RequestItemAuthorExtractor"
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.
<!-- Select the implementation to be used. -->
<alias alias='org.dspace.app.requestitem.RequestItemAuthorExtractor'
name='org.dspace.app.requestitem.RequestItemMetadataStrategy'/>
<property name="emailMetadata" value="schema.element.qualifier" />
<property name="fullNameMetadata" value="schema.element.qualifier" />
-->
<!-- Get recipients from an item metadata field. -->
<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>
<!-- HelpDesk to instead get RequestItem emails-->
<!--<bean class="org.dspace.app.requestitem.RequestItemHelpdeskStrategy"
id="org.dspace.app.requestitem.RequestItemAuthorExtractor"
autowire-candidate='true'></bean>-->
<!--
<bean class="org.dspace.app.requestitem.RequestItemHelpdeskStrategy"
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>

11
pom.xml
View File

@@ -1575,18 +1575,11 @@
<artifactId>ojdbc6</artifactId>
<version>11.2.0.4.0</version>
</dependency>
<!-- Codebase at https://github.com/OCLC-Research/oaiharvester2/ -->
<!-- Codebase at https://github.com/DSpace/oclc-harvester2 -->
<dependency>
<groupId>org.dspace</groupId>
<artifactId>oclc-harvester2</artifactId>
<version>0.1.12</version>
</dependency>
<!-- Xalan is REQUIRED by 'oclc-harvester2' listed above (OAI harvesting fails without it).
Please do NOT use Xalan in DSpace codebase as it is not well maintained. -->
<dependency>
<groupId>xalan</groupId>
<artifactId>xalan</artifactId>
<version>2.7.2</version>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>