diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml
index dd86890435..fc7349b379 100644
--- a/dspace-api/pom.xml
+++ b/dspace-api/pom.xml
@@ -530,14 +530,6 @@
org.apache.pdfboxfontbox
-
- xalan
- xalan
-
-
- xerces
- xercesImpl
- com.ibm.icuicu4j
@@ -687,13 +679,6 @@
1.1.1
-
-
- com.google.code.gson
- gson
- compile
-
-
com.google.guavaguava
diff --git a/dspace-api/src/main/java/org/dspace/access/status/AccessStatusHelper.java b/dspace-api/src/main/java/org/dspace/access/status/AccessStatusHelper.java
new file mode 100644
index 0000000000..1cacbf6aed
--- /dev/null
+++ b/dspace-api/src/main/java/org/dspace/access/status/AccessStatusHelper.java
@@ -0,0 +1,30 @@
+/**
+ * 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.access.status;
+
+import java.sql.SQLException;
+import java.util.Date;
+
+import org.dspace.content.Item;
+import org.dspace.core.Context;
+
+/**
+ * Plugin interface for the access status calculation.
+ */
+public interface AccessStatusHelper {
+ /**
+ * Calculate the access status for the item.
+ *
+ * @param context the DSpace context
+ * @param item the item
+ * @return an access status value
+ * @throws SQLException An exception that provides information on a database access error or other errors.
+ */
+ public String getAccessStatusFromItem(Context context, Item item, Date threshold)
+ throws SQLException;
+}
diff --git a/dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java b/dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java
new file mode 100644
index 0000000000..544dc99cb4
--- /dev/null
+++ b/dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java
@@ -0,0 +1,66 @@
+/**
+ * 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.access.status;
+
+import java.sql.SQLException;
+import java.util.Date;
+
+import org.dspace.access.status.service.AccessStatusService;
+import org.dspace.content.Item;
+import org.dspace.core.Context;
+import org.dspace.core.service.PluginService;
+import org.dspace.services.ConfigurationService;
+import org.joda.time.LocalDate;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Implementation for the access status calculation service.
+ */
+public class AccessStatusServiceImpl implements AccessStatusService {
+ // Plugin implementation, set from the DSpace configuration by init().
+ protected AccessStatusHelper helper = null;
+
+ protected Date forever_date = null;
+
+ @Autowired(required = true)
+ protected ConfigurationService configurationService;
+
+ @Autowired(required = true)
+ protected PluginService pluginService;
+
+ /**
+ * Initialize the bean (after dependency injection has already taken place).
+ * Ensures the configurationService is injected, so that we can get the plugin
+ * and the forever embargo date threshold from the configuration.
+ * Called by "init-method" in Spring configuration.
+ *
+ * @throws Exception on generic exception
+ */
+ public void init() throws Exception {
+ if (helper == null) {
+ helper = (AccessStatusHelper) pluginService.getSinglePlugin(AccessStatusHelper.class);
+ if (helper == null) {
+ throw new IllegalStateException("The AccessStatusHelper plugin was not defined in "
+ + "DSpace configuration.");
+ }
+
+ // Defines the embargo forever date threshold for the access status.
+ // Look at EmbargoService.FOREVER for some improvements?
+ int year = configurationService.getIntProperty("access.status.embargo.forever.year");
+ int month = configurationService.getIntProperty("access.status.embargo.forever.month");
+ int day = configurationService.getIntProperty("access.status.embargo.forever.day");
+
+ forever_date = new LocalDate(year, month, day).toDate();
+ }
+ }
+
+ @Override
+ public String getAccessStatus(Context context, Item item) throws SQLException {
+ return helper.getAccessStatusFromItem(context, item, forever_date);
+ }
+}
diff --git a/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java b/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java
new file mode 100644
index 0000000000..a67fa67af3
--- /dev/null
+++ b/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java
@@ -0,0 +1,159 @@
+/**
+ * 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.access.status;
+
+import java.sql.SQLException;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.commons.lang3.StringUtils;
+import org.dspace.authorize.ResourcePolicy;
+import org.dspace.authorize.factory.AuthorizeServiceFactory;
+import org.dspace.authorize.service.AuthorizeService;
+import org.dspace.authorize.service.ResourcePolicyService;
+import org.dspace.content.Bitstream;
+import org.dspace.content.Bundle;
+import org.dspace.content.DSpaceObject;
+import org.dspace.content.Item;
+import org.dspace.content.factory.ContentServiceFactory;
+import org.dspace.content.service.ItemService;
+import org.dspace.core.Constants;
+import org.dspace.core.Context;
+import org.dspace.eperson.Group;
+
+/**
+ * Default plugin implementation of the access status helper.
+ * The getAccessStatusFromItem method provides a simple logic to
+ * calculate the access status of an item based on the policies of
+ * the primary or the first bitstream in the original bundle.
+ * Users can override this method for enhanced functionality.
+ */
+public class DefaultAccessStatusHelper implements AccessStatusHelper {
+ public static final String EMBARGO = "embargo";
+ public static final String METADATA_ONLY = "metadata.only";
+ public static final String OPEN_ACCESS = "open.access";
+ public static final String RESTRICTED = "restricted";
+ public static final String UNKNOWN = "unknown";
+
+ protected ItemService itemService =
+ ContentServiceFactory.getInstance().getItemService();
+ protected ResourcePolicyService resourcePolicyService =
+ AuthorizeServiceFactory.getInstance().getResourcePolicyService();
+ protected AuthorizeService authorizeService =
+ AuthorizeServiceFactory.getInstance().getAuthorizeService();
+
+ public DefaultAccessStatusHelper() {
+ super();
+ }
+
+ /**
+ * Look at the item's policies to determine an access status value.
+ * It is also considering a date threshold for embargos and restrictions.
+ *
+ * If the item is null, simply returns the "unknown" value.
+ *
+ * @param context the DSpace context
+ * @param item the item to embargo
+ * @param threshold the embargo threshold date
+ * @return an access status value
+ */
+ @Override
+ public String getAccessStatusFromItem(Context context, Item item, Date threshold)
+ throws SQLException {
+ if (item == null) {
+ return UNKNOWN;
+ }
+ // Consider only the original bundles.
+ List bundles = item.getBundles(Constants.DEFAULT_BUNDLE_NAME);
+ // Check for primary bitstreams first.
+ Bitstream bitstream = bundles.stream()
+ .map(bundle -> bundle.getPrimaryBitstream())
+ .filter(Objects::nonNull)
+ .findFirst()
+ .orElse(null);
+ if (bitstream == null) {
+ // If there is no primary bitstream,
+ // take the first bitstream in the bundles.
+ bitstream = bundles.stream()
+ .map(bundle -> bundle.getBitstreams())
+ .flatMap(List::stream)
+ .findFirst()
+ .orElse(null);
+ }
+ return caculateAccessStatusForDso(context, bitstream, threshold);
+ }
+
+ /**
+ * Look at the DSpace object's policies to determine an access status value.
+ *
+ * If the object is null, returns the "metadata.only" value.
+ * If any policy attached to the object is valid for the anonymous group,
+ * returns the "open.access" value.
+ * Otherwise, if the policy start date is before the embargo threshold date,
+ * returns the "embargo" value.
+ * Every other cases return the "restricted" value.
+ *
+ * @param context the DSpace context
+ * @param dso the DSpace object
+ * @param threshold the embargo threshold date
+ * @return an access status value
+ */
+ private String caculateAccessStatusForDso(Context context, DSpaceObject dso, Date threshold)
+ throws SQLException {
+ if (dso == null) {
+ return METADATA_ONLY;
+ }
+ // Only consider read policies.
+ List policies = authorizeService
+ .getPoliciesActionFilter(context, dso, Constants.READ);
+ int openAccessCount = 0;
+ int embargoCount = 0;
+ int restrictedCount = 0;
+ int unknownCount = 0;
+ // Looks at all read policies.
+ for (ResourcePolicy policy : policies) {
+ boolean isValid = resourcePolicyService.isDateValid(policy);
+ Group group = policy.getGroup();
+ // The group must not be null here. However,
+ // if it is, consider this as an unexpected case.
+ if (group == null) {
+ unknownCount++;
+ } else if (StringUtils.equals(group.getName(), Group.ANONYMOUS)) {
+ // Only calculate the status for the anonymous group.
+ if (isValid) {
+ // If the policy is valid, the anonymous group have access
+ // to the bitstream.
+ openAccessCount++;
+ } else {
+ Date startDate = policy.getStartDate();
+ if (startDate != null && !startDate.before(threshold)) {
+ // If the policy start date have a value and if this value
+ // is equal or superior to the configured forever date, the
+ // access status is also restricted.
+ restrictedCount++;
+ } else {
+ // If the current date is not between the policy start date
+ // and end date, the access status is embargo.
+ embargoCount++;
+ }
+ }
+ }
+ }
+ if (openAccessCount > 0) {
+ return OPEN_ACCESS;
+ }
+ if (embargoCount > 0 && restrictedCount == 0) {
+ return EMBARGO;
+ }
+ if (unknownCount > 0) {
+ return UNKNOWN;
+ }
+ return RESTRICTED;
+ }
+}
diff --git a/dspace-api/src/main/java/org/dspace/access/status/factory/AccessStatusServiceFactory.java b/dspace-api/src/main/java/org/dspace/access/status/factory/AccessStatusServiceFactory.java
new file mode 100644
index 0000000000..77d8f6b448
--- /dev/null
+++ b/dspace-api/src/main/java/org/dspace/access/status/factory/AccessStatusServiceFactory.java
@@ -0,0 +1,25 @@
+/**
+ * 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.access.status.factory;
+
+import org.dspace.access.status.service.AccessStatusService;
+import org.dspace.services.factory.DSpaceServicesFactory;
+
+/**
+ * Abstract factory to get services for the access status package,
+ * use AccessStatusServiceFactory.getInstance() to retrieve an implementation.
+ */
+public abstract class AccessStatusServiceFactory {
+
+ public abstract AccessStatusService getAccessStatusService();
+
+ public static AccessStatusServiceFactory getInstance() {
+ return DSpaceServicesFactory.getInstance().getServiceManager()
+ .getServiceByName("accessStatusServiceFactory", AccessStatusServiceFactory.class);
+ }
+}
diff --git a/dspace-api/src/main/java/org/dspace/access/status/factory/AccessStatusServiceFactoryImpl.java b/dspace-api/src/main/java/org/dspace/access/status/factory/AccessStatusServiceFactoryImpl.java
new file mode 100644
index 0000000000..fe3848cb2b
--- /dev/null
+++ b/dspace-api/src/main/java/org/dspace/access/status/factory/AccessStatusServiceFactoryImpl.java
@@ -0,0 +1,26 @@
+/**
+ * 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.access.status.factory;
+
+import org.dspace.access.status.service.AccessStatusService;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Factory implementation to get services for the access status package,
+ * use AccessStatusServiceFactory.getInstance() to retrieve an implementation.
+ */
+public class AccessStatusServiceFactoryImpl extends AccessStatusServiceFactory {
+
+ @Autowired(required = true)
+ private AccessStatusService accessStatusService;
+
+ @Override
+ public AccessStatusService getAccessStatusService() {
+ return accessStatusService;
+ }
+}
diff --git a/dspace-api/src/main/java/org/dspace/access/status/package-info.java b/dspace-api/src/main/java/org/dspace/access/status/package-info.java
new file mode 100644
index 0000000000..2c0ed22cd4
--- /dev/null
+++ b/dspace-api/src/main/java/org/dspace/access/status/package-info.java
@@ -0,0 +1,30 @@
+/**
+ * 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/
+ */
+/**
+ *
+ * Access status allows the users to view the bitstreams availability before
+ * browsing into the item itself.
+ *
+ *
+ * The access status is calculated through a pluggable class:
+ * {@link org.dspace.access.status.AccessStatusHelper}.
+ * The {@link org.dspace.access.status.AccessStatusServiceImpl}
+ * must be configured to specify this class, as well as a forever embargo date
+ * threshold year, month and day.
+ *
+ *
+ * See {@link org.dspace.access.status.DefaultAccessStatusHelper} for a simple calculation
+ * based on the primary or the first bitstream of the original bundle. You can
+ * supply your own class to implement more complex access statuses.
+ *
+ *
+ * For now, the access status is calculated when the item is shown in a list.
+ *
+ */
+
+package org.dspace.access.status;
diff --git a/dspace-api/src/main/java/org/dspace/access/status/service/AccessStatusService.java b/dspace-api/src/main/java/org/dspace/access/status/service/AccessStatusService.java
new file mode 100644
index 0000000000..43de5e3c47
--- /dev/null
+++ b/dspace-api/src/main/java/org/dspace/access/status/service/AccessStatusService.java
@@ -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.access.status.service;
+
+import java.sql.SQLException;
+
+import org.dspace.content.Item;
+import org.dspace.core.Context;
+
+/**
+ * Public interface to the access status subsystem.
+ *
+ * Configuration properties: (with examples)
+ * {@code
+ * # values for the forever embargo date threshold
+ * # This threshold date is used in the default access status helper to dermine if an item is
+ * # restricted or embargoed based on the start date of the primary (or first) file policies.
+ * # In this case, if the policy start date is inferior to the threshold date, the status will
+ * # be embargo, else it will be restricted.
+ * # You might want to change this threshold based on your needs. For example: some databases
+ * # doesn't accept a date superior to 31 december 9999.
+ * access.status.embargo.forever.year = 10000
+ * access.status.embargo.forever.month = 1
+ * access.status.embargo.forever.day = 1
+ * # implementation of access status helper plugin - replace with local implementation if applicable
+ * # This default access status helper provides an item status based on the policies of the primary
+ * # bitstream (or first bitstream in the original bundles if no primary file is specified).
+ * plugin.single.org.dspace.access.status.AccessStatusHelper = org.dspace.access.status.DefaultAccessStatusHelper
+ * }
+ */
+public interface AccessStatusService {
+
+ /**
+ * Calculate the access status for an Item while considering the forever embargo date threshold.
+ *
+ * @param context the DSpace context
+ * @param item the item
+ * @throws SQLException An exception that provides information on a database access error or other errors.
+ */
+ public String getAccessStatus(Context context, Item item) throws SQLException;
+}
diff --git a/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java b/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java
index 42461d7210..2677cb2050 100644
--- a/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java
+++ b/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java
@@ -11,13 +11,16 @@ import java.io.IOException;
import java.sql.SQLException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
-import org.apache.xpath.XPathAPI;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.MetadataField;
import org.dspace.content.MetadataSchema;
@@ -90,7 +93,7 @@ public class MetadataImporter {
public static void main(String[] args)
throws ParseException, SQLException, IOException, TransformerException,
ParserConfigurationException, AuthorizeException, SAXException,
- NonUniqueMetadataException, RegistryImportException {
+ NonUniqueMetadataException, RegistryImportException, XPathExpressionException {
// create an options object and populate it
CommandLineParser parser = new DefaultParser();
@@ -124,8 +127,8 @@ public class MetadataImporter {
* @throws RegistryImportException if import fails
*/
public static void loadRegistry(String file, boolean forceUpdate)
- throws SQLException, IOException, TransformerException, ParserConfigurationException,
- AuthorizeException, SAXException, NonUniqueMetadataException, RegistryImportException {
+ throws SQLException, IOException, TransformerException, ParserConfigurationException, AuthorizeException,
+ SAXException, NonUniqueMetadataException, RegistryImportException, XPathExpressionException {
Context context = null;
try {
@@ -137,7 +140,9 @@ public class MetadataImporter {
Document document = RegistryImporter.loadXML(file);
// Get the nodes corresponding to types
- NodeList schemaNodes = XPathAPI.selectNodeList(document, "/dspace-dc-types/dc-schema");
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ NodeList schemaNodes = (NodeList) xPath.compile("/dspace-dc-types/dc-schema")
+ .evaluate(document, XPathConstants.NODESET);
// Add each one as a new format to the registry
for (int i = 0; i < schemaNodes.getLength(); i++) {
@@ -146,7 +151,8 @@ public class MetadataImporter {
}
// Get the nodes corresponding to types
- NodeList typeNodes = XPathAPI.selectNodeList(document, "/dspace-dc-types/dc-type");
+ NodeList typeNodes = (NodeList) xPath.compile("/dspace-dc-types/dc-type")
+ .evaluate(document, XPathConstants.NODESET);
// Add each one as a new format to the registry
for (int i = 0; i < typeNodes.getLength(); i++) {
@@ -178,8 +184,8 @@ public class MetadataImporter {
* @throws RegistryImportException if import fails
*/
private static void loadSchema(Context context, Node node, boolean updateExisting)
- throws SQLException, IOException, TransformerException,
- AuthorizeException, NonUniqueMetadataException, RegistryImportException {
+ throws SQLException, AuthorizeException, NonUniqueMetadataException, RegistryImportException,
+ XPathExpressionException {
// Get the values
String name = RegistryImporter.getElementData(node, "name");
String namespace = RegistryImporter.getElementData(node, "namespace");
@@ -236,8 +242,8 @@ public class MetadataImporter {
* @throws RegistryImportException if import fails
*/
private static void loadType(Context context, Node node)
- throws SQLException, IOException, TransformerException,
- AuthorizeException, NonUniqueMetadataException, RegistryImportException {
+ throws SQLException, IOException, AuthorizeException, NonUniqueMetadataException, RegistryImportException,
+ XPathExpressionException {
// Get the values
String schema = RegistryImporter.getElementData(node, "schema");
String element = RegistryImporter.getElementData(node, "element");
diff --git a/dspace-api/src/main/java/org/dspace/administer/RegistryImporter.java b/dspace-api/src/main/java/org/dspace/administer/RegistryImporter.java
index 5b5f70412a..27a6534213 100644
--- a/dspace-api/src/main/java/org/dspace/administer/RegistryImporter.java
+++ b/dspace-api/src/main/java/org/dspace/administer/RegistryImporter.java
@@ -13,8 +13,11 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
-import org.apache.xpath.XPathAPI;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
@@ -72,9 +75,10 @@ public class RegistryImporter {
* @throws TransformerException if error
*/
public static String getElementData(Node parentElement, String childName)
- throws TransformerException {
+ throws XPathExpressionException {
// Grab the child node
- Node childNode = XPathAPI.selectSingleNode(parentElement, childName);
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ Node childNode = (Node) xPath.compile(childName).evaluate(parentElement, XPathConstants.NODE);
if (childNode == null) {
// No child node, so no values
@@ -115,9 +119,10 @@ public class RegistryImporter {
* @throws TransformerException if error
*/
public static String[] getRepeatedElementData(Node parentElement,
- String childName) throws TransformerException {
+ String childName) throws XPathExpressionException {
// Grab the child node
- NodeList childNodes = XPathAPI.selectNodeList(parentElement, childName);
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ NodeList childNodes = (NodeList) xPath.compile(childName).evaluate(parentElement, XPathConstants.NODESET);
String[] data = new String[childNodes.getLength()];
diff --git a/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java b/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java
index 2b6a01b558..bbf320a0d5 100644
--- a/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java
+++ b/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java
@@ -16,9 +16,12 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
import org.apache.logging.log4j.Logger;
-import org.apache.xpath.XPathAPI;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.BitstreamFormat;
import org.dspace.content.factory.ContentServiceFactory;
@@ -122,12 +125,13 @@ public class RegistryLoader {
*/
public static void loadBitstreamFormats(Context context, String filename)
throws SQLException, IOException, ParserConfigurationException,
- SAXException, TransformerException, AuthorizeException {
+ SAXException, TransformerException, AuthorizeException, XPathExpressionException {
Document document = loadXML(filename);
// Get the nodes corresponding to formats
- NodeList typeNodes = XPathAPI.selectNodeList(document,
- "dspace-bitstream-types/bitstream-type");
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ NodeList typeNodes = (NodeList) xPath.compile("dspace-bitstream-types/bitstream-type")
+ .evaluate(document, XPathConstants.NODESET);
// Add each one as a new format to the registry
for (int i = 0; i < typeNodes.getLength(); i++) {
@@ -151,8 +155,7 @@ public class RegistryLoader {
* @throws AuthorizeException if authorization error
*/
private static void loadFormat(Context context, Node node)
- throws SQLException, IOException, TransformerException,
- AuthorizeException {
+ throws SQLException, AuthorizeException, XPathExpressionException {
// Get the values
String mimeType = getElementData(node, "mimetype");
String shortDesc = getElementData(node, "short_description");
@@ -231,9 +234,10 @@ public class RegistryLoader {
* @throws TransformerException if transformer error
*/
private static String getElementData(Node parentElement, String childName)
- throws TransformerException {
+ throws XPathExpressionException {
// Grab the child node
- Node childNode = XPathAPI.selectSingleNode(parentElement, childName);
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ Node childNode = (Node) xPath.compile(childName).evaluate(parentElement, XPathConstants.NODE);
if (childNode == null) {
// No child node, so no values
@@ -274,9 +278,10 @@ public class RegistryLoader {
* @throws TransformerException if transformer error
*/
private static String[] getRepeatedElementData(Node parentElement,
- String childName) throws TransformerException {
+ String childName) throws XPathExpressionException {
// Grab the child node
- NodeList childNodes = XPathAPI.selectNodeList(parentElement, childName);
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ NodeList childNodes = (NodeList) xPath.compile(childName).evaluate(parentElement, XPathConstants.NODESET);
String[] data = new String[childNodes.getLength()];
diff --git a/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java b/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java
index 7a60313f8e..41a657dd61 100644
--- a/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java
+++ b/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java
@@ -30,6 +30,10 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
@@ -38,7 +42,6 @@ import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
-import org.apache.xpath.XPathAPI;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Collection;
import org.dspace.content.Community;
@@ -137,8 +140,8 @@ public class StructBuilder {
* @throws TransformerException if the input document is invalid.
*/
public static void main(String[] argv)
- throws ParserConfigurationException, SQLException,
- FileNotFoundException, IOException, TransformerException {
+ throws ParserConfigurationException, SQLException,
+ IOException, TransformerException, XPathExpressionException {
// Define command line options.
Options options = new Options();
@@ -240,7 +243,7 @@ public class StructBuilder {
* @throws SQLException
*/
static void importStructure(Context context, InputStream input, OutputStream output)
- throws IOException, ParserConfigurationException, SQLException, TransformerException {
+ throws IOException, ParserConfigurationException, SQLException, TransformerException, XPathExpressionException {
// load the XML
Document document = null;
@@ -258,13 +261,15 @@ public class StructBuilder {
// is properly structured.
try {
validate(document);
- } catch (TransformerException ex) {
+ } catch (XPathExpressionException ex) {
System.err.format("The input document is invalid: %s%n", ex.getMessage());
System.exit(1);
}
// Check for 'identifier' attributes -- possibly output by this class.
- NodeList identifierNodes = XPathAPI.selectNodeList(document, "//*[@identifier]");
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ NodeList identifierNodes = (NodeList) xPath.compile("//*[@identifier]")
+ .evaluate(document, XPathConstants.NODESET);
if (identifierNodes.getLength() > 0) {
System.err.println("The input document has 'identifier' attributes, which will be ignored.");
}
@@ -287,7 +292,8 @@ public class StructBuilder {
Element[] elements = new Element[]{};
try {
// get the top level community list
- NodeList first = XPathAPI.selectNodeList(document, "/import_structure/community");
+ NodeList first = (NodeList) xPath.compile("/import_structure/community")
+ .evaluate(document, XPathConstants.NODESET);
// run the import starting with the top level communities
elements = handleCommunities(context, first, null);
@@ -456,14 +462,16 @@ public class StructBuilder {
* @throws TransformerException if transformer error
*/
private static void validate(org.w3c.dom.Document document)
- throws TransformerException {
+ throws XPathExpressionException {
StringBuilder err = new StringBuilder();
boolean trip = false;
err.append("The following errors were encountered parsing the source XML.\n");
err.append("No changes have been made to the DSpace instance.\n\n");
- NodeList first = XPathAPI.selectNodeList(document, "/import_structure/community");
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ NodeList first = (NodeList) xPath.compile("/import_structure/community")
+ .evaluate(document, XPathConstants.NODESET);
if (first.getLength() == 0) {
err.append("-There are no top level communities in the source document.");
System.out.println(err.toString());
@@ -493,14 +501,15 @@ public class StructBuilder {
* no errors.
*/
private static String validateCommunities(NodeList communities, int level)
- throws TransformerException {
+ throws XPathExpressionException {
StringBuilder err = new StringBuilder();
boolean trip = false;
String errs = null;
+ XPath xPath = XPathFactory.newInstance().newXPath();
for (int i = 0; i < communities.getLength(); i++) {
Node n = communities.item(i);
- NodeList name = XPathAPI.selectNodeList(n, "name");
+ NodeList name = (NodeList) xPath.compile("name").evaluate(n, XPathConstants.NODESET);
if (name.getLength() != 1) {
String pos = Integer.toString(i + 1);
err.append("-The level ").append(level)
@@ -510,7 +519,7 @@ public class StructBuilder {
}
// validate sub communities
- NodeList subCommunities = XPathAPI.selectNodeList(n, "community");
+ NodeList subCommunities = (NodeList) xPath.compile("community").evaluate(n, XPathConstants.NODESET);
String comErrs = validateCommunities(subCommunities, level + 1);
if (comErrs != null) {
err.append(comErrs);
@@ -518,7 +527,7 @@ public class StructBuilder {
}
// validate collections
- NodeList collections = XPathAPI.selectNodeList(n, "collection");
+ NodeList collections = (NodeList) xPath.compile("collection").evaluate(n, XPathConstants.NODESET);
String colErrs = validateCollections(collections, level + 1);
if (colErrs != null) {
err.append(colErrs);
@@ -542,14 +551,15 @@ public class StructBuilder {
* @return the errors to be generated by the calling method, or null if none
*/
private static String validateCollections(NodeList collections, int level)
- throws TransformerException {
+ throws XPathExpressionException {
StringBuilder err = new StringBuilder();
boolean trip = false;
String errs = null;
+ XPath xPath = XPathFactory.newInstance().newXPath();
for (int i = 0; i < collections.getLength(); i++) {
Node n = collections.item(i);
- NodeList name = XPathAPI.selectNodeList(n, "name");
+ NodeList name = (NodeList) xPath.compile("name").evaluate(n, XPathConstants.NODESET);
if (name.getLength() != 1) {
String pos = Integer.toString(i + 1);
err.append("-The level ").append(level)
@@ -613,8 +623,9 @@ public class StructBuilder {
* created communities (e.g. the handles they have been assigned)
*/
private static Element[] handleCommunities(Context context, NodeList communities, Community parent)
- throws TransformerException, SQLException, AuthorizeException {
+ throws TransformerException, SQLException, AuthorizeException, XPathExpressionException {
Element[] elements = new Element[communities.getLength()];
+ XPath xPath = XPathFactory.newInstance().newXPath();
for (int i = 0; i < communities.getLength(); i++) {
Community community;
@@ -634,7 +645,7 @@ public class StructBuilder {
// now update the metadata
Node tn = communities.item(i);
for (Map.Entry entry : communityMap.entrySet()) {
- NodeList nl = XPathAPI.selectNodeList(tn, entry.getKey());
+ NodeList nl = (NodeList) xPath.compile(entry.getKey()).evaluate(tn, XPathConstants.NODESET);
if (nl.getLength() == 1) {
communityService.setMetadataSingleValue(context, community,
entry.getValue(), null, getStringValue(nl.item(0)));
@@ -700,11 +711,11 @@ public class StructBuilder {
}
// handle sub communities
- NodeList subCommunities = XPathAPI.selectNodeList(tn, "community");
+ NodeList subCommunities = (NodeList) xPath.compile("community").evaluate(tn, XPathConstants.NODESET);
Element[] subCommunityElements = handleCommunities(context, subCommunities, community);
// handle collections
- NodeList collections = XPathAPI.selectNodeList(tn, "collection");
+ NodeList collections = (NodeList) xPath.compile("collection").evaluate(tn, XPathConstants.NODESET);
Element[] collectionElements = handleCollections(context, collections, community);
int j;
@@ -731,8 +742,9 @@ public class StructBuilder {
* created collections (e.g. the handle)
*/
private static Element[] handleCollections(Context context, NodeList collections, Community parent)
- throws TransformerException, SQLException, AuthorizeException {
+ throws SQLException, AuthorizeException, XPathExpressionException {
Element[] elements = new Element[collections.getLength()];
+ XPath xPath = XPathFactory.newInstance().newXPath();
for (int i = 0; i < collections.getLength(); i++) {
Element element = new Element("collection");
@@ -745,7 +757,7 @@ public class StructBuilder {
// import the rest of the metadata
Node tn = collections.item(i);
for (Map.Entry entry : collectionMap.entrySet()) {
- NodeList nl = XPathAPI.selectNodeList(tn, entry.getKey());
+ NodeList nl = (NodeList) xPath.compile(entry.getKey()).evaluate(tn, XPathConstants.NODESET);
if (nl.getLength() == 1) {
collectionService.setMetadataSingleValue(context, collection,
entry.getValue(), null, getStringValue(nl.item(0)));
diff --git a/dspace-api/src/main/java/org/dspace/app/exception/ResourceAlreadyExistsException.java b/dspace-api/src/main/java/org/dspace/app/exception/ResourceAlreadyExistsException.java
index cd65fbcb74..8291af87fc 100644
--- a/dspace-api/src/main/java/org/dspace/app/exception/ResourceAlreadyExistsException.java
+++ b/dspace-api/src/main/java/org/dspace/app/exception/ResourceAlreadyExistsException.java
@@ -23,7 +23,6 @@ public class ResourceAlreadyExistsException extends RuntimeException {
* existing resource.
*
* @param message the error message
- * @param resource the resource that caused the conflict
*/
public ResourceAlreadyExistsException(String message) {
super(message);
diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java
index 6a6a70d574..15997a6786 100644
--- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java
@@ -51,6 +51,10 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
import org.apache.commons.collections4.ComparatorUtils;
import org.apache.commons.io.FileDeleteStrategy;
@@ -59,7 +63,6 @@ import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.logging.log4j.Logger;
-import org.apache.xpath.XPathAPI;
import org.dspace.app.itemimport.service.ItemImportService;
import org.dspace.app.util.LocalSchemaFilenameFilter;
import org.dspace.app.util.RelationshipUtils;
@@ -863,7 +866,7 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
// Load all metadata schemas into the item.
protected void loadMetadata(Context c, Item myitem, String path)
throws SQLException, IOException, ParserConfigurationException,
- SAXException, TransformerException, AuthorizeException {
+ SAXException, TransformerException, AuthorizeException, XPathExpressionException {
// Load the dublin core metadata
loadDublinCore(c, myitem, path + "dublin_core.xml");
@@ -877,14 +880,15 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
protected void loadDublinCore(Context c, Item myitem, String filename)
throws SQLException, IOException, ParserConfigurationException,
- SAXException, TransformerException, AuthorizeException {
+ SAXException, TransformerException, AuthorizeException, XPathExpressionException {
Document document = loadXML(filename);
// Get the schema, for backward compatibility we will default to the
// dublin core schema if the schema name is not available in the import
// file
String schema;
- NodeList metadata = XPathAPI.selectNodeList(document, "/dublin_core");
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ NodeList metadata = (NodeList) xPath.compile("/dublin_core").evaluate(document, XPathConstants.NODESET);
Node schemaAttr = metadata.item(0).getAttributes().getNamedItem(
"schema");
if (schemaAttr == null) {
@@ -894,8 +898,7 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
}
// Get the nodes corresponding to formats
- NodeList dcNodes = XPathAPI.selectNodeList(document,
- "/dublin_core/dcvalue");
+ NodeList dcNodes = (NodeList) xPath.compile("/dublin_core/dcvalue").evaluate(document, XPathConstants.NODESET);
if (!isQuiet) {
System.out.println("\tLoading dublin core from " + filename);
diff --git a/dspace-api/src/main/java/org/dspace/app/itemupdate/MetadataUtilities.java b/dspace-api/src/main/java/org/dspace/app/itemupdate/MetadataUtilities.java
index 5c2138a590..910eb434d1 100644
--- a/dspace-api/src/main/java/org/dspace/app/itemupdate/MetadataUtilities.java
+++ b/dspace-api/src/main/java/org/dspace/app/itemupdate/MetadataUtilities.java
@@ -27,10 +27,12 @@ import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
import org.apache.commons.lang3.StringUtils;
-import org.apache.xpath.XPathAPI;
-import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Item;
import org.dspace.content.MetadataField;
import org.dspace.content.MetadataSchema;
@@ -170,24 +172,21 @@ public class MetadataUtilities {
* @param docBuilder DocumentBuilder
* @param is - InputStream of dublin_core.xml
* @return list of DtoMetadata representing the metadata fields relating to an Item
- * @throws SQLException if database error
* @throws IOException if IO error
* @throws ParserConfigurationException if parser config error
* @throws SAXException if XML error
- * @throws TransformerException if transformer error
- * @throws AuthorizeException if authorization error
*/
public static List loadDublinCore(DocumentBuilder docBuilder, InputStream is)
- throws SQLException, IOException, ParserConfigurationException,
- SAXException, TransformerException, AuthorizeException {
+ throws IOException, XPathExpressionException, SAXException {
Document document = docBuilder.parse(is);
List dtomList = new ArrayList();
// Get the schema, for backward compatibility we will default to the
// dublin core schema if the schema name is not available in the import file
- String schema = null;
- NodeList metadata = XPathAPI.selectNodeList(document, "/dublin_core");
+ String schema;
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ NodeList metadata = (NodeList) xPath.compile("/dublin_core").evaluate(document, XPathConstants.NODESET);
Node schemaAttr = metadata.item(0).getAttributes().getNamedItem("schema");
if (schemaAttr == null) {
schema = MetadataSchemaEnum.DC.getName();
@@ -196,7 +195,7 @@ public class MetadataUtilities {
}
// Get the nodes corresponding to formats
- NodeList dcNodes = XPathAPI.selectNodeList(document, "/dublin_core/dcvalue");
+ NodeList dcNodes = (NodeList) xPath.compile("/dublin_core/dcvalue").evaluate(document, XPathConstants.NODESET);
for (int i = 0; i < dcNodes.getLength(); i++) {
Node n = dcNodes.item(i);
diff --git a/dspace-api/src/main/java/org/dspace/app/profile/ResearcherProfileServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/profile/ResearcherProfileServiceImpl.java
index ca50b68b6f..631fe94e59 100644
--- a/dspace-api/src/main/java/org/dspace/app/profile/ResearcherProfileServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/app/profile/ResearcherProfileServiceImpl.java
@@ -272,11 +272,6 @@ public class ResearcherProfileServiceImpl implements ResearcherProfileService {
return empty();
}
- if (indexableObjects.size() > 1) {
- log.warn("Multiple " + profileType + " type collections were found during profile creation");
- return empty();
- }
-
return ofNullable((Collection) indexableObjects.get(0).getIndexedObject());
}
@@ -296,7 +291,7 @@ public class ResearcherProfileServiceImpl implements ResearcherProfileService {
item = installItemService.installItem(context, workspaceItem);
- if (isNewProfilePrivateByDefault()) {
+ if (isNewProfileNotVisibleByDefault()) {
Group anonymous = groupService.findByName(context, ANONYMOUS);
authorizeService.removeGroupPolicies(context, item, anonymous);
}
@@ -310,8 +305,8 @@ public class ResearcherProfileServiceImpl implements ResearcherProfileService {
return configurationService.getBooleanProperty("researcher-profile.hard-delete.enabled");
}
- private boolean isNewProfilePrivateByDefault() {
- return configurationService.getBooleanProperty("researcher-profile.set-new-profile-private");
+ private boolean isNewProfileNotVisibleByDefault() {
+ return !configurationService.getBooleanProperty("researcher-profile.set-new-profile-visible");
}
private void removeOwnerMetadata(Context context, Item profileItem) throws SQLException {
diff --git a/dspace-api/src/main/java/org/dspace/app/profile/service/ResearcherProfileService.java b/dspace-api/src/main/java/org/dspace/app/profile/service/ResearcherProfileService.java
index 8190b0d98d..359f91761a 100644
--- a/dspace-api/src/main/java/org/dspace/app/profile/service/ResearcherProfileService.java
+++ b/dspace-api/src/main/java/org/dspace/app/profile/service/ResearcherProfileService.java
@@ -67,7 +67,7 @@ public interface ResearcherProfileService {
/**
* Changes the visibility of the given profile using the given new visible
- * value.
+ * value. The visiblity controls whether the Profile is Anonymous READ or not.
*
* @param context the relevant DSpace Context.
* @param profile the researcher profile to update
diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInput.java b/dspace-api/src/main/java/org/dspace/app/util/DCInput.java
index 32fd5d634d..f9fc97ec09 100644
--- a/dspace-api/src/main/java/org/dspace/app/util/DCInput.java
+++ b/dspace-api/src/main/java/org/dspace/app/util/DCInput.java
@@ -561,6 +561,15 @@ public class DCInput {
return true;
}
+ /**
+ * Get the type bind list for use in determining whether
+ * to display this field in angular dynamic form building
+ * @return list of bound types
+ */
+ public List getTypeBindList() {
+ return typeBind;
+ }
+
/**
* Verify whether the current field contains an entity relationship
* This also implies a relationship type is defined for this field
diff --git a/dspace-api/src/main/java/org/dspace/authority/util/XMLUtils.java b/dspace-api/src/main/java/org/dspace/authority/util/XMLUtils.java
index 77568205af..6cf49ac65b 100644
--- a/dspace-api/src/main/java/org/dspace/authority/util/XMLUtils.java
+++ b/dspace-api/src/main/java/org/dspace/authority/util/XMLUtils.java
@@ -14,11 +14,12 @@ import java.util.Iterator;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.TransformerException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
import org.apache.logging.log4j.Logger;
-import org.apache.xpath.XPathAPI;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -62,36 +63,26 @@ public class XMLUtils {
/**
* @param xml The starting context (a Node or a Document, for example).
- * @param NodeListXPath xpath
+ * @param nodeListXPath xpath
* @return A Node matches the NodeListXPath
* null if nothing matches the NodeListXPath
* @throws XPathExpressionException if xpath error
*/
- public static Node getNode(Node xml, String NodeListXPath) throws XPathExpressionException {
- Node result = null;
- try {
- result = XPathAPI.selectSingleNode(xml, NodeListXPath);
- } catch (TransformerException e) {
- log.error("Error", e);
- }
- return result;
+ public static Node getNode(Node xml, String nodeListXPath) throws XPathExpressionException {
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ return (Node) xPath.compile(nodeListXPath).evaluate(xml, XPathConstants.NODE);
}
/**
* @param xml The starting context (a Node or a Document, for example).
- * @param NodeListXPath xpath
+ * @param nodeListXPath xpath
* @return A NodeList containing the nodes that match the NodeListXPath
* null if nothing matches the NodeListXPath
* @throws XPathExpressionException if xpath error
*/
- public static NodeList getNodeList(Node xml, String NodeListXPath) throws XPathExpressionException {
- NodeList nodeList = null;
- try {
- nodeList = XPathAPI.selectNodeList(xml, NodeListXPath);
- } catch (TransformerException e) {
- log.error("Error", e);
- }
- return nodeList;
+ public static NodeList getNodeList(Node xml, String nodeListXPath) throws XPathExpressionException {
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ return (NodeList) xPath.compile(nodeListXPath).evaluate(xml, XPathConstants.NODESET);
}
public static Iterator getNodeListIterator(Node xml, String NodeListXPath) throws XPathExpressionException {
diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTCrosswalk.java
index ba3d5717d6..d4ccebf82e 100644
--- a/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTCrosswalk.java
+++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTCrosswalk.java
@@ -130,12 +130,6 @@ public abstract class XSLTCrosswalk extends SelfNamedPlugin {
return aliasList.toArray(new String[aliasList.size()]);
}
- /**
- * We need to force this, because some dependency elsewhere interferes.
- */
- private static final String TRANSFORMER_FACTORY_CLASS
- = "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl";
-
private Transformer transformer = null;
private File transformFile = null;
private long transformLastModified = 0;
@@ -181,8 +175,7 @@ public abstract class XSLTCrosswalk extends SelfNamedPlugin {
Source transformSource
= new StreamSource(new FileInputStream(transformFile));
TransformerFactory transformerFactory
- = TransformerFactory.newInstance(
- TRANSFORMER_FACTORY_CLASS, null);
+ = TransformerFactory.newInstance();
transformer = transformerFactory.newTransformer(transformSource);
transformLastModified = transformFile.lastModified();
} catch (TransformerConfigurationException | FileNotFoundException e) {
diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java
index f894553e5d..cdb15f90f7 100644
--- a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java
@@ -1174,7 +1174,7 @@ public class SolrServiceImpl implements SearchService, IndexingService {
//DO NOT ESCAPE RANGE QUERIES !
if (!value.matches("\\[.*TO.*\\]")) {
value = ClientUtils.escapeQueryChars(value);
- filterQuery.append("(").append(value).append(")");
+ filterQuery.append("\"").append(value).append("\"");
} else {
filterQuery.append(value);
}
diff --git a/dspace-api/src/main/java/org/dspace/iiif/consumer/CanvasCacheEvictService.java b/dspace-api/src/main/java/org/dspace/iiif/consumer/CanvasCacheEvictService.java
index 56cd432d91..beeb40ceac 100644
--- a/dspace-api/src/main/java/org/dspace/iiif/consumer/CanvasCacheEvictService.java
+++ b/dspace-api/src/main/java/org/dspace/iiif/consumer/CanvasCacheEvictService.java
@@ -23,7 +23,7 @@ public class CanvasCacheEvictService {
CacheManager cacheManager;
public void evictSingleCacheValue(String cacheKey) {
- Objects.requireNonNull(cacheManager.getCache(CACHE_NAME)).evict(cacheKey);
+ Objects.requireNonNull(cacheManager.getCache(CACHE_NAME)).evictIfPresent(cacheKey);
}
}
diff --git a/dspace-api/src/main/java/org/dspace/iiif/consumer/ManifestsCacheEvictService.java b/dspace-api/src/main/java/org/dspace/iiif/consumer/ManifestsCacheEvictService.java
index 967d0667a6..963ce3113f 100644
--- a/dspace-api/src/main/java/org/dspace/iiif/consumer/ManifestsCacheEvictService.java
+++ b/dspace-api/src/main/java/org/dspace/iiif/consumer/ManifestsCacheEvictService.java
@@ -26,11 +26,11 @@ public class ManifestsCacheEvictService {
CacheManager cacheManager;
public void evictSingleCacheValue(String cacheKey) {
- Objects.requireNonNull(cacheManager.getCache(CACHE_NAME)).evict(cacheKey);
+ Objects.requireNonNull(cacheManager.getCache(CACHE_NAME)).evictIfPresent(cacheKey);
}
public void evictAllCacheValues() {
- Objects.requireNonNull(cacheManager.getCache(CACHE_NAME)).clear();
+ Objects.requireNonNull(cacheManager.getCache(CACHE_NAME)).invalidate();
}
}
diff --git a/dspace-api/src/main/java/org/dspace/statistics/DataTermsFacet.java b/dspace-api/src/main/java/org/dspace/statistics/DataTermsFacet.java
deleted file mode 100644
index 9de06b7bb8..0000000000
--- a/dspace-api/src/main/java/org/dspace/statistics/DataTermsFacet.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * 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.statistics;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import com.google.gson.Gson;
-
-/**
- * A neutral data object to hold data for statistics.
- */
-public class DataTermsFacet {
- private List terms;
-
- public DataTermsFacet() {
- terms = new ArrayList();
- }
-
- public void addTermFacet(TermsFacet termsFacet) {
- terms.add(termsFacet);
- }
-
- /**
- * Render this data object into JSON format.
- *
- * An example of the output could be of the format:
- * [{"term":"247166","count":10},{"term":"247168","count":6}]
- *
- * @return JSON-formatted data.
- */
- public String toJson() {
- Gson gson = new Gson();
- return gson.toJson(terms);
- }
-
-
- public static class TermsFacet {
- private String term;
- private Integer count;
-
- public TermsFacet(String term, Integer count) {
- setTerm(term);
- setCount(count);
- }
-
- public String getTerm() {
- return term;
- }
-
- public void setTerm(String term) {
- this.term = term;
- }
-
- public Integer getCount() {
- return count;
- }
-
- public void setCount(Integer count) {
- this.count = count;
- }
-
-
- }
-}
diff --git a/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java b/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java
index c2a9b8a848..7debf3ba44 100644
--- a/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java
+++ b/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java
@@ -12,6 +12,7 @@ import java.io.IOException;
import java.sql.SQLException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
+import javax.xml.xpath.XPathExpressionException;
import org.dspace.administer.MetadataImporter;
import org.dspace.administer.RegistryImportException;
@@ -89,7 +90,7 @@ public class RegistryUpdater implements Callback {
} catch (IOException | SQLException | ParserConfigurationException
| TransformerException | RegistryImportException
| AuthorizeException | NonUniqueMetadataException
- | SAXException e) {
+ | SAXException | XPathExpressionException e) {
log.error("Error attempting to update Bitstream Format and/or Metadata Registries", e);
throw new RuntimeException("Error attempting to update Bitstream Format and/or Metadata Registries", e);
} finally {
diff --git a/dspace-api/src/main/java/org/dspace/submit/migration/SubmissionFormsMigration.java b/dspace-api/src/main/java/org/dspace/submit/migration/SubmissionFormsMigration.java
index 362f2720bb..db1fdcdd19 100644
--- a/dspace-api/src/main/java/org/dspace/submit/migration/SubmissionFormsMigration.java
+++ b/dspace-api/src/main/java/org/dspace/submit/migration/SubmissionFormsMigration.java
@@ -64,12 +64,6 @@ public class SubmissionFormsMigration extends DSpaceRunnable";
private List tempFiles = new ArrayList<>();
- /**
- * We need to force this, because some dependency elsewhere interferes.
- */
- private static final String TRANSFORMER_FACTORY_CLASS
- = "org.apache.xalan.processor.TransformerFactoryImpl";
-
@Override
public void internalRun() throws TransformerException {
if (help) {
@@ -101,8 +95,7 @@ public class SubmissionFormsMigration extends DSpaceRunnable subVocabularies = new ArrayList<>(subNodes.getLength());
for (int i = 0; i < subNodes.getLength(); i++) {
diff --git a/dspace-api/src/test/data/dspaceFolder/config/local.cfg b/dspace-api/src/test/data/dspaceFolder/config/local.cfg
index f05a7af223..cc9ecd81eb 100644
--- a/dspace-api/src/test/data/dspaceFolder/config/local.cfg
+++ b/dspace-api/src/test/data/dspaceFolder/config/local.cfg
@@ -143,4 +143,6 @@ authentication-ip.Student = 6.6.6.6
# NOTE: 127.0.0.1 (localhost) is always a trusted IP, see ClientInfoServiceImpl#parseTrustedProxyRanges
useProxies = true
proxies.trusted.ipranges = 7.7.7.7
-proxies.trusted.include_ui_ip = true
\ No newline at end of file
+proxies.trusted.include_ui_ip = true
+
+researcher-profile.entity-type = Person
\ No newline at end of file
diff --git a/dspace-api/src/test/data/dspaceFolder/config/submission-forms.xml b/dspace-api/src/test/data/dspaceFolder/config/submission-forms.xml
index 67946788b5..2642a5f897 100644
--- a/dspace-api/src/test/data/dspaceFolder/config/submission-forms.xml
+++ b/dspace-api/src/test/data/dspaceFolder/config/submission-forms.xml
@@ -140,6 +140,7 @@
ispartofseriestrue
+ Technical ReportseriesEnter the series and number assigned to this item by your community.
diff --git a/dspace-api/src/test/java/org/dspace/access/status/AccessStatusServiceTest.java b/dspace-api/src/test/java/org/dspace/access/status/AccessStatusServiceTest.java
new file mode 100644
index 0000000000..87127f9cf8
--- /dev/null
+++ b/dspace-api/src/test/java/org/dspace/access/status/AccessStatusServiceTest.java
@@ -0,0 +1,126 @@
+/**
+ * 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.access.status;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.fail;
+
+import java.sql.SQLException;
+
+import org.apache.logging.log4j.Logger;
+import org.dspace.AbstractUnitTest;
+import org.dspace.access.status.factory.AccessStatusServiceFactory;
+import org.dspace.access.status.service.AccessStatusService;
+import org.dspace.authorize.AuthorizeException;
+import org.dspace.content.Collection;
+import org.dspace.content.Community;
+import org.dspace.content.Item;
+import org.dspace.content.factory.ContentServiceFactory;
+import org.dspace.content.service.CollectionService;
+import org.dspace.content.service.CommunityService;
+import org.dspace.content.service.InstallItemService;
+import org.dspace.content.service.ItemService;
+import org.dspace.content.service.WorkspaceItemService;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Unit Tests for access status service
+ */
+public class AccessStatusServiceTest extends AbstractUnitTest {
+
+ private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(AccessStatusServiceTest.class);
+
+ private Collection collection;
+ private Community owningCommunity;
+ private Item item;
+
+ protected CommunityService communityService =
+ ContentServiceFactory.getInstance().getCommunityService();
+ protected CollectionService collectionService =
+ ContentServiceFactory.getInstance().getCollectionService();
+ protected ItemService itemService =
+ ContentServiceFactory.getInstance().getItemService();
+ protected WorkspaceItemService workspaceItemService =
+ ContentServiceFactory.getInstance().getWorkspaceItemService();
+ protected InstallItemService installItemService =
+ ContentServiceFactory.getInstance().getInstallItemService();
+ protected AccessStatusService accessStatusService =
+ AccessStatusServiceFactory.getInstance().getAccessStatusService();
+
+ /**
+ * This method will be run before every test as per @Before. It will
+ * initialize resources required for the tests.
+ *
+ * Other methods can be annotated with @Before here or in subclasses
+ * but no execution order is guaranteed
+ */
+ @Before
+ @Override
+ public void init() {
+ super.init();
+ try {
+ context.turnOffAuthorisationSystem();
+ owningCommunity = communityService.create(null, context);
+ collection = collectionService.create(context, owningCommunity);
+ item = installItemService.installItem(context,
+ workspaceItemService.create(context, collection, true));
+ context.restoreAuthSystemState();
+ } catch (AuthorizeException ex) {
+ log.error("Authorization Error in init", ex);
+ fail("Authorization Error in init: " + ex.getMessage());
+ } catch (SQLException ex) {
+ log.error("SQL Error in init", ex);
+ fail("SQL Error in init: " + ex.getMessage());
+ }
+ }
+
+ /**
+ * This method will be run after every test as per @After. It will
+ * clean resources initialized by the @Before methods.
+ *
+ * Other methods can be annotated with @After here or in subclasses
+ * but no execution order is guaranteed
+ */
+ @After
+ @Override
+ public void destroy() {
+ context.turnOffAuthorisationSystem();
+ try {
+ itemService.delete(context, item);
+ } catch (Exception e) {
+ // ignore
+ }
+ try {
+ collectionService.delete(context, collection);
+ } catch (Exception e) {
+ // ignore
+ }
+ try {
+ communityService.delete(context, owningCommunity);
+ } catch (Exception e) {
+ // ignore
+ }
+ context.restoreAuthSystemState();
+ item = null;
+ collection = null;
+ owningCommunity = null;
+ try {
+ super.destroy();
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+
+ @Test
+ public void testGetAccessStatus() throws Exception {
+ String status = accessStatusService.getAccessStatus(context, item);
+ assertNotEquals("testGetAccessStatus 0", status, DefaultAccessStatusHelper.UNKNOWN);
+ }
+}
diff --git a/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java b/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java
new file mode 100644
index 0000000000..a41e985deb
--- /dev/null
+++ b/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java
@@ -0,0 +1,423 @@
+/**
+ * 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.access.status;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.dspace.AbstractUnitTest;
+import org.dspace.authorize.AuthorizeException;
+import org.dspace.authorize.ResourcePolicy;
+import org.dspace.authorize.factory.AuthorizeServiceFactory;
+import org.dspace.authorize.service.ResourcePolicyService;
+import org.dspace.content.Bitstream;
+import org.dspace.content.Bundle;
+import org.dspace.content.Collection;
+import org.dspace.content.Community;
+import org.dspace.content.Item;
+import org.dspace.content.factory.ContentServiceFactory;
+import org.dspace.content.service.BitstreamService;
+import org.dspace.content.service.BundleService;
+import org.dspace.content.service.CollectionService;
+import org.dspace.content.service.CommunityService;
+import org.dspace.content.service.InstallItemService;
+import org.dspace.content.service.ItemService;
+import org.dspace.content.service.WorkspaceItemService;
+import org.dspace.core.Constants;
+import org.dspace.eperson.Group;
+import org.dspace.eperson.factory.EPersonServiceFactory;
+import org.dspace.eperson.service.GroupService;
+import org.joda.time.LocalDate;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DefaultAccessStatusHelperTest extends AbstractUnitTest {
+
+ private static final Logger log = LogManager.getLogger(DefaultAccessStatusHelperTest.class);
+
+ private Collection collection;
+ private Community owningCommunity;
+ private Item itemWithoutBundle;
+ private Item itemWithoutBitstream;
+ private Item itemWithBitstream;
+ private Item itemWithEmbargo;
+ private Item itemWithDateRestriction;
+ private Item itemWithGroupRestriction;
+ private Item itemWithoutPolicy;
+ private Item itemWithoutPrimaryBitstream;
+ private Item itemWithPrimaryAndMultipleBitstreams;
+ private Item itemWithoutPrimaryAndMultipleBitstreams;
+ private DefaultAccessStatusHelper helper;
+ private Date threshold;
+
+ protected CommunityService communityService =
+ ContentServiceFactory.getInstance().getCommunityService();
+ protected CollectionService collectionService =
+ ContentServiceFactory.getInstance().getCollectionService();
+ protected ItemService itemService =
+ ContentServiceFactory.getInstance().getItemService();
+ protected WorkspaceItemService workspaceItemService =
+ ContentServiceFactory.getInstance().getWorkspaceItemService();
+ protected InstallItemService installItemService =
+ ContentServiceFactory.getInstance().getInstallItemService();
+ protected BundleService bundleService =
+ ContentServiceFactory.getInstance().getBundleService();
+ protected BitstreamService bitstreamService =
+ ContentServiceFactory.getInstance().getBitstreamService();
+ protected ResourcePolicyService resourcePolicyService =
+ AuthorizeServiceFactory.getInstance().getResourcePolicyService();
+ protected GroupService groupService =
+ EPersonServiceFactory.getInstance().getGroupService();
+
+ /**
+ * This method will be run before every test as per @Before. It will
+ * initialize resources required for the tests.
+ *
+ * Other methods can be annotated with @Before here or in subclasses
+ * but no execution order is guaranteed
+ */
+ @Before
+ @Override
+ public void init() {
+ super.init();
+ try {
+ context.turnOffAuthorisationSystem();
+ owningCommunity = communityService.create(null, context);
+ collection = collectionService.create(context, owningCommunity);
+ itemWithoutBundle = installItemService.installItem(context,
+ workspaceItemService.create(context, collection, true));
+ itemWithoutBitstream = installItemService.installItem(context,
+ workspaceItemService.create(context, collection, true));
+ itemWithBitstream = installItemService.installItem(context,
+ workspaceItemService.create(context, collection, true));
+ itemWithEmbargo = installItemService.installItem(context,
+ workspaceItemService.create(context, collection, true));
+ itemWithDateRestriction = installItemService.installItem(context,
+ workspaceItemService.create(context, collection, true));
+ itemWithGroupRestriction = installItemService.installItem(context,
+ workspaceItemService.create(context, collection, true));
+ itemWithoutPolicy = installItemService.installItem(context,
+ workspaceItemService.create(context, collection, true));
+ itemWithoutPrimaryBitstream = installItemService.installItem(context,
+ workspaceItemService.create(context, collection, true));
+ itemWithPrimaryAndMultipleBitstreams = installItemService.installItem(context,
+ workspaceItemService.create(context, collection, true));
+ itemWithoutPrimaryAndMultipleBitstreams = installItemService.installItem(context,
+ workspaceItemService.create(context, collection, true));
+ context.restoreAuthSystemState();
+ } catch (AuthorizeException ex) {
+ log.error("Authorization Error in init", ex);
+ fail("Authorization Error in init: " + ex.getMessage());
+ } catch (SQLException ex) {
+ log.error("SQL Error in init", ex);
+ fail("SQL Error in init: " + ex.getMessage());
+ }
+ helper = new DefaultAccessStatusHelper();
+ threshold = new LocalDate(10000, 1, 1).toDate();
+ }
+
+ /**
+ * This method will be run after every test as per @After. It will
+ * clean resources initialized by the @Before methods.
+ *
+ * Other methods can be annotated with @After here or in subclasses
+ * but no execution order is guaranteed
+ */
+ @After
+ @Override
+ public void destroy() {
+ context.turnOffAuthorisationSystem();
+ try {
+ itemService.delete(context, itemWithoutBundle);
+ itemService.delete(context, itemWithoutBitstream);
+ itemService.delete(context, itemWithBitstream);
+ itemService.delete(context, itemWithEmbargo);
+ itemService.delete(context, itemWithDateRestriction);
+ itemService.delete(context, itemWithGroupRestriction);
+ itemService.delete(context, itemWithoutPolicy);
+ itemService.delete(context, itemWithoutPrimaryBitstream);
+ itemService.delete(context, itemWithPrimaryAndMultipleBitstreams);
+ itemService.delete(context, itemWithoutPrimaryAndMultipleBitstreams);
+ } catch (Exception e) {
+ // ignore
+ }
+ try {
+ collectionService.delete(context, collection);
+ } catch (Exception e) {
+ // ignore
+ }
+ try {
+ communityService.delete(context, owningCommunity);
+ } catch (Exception e) {
+ // ignore
+ }
+ context.restoreAuthSystemState();
+ itemWithoutBundle = null;
+ itemWithoutBitstream = null;
+ itemWithBitstream = null;
+ itemWithEmbargo = null;
+ itemWithDateRestriction = null;
+ itemWithGroupRestriction = null;
+ itemWithoutPolicy = null;
+ itemWithoutPrimaryBitstream = null;
+ itemWithPrimaryAndMultipleBitstreams = null;
+ itemWithoutPrimaryAndMultipleBitstreams = null;
+ collection = null;
+ owningCommunity = null;
+ helper = null;
+ threshold = null;
+ communityService = null;
+ collectionService = null;
+ itemService = null;
+ workspaceItemService = null;
+ installItemService = null;
+ bundleService = null;
+ bitstreamService = null;
+ resourcePolicyService = null;
+ groupService = null;
+ try {
+ super.destroy();
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+
+ /**
+ * Test for a null item
+ * @throws java.lang.Exception passed through.
+ */
+ @Test
+ public void testWithNullItem() throws Exception {
+ String status = helper.getAccessStatusFromItem(context, null, threshold);
+ assertThat("testWithNullItem 0", status, equalTo(DefaultAccessStatusHelper.UNKNOWN));
+ }
+
+ /**
+ * Test for an item with no bundle
+ * @throws java.lang.Exception passed through.
+ */
+ @Test
+ public void testWithoutBundle() throws Exception {
+ String status = helper.getAccessStatusFromItem(context, itemWithoutBundle, threshold);
+ assertThat("testWithoutBundle 0", status, equalTo(DefaultAccessStatusHelper.METADATA_ONLY));
+ }
+
+ /**
+ * Test for an item with no bitstream
+ * @throws java.lang.Exception passed through.
+ */
+ @Test
+ public void testWithoutBitstream() throws Exception {
+ context.turnOffAuthorisationSystem();
+ bundleService.create(context, itemWithoutBitstream, Constants.CONTENT_BUNDLE_NAME);
+ context.restoreAuthSystemState();
+ String status = helper.getAccessStatusFromItem(context, itemWithoutBitstream, threshold);
+ assertThat("testWithoutBitstream 0", status, equalTo(DefaultAccessStatusHelper.METADATA_ONLY));
+ }
+
+ /**
+ * Test for an item with a basic bitstream (open access)
+ * @throws java.lang.Exception passed through.
+ */
+ @Test
+ public void testWithBitstream() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Bundle bundle = bundleService.create(context, itemWithBitstream, Constants.CONTENT_BUNDLE_NAME);
+ Bitstream bitstream = bitstreamService.create(context, bundle,
+ new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
+ bitstream.setName(context, "primary");
+ bundle.setPrimaryBitstreamID(bitstream);
+ context.restoreAuthSystemState();
+ String status = helper.getAccessStatusFromItem(context, itemWithBitstream, threshold);
+ assertThat("testWithBitstream 0", status, equalTo(DefaultAccessStatusHelper.OPEN_ACCESS));
+ }
+
+ /**
+ * Test for an item with an embargo
+ * @throws java.lang.Exception passed through.
+ */
+ @Test
+ public void testWithEmbargo() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Bundle bundle = bundleService.create(context, itemWithEmbargo, Constants.CONTENT_BUNDLE_NAME);
+ Bitstream bitstream = bitstreamService.create(context, bundle,
+ new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
+ bitstream.setName(context, "primary");
+ bundle.setPrimaryBitstreamID(bitstream);
+ List policies = new ArrayList<>();
+ ResourcePolicy policy = resourcePolicyService.create(context);
+ policy.setRpName("Embargo");
+ Group group = groupService.findByName(context, Group.ANONYMOUS);
+ policy.setGroup(group);
+ policy.setAction(Constants.READ);
+ policy.setStartDate(new LocalDate(9999, 12, 31).toDate());
+ policies.add(policy);
+ authorizeService.removeAllPolicies(context, bitstream);
+ authorizeService.addPolicies(context, policies, bitstream);
+ context.restoreAuthSystemState();
+ String status = helper.getAccessStatusFromItem(context, itemWithEmbargo, threshold);
+ assertThat("testWithEmbargo 0", status, equalTo(DefaultAccessStatusHelper.EMBARGO));
+ }
+
+ /**
+ * Test for an item with an anonymous date restriction
+ * @throws java.lang.Exception passed through.
+ */
+ @Test
+ public void testWithDateRestriction() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Bundle bundle = bundleService.create(context, itemWithDateRestriction, Constants.CONTENT_BUNDLE_NAME);
+ Bitstream bitstream = bitstreamService.create(context, bundle,
+ new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
+ bitstream.setName(context, "primary");
+ bundle.setPrimaryBitstreamID(bitstream);
+ List policies = new ArrayList<>();
+ ResourcePolicy policy = resourcePolicyService.create(context);
+ policy.setRpName("Restriction");
+ Group group = groupService.findByName(context, Group.ANONYMOUS);
+ policy.setGroup(group);
+ policy.setAction(Constants.READ);
+ policy.setStartDate(new LocalDate(10000, 1, 1).toDate());
+ policies.add(policy);
+ authorizeService.removeAllPolicies(context, bitstream);
+ authorizeService.addPolicies(context, policies, bitstream);
+ context.restoreAuthSystemState();
+ String status = helper.getAccessStatusFromItem(context, itemWithDateRestriction, threshold);
+ assertThat("testWithDateRestriction 0", status, equalTo(DefaultAccessStatusHelper.RESTRICTED));
+ }
+
+ /**
+ * Test for an item with a group restriction
+ * @throws java.lang.Exception passed through.
+ */
+ @Test
+ public void testWithGroupRestriction() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Bundle bundle = bundleService.create(context, itemWithGroupRestriction, Constants.CONTENT_BUNDLE_NAME);
+ Bitstream bitstream = bitstreamService.create(context, bundle,
+ new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
+ bitstream.setName(context, "primary");
+ bundle.setPrimaryBitstreamID(bitstream);
+ List policies = new ArrayList<>();
+ ResourcePolicy policy = resourcePolicyService.create(context);
+ policy.setRpName("Restriction");
+ Group group = groupService.findByName(context, Group.ADMIN);
+ policy.setGroup(group);
+ policy.setAction(Constants.READ);
+ policies.add(policy);
+ authorizeService.removeAllPolicies(context, bitstream);
+ authorizeService.addPolicies(context, policies, bitstream);
+ context.restoreAuthSystemState();
+ String status = helper.getAccessStatusFromItem(context, itemWithGroupRestriction, threshold);
+ assertThat("testWithGroupRestriction 0", status, equalTo(DefaultAccessStatusHelper.RESTRICTED));
+ }
+
+ /**
+ * Test for an item with no policy
+ * @throws java.lang.Exception passed through.
+ */
+ @Test
+ public void testWithoutPolicy() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Bundle bundle = bundleService.create(context, itemWithoutPolicy, Constants.CONTENT_BUNDLE_NAME);
+ Bitstream bitstream = bitstreamService.create(context, bundle,
+ new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
+ bitstream.setName(context, "primary");
+ bundle.setPrimaryBitstreamID(bitstream);
+ authorizeService.removeAllPolicies(context, bitstream);
+ context.restoreAuthSystemState();
+ String status = helper.getAccessStatusFromItem(context, itemWithoutPolicy, threshold);
+ assertThat("testWithoutPolicy 0", status, equalTo(DefaultAccessStatusHelper.RESTRICTED));
+ }
+
+ /**
+ * Test for an item with no primary bitstream
+ * @throws java.lang.Exception passed through.
+ */
+ @Test
+ public void testWithoutPrimaryBitstream() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Bundle bundle = bundleService.create(context, itemWithoutPrimaryBitstream, Constants.CONTENT_BUNDLE_NAME);
+ Bitstream bitstream = bitstreamService.create(context, bundle,
+ new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
+ bitstream.setName(context, "first");
+ context.restoreAuthSystemState();
+ String status = helper.getAccessStatusFromItem(context, itemWithoutPrimaryBitstream, threshold);
+ assertThat("testWithoutPrimaryBitstream 0", status, equalTo(DefaultAccessStatusHelper.OPEN_ACCESS));
+ }
+
+ /**
+ * Test for an item with an open access bitstream
+ * and another primary bitstream on embargo
+ * @throws java.lang.Exception passed through.
+ */
+ @Test
+ public void testWithPrimaryAndMultipleBitstreams() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Bundle bundle = bundleService.create(context, itemWithPrimaryAndMultipleBitstreams,
+ Constants.CONTENT_BUNDLE_NAME);
+ bitstreamService.create(context, bundle,
+ new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
+ Bitstream primaryBitstream = bitstreamService.create(context, bundle,
+ new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
+ bundle.setPrimaryBitstreamID(primaryBitstream);
+ List policies = new ArrayList<>();
+ ResourcePolicy policy = resourcePolicyService.create(context);
+ policy.setRpName("Embargo");
+ Group group = groupService.findByName(context, Group.ANONYMOUS);
+ policy.setGroup(group);
+ policy.setAction(Constants.READ);
+ policy.setStartDate(new LocalDate(9999, 12, 31).toDate());
+ policies.add(policy);
+ authorizeService.removeAllPolicies(context, primaryBitstream);
+ authorizeService.addPolicies(context, policies, primaryBitstream);
+ context.restoreAuthSystemState();
+ String status = helper.getAccessStatusFromItem(context, itemWithPrimaryAndMultipleBitstreams, threshold);
+ assertThat("testWithPrimaryAndMultipleBitstreams 0", status, equalTo(DefaultAccessStatusHelper.EMBARGO));
+ }
+
+ /**
+ * Test for an item with an open access bitstream
+ * and another bitstream on embargo
+ * @throws java.lang.Exception passed through.
+ */
+ @Test
+ public void testWithNoPrimaryAndMultipleBitstreams() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Bundle bundle = bundleService.create(context, itemWithoutPrimaryAndMultipleBitstreams,
+ Constants.CONTENT_BUNDLE_NAME);
+ bitstreamService.create(context, bundle,
+ new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
+ Bitstream anotherBitstream = bitstreamService.create(context, bundle,
+ new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
+ List policies = new ArrayList<>();
+ ResourcePolicy policy = resourcePolicyService.create(context);
+ policy.setRpName("Embargo");
+ Group group = groupService.findByName(context, Group.ANONYMOUS);
+ policy.setGroup(group);
+ policy.setAction(Constants.READ);
+ policy.setStartDate(new LocalDate(9999, 12, 31).toDate());
+ policies.add(policy);
+ authorizeService.removeAllPolicies(context, anotherBitstream);
+ authorizeService.addPolicies(context, policies, anotherBitstream);
+ context.restoreAuthSystemState();
+ String status = helper.getAccessStatusFromItem(context, itemWithoutPrimaryAndMultipleBitstreams, threshold);
+ assertThat("testWithNoPrimaryAndMultipleBitstreams 0", status, equalTo(DefaultAccessStatusHelper.OPEN_ACCESS));
+ }
+}
diff --git a/dspace-api/src/test/java/org/dspace/builder/VersionBuilder.java b/dspace-api/src/test/java/org/dspace/builder/VersionBuilder.java
index 0b1d8ebecf..7a2b718df6 100644
--- a/dspace-api/src/test/java/org/dspace/builder/VersionBuilder.java
+++ b/dspace-api/src/test/java/org/dspace/builder/VersionBuilder.java
@@ -11,7 +11,8 @@ import java.sql.SQLException;
import java.util.Objects;
import org.apache.commons.lang.StringUtils;
-import org.apache.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Item;
import org.dspace.core.Context;
@@ -26,7 +27,7 @@ import org.dspace.versioning.service.VersioningService;
*/
public class VersionBuilder extends AbstractBuilder {
- private static final Logger log = Logger.getLogger(VersionBuilder.class);
+ private static final Logger log = LogManager.getLogger(VersionBuilder.class);
private Version version;
diff --git a/dspace-iiif/src/main/java/org/dspace/app/iiif/service/WordHighlightSolrSearch.java b/dspace-iiif/src/main/java/org/dspace/app/iiif/service/WordHighlightSolrSearch.java
index 0e614fae2a..da50f33582 100644
--- a/dspace-iiif/src/main/java/org/dspace/app/iiif/service/WordHighlightSolrSearch.java
+++ b/dspace-iiif/src/main/java/org/dspace/app/iiif/service/WordHighlightSolrSearch.java
@@ -12,14 +12,11 @@ import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
import java.util.UUID;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.validator.routines.UrlValidator;
import org.apache.logging.log4j.Logger;
import org.apache.solr.client.solrj.SolrQuery;
@@ -35,7 +32,6 @@ import org.dspace.app.iiif.model.generator.ContentAsTextGenerator;
import org.dspace.app.iiif.model.generator.ManifestGenerator;
import org.dspace.app.iiif.model.generator.SearchResultGenerator;
import org.dspace.app.iiif.service.utils.IIIFUtils;
-import org.dspace.discovery.SolrSearchCore;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -66,9 +62,6 @@ public class WordHighlightSolrSearch implements SearchAnnotationService {
@Autowired
SearchResultGenerator searchResult;
- @Autowired
- SolrSearchCore solrSearchCore;
-
@Autowired
ManifestGenerator manifestGenerator;
@@ -167,26 +160,49 @@ public class WordHighlightSolrSearch implements SearchAnnotationService {
private String getAnnotationList(UUID uuid, String json, String query) {
searchResult.setIdentifier(manifestId + "/search?q="
+ URLEncoder.encode(query, StandardCharsets.UTF_8));
- GsonBuilder builder = new GsonBuilder();
- Gson gson = builder.create();
- JsonObject body = gson.fromJson(json, JsonObject.class);
+
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode body = null;
+ try {
+ body = mapper.readTree(json);
+ } catch (JsonProcessingException e) {
+ log.error("Unable to process json response.", e);
+ }
+ // If error occurred or no body, return immediately
if (body == null) {
- log.warn("Unable to process json response.");
return utils.asJson(searchResult.generateResource());
}
- // outer ocr highlight element
- JsonObject highs = body.getAsJsonObject("ocrHighlighting");
- // highlight entries
- for (Map.Entry ocrIds: highs.entrySet()) {
- // ocr_text
- JsonObject ocrObj = ocrIds.getValue().getAsJsonObject().getAsJsonObject("ocr_text");
- // snippets array
- if (ocrObj != null) {
- for (JsonElement snippetArray : ocrObj.getAsJsonObject().get("snippets").getAsJsonArray()) {
- String pageId = getCanvasId(snippetArray.getAsJsonObject().get("pages"));
- for (JsonElement highlights : snippetArray.getAsJsonObject().getAsJsonArray("highlights")) {
- for (JsonElement highlight : highlights.getAsJsonArray()) {
- searchResult.addResource(getAnnotation(highlight, pageId, uuid));
+
+ // Example structure of Solr response available at
+ // https://github.com/dbmdz/solr-ocrhighlighting/blob/main/docs/query.md
+ // Get the outer ocrHighlighting node
+ JsonNode highs = body.get("ocrHighlighting");
+ if (highs != null) {
+ // Loop through each highlight entry under ocrHighlighting
+ for (final JsonNode highEntry : highs) {
+ // Get the ocr_text node under the entry
+ JsonNode ocrNode = highEntry.get("ocr_text");
+ if (ocrNode != null) {
+ // Loop through the snippets array under that
+ for (final JsonNode snippet : ocrNode.get("snippets")) {
+ if (snippet != null) {
+ // Get a canvas ID based on snippet's pages
+ String pageId = getCanvasId(snippet.get("pages"));
+ if (pageId != null) {
+ // Loop through array of highlights for each snippet.
+ for (final JsonNode highlights : snippet.get("highlights")) {
+ if (highlights != null) {
+ // May be multiple word highlights on a page, so loop through them.
+ for (int i = 0; i < highlights.size(); i++) {
+ // Add annotation associated with each highlight
+ AnnotationGenerator anno = getAnnotation(highlights.get(i), pageId, uuid);
+ if (anno != null) {
+ searchResult.addResource(anno);
+ }
+ }
+ }
+ }
+ }
}
}
}
@@ -198,21 +214,24 @@ public class WordHighlightSolrSearch implements SearchAnnotationService {
/**
* Returns the annotation generator for the highlight.
- * @param highlight highlight element from solor response
+ * @param highlight highlight node from Solr response
* @param pageId page id from solr response
* @return generator for a single annotation
*/
- private AnnotationGenerator getAnnotation(JsonElement highlight, String pageId, UUID uuid) {
- JsonObject hcoords = highlight.getAsJsonObject();
- String text = (hcoords.get("text").getAsString());
- int ulx = hcoords.get("ulx").getAsInt();
- int uly = hcoords.get("uly").getAsInt();
- int lrx = hcoords.get("lrx").getAsInt();
- int lry = hcoords.get("lry").getAsInt();
- String w = Integer.toString(lrx - ulx);
- String h = Integer.toString(lry - uly);
- String params = ulx + "," + uly + "," + w + "," + h;
- return createSearchResultAnnotation(params, text, pageId, uuid);
+ private AnnotationGenerator getAnnotation(JsonNode highlight, String pageId, UUID uuid) {
+ String text = highlight.get("text") != null ? highlight.get("text").asText() : null;
+ int ulx = highlight.get("ulx") != null ? highlight.get("ulx").asInt() : -1;
+ int uly = highlight.get("uly") != null ? highlight.get("uly").asInt() : -1;
+ int lrx = highlight.get("lrx") != null ? highlight.get("lrx").asInt() : -1;
+ int lry = highlight.get("lry") != null ? highlight.get("lry").asInt() : -1;
+ String w = (lrx >= 0 && ulx >= 0) ? Integer.toString(lrx - ulx) : null;
+ String h = (lry >= 0 && uly >= 0) ? Integer.toString(lry - uly) : null;
+
+ if (text != null && w != null && h != null) {
+ String params = ulx + "," + uly + "," + w + "," + h;
+ return createSearchResultAnnotation(params, text, pageId, uuid);
+ }
+ return null;
}
/**
@@ -221,15 +240,22 @@ public class WordHighlightSolrSearch implements SearchAnnotationService {
* delimited with a "." and that the integer corresponds to the
* canvas identifier in the manifest. For METS/ALTO documents, the page
* order can be derived from the METS file when loading the solr index.
- * @param element the pages element
- * @return canvas id
+ * @param pagesNode the pages node
+ * @return canvas id or null if node was null
*/
- private String getCanvasId(JsonElement element) {
- JsonArray pages = element.getAsJsonArray();
- JsonObject page = pages.get(0).getAsJsonObject();
- String[] identArr = page.get("id").getAsString().split("\\.");
- // the canvas id.
- return "c" + identArr[1];
+ private String getCanvasId(JsonNode pagesNode) {
+ if (pagesNode != null) {
+ JsonNode page = pagesNode.get(0);
+ if (page != null) {
+ JsonNode pageId = page.get("id");
+ if (pageId != null) {
+ String[] identArr = pageId.asText().split("\\.");
+ // the canvas id.
+ return "c" + identArr[1];
+ }
+ }
+ }
+ return null;
}
/**
diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml
index 27adc3ef94..d233f4a772 100644
--- a/dspace-oai/pom.xml
+++ b/dspace-oai/pom.xml
@@ -82,10 +82,6 @@
org.mockitomockito-all
-
- xml-apis
- xml-apis
- org.apache.commonscommons-lang3
diff --git a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/DSpaceResourceResolver.java b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/DSpaceResourceResolver.java
index 26dd976495..e67e9c56bd 100644
--- a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/DSpaceResourceResolver.java
+++ b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/DSpaceResourceResolver.java
@@ -22,6 +22,7 @@ import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
public class DSpaceResourceResolver implements ResourceResolver {
+ // Requires usage of Saxon as OAI-PMH uses some XSLT 2 functions
private static final TransformerFactory transformerFactory = TransformerFactory
.newInstance("net.sf.saxon.TransformerFactoryImpl", null);
diff --git a/dspace-oai/src/test/java/org/dspace/xoai/tests/stylesheets/AbstractXSLTest.java b/dspace-oai/src/test/java/org/dspace/xoai/tests/stylesheets/AbstractXSLTest.java
index 6fab56b526..42dbed04b6 100644
--- a/dspace-oai/src/test/java/org/dspace/xoai/tests/stylesheets/AbstractXSLTest.java
+++ b/dspace-oai/src/test/java/org/dspace/xoai/tests/stylesheets/AbstractXSLTest.java
@@ -19,6 +19,7 @@ import javax.xml.transform.stream.StreamSource;
import org.apache.commons.io.IOUtils;
public abstract class AbstractXSLTest {
+ // Requires usage of Saxon as OAI-PMH uses some XSLT 2 functions
private static final TransformerFactory factory = TransformerFactory
.newInstance("net.sf.saxon.TransformerFactoryImpl", null);
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/CanClaimItemFeature.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/CanClaimItemFeature.java
index bca11b8d9b..b6dbf3fa10 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/CanClaimItemFeature.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/CanClaimItemFeature.java
@@ -8,17 +8,20 @@
package org.dspace.app.rest.authorization.impl;
import java.sql.SQLException;
-import java.util.Objects;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
+import org.dspace.app.profile.service.ResearcherProfileService;
import org.dspace.app.rest.authorization.AuthorizationFeature;
import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
import org.dspace.app.rest.model.BaseObjectRest;
import org.dspace.app.rest.model.ItemRest;
+import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Item;
import org.dspace.content.service.ItemService;
import org.dspace.core.Context;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -32,33 +35,41 @@ import org.springframework.stereotype.Component;
*/
@Component
@AuthorizationFeatureDocumentation(name = CanClaimItemFeature.NAME,
- description = "Used to verify if the given user can request the claim of an item")
+ description = "Used to verify if the current user is able to claim this item as their profile. "
+ + "Only available if the current item is not already claimed.")
public class CanClaimItemFeature implements AuthorizationFeature {
public static final String NAME = "canClaimItem";
+ private static final Logger LOG = LoggerFactory.getLogger(CanClaimItemFeature.class);
+
@Autowired
private ItemService itemService;
@Autowired
- private ShowClaimItemFeature showClaimItemFeature;
+ private ResearcherProfileService researcherProfileService;
@Override
@SuppressWarnings("rawtypes")
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
- if (!showClaimItemFeature.isAuthorized(context, object)) {
- return false;
- }
-
- if (!(object instanceof ItemRest) || Objects.isNull(context.getCurrentUser())) {
+ if (!(object instanceof ItemRest) || context.getCurrentUser() == null) {
return false;
}
String id = ((ItemRest) object).getId();
Item item = itemService.find(context, UUID.fromString(id));
- return hasNotOwner(item);
+ return researcherProfileService.hasProfileType(item) && hasNotOwner(item) && hasNotAlreadyAProfile(context);
+ }
+
+ private boolean hasNotAlreadyAProfile(Context context) {
+ try {
+ return researcherProfileService.findById(context, context.getCurrentUser().getID()) == null;
+ } catch (SQLException | AuthorizeException e) {
+ LOG.warn("Error while checking if eperson has a ResearcherProfileAssociated: {}", e.getMessage(), e);
+ return false;
+ }
}
private boolean hasNotOwner(Item item) {
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/ShowClaimItemFeature.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/ShowClaimItemFeature.java
deleted file mode 100644
index ce25988b69..0000000000
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/ShowClaimItemFeature.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/**
- * 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.rest.authorization.impl;
-
-import java.sql.SQLException;
-import java.util.Objects;
-import java.util.UUID;
-
-import org.dspace.app.profile.service.ResearcherProfileService;
-import org.dspace.app.rest.authorization.AuthorizationFeature;
-import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
-import org.dspace.app.rest.model.BaseObjectRest;
-import org.dspace.app.rest.model.ItemRest;
-import org.dspace.authorize.AuthorizeException;
-import org.dspace.content.Item;
-import org.dspace.content.service.ItemService;
-import org.dspace.core.Context;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-/**
- * Checks if the given user can request the claim of an item. Whether or not the
- * user can then make the claim is determined by the feature
- * {@link CanClaimItemFeature}.
- *
- * @author Corrado Lombardi (corrado.lombardi at 4science.it)
- * @author Luca Giamminonni (luca.giamminonni at 4science.it)
- */
-@Component
-@AuthorizationFeatureDocumentation(name = ShowClaimItemFeature.NAME,
- description = "Used to verify if the given user can request the claim of an item")
-public class ShowClaimItemFeature implements AuthorizationFeature {
-
- public static final String NAME = "showClaimItem";
- private static final Logger LOG = LoggerFactory.getLogger(ShowClaimItemFeature.class);
-
- private final ItemService itemService;
- private final ResearcherProfileService researcherProfileService;
-
- @Autowired
- public ShowClaimItemFeature(ItemService itemService, ResearcherProfileService researcherProfileService) {
- this.itemService = itemService;
- this.researcherProfileService = researcherProfileService;
- }
-
- @Override
- @SuppressWarnings("rawtypes")
- public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
-
- if (!(object instanceof ItemRest) || Objects.isNull(context.getCurrentUser())) {
- return false;
- }
-
- String id = ((ItemRest) object).getId();
- Item item = itemService.find(context, UUID.fromString(id));
-
- return researcherProfileService.hasProfileType(item) && hasNotAlreadyAProfile(context);
- }
-
- private boolean hasNotAlreadyAProfile(Context context) {
- try {
- return researcherProfileService.findById(context, context.getCurrentUser().getID()) == null;
- } catch (SQLException | AuthorizeException e) {
- LOG.warn("Error while checking if eperson has a ResearcherProfileAssociated: {}", e.getMessage(), e);
- return false;
- }
- }
-
- @Override
- public String[] getSupportedTypes() {
- return new String[] {ItemRest.CATEGORY + "." + ItemRest.NAME};
- }
-}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionFormConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionFormConverter.java
index 4555d8b00a..4febcd5594 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionFormConverter.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionFormConverter.java
@@ -155,6 +155,7 @@ public class SubmissionFormConverter implements DSpaceConverter languageCodes;
+ /**
+ * The list of type bind value
+ */
+ private List typeBind;
+
/**
* Getter for {@link #selectableMetadata}
*
@@ -266,6 +271,14 @@ public class SubmissionFormFieldRest {
}
}
+ public List getTypeBind() {
+ return typeBind;
+ }
+
+ public void setTypeBind(List typeBind) {
+ this.typeBind = typeBind;
+ }
+
public SelectableRelationship getSelectableRelationship() {
return selectableRelationship;
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/AccessStatusResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/AccessStatusResource.java
new file mode 100644
index 0000000000..c5cd2a5aee
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/AccessStatusResource.java
@@ -0,0 +1,31 @@
+/**
+ * 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.rest.model.hateoas;
+
+import com.fasterxml.jackson.annotation.JsonUnwrapped;
+import org.dspace.app.rest.model.AccessStatusRest;
+import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource;
+
+/**
+ * Access Status Rest HAL Resource. The HAL Resource wraps the REST Resource
+ * adding support for the links and embedded resources
+ */
+@RelNameDSpaceResource(AccessStatusRest.NAME)
+public class AccessStatusResource extends HALResource {
+
+ @JsonUnwrapped
+ private AccessStatusRest data;
+
+ public AccessStatusResource(AccessStatusRest entry) {
+ super(entry);
+ }
+
+ public AccessStatusRest getData() {
+ return data;
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemAccessStatusLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemAccessStatusLinkRepository.java
new file mode 100644
index 0000000000..b2660f51e0
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemAccessStatusLinkRepository.java
@@ -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.rest.repository;
+
+import java.sql.SQLException;
+import java.util.UUID;
+import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
+
+import org.dspace.access.status.service.AccessStatusService;
+import org.dspace.app.rest.model.AccessStatusRest;
+import org.dspace.app.rest.model.ItemRest;
+import org.dspace.app.rest.projection.Projection;
+import org.dspace.content.Item;
+import org.dspace.content.service.ItemService;
+import org.dspace.core.Context;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.rest.webmvc.ResourceNotFoundException;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+/**
+ * Link repository for calculating the access status of an Item
+ */
+@Component(ItemRest.CATEGORY + "." + ItemRest.NAME + "." + ItemRest.ACCESS_STATUS)
+public class ItemAccessStatusLinkRepository extends AbstractDSpaceRestRepository
+ implements LinkRestRepository {
+
+ @Autowired
+ ItemService itemService;
+
+ @Autowired
+ AccessStatusService accessStatusService;
+
+ @PreAuthorize("hasPermission(#itemId, 'ITEM', 'READ')")
+ public AccessStatusRest getAccessStatus(@Nullable HttpServletRequest request,
+ UUID itemId,
+ @Nullable Pageable optionalPageable,
+ Projection projection) {
+ try {
+ Context context = obtainContext();
+ Item item = itemService.find(context, itemId);
+ if (item == null) {
+ throw new ResourceNotFoundException("No such item: " + itemId);
+ }
+ AccessStatusRest accessStatusRest = new AccessStatusRest();
+ String accessStatus = accessStatusService.getAccessStatus(context, item);
+ accessStatusRest.setStatus(accessStatus);
+ return accessStatusRest;
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataFieldRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataFieldRestRepository.java
index 177d149fc7..157a80e264 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataFieldRestRepository.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataFieldRestRepository.java
@@ -18,9 +18,9 @@ import java.util.List;
import java.util.Objects;
import javax.servlet.http.HttpServletRequest;
+import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.gson.Gson;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.dspace.app.rest.Parameter;
@@ -296,9 +296,14 @@ public class MetadataFieldRestRepository extends DSpaceRestRepository documentType = itemService.getMetadataByMetadataString(obj.getItem(),
+ configurationService.getProperty("submit.type-bind.field", "dc.type"));
+ if (documentType.size() > 0) {
+ documentTypeValue = documentType.get(0).getValue();
+ }
for (DCInput[] row : inputConfig.getFields()) {
for (DCInput input : row) {
+ // Is this input allowed for the document type, as per type bind config? If there is no type
+ // bind set, this is always true
+ boolean allowed = input.isAllowedFor(documentTypeValue);
List fieldsName = new ArrayList();
if (input.isQualdropValue()) {
@@ -91,20 +106,30 @@ public class DescribeStep extends AbstractProcessingStep {
String[] metadataToCheck = Utils.tokenize(md.getMetadataField().toString());
if (data.getMetadata().containsKey(
Utils.standardize(metadataToCheck[0], metadataToCheck[1], metadataToCheck[2], "."))) {
- data.getMetadata()
- .get(Utils.standardize(md.getMetadataField().getMetadataSchema().getName(),
- md.getMetadataField().getElement(),
- md.getMetadataField().getQualifier(),
- "."))
- .add(dto);
+ // If field is allowed by type bind, add value to existing field set, otherwise remove
+ // all values for this field
+ if (allowed) {
+ data.getMetadata()
+ .get(Utils.standardize(md.getMetadataField().getMetadataSchema().getName(),
+ md.getMetadataField().getElement(),
+ md.getMetadataField().getQualifier(),
+ "."))
+ .add(dto);
+ } else {
+ data.getMetadata().remove(Utils.standardize(metadataToCheck[0], metadataToCheck[1],
+ metadataToCheck[2], "."));
+ }
} else {
- List listDto = new ArrayList<>();
- listDto.add(dto);
- data.getMetadata()
- .put(Utils.standardize(md.getMetadataField().getMetadataSchema().getName(),
- md.getMetadataField().getElement(),
- md.getMetadataField().getQualifier(),
- "."), listDto);
+ // Add values only if allowed by type bind
+ if (allowed) {
+ List listDto = new ArrayList<>();
+ listDto.add(dto);
+ data.getMetadata()
+ .put(Utils.standardize(md.getMetadataField().getMetadataSchema().getName(),
+ md.getMetadataField().getElement(),
+ md.getMetadataField().getQualifier(),
+ "."), listDto);
+ }
}
}
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/MetadataValidation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/MetadataValidation.java
index e5ba916e0f..813924ffb3 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/MetadataValidation.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/MetadataValidation.java
@@ -16,6 +16,7 @@ import org.apache.logging.log4j.Logger;
import org.dspace.app.rest.model.ErrorRest;
import org.dspace.app.rest.repository.WorkspaceItemRestRepository;
import org.dspace.app.rest.submit.SubmissionService;
+import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.util.DCInput;
import org.dspace.app.util.DCInputSet;
import org.dspace.app.util.DCInputsReader;
@@ -25,6 +26,7 @@ import org.dspace.content.InProgressSubmission;
import org.dspace.content.MetadataValue;
import org.dspace.content.authority.service.MetadataAuthorityService;
import org.dspace.content.service.ItemService;
+import org.dspace.services.ConfigurationService;
/**
* Execute three validation check on fields validation:
@@ -50,12 +52,20 @@ public class MetadataValidation extends AbstractValidation {
private MetadataAuthorityService metadataAuthorityService;
+ private ConfigurationService configurationService;
+
@Override
public List validate(SubmissionService submissionService, InProgressSubmission obj,
SubmissionStepConfig config) throws DCInputsReaderException, SQLException {
List errors = new ArrayList<>();
+ String documentTypeValue = "";
DCInputSet inputConfig = getInputReader().getInputsByFormName(config.getId());
+ List documentType = itemService.getMetadataByMetadataString(obj.getItem(),
+ configurationService.getProperty("submit.type-bind.field", "dc.type"));
+ if (documentType.size() > 0) {
+ documentTypeValue = documentType.get(0).getValue();
+ }
for (DCInput[] row : inputConfig.getFields()) {
for (DCInput input : row) {
String fieldKey =
@@ -71,12 +81,21 @@ public class MetadataValidation extends AbstractValidation {
for (int i = 1; i < inputPairs.size(); i += 2) {
String fullFieldname = input.getFieldName() + "." + (String) inputPairs.get(i);
List mdv = itemService.getMetadataByMetadataString(obj.getItem(), fullFieldname);
- validateMetadataValues(mdv, input, config, isAuthorityControlled, fieldKey, errors);
- if (mdv.size() > 0 && input.isVisible(DCInput.SUBMISSION_SCOPE)) {
- foundResult = true;
+ // If the input is not allowed for this type, strip it from item metadata.
+ if (!input.isAllowedFor(documentTypeValue)) {
+ itemService.removeMetadataValues(ContextUtil.obtainCurrentRequestContext(),
+ obj.getItem(), mdv);
+ } else {
+ validateMetadataValues(mdv, input, config, isAuthorityControlled, fieldKey, errors);
+ if (mdv.size() > 0 && input.isVisible(DCInput.SUBMISSION_SCOPE)) {
+ foundResult = true;
+ }
}
}
- if (input.isRequired() && ! foundResult) {
+ // If the input is required but not allowed for this type, and we removed, don't throw
+ // an error - this way, a field can be required for "Book" to which it is bound, but not
+ // other types. A user may have switched between types before a final deposit
+ if (input.isRequired() && !foundResult && input.isAllowedFor(documentTypeValue)) {
// for this required qualdrop no value was found, add to the list of error fields
addError(errors, ERROR_VALIDATION_REQUIRED,
"/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/" + config.getId() + "/" +
@@ -89,6 +108,12 @@ public class MetadataValidation extends AbstractValidation {
for (String fieldName : fieldsName) {
List mdv = itemService.getMetadataByMetadataString(obj.getItem(), fieldName);
+ if (!input.isAllowedFor(documentTypeValue)) {
+ itemService.removeMetadataValues(ContextUtil.obtainCurrentRequestContext(), obj.getItem(), mdv);
+ // Continue here, this skips the required check since we've just removed values that previously
+ // appeared, and the configuration already indicates this field shouldn't be included
+ continue;
+ }
validateMetadataValues(mdv, input, config, isAuthorityControlled, fieldKey, errors);
if ((input.isRequired() && mdv.size() == 0) && input.isVisible(DCInput.SUBMISSION_SCOPE)) {
// since this field is missing add to list of error
@@ -124,6 +149,10 @@ public class MetadataValidation extends AbstractValidation {
}
}
+ public void setConfigurationService(ConfigurationService configurationService) {
+ this.configurationService = configurationService;
+ }
+
public void setItemService(ItemService itemService) {
this.itemService = itemService;
}
diff --git a/dspace-server-webapp/src/main/resources/spring/spring-dspace-addon-validation-services.xml b/dspace-server-webapp/src/main/resources/spring/spring-dspace-addon-validation-services.xml
index 0dc968674a..f39d553c96 100644
--- a/dspace-server-webapp/src/main/resources/spring/spring-dspace-addon-validation-services.xml
+++ b/dspace-server-webapp/src/main/resources/spring/spring-dspace-addon-validation-services.xml
@@ -16,6 +16,7 @@
+
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java
index fcb814d82d..f07c816b9c 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java
@@ -10,7 +10,7 @@ package org.dspace.app.rest;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
@@ -118,7 +118,11 @@ public class CCLicenseAddPatchOperationIT extends AbstractControllerIntegrationT
.content(patchBody)
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
.andExpect(status().isOk())
- .andExpect(jsonPath("$.sections", not(hasJsonPath("cclicense"))));
+ .andExpect(jsonPath("$.sections.cclicense", allOf(
+ hasJsonPath("$.uri", nullValue()),
+ hasJsonPath("$.rights",nullValue()),
+ hasJsonPath("$.file", nullValue())
+ )));
}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java
index c003cf2809..8e01678899 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java
@@ -10,7 +10,7 @@ package org.dspace.app.rest;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -95,7 +95,11 @@ public class CCLicenseRemovePatchOperationIT extends AbstractControllerIntegrati
.content(removePatch)
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
.andExpect(status().isOk())
- .andExpect(jsonPath("$.sections", not(hasJsonPath("cclicense"))));
+ .andExpect(jsonPath("$.sections.cclicense", allOf(
+ hasJsonPath("$.uri", nullValue()),
+ hasJsonPath("$.rights",nullValue()),
+ hasJsonPath("$.file", nullValue())
+ )));
}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonAuthorityIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonAuthorityIT.java
new file mode 100644
index 0000000000..4626144fd9
--- /dev/null
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonAuthorityIT.java
@@ -0,0 +1,119 @@
+/**
+ * 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.rest;
+
+import static org.dspace.app.rest.matcher.VocabularyMatcher.matchVocabularyEntry;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import java.sql.SQLException;
+
+import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
+import org.dspace.builder.EPersonBuilder;
+import org.hamcrest.Matchers;
+import org.junit.Test;
+
+/**
+ * Integration tests for {@link EPersonAuthority}.
+ *
+ * @author Luca Giamminonni (luca.giamminonni at 4science.it)
+ *
+ */
+public class EPersonAuthorityIT extends AbstractControllerIntegrationTest {
+
+
+ @Test
+ public void testEPersonAuthorityWithFirstName() throws Exception {
+
+ context.turnOffAuthorisationSystem();
+ String firstEPersonId = createEPerson("Luca", "Giamminonni");
+ String secondEPersonId = createEPerson("Andrea", "Bollini");
+ String thirdEPersonId = createEPerson("Luca", "Bollini");
+ context.restoreAuthSystemState();
+
+ String token = getAuthToken(eperson.getEmail(), password);
+ getClient(token).perform(get("/api/submission/vocabularies/EPersonAuthority/entries")
+ .param("filter", "Luca"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.entries", containsInAnyOrder(
+ matchVocabularyEntry("Luca Giamminonni", "Luca Giamminonni", "vocabularyEntry", firstEPersonId),
+ matchVocabularyEntry("Luca Bollini", "Luca Bollini", "vocabularyEntry", thirdEPersonId))))
+ .andExpect(jsonPath("$.page.totalElements", Matchers.is(2)));
+
+ getClient(token).perform(get("/api/submission/vocabularies/EPersonAuthority/entries")
+ .param("filter", "Andrea"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.entries", containsInAnyOrder(
+ matchVocabularyEntry("Andrea Bollini", "Andrea Bollini", "vocabularyEntry", secondEPersonId))))
+ .andExpect(jsonPath("$.page.totalElements", Matchers.is(1)));
+
+ }
+
+ @Test
+ public void testEPersonAuthorityWithLastName() throws Exception {
+
+ context.turnOffAuthorisationSystem();
+ String firstEPersonId = createEPerson("Luca", "Giamminonni");
+ String secondEPersonId = createEPerson("Andrea", "Bollini");
+ String thirdEPersonId = createEPerson("Luca", "Bollini");
+ context.restoreAuthSystemState();
+
+ String token = getAuthToken(eperson.getEmail(), password);
+ getClient(token).perform(get("/api/submission/vocabularies/EPersonAuthority/entries")
+ .param("filter", "Giamminonni"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.entries", containsInAnyOrder(
+ matchVocabularyEntry("Luca Giamminonni", "Luca Giamminonni", "vocabularyEntry", firstEPersonId))))
+ .andExpect(jsonPath("$.page.totalElements", Matchers.is(1)));
+
+ getClient(token).perform(get("/api/submission/vocabularies/EPersonAuthority/entries")
+ .param("filter", "Bollini"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.entries", containsInAnyOrder(
+ matchVocabularyEntry("Andrea Bollini", "Andrea Bollini", "vocabularyEntry", secondEPersonId),
+ matchVocabularyEntry("Luca Bollini", "Luca Bollini", "vocabularyEntry", thirdEPersonId))))
+ .andExpect(jsonPath("$.page.totalElements", Matchers.is(2)));
+
+ }
+
+ @Test
+ public void testEPersonAuthorityWithId() throws Exception {
+
+ context.turnOffAuthorisationSystem();
+ String firstEPersonId = createEPerson("Luca", "Giamminonni");
+ String secondEPersonId = createEPerson("Andrea", "Bollini");
+ context.restoreAuthSystemState();
+
+ String token = getAuthToken(eperson.getEmail(), password);
+ getClient(token).perform(get("/api/submission/vocabularies/EPersonAuthority/entries")
+ .param("filter", firstEPersonId))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.entries", containsInAnyOrder(
+ matchVocabularyEntry("Luca Giamminonni", "Luca Giamminonni", "vocabularyEntry", firstEPersonId))))
+ .andExpect(jsonPath("$.page.totalElements", Matchers.is(1)));
+
+ getClient(token).perform(get("/api/submission/vocabularies/EPersonAuthority/entries")
+ .param("filter", secondEPersonId))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.entries", containsInAnyOrder(
+ matchVocabularyEntry("Andrea Bollini", "Andrea Bollini", "vocabularyEntry", secondEPersonId))))
+ .andExpect(jsonPath("$.page.totalElements", Matchers.is(1)));
+
+ }
+
+ private String createEPerson(String firstName, String lastName) throws SQLException {
+ return EPersonBuilder.createEPerson(context)
+ .withNameInMetadata(firstName, lastName)
+ .build()
+ .getID()
+ .toString();
+ }
+
+}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java
index 95ec537727..c1327355b9 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java
@@ -12,6 +12,7 @@ import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadata;
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadataDoesNotExist;
import static org.dspace.core.Constants.WRITE;
+import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.Matchers.emptyOrNullString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
@@ -3861,6 +3862,8 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
.andExpect(jsonPath("$.inArchive", Matchers.is(false)))
.andExpect(jsonPath("$._links.self.href",
Matchers.containsString("/api/core/items/" + item.getID().toString())))
+ .andExpect(jsonPath("$._links.accessStatus.href",
+ Matchers.containsString("/api/core/items/" + item.getID().toString() + "/accessStatus")))
.andExpect(jsonPath("$._links.bundles.href",
Matchers.containsString("/api/core/items/" + item.getID().toString() + "/bundles")))
.andExpect(jsonPath("$._links.mappedCollections.href",
@@ -3893,6 +3896,8 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
.andExpect(jsonPath("$.inArchive", Matchers.is(false)))
.andExpect(jsonPath("$._links.self.href",
Matchers.containsString("/api/core/items/" + item.getID().toString())))
+ .andExpect(jsonPath("$._links.accessStatus.href",
+ Matchers.containsString("/api/core/items/" + item.getID().toString() + "/accessStatus")))
.andExpect(jsonPath("$._links.bundles.href",
Matchers.containsString("/api/core/items/" + item.getID().toString() + "/bundles")))
.andExpect(jsonPath("$._links.mappedCollections.href",
@@ -3926,6 +3931,8 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
Matchers.containsString("/api/core/items/" + item.getID().toString())))
.andExpect(jsonPath("$._links.self.href",
Matchers.containsString("/api/core/items/" + item.getID().toString())))
+ .andExpect(jsonPath("$._links.accessStatus.href",
+ Matchers.containsString("/api/core/items/" + item.getID().toString() + "/accessStatus")))
.andExpect(jsonPath("$._links.bundles.href",
Matchers.containsString("/api/core/items/" + item.getID().toString() + "/bundles")))
.andExpect(jsonPath("$._links.mappedCollections.href",
@@ -4376,4 +4383,35 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
.andExpect(status().isUnauthorized());
}
+ @Test
+ public void findAccessStatusForItemBadRequestTest() throws Exception {
+ getClient().perform(get("/api/core/items/{uuid}/accessStatus", "1"))
+ .andExpect(status().isBadRequest());
+ }
+
+ @Test
+ public void findAccessStatusForItemNotFoundTest() throws Exception {
+ UUID fakeUUID = UUID.randomUUID();
+ getClient().perform(get("/api/core/items/{uuid}/accessStatus", fakeUUID))
+ .andExpect(status().isNotFound());
+ }
+
+ @Test
+ public void findAccessStatusForItemTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Collection owningCollection = CollectionBuilder.createCollection(context, parentCommunity)
+ .withName("Owning Collection")
+ .build();
+ Item item = ItemBuilder.createItem(context, owningCollection)
+ .withTitle("Test item")
+ .build();
+ context.restoreAuthSystemState();
+ getClient().perform(get("/api/core/items/{uuid}/accessStatus", item.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.status", notNullValue()));
+ }
+
}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java
index cdd9c7f2c3..eb1783d1a0 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java
@@ -28,6 +28,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -35,7 +36,6 @@ import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.gson.JsonObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.response.QueryResponse;
@@ -566,13 +566,14 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
.andExpect(jsonPath("$.leftwardValue", is(nullValue())))
.andExpect(jsonPath("$.rightwardValue", is(nullValue())));
- JsonObject contentObj = new JsonObject();
- contentObj.addProperty("leftwardValue", leftwardValue);
+ Map map = new HashMap<>();
+ map.put("leftwardValue", leftwardValue);
+ String json = new ObjectMapper().writeValueAsString(map);
// Add leftwardValue
getClient(token).perform(put("/api/core/relationships/" + idRef)
.contentType("application/json")
- .content(contentObj.toString()))
+ .content(json))
.andExpect(status().isOk());
// Verify leftwardValue is present and rightwardValue not
@@ -624,14 +625,15 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
.andExpect(jsonPath("$.leftwardValue", is(nullValue())))
.andExpect(jsonPath("$.rightwardValue", is(nullValue())));
- JsonObject contentObj = new JsonObject();
- contentObj.addProperty("leftwardValue", leftwardValue);
- contentObj.addProperty("rightwardValue", rightwardValue);
+ Map map = new HashMap<>();
+ map.put("leftwardValue", leftwardValue);
+ map.put("rightwardValue", rightwardValue);
+ String json = new ObjectMapper().writeValueAsString(map);
// Add leftwardValue and rightwardValue
getClient(token).perform(put("/api/core/relationships/" + idRef)
.contentType("application/json")
- .content(contentObj.toString()))
+ .content(json))
.andExpect(status().isOk());
// Verify leftwardValue and rightwardValue are present
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java
index af1414d795..4baad1c686 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java
@@ -275,7 +275,7 @@ public class ResearcherProfileRestRepositoryIT extends AbstractControllerIntegra
@Test
public void testCreateAndReturnWithPublicProfile() throws Exception {
- configurationService.setProperty("researcher-profile.set-new-profile-private", false);
+ configurationService.setProperty("researcher-profile.set-new-profile-visible", true);
String id = user.getID().toString();
String authToken = getAuthToken(user.getEmail(), password);
@@ -338,6 +338,30 @@ public class ResearcherProfileRestRepositoryIT extends AbstractControllerIntegra
.andExpect(jsonPath("$", matchLinks("http://localhost/api/eperson/profiles/" + id, "item", "eperson")));
}
+ @Test
+ public void testCreateAndReturnWithoutCollectionIdSet() throws Exception {
+
+ String id = user.getID().toString();
+
+ configurationService.setProperty("researcher-profile.collection.uuid", null);
+
+ String authToken = getAuthToken(user.getEmail(), password);
+
+ getClient(authToken).perform(post("/api/eperson/profiles/")
+ .contentType(MediaType.APPLICATION_JSON_VALUE))
+ .andExpect(status().isCreated())
+ .andExpect(jsonPath("$.id", is(id)))
+ .andExpect(jsonPath("$.visible", is(false)))
+ .andExpect(jsonPath("$.type", is("profile")))
+ .andExpect(jsonPath("$", matchLinks("http://localhost/api/eperson/profiles/" + id, "item", "eperson")));
+
+ String itemId = getItemIdByProfileId(authToken, id);
+ Item profileItem = itemService.find(context, UUIDUtils.fromString(itemId));
+ assertThat(profileItem, notNullValue());
+ assertThat(profileItem.getOwningCollection(), is(personCollection));
+
+ }
+
/**
* Verify that a standard user can't call the createAndReturn endpoint to store
* a new researcher profile related to another user.
@@ -587,6 +611,11 @@ public class ResearcherProfileRestRepositoryIT extends AbstractControllerIntegra
.andExpect(status().isOk())
.andExpect(jsonPath("$.visible", is(false)));
+ String itemId = getItemIdByProfileId(authToken, id);
+
+ getClient().perform(get("/api/core/items/{id}", itemId))
+ .andExpect(status().isUnauthorized());
+
// change the visibility to true
List operations = asList(new ReplaceOperation("/visible", true));
@@ -600,6 +629,9 @@ public class ResearcherProfileRestRepositoryIT extends AbstractControllerIntegra
.andExpect(status().isOk())
.andExpect(jsonPath("$.visible", is(true)));
+ getClient().perform(get("/api/core/items/{id}", itemId))
+ .andExpect(status().isOk());
+
// change the visibility to false
operations = asList(new ReplaceOperation("/visible", false));
@@ -613,6 +645,9 @@ public class ResearcherProfileRestRepositoryIT extends AbstractControllerIntegra
.andExpect(status().isOk())
.andExpect(jsonPath("$.visible", is(false)));
+ getClient().perform(get("/api/core/items/{id}", itemId))
+ .andExpect(status().isUnauthorized());
+
}
/**
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScriptRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScriptRestRepositoryIT.java
index bf70eb2e0b..06ed76831a 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScriptRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScriptRestRepositoryIT.java
@@ -30,7 +30,7 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
-import com.google.gson.Gson;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.collections4.CollectionUtils;
import org.dspace.app.rest.converter.DSpaceRunnableParameterConverter;
import org.dspace.app.rest.matcher.BitstreamMatcher;
@@ -277,7 +277,7 @@ public class ScriptRestRepositoryIT extends AbstractControllerIntegrationTest {
try {
getClient(token)
.perform(multipart("/api/system/scripts/mock-script/processes")
- .param("properties", new Gson().toJson(list)))
+ .param("properties", new ObjectMapper().writeValueAsString(list)))
.andExpect(status().isAccepted())
.andExpect(jsonPath("$", is(
ProcessMatcher.matchProcess("mock-script",
@@ -321,7 +321,7 @@ public class ScriptRestRepositoryIT extends AbstractControllerIntegrationTest {
try {
getClient(token)
.perform(multipart("/api/system/scripts/mock-script/processes")
- .param("properties", new Gson().toJson(list)))
+ .param("properties", new ObjectMapper().writeValueAsString(list)))
.andExpect(status().isAccepted())
.andExpect(jsonPath("$", is(
ProcessMatcher.matchProcess("mock-script",
@@ -358,7 +358,7 @@ public class ScriptRestRepositoryIT extends AbstractControllerIntegrationTest {
try {
getClient(token)
.perform(multipart("/api/system/scripts/mock-script/processes")
- .param("properties", new Gson().toJson(list)))
+ .param("properties", new ObjectMapper().writeValueAsString(list)))
.andExpect(status().isAccepted())
.andExpect(jsonPath("$", is(
ProcessMatcher.matchProcess("mock-script",
@@ -466,7 +466,7 @@ public class ScriptRestRepositoryIT extends AbstractControllerIntegrationTest {
.perform(multipart("/api/system/scripts/mock-script/processes")
.file(bitstreamFile)
.characterEncoding("UTF-8")
- .param("properties", new Gson().toJson(list)))
+ .param("properties", new ObjectMapper().writeValueAsString(list)))
.andExpect(status().isAccepted())
.andExpect(jsonPath("$", is(
ProcessMatcher.matchProcess("mock-script",
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionFormsControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionFormsControllerIT.java
index 6a83401f93..bcbf957dc6 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionFormsControllerIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionFormsControllerIT.java
@@ -11,6 +11,7 @@ import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@@ -113,20 +114,20 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.startsWith(REST_SERVER_URL + "config/submissionforms/traditionalpageone")))
// check the first two rows
.andExpect(jsonPath("$.rows[0].fields", contains(
- SubmissionFormFieldMatcher.matchFormFieldDefinition("name", "Author",
+ SubmissionFormFieldMatcher.matchFormFieldDefinition("name", "Author", null,
null, true,"Add an author", "dc.contributor.author"))))
.andExpect(jsonPath("$.rows[1].fields", contains(
- SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Title",
+ SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Title", null,
"You must enter a main title for this item.", false,
"Enter the main title of the item.", "dc.title"))))
// check a row with multiple fields
.andExpect(jsonPath("$.rows[3].fields",
contains(
SubmissionFormFieldMatcher.matchFormFieldDefinition("date", "Date of Issue",
- "You must enter at least the year.", false,
+ null, "You must enter at least the year.", false,
"Please give the date", "col-sm-4",
"dc.date.issued"),
- SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Publisher",
+ SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Publisher", null,
null, false,"Enter the name of",
"col-sm-8","dc.publisher"))))
;
@@ -144,18 +145,18 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.andExpect(jsonPath("$._links.self.href", Matchers
.startsWith(REST_SERVER_URL + "config/submissionforms/traditionalpageone")))
.andExpect(jsonPath("$.rows[0].fields", contains(
- SubmissionFormFieldMatcher.matchFormFieldDefinition("name", "Author",
+ SubmissionFormFieldMatcher.matchFormFieldDefinition("name", "Author", null,
null, true,"Add an author", "dc.contributor.author"))))
.andExpect(jsonPath("$.rows[1].fields", contains(
- SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Title",
+ SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Title", null,
"You must enter a main title for this item.", false,
"Enter the main title of the item.", "dc.title"))))
.andExpect(jsonPath("$.rows[3].fields",contains(
- SubmissionFormFieldMatcher.matchFormFieldDefinition("date", "Date of Issue",
+ SubmissionFormFieldMatcher.matchFormFieldDefinition("date", "Date of Issue", null,
"You must enter at least the year.", false,
"Please give the date", "col-sm-4",
"dc.date.issued"),
- SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Publisher",
+ SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Publisher", null,
null, false,"Enter the name of",
"col-sm-8","dc.publisher"))));
}
@@ -220,20 +221,20 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
// dc.subject fields with in separate rows all linked to an authority with different
// presentation modes (suggestion, name-lookup, lookup)
.andExpect(jsonPath("$.rows[0].fields", contains(
- SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Author",
+ SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Author", null,
null, true,
"Author field that can be associated with an authority providing suggestion",
null, "dc.contributor.author", "SolrAuthorAuthority")
)))
.andExpect(jsonPath("$.rows[1].fields", contains(
- SubmissionFormFieldMatcher.matchFormFieldDefinition("lookup-name", "Editor",
+ SubmissionFormFieldMatcher.matchFormFieldDefinition("lookup-name", "Editor", null,
null, false,
"Editor field that can be associated with an authority "
+ "providing the special name lookup",
null, "dc.contributor.editor", "SolrEditorAuthority")
)))
.andExpect(jsonPath("$.rows[2].fields", contains(
- SubmissionFormFieldMatcher.matchFormFieldDefinition("lookup", "Subject",
+ SubmissionFormFieldMatcher.matchFormFieldDefinition("lookup", "Subject", null,
null, true,
"Subject field that can be associated with an authority providing lookup",
null, "dc.subject", "SolrSubjectAuthority")
@@ -266,7 +267,7 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.startsWith(REST_SERVER_URL + "config/submissionforms/traditionalpageone")))
// our test configuration include the dc.type field with a value pair in the 8th row
.andExpect(jsonPath("$.rows[7].fields", contains(
- SubmissionFormFieldMatcher.matchFormFieldDefinition("dropdown", "Type",
+ SubmissionFormFieldMatcher.matchFormFieldDefinition("dropdown", "Type", null,
null, true,
"Select the type(s) of content of the item. To select more than one value in the " +
"list, you may have to hold down the \"CTRL\" or \"Shift\" key.",
@@ -275,6 +276,35 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
;
}
+ @Test
+ public void findFieldWithTypeBindConfig() throws Exception {
+ String token = getAuthToken(admin.getEmail(), password);
+
+ getClient(token).perform(get("/api/config/submissionforms/traditionalpageone"))
+ // The status has to be 200 OK
+ .andExpect(status().isOk())
+ // We expect the content type to be "application/hal+json;charset=UTF-8"
+ .andExpect(content().contentType(contentType))
+ // Check that the JSON root matches the expected "traditionalpageone" input forms
+ .andExpect(jsonPath("$.id", is("traditionalpageone")))
+ .andExpect(jsonPath("$.name", is("traditionalpageone")))
+ .andExpect(jsonPath("$.type", is("submissionform")))
+ .andExpect(jsonPath("$._links.self.href", Matchers
+ .startsWith(REST_SERVER_URL + "config/submissionforms/traditionalpageone")))
+ // check a row with type-bind 'Technical Report'
+ .andExpect(jsonPath("$.rows[5].fields", contains(
+ SubmissionFormFieldMatcher.matchFormFieldDefinition("series", "Series/Report No.",
+ "Technical Report", null, true,
+ "Enter the series and number assigned to this item by your community.",
+ "dc.relation.ispartofseries"))))
+ // check the same row with a NON-matching type-bind 'Article' (expect false)
+ .andExpect(((jsonPath("$.rows[5].fields", not(contains(
+ SubmissionFormFieldMatcher.matchFormFieldDefinition("series", "Series/Report No.",
+ "Article", null, true,
+ "Enter the series and number assigned to this item by your community.",
+ "dc.relation.ispartofseries")))))));
+ }
+
@Test
public void findOpenRelationshipConfig() throws Exception {
String token = getAuthToken(admin.getEmail(), password);
@@ -352,14 +382,15 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.andExpect(jsonPath("$._links.self.href", Matchers
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
- .matchFormFieldDefinition("name", "Autore", "\u00C8" + " richiesto almeno un autore", true,
+ .matchFormFieldDefinition("name", "Autore", null,
+ "\u00C8" + " richiesto almeno un autore", true,
"Aggiungi un autore", "dc.contributor.author"))))
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
- .matchFormFieldDefinition("onebox", "Titolo",
+ .matchFormFieldDefinition("onebox", "Titolo", null,
"\u00C8" + " necessario inserire un titolo principale per questo item", false,
"Inserisci titolo principale di questo item", "dc.title"))))
.andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher
- .matchFormFieldDefinition("dropdown", "Lingua", null, false,
+ .matchFormFieldDefinition("dropdown", "Lingua", null, null, false,
"Selezionare la lingua del contenuto principale dell'item."
+ " Se la lingua non compare nell'elenco, selezionare (Altro)."
+ " Se il contenuto non ha davvero una lingua"
@@ -376,14 +407,14 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.andExpect(jsonPath("$._links.self.href", Matchers
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
- .matchFormFieldDefinition("name", "Автор", "Потрібно ввести хочаб одного автора!",
+ .matchFormFieldDefinition("name", "Автор", null, "Потрібно ввести хочаб одного автора!",
true, "Додати автора", "dc.contributor.author"))))
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
- .matchFormFieldDefinition("onebox", "Заголовок",
+ .matchFormFieldDefinition("onebox", "Заголовок", null,
"Заговолок файла обов'язковий !", false,
"Ввести основний заголовок файла", "dc.title"))))
.andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher
- .matchFormFieldDefinition("dropdown", "Мова", null, false,
+ .matchFormFieldDefinition("dropdown", "Мова", null, null, false,
"Виберiть мову головного змiсту файлу, як що мови немає у списку, вибрати (Iнша)."
+ " Як що вмiст вайлу не є текстовим, наприклад є фотографiєю, тодi вибрати (N/A)",
null, "dc.language.iso", "common_iso_languages"))));
@@ -431,14 +462,15 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.andExpect(jsonPath("$._links.self.href", Matchers
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
- .matchFormFieldDefinition("name", "Autore", "\u00C8" + " richiesto almeno un autore", true,
+ .matchFormFieldDefinition("name", "Autore", null,
+ "\u00C8" + " richiesto almeno un autore", true,
"Aggiungi un autore", "dc.contributor.author"))))
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
- .matchFormFieldDefinition("onebox", "Titolo",
+ .matchFormFieldDefinition("onebox", "Titolo", null,
"\u00C8" + " necessario inserire un titolo principale per questo item", false,
"Inserisci titolo principale di questo item", "dc.title"))))
.andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher
- .matchFormFieldDefinition("dropdown", "Lingua", null, false,
+ .matchFormFieldDefinition("dropdown", "Lingua", null, null, false,
"Selezionare la lingua del contenuto principale dell'item."
+ " Se la lingua non compare nell'elenco, selezionare (Altro)."
+ " Se il contenuto non ha davvero una lingua"
@@ -455,14 +487,14 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.andExpect(jsonPath("$._links.self.href", Matchers
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
- .matchFormFieldDefinition("name", "Автор", "Потрібно ввести хочаб одного автора!",
+ .matchFormFieldDefinition("name", "Автор", null, "Потрібно ввести хочаб одного автора!",
true, "Додати автора", "dc.contributor.author"))))
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
- .matchFormFieldDefinition("onebox", "Заголовок",
+ .matchFormFieldDefinition("onebox", "Заголовок", null,
"Заговолок файла обов'язковий !", false,
"Ввести основний заголовок файла", "dc.title"))))
.andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher
- .matchFormFieldDefinition("dropdown", "Мова", null, false,
+ .matchFormFieldDefinition("dropdown", "Мова", null, null, false,
"Виберiть мову головного змiсту файлу, як що мови немає у списку, вибрати (Iнша)."
+ " Як що вмiст вайлу не є текстовим, наприклад є фотографiєю, тодi вибрати (N/A)",
null, "dc.language.iso", "common_iso_languages"))));
@@ -505,14 +537,15 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.andExpect(jsonPath("$._links.self.href", Matchers
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
- .matchFormFieldDefinition("name", "Autore", "\u00C8" + " richiesto almeno un autore", true,
+ .matchFormFieldDefinition("name", "Autore", null,
+ "\u00C8" + " richiesto almeno un autore", true,
"Aggiungi un autore", "dc.contributor.author"))))
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
- .matchFormFieldDefinition("onebox", "Titolo",
+ .matchFormFieldDefinition("onebox", "Titolo", null,
"\u00C8" + " necessario inserire un titolo principale per questo item", false,
"Inserisci titolo principale di questo item", "dc.title"))))
.andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher
- .matchFormFieldDefinition("dropdown", "Lingua", null, false,
+ .matchFormFieldDefinition("dropdown", "Lingua", null, null, false,
"Selezionare la lingua del contenuto principale dell'item."
+ " Se la lingua non compare nell'elenco, selezionare (Altro)."
+ " Se il contenuto non ha davvero una lingua"
@@ -547,10 +580,10 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.andExpect(jsonPath("$._links.self.href", Matchers
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
- .matchFormFieldDefinition("name", "Autore", "\u00C8 richiesto almeno un autore", true,
+ .matchFormFieldDefinition("name", "Autore", null, "\u00C8 richiesto almeno un autore", true,
"Aggiungi un autore", "dc.contributor.author"))))
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
- .matchFormFieldDefinition("onebox", "Titolo",
+ .matchFormFieldDefinition("onebox", "Titolo", null,
"\u00C8 necessario inserire un titolo principale per questo item", false,
"Inserisci titolo principale di questo item", "dc.title"))));
resetLocalesConfiguration();
@@ -582,10 +615,10 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.andExpect(jsonPath("$._links.self.href", Matchers
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
- .matchFormFieldDefinition("name", "Autore", "\u00C8 richiesto almeno un autore", true,
+ .matchFormFieldDefinition("name", "Autore", null, "\u00C8 richiesto almeno un autore", true,
"Aggiungi un autore", "dc.contributor.author"))))
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
- .matchFormFieldDefinition("onebox", "Titolo",
+ .matchFormFieldDefinition("onebox", "Titolo", null,
"\u00C8 necessario inserire un titolo principale per questo item", false,
"Inserisci titolo principale di questo item", "dc.title"))));
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java
index 2988d3c350..22c2c4a0ee 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java
@@ -1935,6 +1935,141 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
;
}
+ @Test
+ /**
+ * Test the update of metadata for fields configured with type-bind
+ *
+ * @throws Exception
+ */
+ public void patchUpdateMetadataWithBindTest() 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").build();
+ String authToken = getAuthToken(eperson.getEmail(), password);
+
+ WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
+ .withTitle("Workspace Item 1")
+ .withIssueDate("2017-10-17")
+ .withSubject("ExtraEntry")
+ .grantLicense()
+ .build();
+
+ //disable file upload mandatory
+ configurationService.setProperty("webui.submit.upload.required", false);
+
+ context.restoreAuthSystemState();
+
+ // Try to add isPartOfSeries (type bound to technical report) - this should not work and instead we'll get
+ // no JSON path for that field
+ List updateSeries = new ArrayList();
+ List
-
- xml-apis
- xml-apis
-
@@ -127,7 +123,7 @@
xomxom
- 1.2.5
+ 1.3.7commons-io
diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg
index 43f9e226ed..24e1174624 100644
--- a/dspace/config/dspace.cfg
+++ b/dspace/config/dspace.cfg
@@ -806,6 +806,22 @@ plugin.single.org.dspace.embargo.EmbargoSetter = org.dspace.embargo.DefaultEmbar
# implementation of embargo lifter plugin - - replace with local implementation if applicable
plugin.single.org.dspace.embargo.EmbargoLifter = org.dspace.embargo.DefaultEmbargoLifter
+# values for the forever embargo date threshold
+# This threshold date is used in the default access status helper to dermine if an item is
+# restricted or embargoed based on the start date of the primary (or first) file policies.
+# In this case, if the policy start date is inferior to the threshold date, the status will
+# be embargo, else it will be restricted.
+# You might want to change this threshold based on your needs. For example: some databases
+# doesn't accept a date superior to 31 december 9999.
+access.status.embargo.forever.year = 10000
+access.status.embargo.forever.month = 1
+access.status.embargo.forever.day = 1
+
+# implementation of access status helper plugin - replace with local implementation if applicable
+# This default access status helper provides an item status based on the policies of the primary
+# bitstream (or first bitstream in the original bundles if no primary file is specified).
+plugin.single.org.dspace.access.status.AccessStatusHelper = org.dspace.access.status.DefaultAccessStatusHelper
+
#### Checksum Checker Settings ####
# Default dispatcher in case none specified
plugin.single.org.dspace.checker.BitstreamDispatcher=org.dspace.checker.SimpleDispatcher
@@ -924,6 +940,11 @@ metadata.hide.eperson.orcid.refresh-token = true
# Defaults to true; If set to 'false', submitter has option to skip upload
#webui.submit.upload.required = true
+# Which field should be used for type-bind
+# Defaults to 'dc.type'; If changing this value, you must also update the related
+# dspace-angular environment configuration property submission.typeBind.field
+#submit.type-bind.field = dc.type
+
#### Creative Commons settings ######
# The url to the web service API
@@ -1514,6 +1535,11 @@ request.item.type = all
# Should all Request Copy emails go to the helpdesk instead of the item submitter?
request.item.helpdesk.override = false
+#------------------------------------------------------------------#
+#------------------SUBMISSION CONFIGURATION------------------------#
+#------------------------------------------------------------------#
+# Field to use for type binding, default dc.type
+submit.type-bind.field = dc.type
#------------------------------------------------------------------#
#-------------------MODULE CONFIGURATIONS--------------------------#
diff --git a/dspace/config/emails/request_item.author b/dspace/config/emails/request_item.author
index 9166d31d78..ac79270e7f 100644
--- a/dspace/config/emails/request_item.author
+++ b/dspace/config/emails/request_item.author
@@ -1,3 +1,16 @@
+## E-mail sent to a restricted Item's author when a user requests a copy.
+##
+## Parameters: 0 requester's name
+## 1 requester's address
+## 2 name of a single bitstream, or "all"
+## 3 item Handle
+## 4 item title
+## 5 message from requester
+## 6 link back to DSpace for action
+## 7 corresponding author name
+## 8 corresponding author email
+## 9 configuration property "dspace.name"
+## 10 configuration property "mail.helpdesk"
#set($subject = 'Request copy of document')
Dear ${params[7]},
diff --git a/dspace/config/emails/request_item.to_admin b/dspace/config/emails/request_item.to_admin
deleted file mode 100644
index 244fa5647b..0000000000
--- a/dspace/config/emails/request_item.to_admin
+++ /dev/null
@@ -1,13 +0,0 @@
-Subject: Request copy of document
-
-Dear Administrator,
-
-A user of {7}, named {0} and using the email {1}, requested a copy of the file(s) associated with the document: "{4}" ({3}).
-
-This request came along with the following message:
-
-"{5}"
-
-To answer, click {6}.
-
-PLEASE REDIRECT THIS MESSAGE TO THE AUTHOR(S).
diff --git a/dspace/config/emails/suggest b/dspace/config/emails/suggest
deleted file mode 100644
index 2f90b69ce1..0000000000
--- a/dspace/config/emails/suggest
+++ /dev/null
@@ -1,26 +0,0 @@
-## E-mail sent with the information filled out in a suggest form.
-##
-## Parameters: {0} recipient name
-## {1} sender name
-## {2} repository name
-## {3} item title
-## {4} item handle URI
-## {5} item local URL - may be used in lieu of {4} if not using handle server
-## {6} collection name
-## {7} sender message
-## See org.dspace.core.Email for information on the format of this file.
-##
-#set($subject = 'An item of interest from DSpace')
-
-Hello ${params[0]}:
-
-${params[1]} requested we send you this email regarding an item available in ${params[2]}.
-
-Title: ${params[3]}
-Location: ${params[5]}
-In Collection: ${params[6]}
-Personal Message: ${params[7]}
-
-The DSpace digital repository system captures, stores, indexes, preserves, and distributes digital material.
-For more information, visit www.dspace.org
-
diff --git a/dspace/config/modules/authority.cfg b/dspace/config/modules/authority.cfg
index c9362a1f18..b2d626b5bb 100644
--- a/dspace/config/modules/authority.cfg
+++ b/dspace/config/modules/authority.cfg
@@ -81,6 +81,8 @@ authority.minconfidence = ambiguous
plugin.named.org.dspace.content.authority.ChoiceAuthority = \
org.dspace.content.authority.EPersonAuthority = EPersonAuthority
+# Configuration settings required for Researcher Profiles
+# These settings ensure "dspace.object.owner" field are indexed by Authority Control
choices.plugin.dspace.object.owner = EPersonAuthority
choices.presentation.dspace.object.owner = suggest
authority.controlled.dspace.object.owner = true
\ No newline at end of file
diff --git a/dspace/config/modules/researcher-profile.cfg b/dspace/config/modules/researcher-profile.cfg
index 03d542c39c..0610de000d 100644
--- a/dspace/config/modules/researcher-profile.cfg
+++ b/dspace/config/modules/researcher-profile.cfg
@@ -2,14 +2,16 @@
#------------------- PROFILE CONFIGURATIONS --------------------#
#---------------------------------------------------------------#
-#the entity type of the researcher profile
-researcher-profile.entity-type = Person
+# The Entity Type to use for the Researcher Profile. Defaults to "Person" as this is the recommended Entity Type to use
+#researcher-profile.entity-type = Person
-# the uuid of the collection where store the researcher profiles created from scratch
-researcher-profile.collection.uuid =
+# The UUID of the default Collection where newly created Entities will be stored. If unspecified, the first Collection which supports "entity-type" will be used.
+#researcher-profile.collection.uuid =
-# if true when the profile is deleted even the related item is deleted, if false only the link between profile and item is removed
+# Whether or not to delete the Entity (Item) when a Profile is deleted. Default value is "false" which means that when a user deletes their profile,
+# the Entity remains (retaining its data and relationships). When set to "true", the Entity (and its relationships) will be deleted if a user deletes their Profile.
researcher-profile.hard-delete.enabled = false
-#true if the new profiles should be private (without anonymous read policy), false otherwise
-researcher-profile.set-new-profile-private = true
\ No newline at end of file
+# Whether a newly created profile should be visible by default. Default value is "false" which means a newly created profile is not readable to
+# anonymous users. Setting to "true" means a newly created profile is immediately readable to anonymous users.
+researcher-profile.set-new-profile-visible = false
\ No newline at end of file
diff --git a/dspace/config/modules/rest.cfg b/dspace/config/modules/rest.cfg
index 31a9d8b4b0..a60eb3cb98 100644
--- a/dspace/config/modules/rest.cfg
+++ b/dspace/config/modules/rest.cfg
@@ -38,6 +38,7 @@ rest.properties.exposed = orcid.application-client-id
rest.properties.exposed = orcid.authorize-url
rest.properties.exposed = orcid.scope
rest.properties.exposed = orcid.disconnection.allowed-users
+rest.properties.exposed = submit.type-bind.field
#---------------------------------------------------------------#
# These configs are used by the deprecated REST (v4-6) module #
diff --git a/dspace/config/spring/api/core-factory-services.xml b/dspace/config/spring/api/core-factory-services.xml
index 2712ad21d0..6aadb591f2 100644
--- a/dspace/config/spring/api/core-factory-services.xml
+++ b/dspace/config/spring/api/core-factory-services.xml
@@ -27,6 +27,7 @@
+
diff --git a/dspace/config/spring/api/core-services.xml b/dspace/config/spring/api/core-services.xml
index 036ad54363..34f8829337 100644
--- a/dspace/config/spring/api/core-services.xml
+++ b/dspace/config/spring/api/core-services.xml
@@ -90,8 +90,9 @@
-
+
+
diff --git a/dspace/config/spring/api/discovery.xml b/dspace/config/spring/api/discovery.xml
index 923204f062..580a0fb45a 100644
--- a/dspace/config/spring/api/discovery.xml
+++ b/dspace/config/spring/api/discovery.xml
@@ -1438,7 +1438,7 @@
search.resourcetype:Item
- search.entitytype:${researcher-profile.entity-type}
+ search.entitytype:${researcher-profile.entity-type:Person}
diff --git a/pom.xml b/pom.xml
index f3470ccead..fe95a7c685 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,8 +29,9 @@
3.4.02.10.0
-
- 2.12.3
+
+ 2.12.6
+ 2.12.6.11.3.22.3.12.3.1
@@ -1542,16 +1543,13 @@
fontbox${pdfbox-version}
-
- xalan
- xalan
- 2.7.0
-
+
xercesxercesImpl2.12.2
+
xml-apisxml-apis
@@ -1648,12 +1646,6 @@
2.1.210test
-
- com.google.code.gson
- gson
- 2.8.6
- compile
- com.google.apis
@@ -1724,7 +1716,7 @@
com.fasterxml.jackson.corejackson-databind
- ${jackson.version}
+ ${jackson-databind.version}com.google.guava