bundles;
+ try {
+ bundles = bitstream.getBundles();
+ } catch (SQLException ex) {
+ logError("Unexpected error fetching Bundles", ex);
+ bundles = Collections.EMPTY_LIST;
+ }
+ StringBuilder sb = new StringBuilder("ERROR filtering, skipping bitstream:\n");
+ sb.append("\tItem Handle: ").append(itemHandle);
+ for (Bundle bundle : bundles) {
+ sb.append("\tBundle Name: ").append(bundle.getName());
+ }
+ sb.append("\tFile Size: ").append(bitstream.getSizeBytes());
+ sb.append("\tChecksum: ").append(bitstream.getChecksum())
+ .append(" (").append(bitstream.getChecksumAlgorithm()).append(')');
+ sb.append("\tAsset Store: ").append(bitstream.getStoreNumber());
+ sb.append("\tInternal ID: ").append(bitstream.getInternalId());
+ return sb.toString();
+ }
+
private void logInfo(String message) {
if (handler != null) {
handler.logInfo(message);
diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/service/MediaFilterService.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/service/MediaFilterService.java
index 50a6bb3a202..bc92ff52109 100644
--- a/dspace-api/src/main/java/org/dspace/app/mediafilter/service/MediaFilterService.java
+++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/service/MediaFilterService.java
@@ -7,10 +7,12 @@
*/
package org.dspace.app.mediafilter.service;
+import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import org.dspace.app.mediafilter.FormatFilter;
+import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream;
import org.dspace.content.Collection;
import org.dspace.content.Community;
@@ -91,6 +93,22 @@ public void applyFiltersCollection(Context context, Collection collection)
public boolean processBitstream(Context context, Item item, Bitstream source, FormatFilter formatFilter)
throws Exception;
+ /**
+ * update resource polices of derivative bitstreams
+ * related to source bitstream.
+ * set derivative bitstreams to be publicly accessible or
+ * replace derivative bitstreams policies using
+ * the same in the source bitstream.
+ *
+ * @param context context
+ * @param item item containing bitstreams
+ * @param source source bitstream
+ * @throws SQLException If something goes wrong in the database
+ * @throws AuthorizeException if authorization error
+ */
+ public void updatePoliciesOfDerivativeBitstreams(Context context, Item item, Bitstream source)
+ throws SQLException, AuthorizeException;
+
/**
* Return the item that is currently being processed/filtered
* by the MediaFilterManager.
diff --git a/dspace-api/src/main/java/org/dspace/app/requestitem/RequestItemEmailNotifier.java b/dspace-api/src/main/java/org/dspace/app/requestitem/RequestItemEmailNotifier.java
index 384f33decaf..6499c45a783 100644
--- a/dspace-api/src/main/java/org/dspace/app/requestitem/RequestItemEmailNotifier.java
+++ b/dspace-api/src/main/java/org/dspace/app/requestitem/RequestItemEmailNotifier.java
@@ -11,55 +11,59 @@
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
+import javax.annotation.ManagedBean;
+import javax.inject.Inject;
+import javax.inject.Singleton;
import javax.mail.MessagingException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import org.dspace.app.requestitem.factory.RequestItemServiceFactory;
import org.dspace.app.requestitem.service.RequestItemService;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.dspace.content.Item;
-import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.BitstreamService;
import org.dspace.core.Context;
import org.dspace.core.Email;
import org.dspace.core.I18nUtil;
import org.dspace.core.LogHelper;
import org.dspace.eperson.EPerson;
-import org.dspace.handle.factory.HandleServiceFactory;
import org.dspace.handle.service.HandleService;
import org.dspace.services.ConfigurationService;
-import org.dspace.services.factory.DSpaceServicesFactory;
/**
* Send item requests and responses by email.
*
+ * The "strategy" by which approvers are chosen is in an implementation of
+ * {@link RequestItemAuthorExtractor} which is injected by the name
+ * {@code requestItemAuthorExtractor}. See the DI configuration documents.
+ *
* @author Mark H. Wood
*/
+@Singleton
+@ManagedBean
public class RequestItemEmailNotifier {
private static final Logger LOG = LogManager.getLogger();
- private static final BitstreamService bitstreamService
- = ContentServiceFactory.getInstance().getBitstreamService();
+ @Inject
+ protected BitstreamService bitstreamService;
- private static final ConfigurationService configurationService
- = DSpaceServicesFactory.getInstance().getConfigurationService();
+ @Inject
+ protected ConfigurationService configurationService;
- private static final HandleService handleService
- = HandleServiceFactory.getInstance().getHandleService();
+ @Inject
+ protected HandleService handleService;
- private static final RequestItemService requestItemService
- = RequestItemServiceFactory.getInstance().getRequestItemService();
+ @Inject
+ protected RequestItemService requestItemService;
- private static final RequestItemAuthorExtractor requestItemAuthorExtractor
- = DSpaceServicesFactory.getInstance()
- .getServiceManager()
- .getServiceByName("requestItemAuthorExtractor",
- RequestItemAuthorExtractor.class);
+ protected final RequestItemAuthorExtractor requestItemAuthorExtractor;
- private RequestItemEmailNotifier() {}
+ @Inject
+ public RequestItemEmailNotifier(RequestItemAuthorExtractor requestItemAuthorExtractor) {
+ this.requestItemAuthorExtractor = requestItemAuthorExtractor;
+ }
/**
* Send the request to the approver(s).
@@ -70,7 +74,7 @@ private RequestItemEmailNotifier() {}
* @throws IOException passed through.
* @throws SQLException if the message was not sent.
*/
- static public void sendRequest(Context context, RequestItem ri, String responseLink)
+ public void sendRequest(Context context, RequestItem ri, String responseLink)
throws IOException, SQLException {
// Who is making this request?
List authors = requestItemAuthorExtractor
@@ -147,12 +151,38 @@ static public void sendRequest(Context context, RequestItem ri, String responseL
* @param message email body (may be empty).
* @throws IOException if sending failed.
*/
- static public void sendResponse(Context context, RequestItem ri, String subject,
+ public void sendResponse(Context context, RequestItem ri, String subject,
String message)
throws IOException {
+ // Who granted this request?
+ List grantors;
+ try {
+ grantors = requestItemAuthorExtractor.getRequestItemAuthor(context, ri.getItem());
+ } catch (SQLException e) {
+ LOG.warn("Failed to get grantor's name and address: {}", e.getMessage());
+ grantors = List.of();
+ }
+
+ String grantorName;
+ String grantorAddress;
+ if (grantors.isEmpty()) {
+ grantorName = configurationService.getProperty("mail.admin.name");
+ grantorAddress = configurationService.getProperty("mail.admin");
+ } else {
+ RequestItemAuthor grantor = grantors.get(0); // XXX Cannot know which one
+ grantorName = grantor.getFullName();
+ grantorAddress = grantor.getEmail();
+ }
+
// Build an email back to the requester.
- Email email = new Email();
- email.setContent("body", message);
+ Email email = Email.getEmail(I18nUtil.getEmailFilename(context.getCurrentLocale(),
+ ri.isAccept_request() ? "request_item.granted" : "request_item.rejected"));
+ email.addArgument(ri.getReqName()); // {0} requestor's name
+ email.addArgument(handleService.getCanonicalForm(ri.getItem().getHandle())); // {1} URL of the requested Item
+ email.addArgument(ri.getItem().getName()); // {2} title of the requested Item
+ email.addArgument(grantorName); // {3} name of the grantor
+ email.addArgument(grantorAddress); // {4} email of the grantor
+ email.addArgument(message); // {5} grantor's optional message
email.setSubject(subject);
email.addRecipient(ri.getReqEmail());
// Attach bitstreams.
@@ -167,17 +197,25 @@ static public void sendResponse(Context context, RequestItem ri, String subject,
if (!bitstream.getFormat(context).isInternal() &&
requestItemService.isRestricted(context,
bitstream)) {
- email.addAttachment(bitstreamService.retrieve(context,
- bitstream), bitstream.getName(),
+ // #8636 Anyone receiving the email can respond to the
+ // request without authenticating into DSpace
+ context.turnOffAuthorisationSystem();
+ email.addAttachment(
+ bitstreamService.retrieve(context, bitstream),
+ bitstream.getName(),
bitstream.getFormat(context).getMIMEType());
+ context.restoreAuthSystemState();
}
}
}
} else {
Bitstream bitstream = ri.getBitstream();
+ // #8636 Anyone receiving the email can respond to the request without authenticating into DSpace
+ context.turnOffAuthorisationSystem();
email.addAttachment(bitstreamService.retrieve(context, bitstream),
bitstream.getName(),
bitstream.getFormat(context).getMIMEType());
+ context.restoreAuthSystemState();
}
email.send();
} else {
@@ -207,7 +245,7 @@ static public void sendResponse(Context context, RequestItem ri, String subject,
* @throws IOException if the message body cannot be loaded or the message
* cannot be sent.
*/
- static public void requestOpenAccess(Context context, RequestItem ri)
+ public void requestOpenAccess(Context context, RequestItem ri)
throws IOException {
Email message = Email.getEmail(I18nUtil.getEmailFilename(context.getCurrentLocale(),
"request_item.admin"));
diff --git a/dspace-api/src/main/java/org/dspace/app/requestitem/package-info.java b/dspace-api/src/main/java/org/dspace/app/requestitem/package-info.java
index 5886f16fde1..fa7c15b2306 100644
--- a/dspace-api/src/main/java/org/dspace/app/requestitem/package-info.java
+++ b/dspace-api/src/main/java/org/dspace/app/requestitem/package-info.java
@@ -12,10 +12,15 @@
* e-mailed to a responsible party for consideration and action. Find details
* in the user documentation under the rubric "Request a Copy".
*
- * This package includes several "strategy" classes which discover responsible
- * parties in various ways. See {@link RequestItemSubmitterStrategy} and the
- * classes which extend it. A strategy class must be configured and identified
- * as {@link RequestItemAuthorExtractor} for injection into code which requires
- * Request a Copy services.
+ *
Mailing is handled by {@link RequestItemEmailNotifier}. Responsible
+ * parties are represented by {@link RequestItemAuthor}
+ *
+ *
This package includes several "strategy" classes which discover
+ * responsible parties in various ways. See
+ * {@link RequestItemSubmitterStrategy} and the classes which extend it, and
+ * others which implement {@link RequestItemAuthorExtractor}. A strategy class
+ * must be configured and identified as {@link requestItemAuthorExtractor}
+ * (note capitalization) for injection into code which requires Request
+ * a Copy services.
*/
package org.dspace.app.requestitem;
diff --git a/dspace-api/src/main/java/org/dspace/app/sitemap/GenerateSitemaps.java b/dspace-api/src/main/java/org/dspace/app/sitemap/GenerateSitemaps.java
index 6188272aca4..90962d12aa7 100644
--- a/dspace-api/src/main/java/org/dspace/app/sitemap/GenerateSitemaps.java
+++ b/dspace-api/src/main/java/org/dspace/app/sitemap/GenerateSitemaps.java
@@ -7,20 +7,11 @@
*/
package org.dspace.app.sitemap;
-import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.UnsupportedEncodingException;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLEncoder;
import java.sql.SQLException;
import java.util.Date;
-import java.util.Iterator;
import java.util.List;
-import java.util.Optional;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
@@ -30,13 +21,8 @@
import org.apache.commons.cli.ParseException;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
-import org.dspace.app.customurl.CustomUrlService;
-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;
@@ -45,12 +31,12 @@
import org.dspace.core.LogHelper;
import org.dspace.discovery.DiscoverQuery;
import org.dspace.discovery.DiscoverResult;
+import org.dspace.discovery.IndexableObject;
import org.dspace.discovery.SearchService;
import org.dspace.discovery.SearchServiceException;
import org.dspace.discovery.SearchUtils;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
-import org.dspace.utils.DSpace;
/**
* Command-line utility for generating HTML and Sitemaps.org protocol Sitemaps.
@@ -71,8 +57,7 @@ public class GenerateSitemaps {
private static final ConfigurationService configurationService =
DSpaceServicesFactory.getInstance().getConfigurationService();
private static final SearchService searchService = SearchUtils.getSearchService();
-
- private static final CustomUrlService customUrlService = new DSpace().getSingletonService(CustomUrlService.class);
+ private static final int PAGE_SIZE = 100;
/**
* Default constructor
@@ -92,11 +77,6 @@ public static void main(String[] args) throws Exception {
"do not generate sitemaps.org protocol sitemap");
options.addOption("b", "no_htmlmap", false,
"do not generate a basic HTML sitemap");
- options.addOption("a", "ping_all", false,
- "ping configured search engines");
- options
- .addOption("p", "ping", true,
- "ping specified search engine URL");
options
.addOption("d", "delete", false,
"delete sitemaps dir and its contents");
@@ -121,14 +101,13 @@ public static void main(String[] args) throws Exception {
}
/*
- * Sanity check -- if no sitemap generation or pinging to do, or deletion, print usage
+ * Sanity check -- if no sitemap generation or deletion, print usage
*/
if (line.getArgs().length != 0 || line.hasOption('d') || line.hasOption('b')
&& line.hasOption('s') && !line.hasOption('g')
- && !line.hasOption('m') && !line.hasOption('y')
- && !line.hasOption('p')) {
+ && !line.hasOption('m') && !line.hasOption('y')) {
System.err
- .println("Nothing to do (no sitemap to generate, no search engines to ping)");
+ .println("Nothing to do (no sitemap to generate)");
hf.printHelp(usage, options);
System.exit(1);
}
@@ -142,20 +121,6 @@ public static void main(String[] args) throws Exception {
deleteSitemaps();
}
- if (line.hasOption('a')) {
- pingConfiguredSearchEngines();
- }
-
- if (line.hasOption('p')) {
- try {
- pingSearchEngine(line.getOptionValue('p'));
- } catch (MalformedURLException me) {
- System.err
- .println("Bad search engine URL (include all except sitemap URL)");
- System.exit(1);
- }
- }
-
System.exit(0);
}
@@ -194,7 +159,10 @@ public static void deleteSitemaps() throws IOException {
*/
public static void generateSitemaps(boolean makeHTMLMap, boolean makeSitemapOrg) throws SQLException, IOException {
String uiURLStem = configurationService.getProperty("dspace.ui.url");
- String sitemapStem = uiURLStem + "/sitemap";
+ if (!uiURLStem.endsWith("/")) {
+ uiURLStem = uiURLStem + '/';
+ }
+ String sitemapStem = uiURLStem + "sitemap";
File outputDir = new File(configurationService.getProperty("sitemap.dir"));
if (!outputDir.exists() && !outputDir.mkdir()) {
@@ -213,186 +181,113 @@ public static void generateSitemaps(boolean makeHTMLMap, boolean makeSitemapOrg)
}
Context c = new Context(Context.Mode.READ_ONLY);
+ int offset = 0;
+ long commsCount = 0;
+ long collsCount = 0;
+ long itemsCount = 0;
- List comms = communityService.findAll(c);
-
- for (Community comm : comms) {
- String url = uiURLStem + "/communities/" + comm.getID();
-
- if (makeHTMLMap) {
- html.addURL(url, null);
- }
- if (makeSitemapOrg) {
- sitemapsOrg.addURL(url, null);
- }
-
- c.uncacheEntity(comm);
- }
-
- List colls = collectionService.findAll(c);
-
- for (Collection coll : colls) {
- String url = uiURLStem + "/collections/" + coll.getID();
-
- if (makeHTMLMap) {
- html.addURL(url, null);
- }
- if (makeSitemapOrg) {
- sitemapsOrg.addURL(url, null);
- }
-
- c.uncacheEntity(coll);
- }
-
- Iterator- allItems = itemService.findAll(c);
- int itemCount = 0;
-
- while (allItems.hasNext()) {
- Item i = allItems.next();
-
- Optional customUrl = customUrlService.getCustomUrl(i);
- if (customUrl.isPresent()) {
-
- String url = uiURLStem + "/entities/" + StringUtils.lowerCase(itemService.getEntityTypeLabel(i))
- + "/" + customUrl.get();
-
- if (makeHTMLMap) {
- html.addURL(url, null);
- }
- if (makeSitemapOrg) {
- sitemapsOrg.addURL(url, null);
- }
-
- }
-
- DiscoverQuery entityQuery = new DiscoverQuery();
- entityQuery.setQuery("search.uniqueid:\"Item-" + i.getID() + "\" and entityType:*");
- entityQuery.addSearchField("entityType");
-
- try {
- DiscoverResult discoverResult = searchService.search(c, entityQuery);
-
- String url;
- if (CollectionUtils.isNotEmpty(discoverResult.getIndexableObjects())
- && CollectionUtils.isNotEmpty(discoverResult.getSearchDocument(
- discoverResult.getIndexableObjects().get(0)).get(0).getSearchFieldValues("entityType"))
- && StringUtils.isNotBlank(discoverResult.getSearchDocument(
- discoverResult.getIndexableObjects().get(0)).get(0).getSearchFieldValues("entityType").get(0))
- ) {
- url = uiURLStem + "/entities/" + StringUtils.lowerCase(discoverResult.getSearchDocument(
- discoverResult.getIndexableObjects().get(0))
- .get(0).getSearchFieldValues("entityType").get(0)) + "/" + i.getID();
- } else {
- url = uiURLStem + "/items/" + i.getID();
+ try {
+ DiscoverQuery discoveryQuery = new DiscoverQuery();
+ discoveryQuery.setMaxResults(PAGE_SIZE);
+ discoveryQuery.setQuery("search.resourcetype:Community");
+ do {
+ discoveryQuery.setStart(offset);
+ DiscoverResult discoverResult = searchService.search(c, discoveryQuery);
+ List docs = discoverResult.getIndexableObjects();
+ commsCount = discoverResult.getTotalSearchResults();
+
+ for (IndexableObject doc : docs) {
+ String url = uiURLStem + "communities/" + doc.getID();
+ c.uncacheEntity(doc.getIndexedObject());
+
+ if (makeHTMLMap) {
+ html.addURL(url, null);
+ }
+ if (makeSitemapOrg) {
+ sitemapsOrg.addURL(url, null);
+ }
}
- Date lastMod = i.getLastModified();
-
- if (makeHTMLMap) {
- html.addURL(url, lastMod);
+ offset += PAGE_SIZE;
+ } while (offset < commsCount);
+
+ offset = 0;
+ discoveryQuery = new DiscoverQuery();
+ discoveryQuery.setMaxResults(PAGE_SIZE);
+ discoveryQuery.setQuery("search.resourcetype:Collection");
+ do {
+ discoveryQuery.setStart(offset);
+ DiscoverResult discoverResult = searchService.search(c, discoveryQuery);
+ List docs = discoverResult.getIndexableObjects();
+ collsCount = discoverResult.getTotalSearchResults();
+
+ for (IndexableObject doc : docs) {
+ String url = uiURLStem + "collections/" + doc.getID();
+ c.uncacheEntity(doc.getIndexedObject());
+
+ if (makeHTMLMap) {
+ html.addURL(url, null);
+ }
+ if (makeSitemapOrg) {
+ sitemapsOrg.addURL(url, null);
+ }
}
- if (makeSitemapOrg) {
- sitemapsOrg.addURL(url, lastMod);
+ offset += PAGE_SIZE;
+ } while (offset < collsCount);
+
+ offset = 0;
+ discoveryQuery = new DiscoverQuery();
+ discoveryQuery.setMaxResults(PAGE_SIZE);
+ discoveryQuery.setQuery("search.resourcetype:Item");
+ discoveryQuery.addSearchField("search.entitytype");
+ do {
+
+ discoveryQuery.setStart(offset);
+ DiscoverResult discoverResult = searchService.search(c, discoveryQuery);
+ List docs = discoverResult.getIndexableObjects();
+ itemsCount = discoverResult.getTotalSearchResults();
+
+ for (IndexableObject doc : docs) {
+ String url;
+ List entityTypeFieldValues = discoverResult.getSearchDocument(doc).get(0)
+ .getSearchFieldValues("search.entitytype");
+ if (CollectionUtils.isNotEmpty(entityTypeFieldValues)) {
+ url = uiURLStem + "entities/" + StringUtils.lowerCase(entityTypeFieldValues.get(0)) + "/"
+ + doc.getID();
+ } else {
+ url = uiURLStem + "items/" + doc.getID();
+ }
+ Date lastMod = doc.getLastModified();
+ c.uncacheEntity(doc.getIndexedObject());
+
+ if (makeHTMLMap) {
+ html.addURL(url, null);
+ }
+ if (makeSitemapOrg) {
+ sitemapsOrg.addURL(url, null);
+ }
}
- } catch (SearchServiceException e) {
- log.error("Failed getting entitytype through solr for item " + i.getID() + ": " + e.getMessage());
- }
-
- c.uncacheEntity(i);
-
- itemCount++;
- }
-
- if (makeHTMLMap) {
- int files = html.finish();
- log.info(LogHelper.getHeader(c, "write_sitemap",
- "type=html,num_files=" + files + ",communities="
- + comms.size() + ",collections=" + colls.size()
- + ",items=" + itemCount));
- }
+ offset += PAGE_SIZE;
+ } while (offset < itemsCount);
- if (makeSitemapOrg) {
- int files = sitemapsOrg.finish();
- log.info(LogHelper.getHeader(c, "write_sitemap",
- "type=html,num_files=" + files + ",communities="
- + comms.size() + ",collections=" + colls.size()
- + ",items=" + itemCount));
- }
-
- c.abort();
- }
-
- /**
- * Ping all search engines configured in {@code dspace.cfg}.
- *
- * @throws UnsupportedEncodingException theoretically should never happen
- */
- public static void pingConfiguredSearchEngines()
- throws UnsupportedEncodingException {
- String[] engineURLs = configurationService
- .getArrayProperty("sitemap.engineurls");
-
- if (ArrayUtils.isEmpty(engineURLs)) {
- log.warn("No search engine URLs configured to ping");
- return;
- }
-
- for (int i = 0; i < engineURLs.length; i++) {
- try {
- pingSearchEngine(engineURLs[i]);
- } catch (MalformedURLException me) {
- log.warn("Bad search engine URL in configuration: "
- + engineURLs[i]);
- }
- }
- }
-
- /**
- * Ping the given search engine.
- *
- * @param engineURL Search engine URL minus protocol etc, e.g.
- * {@code www.google.com}
- * @throws MalformedURLException if the passed in URL is malformed
- * @throws UnsupportedEncodingException theoretically should never happen
- */
- public static void pingSearchEngine(String engineURL)
- throws MalformedURLException, UnsupportedEncodingException {
- // Set up HTTP proxy
- if ((StringUtils.isNotBlank(configurationService.getProperty("http.proxy.host")))
- && (StringUtils.isNotBlank(configurationService.getProperty("http.proxy.port")))) {
- System.setProperty("proxySet", "true");
- System.setProperty("proxyHost", configurationService
- .getProperty("http.proxy.host"));
- System.getProperty("proxyPort", configurationService
- .getProperty("http.proxy.port"));
- }
-
- String sitemapURL = configurationService.getProperty("dspace.ui.url")
- + "/sitemap";
-
- URL url = new URL(engineURL + URLEncoder.encode(sitemapURL, "UTF-8"));
-
- try {
- HttpURLConnection connection = (HttpURLConnection) url
- .openConnection();
-
- BufferedReader in = new BufferedReader(new InputStreamReader(
- connection.getInputStream()));
-
- String inputLine;
- StringBuffer resp = new StringBuffer();
- while ((inputLine = in.readLine()) != null) {
- resp.append(inputLine).append("\n");
+ if (makeHTMLMap) {
+ int files = html.finish();
+ log.info(LogHelper.getHeader(c, "write_sitemap",
+ "type=html,num_files=" + files + ",communities="
+ + commsCount + ",collections=" + collsCount
+ + ",items=" + itemsCount));
}
- in.close();
- if (connection.getResponseCode() == 200) {
- log.info("Pinged " + url.toString() + " successfully");
- } else {
- log.warn("Error response pinging " + url.toString() + ":\n"
- + resp);
+ if (makeSitemapOrg) {
+ int files = sitemapsOrg.finish();
+ log.info(LogHelper.getHeader(c, "write_sitemap",
+ "type=html,num_files=" + files + ",communities="
+ + commsCount + ",collections=" + collsCount
+ + ",items=" + itemsCount));
}
- } catch (IOException e) {
- log.warn("Error pinging " + url.toString(), e);
+ } catch (SearchServiceException e) {
+ throw new RuntimeException(e);
+ } finally {
+ c.abort();
}
}
}
diff --git a/dspace-api/src/main/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncCliScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncCliScriptConfiguration.java
index b238ccf061f..067c76cce8b 100644
--- a/dspace-api/src/main/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncCliScriptConfiguration.java
+++ b/dspace-api/src/main/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncCliScriptConfiguration.java
@@ -8,7 +8,6 @@
package org.dspace.app.solrdatabaseresync;
import org.apache.commons.cli.Options;
-import org.dspace.core.Context;
import org.dspace.scripts.configuration.ScriptConfiguration;
/**
@@ -27,11 +26,6 @@ public void setDspaceRunnableClass(Class dspaceRunnableCl
this.dspaceRunnableClass = dspaceRunnableClass;
}
- @Override
- public boolean isAllowedToExecute(Context context) {
- return true;
- }
-
@Override
public Options getOptions() {
if (options == null) {
diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInputSet.java b/dspace-api/src/main/java/org/dspace/app/util/DCInputSet.java
index aae042d0cf0..6feb1e24755 100644
--- a/dspace-api/src/main/java/org/dspace/app/util/DCInputSet.java
+++ b/dspace-api/src/main/java/org/dspace/app/util/DCInputSet.java
@@ -103,9 +103,9 @@ public boolean isDefinedMultTitles() {
* @return true if the current set has all the prev. published fields
*/
public boolean isDefinedPubBefore() {
- return (isFieldPresent("dc.date.issued") &&
+ return isFieldPresent("dc.date.issued") &&
isFieldPresent("dc.identifier.citation") &&
- isFieldPresent("dc.publisher"));
+ isFieldPresent("dc.publisher");
}
/**
@@ -145,6 +145,9 @@ public Optional getField(String fieldName) {
} catch (DCInputsReaderException e) {
log.error(e.getMessage(), e);
}
+ } else if (field.isRelationshipField() &&
+ ("relation." + field.getRelationshipType()).equals(fieldName)) {
+ return Optional.of(field);
} else {
String fullName = field.getFieldName();
if (fullName.equals(fieldName)) {
diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java
index 86b45c36794..936d5d9c62d 100644
--- a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java
+++ b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java
@@ -31,6 +31,7 @@
import org.dspace.content.MetadataSchemaEnum;
import org.dspace.core.Utils;
import org.dspace.services.factory.DSpaceServicesFactory;
+import org.dspace.submit.factory.SubmissionServiceFactory;
import org.dspace.submit.model.UploadConfiguration;
import org.dspace.submit.model.UploadConfigurationService;
import org.dspace.utils.DSpace;
@@ -167,7 +168,8 @@ public List getInputsByCollection(Collection collection)
throws DCInputsReaderException {
SubmissionConfig config;
try {
- config = new SubmissionConfigReader().getSubmissionConfigByCollection(collection);
+ config = SubmissionServiceFactory.getInstance().getSubmissionConfigService()
+ .getSubmissionConfigByCollection(collection);
String formName = config.getSubmissionName();
if (formName == null) {
throw new DCInputsReaderException("No form designated as default");
@@ -238,7 +240,8 @@ public List getInputsBySubmissionName(String name)
throws DCInputsReaderException {
SubmissionConfig config;
try {
- config = new SubmissionConfigReader().getSubmissionConfigByName(name);
+ config = SubmissionServiceFactory.getInstance().getSubmissionConfigService()
+ .getSubmissionConfigByName(name);
String formName = config.getSubmissionName();
if (formName == null) {
throw new DCInputsReaderException("No form designated as default");
diff --git a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java
index 8eb3a067404..57e6a3fafce 100644
--- a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java
+++ b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java
@@ -7,8 +7,6 @@
*/
package org.dspace.app.util;
-import static org.dspace.content.Item.ANY;
-
import java.io.File;
import java.sql.SQLException;
import java.util.ArrayList;
@@ -25,12 +23,17 @@
import org.dspace.content.Collection;
import org.dspace.content.DSpaceObject;
import org.dspace.content.InProgressSubmission;
+import org.dspace.content.Item;
import org.dspace.content.edit.EditItem;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CollectionService;
import org.dspace.core.Context;
+import org.dspace.discovery.SearchServiceException;
import org.dspace.handle.factory.HandleServiceFactory;
+import org.dspace.services.RequestService;
import org.dspace.services.factory.DSpaceServicesFactory;
+import org.dspace.versioning.ItemCorrectionService;
+import org.springframework.beans.factory.annotation.Autowired;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
@@ -58,6 +61,10 @@
*/
public class SubmissionConfigReader {
+
+ @Autowired
+ RequestService requestService;
+
/**
* The ID of the default collection. Will never be the ID of a named
* collection
@@ -111,6 +118,23 @@ public class SubmissionConfigReader {
*/
private SubmissionConfig lastSubmissionConfig = null;
+ /**
+ * Collection Service instance, needed to interact with collection's
+ * stored data
+ */
+ protected static final CollectionService collectionService
+ = ContentServiceFactory.getInstance().getCollectionService();
+
+ /**
+ * itemCorrectionService instance, needed to retrieve the handle correctly
+ * item correction actions
+ *
+ */
+ protected static final ItemCorrectionService itemCorrectionService =
+ DSpaceServicesFactory.getInstance().getServiceManager()
+ .getServicesByType(ItemCorrectionService.class)
+ .get(0);
+
/**
* Load Submission Configuration from the
* item-submission.xml configuration file
@@ -158,6 +182,9 @@ private void buildInputs(String fileName) throws SubmissionConfigReaderException
} catch (FactoryConfigurationError fe) {
throw new SubmissionConfigReaderException(
"Cannot create Item Submission Configuration parser", fe);
+ } catch (SearchServiceException se) {
+ throw new SubmissionConfigReaderException(
+ "Cannot perform a discovery search for Item Submission Configuration", se);
} catch (Exception e) {
throw new SubmissionConfigReaderException(
"Error creating Item Submission Configuration: " + e);
@@ -229,8 +256,10 @@ public SubmissionConfig getSubmissionConfigByCollection(String collectionHandle)
public SubmissionConfig getCorrectionSubmissionConfigByCollection(Collection collection) {
CollectionService collService = ContentServiceFactory.getInstance().getCollectionService();
- String submitName = collService.getMetadataFirstValue(collection,
- "cris", "submission", "definition-correction", ANY);
+ String submitName =
+ collService.getMetadataFirstValue(
+ collection, "cris", "submission", "definition-correction", Item.ANY
+ );
if (submitName != null) {
SubmissionConfig subConfig = getSubmissionConfigByName(submitName);
@@ -377,7 +406,7 @@ public SubmissionStepConfig getStepConfig(String stepID)
* should correspond to the collection-form maps, the form definitions, and
* the display/storage word pairs.
*/
- private void doNodes(Node n) throws SAXException, SubmissionConfigReaderException {
+ private void doNodes(Node n) throws SAXException, SearchServiceException, SubmissionConfigReaderException {
if (n == null) {
return;
}
@@ -418,24 +447,32 @@ private void doNodes(Node n) throws SAXException, SubmissionConfigReaderExceptio
}
}
+
+
+
/**
* Process the submission-map section of the XML file. Each element looks
* like: Extract
* the collection handle and item submission name, put name in hashmap keyed
* by the collection handle.
*/
- private void processMap(Node e) throws SAXException {
+ private void processMap(Node e) throws SAXException, SearchServiceException {
+ // create a context
+ Context context = new Context();
+
NodeList nl = e.getChildNodes();
int len = nl.getLength();
for (int i = 0; i < len; i++) {
Node nd = nl.item(i);
if (nd.getNodeName().equals("name-map")) {
String id = getAttribute(nd, "collection-handle");
+ String entityType = getAttribute(nd, "collection-entity-type");
String value = getAttribute(nd, "submission-name");
String content = getValue(nd);
- if (id == null) {
+ if (id == null && entityType == null) {
throw new SAXException(
- "name-map element is missing collection-handle attribute in 'item-submission.xml'");
+ "name-map element is missing collection-handle or collection-entity-type attribute " +
+ "in 'item-submission.xml'");
}
if (value == null) {
throw new SAXException(
@@ -445,7 +482,17 @@ private void processMap(Node e) throws SAXException {
throw new SAXException(
"name-map element has content in 'item-submission.xml', it should be empty.");
}
- collectionToSubmissionConfig.put(id, value);
+ if (id != null) {
+ collectionToSubmissionConfig.put(id, value);
+
+ } else {
+ // get all collections for this entity-type
+ List collections = collectionService.findAllCollectionsByEntityType( context,
+ entityType);
+ for (Collection collection : collections) {
+ collectionToSubmissionConfig.putIfAbsent(collection.getHandle(), value);
+ }
+ }
} // ignore any child node that isn't a "name-map"
}
}
@@ -731,12 +778,25 @@ public List getCollectionsBySubmissionConfig(Context context, String
return results;
}
- public SubmissionConfig getSubmissionConfigByInProgressSubmission(InProgressSubmission> object) {
+ public SubmissionConfig getSubmissionConfigByInProgressSubmission(InProgressSubmission> object, Context context) {
if (object instanceof EditItem) {
String submissionDefinition = ((EditItem) object).getMode().getSubmissionDefinition();
return getSubmissionConfigByName(submissionDefinition);
}
- return getSubmissionConfigByCollection(object.getCollection());
+ if (isCorrectionItem(object.getItem(), context)) {
+ return getCorrectionSubmissionConfigByCollection(object.getCollection());
+ } else {
+ return getSubmissionConfigByCollection(object.getCollection());
+ }
+ }
+
+ private boolean isCorrectionItem(Item item, Context context) {
+ try {
+ return itemCorrectionService.checkIfIsCorrectionItem(context, item);
+ } catch (Exception ex) {
+ log.error("An error occurs checking if the given item is a correction item.", ex);
+ return false;
+ }
}
-}
+}
\ No newline at end of file
diff --git a/dspace-api/src/main/java/org/dspace/app/util/SyndicationFeed.java b/dspace-api/src/main/java/org/dspace/app/util/SyndicationFeed.java
index 8f155b63307..c1402499c44 100644
--- a/dspace-api/src/main/java/org/dspace/app/util/SyndicationFeed.java
+++ b/dspace-api/src/main/java/org/dspace/app/util/SyndicationFeed.java
@@ -51,6 +51,7 @@
import org.dspace.content.service.CommunityService;
import org.dspace.content.service.ItemService;
import org.dspace.core.Context;
+import org.dspace.core.I18nUtil;
import org.dspace.discovery.IndexableObject;
import org.dspace.discovery.indexobject.IndexableCollection;
import org.dspace.discovery.indexobject.IndexableCommunity;
@@ -91,6 +92,7 @@ public class SyndicationFeed {
// default DC fields for entry
protected String defaultTitleField = "dc.title";
+ protected String defaultDescriptionField = "dc.description";
protected String defaultAuthorField = "dc.contributor.author";
protected String defaultDateField = "dc.date.issued";
private static final String[] defaultDescriptionFields =
@@ -196,15 +198,15 @@ public void populate(HttpServletRequest request, Context context, IndexableObjec
// dso is null for the whole site, or a search without scope
if (dso == null) {
defaultTitle = configurationService.getProperty("dspace.name");
- feed.setDescription(localize(labels, MSG_FEED_DESCRIPTION));
+ defaultDescriptionField = localize(labels, MSG_FEED_DESCRIPTION);
objectURL = resolveURL(request, null);
} else {
Bitstream logo = null;
if (dso instanceof IndexableCollection) {
Collection col = ((IndexableCollection) dso).getIndexedObject();
defaultTitle = col.getName();
- feed.setDescription(collectionService.getMetadataFirstValue(col,
- CollectionService.MD_SHORT_DESCRIPTION, Item.ANY));
+ defaultDescriptionField = collectionService.getMetadataFirstValue(col,
+ CollectionService.MD_SHORT_DESCRIPTION, Item.ANY);
logo = col.getLogo();
String cols = configurationService.getProperty("webui.feed.podcast.collections");
if (cols != null && cols.length() > 1 && cols.contains(col.getHandle())) {
@@ -214,8 +216,8 @@ public void populate(HttpServletRequest request, Context context, IndexableObjec
} else if (dso instanceof IndexableCommunity) {
Community comm = ((IndexableCommunity) dso).getIndexedObject();
defaultTitle = comm.getName();
- feed.setDescription(communityService.getMetadataFirstValue(comm,
- CommunityService.MD_SHORT_DESCRIPTION, Item.ANY));
+ defaultDescriptionField = communityService.getMetadataFirstValue(comm,
+ CommunityService.MD_SHORT_DESCRIPTION, Item.ANY);
logo = comm.getLogo();
String comms = configurationService.getProperty("webui.feed.podcast.communities");
if (comms != null && comms.length() > 1 && comms.contains(comm.getHandle())) {
@@ -230,6 +232,12 @@ public void populate(HttpServletRequest request, Context context, IndexableObjec
}
feed.setTitle(labels.containsKey(MSG_FEED_TITLE) ?
localize(labels, MSG_FEED_TITLE) : defaultTitle);
+
+ if (defaultDescriptionField == null || defaultDescriptionField == "") {
+ defaultDescriptionField = I18nUtil.getMessage("org.dspace.app.util.SyndicationFeed.no-description");
+ }
+
+ feed.setDescription(defaultDescriptionField);
feed.setLink(objectURL);
feed.setPublishedDate(new Date());
feed.setUri(objectURL);
diff --git a/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationMethod.java b/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationMethod.java
index 274779e9287..500ee04a979 100644
--- a/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationMethod.java
+++ b/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationMethod.java
@@ -153,6 +153,22 @@ public boolean allowSetPassword(Context context,
public List getSpecialGroups(Context context, HttpServletRequest request)
throws SQLException;
+ /**
+ * Returns true if the special groups returned by
+ * {@link org.dspace.authenticate.AuthenticationMethod#getSpecialGroups(Context, HttpServletRequest)}
+ * should be implicitly be added to the groups related to the current user. By
+ * default this is true if the authentication method is the actual
+ * authentication mechanism used by the user.
+ * @param context A valid DSpace context.
+ * @param request The request that started this operation, or null if not
+ * applicable.
+ * @return true is the special groups must be considered, false
+ * otherwise
+ */
+ public default boolean areSpecialGroupsApplicable(Context context, HttpServletRequest request) {
+ return getName().equals(context.getAuthenticationMethod());
+ }
+
/**
* Authenticate the given or implicit credentials.
* This is the heart of the authentication method: test the
diff --git a/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationServiceImpl.java b/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationServiceImpl.java
index a9449b87d4e..1d67da37ecb 100644
--- a/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationServiceImpl.java
@@ -179,10 +179,15 @@ public List getSpecialGroups(Context context,
int totalLen = 0;
for (AuthenticationMethod method : getAuthenticationMethodStack()) {
- List gl = method.getSpecialGroups(context, request);
- if (gl.size() > 0) {
- result.addAll(gl);
- totalLen += gl.size();
+
+ if (method.areSpecialGroupsApplicable(context, request)) {
+
+ List gl = method.getSpecialGroups(context, request);
+ if (gl.size() > 0) {
+ result.addAll(gl);
+ totalLen += gl.size();
+ }
+
}
}
diff --git a/dspace-api/src/main/java/org/dspace/authenticate/IPAuthentication.java b/dspace-api/src/main/java/org/dspace/authenticate/IPAuthentication.java
index 9c37fcee475..0c2be211a53 100644
--- a/dspace-api/src/main/java/org/dspace/authenticate/IPAuthentication.java
+++ b/dspace-api/src/main/java/org/dspace/authenticate/IPAuthentication.java
@@ -52,11 +52,6 @@ public class IPAuthentication implements AuthenticationMethod {
*/
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(IPAuthentication.class);
- /**
- * Whether to look for x-forwarded headers for logging IP addresses
- */
- protected static Boolean useProxies;
-
/**
* All the IP matchers
*/
@@ -250,13 +245,18 @@ public List getSpecialGroups(Context context, HttpServletRequest request)
log.debug(LogHelper.getHeader(context, "authenticated",
"special_groups=" + gsb.toString()
- + " (by IP=" + addr + ", useProxies=" + useProxies.toString() + ")"
+ + " (by IP=" + addr + ")"
));
}
return groups;
}
+ @Override
+ public boolean areSpecialGroupsApplicable(Context context, HttpServletRequest request) {
+ return true;
+ }
+
@Override
public int authenticate(Context context, String username, String password,
String realm, HttpServletRequest request) throws SQLException {
diff --git a/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java b/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java
index f3c6022e02c..585eaf9cd8b 100644
--- a/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java
+++ b/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java
@@ -11,9 +11,11 @@
import java.io.IOException;
import java.sql.SQLException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Hashtable;
+import java.util.Iterator;
import java.util.List;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
@@ -64,6 +66,7 @@
* @author Reuben Pasquini
* @author Samuel Ottenhoff
* @author Ivan Masár
+ * @author Michael Plate
*/
public class LDAPAuthentication
implements AuthenticationMethod {
@@ -391,7 +394,7 @@ private static class SpeakerToLDAP {
protected String ldapGivenName = null;
protected String ldapSurname = null;
protected String ldapPhone = null;
- protected String ldapGroup = null;
+ protected ArrayList ldapGroup = null;
/**
* LDAP settings
@@ -406,9 +409,9 @@ private static class SpeakerToLDAP {
final String ldap_surname_field;
final String ldap_phone_field;
final String ldap_group_field;
-
final boolean useTLS;
+
SpeakerToLDAP(Logger thelog) {
ConfigurationService configurationService
= DSpaceServicesFactory.getInstance().getConfigurationService();
@@ -491,6 +494,8 @@ protected String getDNOfUser(String adminUser, String adminPassword, Context con
try {
SearchControls ctrls = new SearchControls();
ctrls.setSearchScope(ldap_search_scope_value);
+ // Fetch both user attributes '*' (eg. uid, cn) and operational attributes '+' (eg. memberOf)
+ ctrls.setReturningAttributes(new String[] {"*", "+"});
String searchName;
if (useTLS) {
@@ -547,7 +552,11 @@ protected String getDNOfUser(String adminUser, String adminPassword, Context con
if (attlist[4] != null) {
att = atts.get(attlist[4]);
if (att != null) {
- ldapGroup = (String) att.get();
+ // loop through all groups returned by LDAP
+ ldapGroup = new ArrayList();
+ for (NamingEnumeration val = att.getAll(); val.hasMoreElements(); ) {
+ ldapGroup.add((String) val.next());
+ }
}
}
@@ -693,15 +702,26 @@ public String getName() {
/*
* Add authenticated users to the group defined in dspace.cfg by
* the authentication-ldap.login.groupmap.* key.
+ *
+ * @param dn
+ * The string containing distinguished name of the user
+ *
+ * @param group
+ * List of strings with LDAP dn of groups
+ *
+ * @param context
+ * DSpace context
*/
- private void assignGroups(String dn, String group, Context context) {
+ private void assignGroups(String dn, ArrayList group, Context context) {
if (StringUtils.isNotBlank(dn)) {
System.out.println("dn:" + dn);
- int i = 1;
- String groupMap = configurationService.getProperty("authentication-ldap.login.groupmap." + i);
-
+ int groupmapIndex = 1;
+ String groupMap = configurationService.getProperty("authentication-ldap.login.groupmap." + groupmapIndex);
boolean cmp;
+
+ // groupmap contains the mapping of LDAP groups to DSpace groups
+ // outer loop with the DSpace groups
while (groupMap != null) {
String t[] = groupMap.split(":");
String ldapSearchString = t[0];
@@ -709,40 +729,73 @@ private void assignGroups(String dn, String group, Context context) {
if (group == null) {
cmp = StringUtils.containsIgnoreCase(dn, ldapSearchString + ",");
+
+ if (cmp) {
+ assignGroup(context, groupmapIndex, dspaceGroupName);
+ }
} else {
- cmp = StringUtils.equalsIgnoreCase(group, ldapSearchString);
- }
+ // list of strings with dn from LDAP groups
+ // inner loop
+ Iterator groupIterator = group.iterator();
+ while (groupIterator.hasNext()) {
- if (cmp) {
- // assign user to this group
- try {
- Group ldapGroup = groupService.findByName(context, dspaceGroupName);
- if (ldapGroup != null) {
- groupService.addMember(context, ldapGroup, context.getCurrentUser());
- groupService.update(context, ldapGroup);
+ // save the current entry from iterator for further use
+ String currentGroup = groupIterator.next();
+
+ // very much the old code from DSpace <= 7.5
+ if (currentGroup == null) {
+ cmp = StringUtils.containsIgnoreCase(dn, ldapSearchString + ",");
} else {
- // The group does not exist
- log.warn(LogHelper.getHeader(context,
- "ldap_assignGroupsBasedOnLdapDn",
- "Group defined in authentication-ldap.login.groupmap." + i
- + " does not exist :: " + dspaceGroupName));
+ cmp = StringUtils.equalsIgnoreCase(currentGroup, ldapSearchString);
+ }
+
+ if (cmp) {
+ assignGroup(context, groupmapIndex, dspaceGroupName);
}
- } catch (AuthorizeException ae) {
- log.debug(LogHelper.getHeader(context,
- "assignGroupsBasedOnLdapDn could not authorize addition to " +
- "group",
- dspaceGroupName));
- } catch (SQLException e) {
- log.debug(LogHelper.getHeader(context, "assignGroupsBasedOnLdapDn could not find group",
- dspaceGroupName));
}
}
- groupMap = configurationService.getProperty("authentication-ldap.login.groupmap." + ++i);
+ groupMap = configurationService.getProperty("authentication-ldap.login.groupmap." + ++groupmapIndex);
}
}
}
+ /**
+ * Add the current authenticated user to the specified group
+ *
+ * @param context
+ * DSpace context
+ *
+ * @param groupmapIndex
+ * authentication-ldap.login.groupmap.* key index defined in dspace.cfg
+ *
+ * @param dspaceGroupName
+ * The DSpace group to add the user to
+ */
+ private void assignGroup(Context context, int groupmapIndex, String dspaceGroupName) {
+ try {
+ Group ldapGroup = groupService.findByName(context, dspaceGroupName);
+ if (ldapGroup != null) {
+ groupService.addMember(context, ldapGroup, context.getCurrentUser());
+ groupService.update(context, ldapGroup);
+ } else {
+ // The group does not exist
+ log.warn(LogHelper.getHeader(context,
+ "ldap_assignGroupsBasedOnLdapDn",
+ "Group defined in authentication-ldap.login.groupmap." + groupmapIndex
+ + " does not exist :: " + dspaceGroupName));
+ }
+ } catch (AuthorizeException ae) {
+ log.debug(LogHelper.getHeader(context,
+ "assignGroupsBasedOnLdapDn could not authorize addition to " +
+ "group",
+ dspaceGroupName));
+ } catch (SQLException e) {
+ log.debug(LogHelper.getHeader(context, "assignGroupsBasedOnLdapDn could not find group",
+ dspaceGroupName));
+ }
+ }
+
@Override
public boolean isUsed(final Context context, final HttpServletRequest request) {
if (request != null &&
diff --git a/dspace-api/src/main/java/org/dspace/authenticate/PasswordAuthentication.java b/dspace-api/src/main/java/org/dspace/authenticate/PasswordAuthentication.java
index 6d1ca862d30..0bf0f9bcbc9 100644
--- a/dspace-api/src/main/java/org/dspace/authenticate/PasswordAuthentication.java
+++ b/dspace-api/src/main/java/org/dspace/authenticate/PasswordAuthentication.java
@@ -11,6 +11,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -23,6 +24,7 @@
import org.dspace.eperson.Group;
import org.dspace.eperson.factory.EPersonServiceFactory;
import org.dspace.eperson.service.EPersonService;
+import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
/**
@@ -52,12 +54,14 @@ public class PasswordAuthentication
*/
private static final Logger log = LogManager.getLogger();
+ private static final ConfigurationService configurationService =
+ DSpaceServicesFactory.getInstance().getConfigurationService();
+
private static final String PASSWORD_AUTHENTICATED = "password.authenticated";
private EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService();
-
/**
* Look to see if this email address is allowed to register.
*
@@ -76,8 +80,7 @@ public boolean canSelfRegister(Context context,
String email)
throws SQLException {
// Is there anything set in domain.valid?
- String[] domains = DSpaceServicesFactory.getInstance().getConfigurationService()
- .getArrayProperty("authentication-password.domain.valid");
+ String[] domains = configurationService.getArrayProperty("authentication-password.domain.valid");
if ((domains == null) || (domains.length == 0)) {
// No conditions set, so must be able to self register
return true;
@@ -146,8 +149,7 @@ public List getSpecialGroups(Context context, HttpServletRequest request)
&& StringUtils.isNotBlank(
EPersonServiceFactory.getInstance().getEPersonService().getPasswordHash(context.getCurrentUser())
.toString())) {
- String groupName = DSpaceServicesFactory.getInstance().getConfigurationService()
- .getProperty("authentication-password.login.specialgroup");
+ String groupName = configurationService.getProperty("authentication-password.login.specialgroup");
if ((groupName != null) && !groupName.trim().isEmpty()) {
Group specialGroup = EPersonServiceFactory.getInstance().getGroupService()
.findByName(context, groupName);
@@ -169,6 +171,7 @@ public List getSpecialGroups(Context context, HttpServletRequest request)
return Collections.EMPTY_LIST;
}
+
/**
* Check credentials: username must match the email address of an
* EPerson record, and that EPerson must be allowed to login.
@@ -275,4 +278,21 @@ public boolean canChangePassword(Context context, EPerson ePerson, String curren
}
return ePersonService.checkPassword(context, ePerson, currentPassword);
}
+
+ @Override
+ public boolean areSpecialGroupsApplicable(Context context, HttpServletRequest request) {
+ return isPasswordAuthenticationMethodInContext(context, request) ||
+ isPasswordAuthenticatedInRequest(context, request);
+ }
+
+ private boolean isPasswordAuthenticatedInRequest(Context context, HttpServletRequest request) {
+ return (context == null || StringUtils.isBlank(context.getAuthenticationMethod())) &&
+ request != null && Optional.ofNullable(request.getAttribute(PASSWORD_AUTHENTICATED))
+ .map(Boolean.class::cast)
+ .orElse(false);
+ }
+
+ private boolean isPasswordAuthenticationMethodInContext(Context context, HttpServletRequest request) {
+ return AuthenticationMethod.super.areSpecialGroupsApplicable(context, request);
+ }
}
diff --git a/dspace-api/src/main/java/org/dspace/authority/CrisConsumer.java b/dspace-api/src/main/java/org/dspace/authority/CrisConsumer.java
index f5d88e1045e..a83cc8692e3 100644
--- a/dspace-api/src/main/java/org/dspace/authority/CrisConsumer.java
+++ b/dspace-api/src/main/java/org/dspace/authority/CrisConsumer.java
@@ -193,7 +193,7 @@ private boolean isMetadataSkippable(MetadataValue metadata) {
return true;
}
- if (isBlank(authority) && isMetadataWithEmptyAuthoritySkippable(metadata)) {
+ if (isBlank(authority) && (isBlank(metadata.getValue()) || isMetadataWithEmptyAuthoritySkippable(metadata))) {
return true;
}
diff --git a/dspace-api/src/main/java/org/dspace/authority/filler/ExternalDataProviderImportFiller.java b/dspace-api/src/main/java/org/dspace/authority/filler/ExternalDataProviderImportFiller.java
index ef218c76fb3..7a7d10e6349 100644
--- a/dspace-api/src/main/java/org/dspace/authority/filler/ExternalDataProviderImportFiller.java
+++ b/dspace-api/src/main/java/org/dspace/authority/filler/ExternalDataProviderImportFiller.java
@@ -7,7 +7,6 @@
*/
package org.dspace.authority.filler;
-import static org.apache.commons.collections.CollectionUtils.isEmpty;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.removeStart;
import static org.apache.commons.lang3.StringUtils.startsWith;
@@ -110,7 +109,11 @@ private void enrichItemWithExternalData(Context context, Item item, ExternalData
}
private boolean notAlreadyPresent(Item item, MetadataValueDTO value) {
- return isEmpty(itemService.getMetadata(item, value.getSchema(), value.getElement(), value.getQualifier(), ANY));
+ List metadataValues = itemService.getMetadata(item, value.getSchema(),
+ value.getElement(), value.getQualifier(), ANY);
+
+ return metadataValues.stream().noneMatch(metadataValue ->
+ metadataValue.getValue().equals(value.getValue()));
}
private boolean isTitleNotSet(Item item) {
diff --git a/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java b/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java
index 014de4671c8..b5b53963eab 100644
--- a/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java
@@ -47,6 +47,7 @@
import org.dspace.discovery.SearchServiceException;
import org.dspace.discovery.indexobject.IndexableCollection;
import org.dspace.discovery.indexobject.IndexableCommunity;
+import org.dspace.discovery.indexobject.IndexableItem;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.dspace.eperson.service.GroupService;
@@ -466,7 +467,7 @@ public boolean isAdmin(Context c, EPerson e) throws SQLException {
if (e == null) {
return false; // anonymous users can't be admins....
} else {
- return groupService.isMember(c, e, Group.ADMIN);
+ return groupService.isMember(c, e, c.getAdminGroup());
}
}
@@ -676,60 +677,6 @@ public ResourcePolicy findByTypeGroupAction(Context c, DSpaceObject dso, Group g
}
}
- /**
- * Generate Policies policies READ for the date in input adding reason. New policies are assigned automatically
- * at the groups that
- * have right on the collection. E.g., if the anonymous can access the collection policies are assigned to
- * anonymous.
- *
- * @param context The relevant DSpace Context.
- * @param embargoDate embargo end date
- * @param reason embargo reason
- * @param dso DSpace object
- * @param owningCollection collection to get group policies from
- * @throws SQLException if database error
- * @throws AuthorizeException if authorization error
- */
- @Override
- public void generateAutomaticPolicies(Context context, Date embargoDate,
- String reason, DSpaceObject dso, Collection owningCollection)
- throws SQLException, AuthorizeException {
-
- if (embargoDate != null || (embargoDate == null && dso instanceof Bitstream)) {
-
- List authorizedGroups = getAuthorizedGroups(context, owningCollection, Constants.DEFAULT_ITEM_READ);
-
- removeAllPoliciesByDSOAndType(context, dso, ResourcePolicy.TYPE_CUSTOM);
-
- // look for anonymous
- boolean isAnonymousInPlace = false;
- for (Group g : authorizedGroups) {
- if (StringUtils.equals(g.getName(), Group.ANONYMOUS)) {
- isAnonymousInPlace = true;
- }
- }
- if (!isAnonymousInPlace) {
- // add policies for all the groups
- for (Group g : authorizedGroups) {
- ResourcePolicy rp = createOrModifyPolicy(null, context, null, g, null, embargoDate, Constants.READ,
- reason, dso);
- if (rp != null) {
- resourcePolicyService.update(context, rp);
- }
- }
-
- } else {
- // add policy just for anonymous
- ResourcePolicy rp = createOrModifyPolicy(null, context, null,
- groupService.findByName(context, Group.ANONYMOUS), null,
- embargoDate, Constants.READ, reason, dso);
- if (rp != null) {
- resourcePolicyService.update(context, rp);
- }
- }
- }
- }
-
@Override
public ResourcePolicy createResourcePolicy(Context context, DSpaceObject dso, Group group, EPerson eperson,
int type, String rpType) throws SQLException, AuthorizeException {
@@ -831,6 +778,19 @@ public boolean isCollectionAdmin(Context context) throws SQLException {
return performCheck(context, "search.resourcetype:" + IndexableCollection.TYPE);
}
+ /**
+ * Checks that the context's current user is an item admin in the site by querying the solr database.
+ *
+ * @param context context with the current user
+ * @return true if the current user is an item admin in the site
+ * false when this is not the case, or an exception occurred
+ * @throws java.sql.SQLException passed through.
+ */
+ @Override
+ public boolean isItemAdmin(Context context) throws SQLException {
+ return performCheck(context, "search.resourcetype:" + IndexableItem.TYPE);
+ }
+
/**
* Checks that the context's current user is a community or collection admin in the site.
*
diff --git a/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicy.java b/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicy.java
index 954bb969903..c781400bae4 100644
--- a/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicy.java
+++ b/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicy.java
@@ -41,9 +41,16 @@
@Entity
@Table(name = "resourcepolicy")
public class ResourcePolicy implements ReloadableEntity {
+ /** This policy was set on submission, to give the submitter access. */
public static String TYPE_SUBMISSION = "TYPE_SUBMISSION";
+
+ /** This policy was set to allow access by a workflow group. */
public static String TYPE_WORKFLOW = "TYPE_WORKFLOW";
+
+ /** This policy was explicitly set on this object. */
public static String TYPE_CUSTOM = "TYPE_CUSTOM";
+
+ /** This policy was copied from the containing object's default policies. */
public static String TYPE_INHERITED = "TYPE_INHERITED";
@Id
@@ -93,7 +100,7 @@ public class ResourcePolicy implements ReloadableEntity {
private String rptype;
@Lob
- @Type(type = "org.dspace.storage.rdbms.hibernate.DatabaseAwareLobType")
+ @Type(type = "org.hibernate.type.TextType")
@Column(name = "rpdescription")
private String rpdescription;
diff --git a/dspace-api/src/main/java/org/dspace/authorize/dao/ResourcePolicyDAO.java b/dspace-api/src/main/java/org/dspace/authorize/dao/ResourcePolicyDAO.java
index d707bf200b4..87bf459bcbe 100644
--- a/dspace-api/src/main/java/org/dspace/authorize/dao/ResourcePolicyDAO.java
+++ b/dspace-api/src/main/java/org/dspace/authorize/dao/ResourcePolicyDAO.java
@@ -40,6 +40,9 @@ public List findByDsoAndType(Context context, DSpaceObject dSpac
public List findByDSoAndAction(Context context, DSpaceObject dso, int actionId) throws SQLException;
+ public void deleteByDsoAndTypeAndAction(Context context, DSpaceObject dSpaceObject, String type, int action)
+ throws SQLException;
+
public List findByDSoAndActionAndType(Context c, DSpaceObject o, int actionId, String type)
throws SQLException;
@@ -64,9 +67,6 @@ public List findByEPersonGroupTypeIdAction(Context context, EPer
public void deleteByDsoAndAction(Context context, DSpaceObject dso, int actionId) throws SQLException;
- public void deleteByDsoAndTypeAndAction(Context context, DSpaceObject dSpaceObject, String type, int action)
- throws SQLException;
-
public void deleteByDsoAndType(Context context, DSpaceObject dSpaceObject, String type) throws SQLException;
public void deleteByGroup(Context context, Group group) throws SQLException;
diff --git a/dspace-api/src/main/java/org/dspace/authorize/dao/impl/ResourcePolicyDAOImpl.java b/dspace-api/src/main/java/org/dspace/authorize/dao/impl/ResourcePolicyDAOImpl.java
index ee79933361d..3c002459ff1 100644
--- a/dspace-api/src/main/java/org/dspace/authorize/dao/impl/ResourcePolicyDAOImpl.java
+++ b/dspace-api/src/main/java/org/dspace/authorize/dao/impl/ResourcePolicyDAOImpl.java
@@ -125,6 +125,19 @@ public List findByDSoAndActionAndType(Context context, DSpaceObj
return list(context, criteriaQuery, false, ResourcePolicy.class, -1, -1);
}
+ @Override
+ public void deleteByDsoAndTypeAndAction(Context context, DSpaceObject dso, String type, int actionId)
+ throws SQLException {
+ String queryString = "delete from ResourcePolicy where dSpaceObject.id = :dsoId "
+ + "AND rptype = :rptype AND actionId= :actionId";
+ Query query = createQuery(context, queryString);
+ query.setParameter("dsoId", dso.getID());
+ query.setParameter("rptype", type);
+ query.setParameter("actionId", actionId);
+ query.executeUpdate();
+
+ }
+
@Override
public List findByTypeGroupAction(Context context, DSpaceObject dso, Group group, int action)
throws SQLException {
@@ -203,19 +216,6 @@ public void deleteByDsoAndType(Context context, DSpaceObject dso, String type) t
query.executeUpdate();
}
- @Override
- public void deleteByDsoAndTypeAndAction(Context context, DSpaceObject dso, String type, int actionId)
- throws SQLException {
- String queryString = "delete from ResourcePolicy where dSpaceObject.id = :dsoId "
- + "AND rptype = :rptype AND actionId= :actionId";
- Query query = createQuery(context, queryString);
- query.setParameter("dsoId", dso.getID());
- query.setParameter("rptype", type);
- query.setParameter("actionId", actionId);
- query.executeUpdate();
-
- }
-
@Override
public void deleteByGroup(Context context, Group group) throws SQLException {
String queryString = "delete from ResourcePolicy where epersonGroup= :epersonGroup";
diff --git a/dspace-api/src/main/java/org/dspace/authorize/package-info.java b/dspace-api/src/main/java/org/dspace/authorize/package-info.java
new file mode 100644
index 00000000000..f36c39cfe35
--- /dev/null
+++ b/dspace-api/src/main/java/org/dspace/authorize/package-info.java
@@ -0,0 +1,67 @@
+/**
+ * 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/
+ */
+
+/**
+ * Represents permissions for access to DSpace content.
+ *
+ * Philosophy
+ * DSpace's authorization system follows the classical "police state"
+ * philosophy of security - the user can do nothing, unless it is
+ * specifically allowed. Those permissions are spelled out with
+ * {@link ResourcePolicy} objects, stored in the {@code resourcepolicy} table
+ * in the database.
+ *
+ * Policies are attached to Content
+ * Resource Policies get assigned to all of the content objects in
+ * DSpace - collections, communities, items, bundles, and bitstreams.
+ * (Currently they are not attached to non-content objects such as
+ * {@code EPerson} or {@code Group}. But they could be, hence the name
+ * {@code ResourcePolicy} instead of {@code ContentPolicy}.)
+ *
+ * Policies are tuples
+ * Authorization is based on evaluating the tuple of (object, action, actor),
+ * such as (ITEM, READ, EPerson John Smith) to check if the {@code EPerson}
+ * "John Smith" can read an item. {@code ResourcePolicy} objects are pretty
+ * simple, describing a single instance of (object, action, actor). If
+ * multiple actors are desired, such as groups 10, 11, and 12 are allowed to
+ * READ Item 13, you simply create a {@code ResourcePolicy} for each group.
+ *
+ * Built-in groups
+ * The install process should create two built-in groups - {@code Anonymous}
+ * for anonymous/public access, and {@code Administrators} for administrators.
+ * Group {@code Anonymous} allows anyone access, even if not authenticated.
+ * Group {@code Administrators}' members have super-user rights,
+ * and are allowed to do any action to any object.
+ *
+ * Policy types
+ * Policies have a "type" used to distinguish policies which are applied for
+ * specific purposes.
+ *
+ * - CUSTOM
+ * - These are created and assigned explicitly by users.
+ * - INHERITED
+ * - These are copied from a containing object's default policies.
+ * - SUBMISSION
+ * - These are applied during submission to give the submitter access while
+ * composing a submission.
+ * - WORKFLOW
+ * - These are automatically applied during workflow, to give curators
+ * access to submissions in their curation queues. They usually have an
+ * automatically-created workflow group as the actor.
+ *
+ * Start and End dates
+ * A policy may have a start date and/or an end date. The policy is
+ * considered not valid before the start date or after the end date. No date
+ * means do not apply the related test. For example, embargo until a given
+ * date can be expressed by a READ policy with a given start date, and a
+ * limited-time offer by a READ policy with a given end date.
+ *
+ * @author dstuve
+ * @author mwood
+ */
+package org.dspace.authorize;
diff --git a/dspace-api/src/main/java/org/dspace/authorize/package.html b/dspace-api/src/main/java/org/dspace/authorize/package.html
deleted file mode 100644
index 66ce0f82477..00000000000
--- a/dspace-api/src/main/java/org/dspace/authorize/package.html
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
-
-
-
-
-
-Handles permissions for DSpace content.
-
-
-Philosophy
-DSpace's authorization system follows the classical "police state"
-philosophy of security - the user can do nothing, unless it is
-specifically allowed. Those permissions are spelled out with
-ResourcePolicy objects, stored in the resourcepolicy table in the
-database.
-
-
-Policies are attached to Content
-Policies are attached to Content
-Resource Policies get assigned to all of the content objects in
-DSpace - collections, communities, items, bundles, and bitstreams.
-(Currently they are not attached to non-content objects such as EPerson
-or Group. But they could be, hence the name ResourcePolicy instead of
-ContentPolicy.)
-
-
-Policies are tuples
-Authorization is based on evaluating the tuple of (object, action, who),
-such as (ITEM, READ, EPerson John Smith) to check if the EPerson "John Smith"
-can read an item. ResourcePolicy objects are pretty simple, describing a single instance of
-(object, action, who). If multiple who's are desired, such as Groups 10, 11, and
-12 are allowed to READ Item 13, you simply create a ResourcePolicy for each
-group.
-
-
-Special Groups
-The install process should create two special groups - group 0, for
-anonymous/public access, and group 1 for administrators.
-Group 0 (public/anonymous) allows anyone access, even if they are not
-authenticated. Group 1's (admin) members have super-user rights, and
-are allowed to do any action to any object.
-
-
-Unused ResourcePolicy attributes
-ResourcePolicies have a few attributes that are currently unused,
-but are included with the intent that they will be used someday.
-One is start and end dates, for when policies will be active, so that
-permissions for content can change over time. The other is the EPerson -
-policies could apply to only a single EPerson, but for ease of
-administration currently a Group is the recommended unit to use to
-describe 'who'.
-
-
-
-
diff --git a/dspace-api/src/main/java/org/dspace/authorize/service/AuthorizeService.java b/dspace-api/src/main/java/org/dspace/authorize/service/AuthorizeService.java
index 43ae51544c9..3db676d88b2 100644
--- a/dspace-api/src/main/java/org/dspace/authorize/service/AuthorizeService.java
+++ b/dspace-api/src/main/java/org/dspace/authorize/service/AuthorizeService.java
@@ -489,24 +489,6 @@ public boolean isAnIdenticalPolicyAlreadyInPlace(Context c, DSpaceObject o, Grou
public ResourcePolicy findByTypeGroupAction(Context c, DSpaceObject dso, Group group, int action)
throws SQLException;
-
- /**
- * Generate Policies policies READ for the date in input adding reason. New policies are assigned automatically
- * at the groups that
- * have right on the collection. E.g., if the anonymous can access the collection policies are assigned to
- * anonymous.
- *
- * @param context current context
- * @param embargoDate date
- * @param reason reason
- * @param dso DSpaceObject
- * @param owningCollection collection
- * @throws SQLException if database error
- * @throws AuthorizeException if authorization error
- */
- public void generateAutomaticPolicies(Context context, Date embargoDate, String reason, DSpaceObject dso,
- Collection owningCollection) throws SQLException, AuthorizeException;
-
public ResourcePolicy createResourcePolicy(Context context, DSpaceObject dso, Group group, EPerson eperson,
int type, String rpType) throws SQLException, AuthorizeException;
@@ -551,6 +533,15 @@ void switchPoliciesAction(Context context, DSpaceObject dso, int fromAction, int
*/
boolean isCollectionAdmin(Context context) throws SQLException;
+ /**
+ * Checks that the context's current user is an item admin in the site by querying the solr database.
+ *
+ * @param context context with the current user
+ * @return true if the current user is an item admin in the site
+ * false when this is not the case, or an exception occurred
+ */
+ boolean isItemAdmin(Context context) throws SQLException;
+
/**
* Checks that the context's current user is a community or collection admin in the site.
*
@@ -646,7 +637,7 @@ long countAdminAuthorizedCollection(Context context, String query)
/**
* Replace all the policies in the target object with exactly the same policies that exist in the source object
- *
+ *
* @param context DSpace Context
* @param source source of policies
* @param dest destination of inherited policies
diff --git a/dspace-api/src/main/java/org/dspace/authorize/service/ResourcePolicyService.java b/dspace-api/src/main/java/org/dspace/authorize/service/ResourcePolicyService.java
index beb3c34662d..662b14b18b2 100644
--- a/dspace-api/src/main/java/org/dspace/authorize/service/ResourcePolicyService.java
+++ b/dspace-api/src/main/java/org/dspace/authorize/service/ResourcePolicyService.java
@@ -56,12 +56,19 @@ public List find(Context c, EPerson e, List groups, int a
throws SQLException;
/**
- * Look for ResourcePolicies by DSpaceObject, Group, and action, ignoring IDs with a specific PolicyID.
- * This method can be used to detect duplicate ResourcePolicies.
+ * Look for ResourcePolicies by DSpaceObject, Group, and action, ignoring
+ * IDs with a specific PolicyID. This method can be used to detect duplicate
+ * ResourcePolicies.
*
- * @param notPolicyID ResourcePolicies with this ID will be ignored while looking out for equal ResourcePolicies.
- * @return List of resource policies for the same DSpaceObject, group and action but other policyID.
- * @throws SQLException
+ * @param context current DSpace session.
+ * @param dso find policies for this object.
+ * @param group find policies referring to this group.
+ * @param action find policies for this action.
+ * @param notPolicyID ResourcePolicies with this ID will be ignored while
+ * looking out for equal ResourcePolicies.
+ * @return List of resource policies for the same DSpaceObject, group and
+ * action but other policyID.
+ * @throws SQLException passed through.
*/
public List findByTypeGroupActionExceptId(Context context, DSpaceObject dso, Group group,
int action, int notPolicyID)
@@ -71,6 +78,16 @@ public List findByTypeGroupActionExceptId(Context context, DSpac
public boolean isDateValid(ResourcePolicy resourcePolicy);
+ /**
+ * Create and persist a copy of a given ResourcePolicy, with an empty
+ * dSpaceObject field.
+ *
+ * @param context current DSpace session.
+ * @param resourcePolicy the policy to be copied.
+ * @return the copy.
+ * @throws SQLException passed through.
+ * @throws AuthorizeException passed through.
+ */
public ResourcePolicy clone(Context context, ResourcePolicy resourcePolicy) throws SQLException, AuthorizeException;
public void removeAllPolicies(Context c, DSpaceObject o) throws SQLException, AuthorizeException;
@@ -123,6 +140,7 @@ public List findExceptRpType(Context c, DSpaceObject o, int acti
* @param ePerson ePerson whose policies want to find
* @param offset the position of the first result to return
* @param limit paging limit
+ * @return some of the policies referring to {@code ePerson}.
* @throws SQLException if database error
*/
public List findByEPerson(Context context, EPerson ePerson, int offset, int limit)
diff --git a/dspace-api/src/main/java/org/dspace/browse/BrowseIndex.java b/dspace-api/src/main/java/org/dspace/browse/BrowseIndex.java
index 14e439d5908..5d5f2ccb755 100644
--- a/dspace-api/src/main/java/org/dspace/browse/BrowseIndex.java
+++ b/dspace-api/src/main/java/org/dspace/browse/BrowseIndex.java
@@ -22,11 +22,13 @@
* This class holds all the information about a specifically configured
* BrowseIndex. It is responsible for parsing the configuration, understanding
* about what sort options are available, and what the names of the database
- * tables that hold all the information are actually called.
+ * tables that hold all the information are actually called. Hierarchical browse
+ * indexes also contain information about the vocabulary they're using, see:
+ * {@link org.dspace.content.authority.DSpaceControlledVocabularyIndex}
*
* @author Richard Jones
*/
-public final class BrowseIndex {
+public class BrowseIndex {
/** the configuration number, as specified in the config */
/**
* used for single metadata browse tables for generating the table name
@@ -99,10 +101,10 @@ private BrowseIndex() {
/**
* Constructor for creating generic / internal index objects
- *
+ *
* @param baseName The base of the table name
*/
- private BrowseIndex(String baseName) {
+ protected BrowseIndex(String baseName) {
this(baseName, "item");
}
@@ -735,7 +737,7 @@ public static BrowseIndex getBrowseIndex(SortOption so) throws BrowseException {
/**
* Get the internally defined browse index for archived items.
- *
+ *
* @param displayType
*
* @return browse index
diff --git a/dspace-api/src/main/java/org/dspace/browse/CrossLinks.java b/dspace-api/src/main/java/org/dspace/browse/CrossLinks.java
index 1ce2e558866..ec4cb199ea1 100644
--- a/dspace-api/src/main/java/org/dspace/browse/CrossLinks.java
+++ b/dspace-api/src/main/java/org/dspace/browse/CrossLinks.java
@@ -108,7 +108,7 @@ public String findLinkType(String metadata) {
} else {
// Exact match, if the key field has no .* wildcard
if (links.containsKey(metadata)) {
- return links.get(key);
+ return links.get(metadata);
}
}
}
diff --git a/dspace-api/src/main/java/org/dspace/browse/ItemCounter.java b/dspace-api/src/main/java/org/dspace/browse/ItemCounter.java
index c9c140fb0b5..20c43fc3729 100644
--- a/dspace-api/src/main/java/org/dspace/browse/ItemCounter.java
+++ b/dspace-api/src/main/java/org/dspace/browse/ItemCounter.java
@@ -18,6 +18,7 @@
import org.dspace.core.Context;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
+import org.dspace.web.ContextUtil;
/**
* This class provides a standard interface to all item counting
@@ -49,9 +50,20 @@ public class ItemCounter {
*/
private Context context;
+ /**
+ * This field is used to hold singular instance of a class.
+ * Singleton pattern is used but this class should be
+ * refactored to modern DSpace approach (injectible service).
+ */
+
+ private static ItemCounter instance;
+
protected ItemService itemService;
protected ConfigurationService configurationService;
+ private boolean showStrengths;
+ private boolean useCache;
+
/**
* Construct a new item counter which will use the given DSpace Context
*
@@ -63,21 +75,42 @@ public ItemCounter(Context context) throws ItemCountException {
this.dao = ItemCountDAOFactory.getInstance(this.context);
this.itemService = ContentServiceFactory.getInstance().getItemService();
this.configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
+ this.showStrengths = configurationService.getBooleanProperty("webui.strengths.show", false);
+ this.useCache = configurationService.getBooleanProperty("webui.strengths.cache", true);
}
/**
- * Get the count of the items in the given container. If the configuration
- * value webui.strengths.cache is equal to 'true' this will return the
- * cached value if it exists. If it is equal to 'false' it will count
- * the number of items in the container in real time.
+ * Get the singular instance of a class.
+ * It creates a new instance at the first usage of this method.
+ *
+ * @return instance af a class
+ * @throws ItemCountException when error occurs
+ */
+ public static ItemCounter getInstance() throws ItemCountException {
+ if (instance == null) {
+ instance = new ItemCounter(ContextUtil.obtainCurrentRequestContext());
+ }
+ return instance;
+ }
+
+ /**
+ * Get the count of the items in the given container. If the configuration
+ * value webui.strengths.show is equal to 'true' this method will return all
+ * archived items. If the configuration value webui.strengths.show is equal to
+ * 'false' this method will return -1.
+ * If the configuration value webui.strengths.cache
+ * is equal to 'true' this will return the cached value if it exists.
+ * If it is equal to 'false' it will count the number of items
+ * in the container in real time.
*
* @param dso DSpaceObject
* @return count
* @throws ItemCountException when error occurs
*/
public int getCount(DSpaceObject dso) throws ItemCountException {
- boolean useCache = configurationService.getBooleanProperty(
- "webui.strengths.cache", true);
+ if (!showStrengths) {
+ return -1;
+ }
if (useCache) {
return dao.getCount(dso);
diff --git a/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java b/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java
index 0194be59f3a..3676133a89f 100644
--- a/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java
+++ b/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java
@@ -264,7 +264,7 @@ private void addLocationScopeFilter(DiscoverQuery query) {
}
private void addDefaultFilterQueries(DiscoverQuery query) {
- DiscoveryConfiguration discoveryConfiguration = SearchUtils.getDiscoveryConfiguration(container);
+ DiscoveryConfiguration discoveryConfiguration = SearchUtils.getDiscoveryConfiguration(context, container);
discoveryConfiguration.getDefaultFilterQueries().forEach(query::addFilterQueries);
}
diff --git a/dspace-api/src/main/java/org/dspace/checker/CheckerCommand.java b/dspace-api/src/main/java/org/dspace/checker/CheckerCommand.java
index 6b16d51bfe1..a12ac3b98a2 100644
--- a/dspace-api/src/main/java/org/dspace/checker/CheckerCommand.java
+++ b/dspace-api/src/main/java/org/dspace/checker/CheckerCommand.java
@@ -245,7 +245,7 @@ protected void processBitstream(MostRecentChecksum info) throws SQLException {
info.setProcessStartDate(new Date());
try {
- Map checksumMap = bitstreamStorageService.computeChecksum(context, info.getBitstream());
+ Map checksumMap = bitstreamStorageService.computeChecksum(context, info.getBitstream());
if (MapUtils.isNotEmpty(checksumMap)) {
info.setBitstreamFound(true);
if (checksumMap.containsKey("checksum")) {
@@ -255,10 +255,16 @@ protected void processBitstream(MostRecentChecksum info) throws SQLException {
if (checksumMap.containsKey("checksum_algorithm")) {
info.setChecksumAlgorithm(checksumMap.get("checksum_algorithm").toString());
}
+
+ // compare new checksum to previous checksum
+ info.setChecksumResult(compareChecksums(info.getExpectedChecksum(), info.getCurrentChecksum()));
+
+ } else {
+ info.setCurrentChecksum("");
+ info.setChecksumResult(getChecksumResultByCode(ChecksumResultCode.BITSTREAM_NOT_FOUND));
+ info.setToBeProcessed(false);
}
- // compare new checksum to previous checksum
- info.setChecksumResult(compareChecksums(info.getExpectedChecksum(), info.getCurrentChecksum()));
} catch (IOException e) {
// bitstream located, but file missing from asset store
info.setChecksumResult(getChecksumResultByCode(ChecksumResultCode.BITSTREAM_NOT_FOUND));
diff --git a/dspace-api/src/main/java/org/dspace/cli/DSpaceSkipUnknownArgumentsParser.java b/dspace-api/src/main/java/org/dspace/cli/DSpaceSkipUnknownArgumentsParser.java
new file mode 100644
index 00000000000..afd74a588d1
--- /dev/null
+++ b/dspace-api/src/main/java/org/dspace/cli/DSpaceSkipUnknownArgumentsParser.java
@@ -0,0 +1,77 @@
+/**
+ * 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.cli;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+/**
+ * Extended version of the DefaultParser. This parser skip/ignore unknown arguments.
+ */
+public class DSpaceSkipUnknownArgumentsParser extends DefaultParser {
+
+
+ @Override
+ public CommandLine parse(Options options, String[] arguments) throws ParseException {
+ return super.parse(options, getOnlyKnownArguments(options, arguments));
+ }
+
+ @Override
+ public CommandLine parse(Options options, String[] arguments, Properties properties) throws ParseException {
+ return super.parse(options, getOnlyKnownArguments(options, arguments), properties);
+ }
+
+ /**
+ * Parse the arguments according to the specified options and properties.
+ * @param options the specified Options
+ * @param arguments the command line arguments
+ * @param stopAtNonOption can be ignored - an unrecognized argument is ignored, an unrecognized argument doesn't
+ * stop the parsing and doesn't trigger a ParseException
+ *
+ * @return the list of atomic option and value tokens
+ * @throws ParseException if there are any problems encountered while parsing the command line tokens.
+ */
+ @Override
+ public CommandLine parse(Options options, String[] arguments, boolean stopAtNonOption) throws ParseException {
+ return super.parse(options, getOnlyKnownArguments(options, arguments), stopAtNonOption);
+ }
+
+ /**
+ * Parse the arguments according to the specified options and properties.
+ * @param options the specified Options
+ * @param arguments the command line arguments
+ * @param properties command line option name-value pairs
+ * @param stopAtNonOption can be ignored - an unrecognized argument is ignored, an unrecognized argument doesn't
+ * stop the parsing and doesn't trigger a ParseException
+ *
+ * @return the list of atomic option and value tokens
+ * @throws ParseException if there are any problems encountered while parsing the command line tokens.
+ */
+ @Override
+ public CommandLine parse(Options options, String[] arguments, Properties properties, boolean stopAtNonOption)
+ throws ParseException {
+ return super.parse(options, getOnlyKnownArguments(options, arguments), properties, stopAtNonOption);
+ }
+
+
+ private String[] getOnlyKnownArguments(Options options, String[] arguments) {
+ List knownArguments = new ArrayList<>();
+ for (String arg : arguments) {
+ if (options.hasOption(arg)) {
+ knownArguments.add(arg);
+ }
+ }
+ return knownArguments.toArray(new String[0]);
+ }
+}
diff --git a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java
index b07f23ee23f..0682082e03f 100644
--- a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java
@@ -289,6 +289,11 @@ public void delete(Context context, Bitstream bitstream) throws SQLException, Au
//Remove our bitstream from all our bundles
final List bundles = bitstream.getBundles();
for (Bundle bundle : bundles) {
+ authorizeService.authorizeAction(context, bundle, Constants.REMOVE);
+ //We also need to remove the bitstream id when it's set as bundle's primary bitstream
+ if (bitstream.equals(bundle.getPrimaryBitstream())) {
+ bundle.unsetPrimaryBitstreamID();
+ }
bundle.removeBitstream(bitstream);
}
@@ -402,6 +407,13 @@ public Bitstream getBitstreamByName(Item item, String bundleName, String bitstre
return null;
}
+ @Override
+ public List getBitstreamByBundleName(Item item, String bundleName) throws SQLException {
+ return itemService.getBundles(item, bundleName).stream()
+ .flatMap(bundle -> bundle.getBitstreams().stream())
+ .collect(Collectors.toList());
+ }
+
@Override
public Bitstream getFirstBitstream(Item item, String bundleName) throws SQLException {
List bundles = itemService.getBundles(item, bundleName);
@@ -416,7 +428,7 @@ public Bitstream getFirstBitstream(Item item, String bundleName) throws SQLExcep
@Override
public Bitstream getThumbnail(Context context, Bitstream bitstream) throws SQLException {
- Pattern pattern = Pattern.compile("^" + bitstream.getName() + ".([^.]+)$");
+ Pattern pattern = getBitstreamNamePattern(bitstream);
for (Bundle bundle : bitstream.getBundles()) {
for (Item item : bundle.getItems()) {
@@ -446,6 +458,13 @@ public Bitstream getThumbnail(Context context, Bitstream bitstream) throws SQLEx
return null;
}
+ protected Pattern getBitstreamNamePattern(Bitstream bitstream) {
+ if (bitstream.getName() != null) {
+ return Pattern.compile("^" + Pattern.quote(bitstream.getName()) + ".([^.]+)$");
+ }
+ return Pattern.compile("^" + bitstream.getName() + ".([^.]+)$");
+ }
+
@Override
public BitstreamFormat getFormat(Context context, Bitstream bitstream) throws SQLException {
if (bitstream.getBitstreamFormat() == null) {
@@ -533,6 +552,10 @@ public List findByItemAndBundleAndMetadata(Context context, Item item
}
+ public boolean exists(Context context, UUID id) throws SQLException {
+ return this.bitstreamDAO.exists(context, Bitstream.class, id);
+ }
+
private boolean isContainedInBundleNamed(Bitstream bitstream, String name) {
if (StringUtils.isEmpty(name)) {
diff --git a/dspace-api/src/main/java/org/dspace/content/Bundle.java b/dspace-api/src/main/java/org/dspace/content/Bundle.java
index 6c62c3dc913..e5cbdb6ff24 100644
--- a/dspace-api/src/main/java/org/dspace/content/Bundle.java
+++ b/dspace-api/src/main/java/org/dspace/content/Bundle.java
@@ -126,7 +126,7 @@ public void setPrimaryBitstreamID(Bitstream bitstream) {
* Unset the primary bitstream ID of the bundle
*/
public void unsetPrimaryBitstreamID() {
- primaryBitstream = null;
+ setPrimaryBitstreamID(null);
}
/**
diff --git a/dspace-api/src/main/java/org/dspace/content/BundleServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/BundleServiceImpl.java
index 485f1d64513..e70af09bb61 100644
--- a/dspace-api/src/main/java/org/dspace/content/BundleServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/BundleServiceImpl.java
@@ -8,6 +8,7 @@
package org.dspace.content;
import static org.dspace.core.Constants.ADD;
+import static org.dspace.core.Constants.READ;
import static org.dspace.core.Constants.REMOVE;
import static org.dspace.core.Constants.WRITE;
@@ -34,6 +35,7 @@
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.LogHelper;
+import org.dspace.eperson.Group;
import org.dspace.event.Event;
import org.springframework.beans.factory.annotation.Autowired;
@@ -74,14 +76,14 @@ public Bundle find(Context context, UUID id) throws SQLException {
if (bundle == null) {
if (log.isDebugEnabled()) {
log.debug(LogHelper.getHeader(context, "find_bundle",
- "not_found,bundle_id=" + id));
+ "not_found,bundle_id=" + id));
}
return null;
} else {
if (log.isDebugEnabled()) {
log.debug(LogHelper.getHeader(context, "find_bundle",
- "bundle_id=" + id));
+ "bundle_id=" + id));
}
return bundle;
@@ -106,7 +108,7 @@ public Bundle create(Context context, Item item, String name) throws SQLExceptio
log.info(LogHelper.getHeader(context, "create_bundle", "bundle_id="
- + bundle.getID()));
+ + bundle.getID()));
// if we ever use the identifier service for bundles, we should
// create the bundle before we create the Event and should add all
@@ -132,12 +134,12 @@ public Bitstream getBitstreamByName(Bundle bundle, String name) {
@Override
public void addBitstream(Context context, Bundle bundle, Bitstream bitstream)
- throws SQLException, AuthorizeException {
+ throws SQLException, AuthorizeException {
// Check authorisation
authorizeService.authorizeAction(context, bundle, Constants.ADD);
log.info(LogHelper.getHeader(context, "add_bitstream", "bundle_id="
- + bundle.getID() + ",bitstream_id=" + bitstream.getID()));
+ + bundle.getID() + ",bitstream_id=" + bitstream.getID()));
// First check that the bitstream isn't already in the list
List bitstreams = bundle.getBitstreams();
@@ -167,12 +169,45 @@ public void addBitstream(Context context, Bundle bundle, Bitstream bitstream)
context.addEvent(new Event(Event.ADD, Constants.BUNDLE, bundle.getID(),
- Constants.BITSTREAM, bitstream.getID(), String.valueOf(bitstream.getSequenceID()),
- getIdentifiers(context, bundle)));
+ Constants.BITSTREAM, bitstream.getID(), String.valueOf(bitstream.getSequenceID()),
+ getIdentifiers(context, bundle)));
// copy authorization policies from bundle to bitstream
// FIXME: multiple inclusion is affected by this...
authorizeService.inheritPolicies(context, bundle, bitstream);
+ // The next logic is a bit overly cautious but ensures that if there are any future start dates
+ // on the item or bitstream read policies, that we'll skip inheriting anything from the owning collection
+ // just in case. In practice, the item install process would overwrite these anyway but it may satisfy
+ // some other bitstream creation methods and integration tests
+ boolean isEmbargoed = false;
+ for (ResourcePolicy resourcePolicy : authorizeService.getPoliciesActionFilter(context, owningItem, READ)) {
+ if (!resourcePolicyService.isDateValid(resourcePolicy)) {
+ isEmbargoed = true;
+ break;
+ }
+ }
+ if (owningItem != null && !isEmbargoed) {
+ // Resolve owning collection
+ Collection owningCollection = owningItem.getOwningCollection();
+ if (owningCollection != null) {
+ // Get DEFAULT_BITSTREAM_READ policy from the collection
+ List defaultBitstreamReadGroups =
+ authorizeService.getAuthorizedGroups(context, owningCollection,
+ Constants.DEFAULT_BITSTREAM_READ);
+ log.info(defaultBitstreamReadGroups.size());
+ // If this collection is configured with a DEFAULT_BITSTREAM_READ group, overwrite the READ policy
+ // inherited from the bundle with this policy.
+ if (!defaultBitstreamReadGroups.isEmpty()) {
+ // Remove read policies from the bitstream
+ authorizeService.removePoliciesActionFilter(context, bitstream, Constants.READ);
+ for (Group defaultBitstreamReadGroup : defaultBitstreamReadGroups) {
+ // Inherit this policy as READ, directly from the collection roles
+ authorizeService.addPolicy(context, bitstream,
+ Constants.READ, defaultBitstreamReadGroup, ResourcePolicy.TYPE_INHERITED);
+ }
+ }
+ }
+ }
bitstreamService.update(context, bitstream);
}
@@ -183,12 +218,12 @@ public void removeBitstream(Context context, Bundle bundle, Bitstream bitstream)
authorizeService.authorizeAction(context, bundle, Constants.REMOVE);
log.info(LogHelper.getHeader(context, "remove_bitstream",
- "bundle_id=" + bundle.getID() + ",bitstream_id=" + bitstream.getID()));
+ "bundle_id=" + bundle.getID() + ",bitstream_id=" + bitstream.getID()));
context.addEvent(new Event(Event.REMOVE, Constants.BUNDLE, bundle.getID(),
- Constants.BITSTREAM, bitstream.getID(), String.valueOf(bitstream.getSequenceID()),
- getIdentifiers(context, bundle)));
+ Constants.BITSTREAM, bitstream.getID(), String.valueOf(bitstream.getSequenceID()),
+ getIdentifiers(context, bundle)));
//Ensure that the last modified from the item is triggered !
Item owningItem = (Item) getParentObject(context, bundle);
@@ -221,9 +256,9 @@ public void removeBitstream(Context context, Bundle bundle, Bitstream bitstream)
@Override
public void inheritCollectionDefaultPolicies(Context context, Bundle bundle, Collection collection)
- throws SQLException, AuthorizeException {
+ throws SQLException, AuthorizeException {
List policies = authorizeService.getPoliciesActionFilter(context, collection,
- Constants.DEFAULT_BITSTREAM_READ);
+ Constants.DEFAULT_BITSTREAM_READ);
// change the action to just READ
// just don't call update on the resourcepolicies!!!
@@ -231,7 +266,7 @@ public void inheritCollectionDefaultPolicies(Context context, Bundle bundle, Col
if (!i.hasNext()) {
throw new java.sql.SQLException("Collection " + collection.getID()
- + " has no default bitstream READ policies");
+ + " has no default bitstream READ policies");
}
List newPolicies = new ArrayList();
@@ -246,7 +281,7 @@ public void inheritCollectionDefaultPolicies(Context context, Bundle bundle, Col
@Override
public void replaceAllBitstreamPolicies(Context context, Bundle bundle, List newpolicies)
- throws SQLException, AuthorizeException {
+ throws SQLException, AuthorizeException {
List bitstreams = bundle.getBitstreams();
if (CollectionUtils.isNotEmpty(bitstreams)) {
for (Bitstream bs : bitstreams) {
@@ -368,16 +403,16 @@ public void setOrder(Context context, Bundle bundle, UUID[] bitstreamIds) throws
if (bitstream == null) {
//This should never occur but just in case
log.warn(LogHelper.getHeader(context, "Invalid bitstream id while changing bitstream order",
- "Bundle: " + bundle.getID() + ", bitstream id: " + bitstreamId));
+ "Bundle: " + bundle.getID() + ", bitstream id: " + bitstreamId));
continue;
}
// If we have a Bitstream not in the current list, log a warning & exit immediately
if (!currentBitstreams.contains(bitstream)) {
log.warn(LogHelper.getHeader(context,
- "Encountered a bitstream not in this bundle while changing bitstream " +
- "order. Bitstream order will not be changed.",
- "Bundle: " + bundle.getID() + ", bitstream id: " + bitstreamId));
+ "Encountered a bitstream not in this bundle while changing bitstream " +
+ "order. Bitstream order will not be changed.",
+ "Bundle: " + bundle.getID() + ", bitstream id: " + bitstreamId));
return;
}
updatedBitstreams.add(bitstream);
@@ -386,9 +421,9 @@ public void setOrder(Context context, Bundle bundle, UUID[] bitstreamIds) throws
// If our lists are different sizes, exit immediately
if (updatedBitstreams.size() != currentBitstreams.size()) {
log.warn(LogHelper.getHeader(context,
- "Size of old list and new list do not match. Bitstream order will not be " +
- "changed.",
- "Bundle: " + bundle.getID()));
+ "Size of old list and new list do not match. Bitstream order will not be " +
+ "changed.",
+ "Bundle: " + bundle.getID()));
return;
}
@@ -434,7 +469,7 @@ public DSpaceObject getAdminObject(Context context, Bundle bundle, int action) t
} else if (AuthorizeConfiguration.canCollectionAdminPerformBitstreamDeletion()) {
adminObject = collection;
} else if (AuthorizeConfiguration
- .canCommunityAdminPerformBitstreamDeletion()) {
+ .canCommunityAdminPerformBitstreamDeletion()) {
adminObject = community;
}
break;
@@ -442,10 +477,10 @@ public DSpaceObject getAdminObject(Context context, Bundle bundle, int action) t
if (AuthorizeConfiguration.canItemAdminPerformBitstreamCreation()) {
adminObject = item;
} else if (AuthorizeConfiguration
- .canCollectionAdminPerformBitstreamCreation()) {
+ .canCollectionAdminPerformBitstreamCreation()) {
adminObject = collection;
} else if (AuthorizeConfiguration
- .canCommunityAdminPerformBitstreamCreation()) {
+ .canCommunityAdminPerformBitstreamCreation()) {
adminObject = community;
}
break;
@@ -477,7 +512,7 @@ public void update(Context context, Bundle bundle) throws SQLException, Authoriz
// Check authorisation
//AuthorizeManager.authorizeAction(ourContext, this, Constants.WRITE);
log.info(LogHelper.getHeader(context, "update_bundle", "bundle_id="
- + bundle.getID()));
+ + bundle.getID()));
super.update(context, bundle);
bundleDAO.save(context, bundle);
@@ -485,10 +520,10 @@ public void update(Context context, Bundle bundle) throws SQLException, Authoriz
if (bundle.isModified() || bundle.isMetadataModified()) {
if (bundle.isMetadataModified()) {
context.addEvent(new Event(Event.MODIFY_METADATA, bundle.getType(), bundle.getID(), bundle.getDetails(),
- getIdentifiers(context, bundle)));
+ getIdentifiers(context, bundle)));
}
context.addEvent(new Event(Event.MODIFY, Constants.BUNDLE, bundle.getID(),
- null, getIdentifiers(context, bundle)));
+ null, getIdentifiers(context, bundle)));
bundle.clearModified();
bundle.clearDetails();
}
@@ -497,12 +532,12 @@ public void update(Context context, Bundle bundle) throws SQLException, Authoriz
@Override
public void delete(Context context, Bundle bundle) throws SQLException, AuthorizeException, IOException {
log.info(LogHelper.getHeader(context, "delete_bundle", "bundle_id="
- + bundle.getID()));
+ + bundle.getID()));
authorizeService.authorizeAction(context, bundle, Constants.DELETE);
context.addEvent(new Event(Event.DELETE, Constants.BUNDLE, bundle.getID(),
- bundle.getName(), getIdentifiers(context, bundle)));
+ bundle.getName(), getIdentifiers(context, bundle)));
// Remove bitstreams
List bitstreams = bundle.getBitstreams();
@@ -544,4 +579,8 @@ public Bundle findByLegacyId(Context context, int id) throws SQLException {
public int countTotal(Context context) throws SQLException {
return bundleDAO.countRows(context);
}
+
+ public boolean exists(Context context, UUID id) throws SQLException {
+ return this.bundleDAO.exists(context, Bundle.class, id);
+ }
}
diff --git a/dspace-api/src/main/java/org/dspace/content/Collection.java b/dspace-api/src/main/java/org/dspace/content/Collection.java
index 7dadde72c90..dbe2d35efe1 100644
--- a/dspace-api/src/main/java/org/dspace/content/Collection.java
+++ b/dspace-api/src/main/java/org/dspace/content/Collection.java
@@ -29,6 +29,7 @@
import javax.persistence.Transient;
import org.dspace.authorize.AuthorizeException;
+import org.dspace.browse.ItemCountException;
import org.dspace.content.comparator.NameAscendingComparator;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CollectionService;
@@ -341,4 +342,17 @@ private CollectionService getCollectionService() {
return collectionService;
}
+ /**
+ * return count of the collection items
+ *
+ * @return int
+ */
+ public int countArchivedItems() {
+ try {
+ return collectionService.countArchivedItems(this);
+ } catch (ItemCountException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
}
diff --git a/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java
index 367c7a5d34b..4b38b9b1c07 100644
--- a/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java
@@ -35,6 +35,8 @@
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.authorize.service.ResourcePolicyService;
+import org.dspace.browse.ItemCountException;
+import org.dspace.browse.ItemCounter;
import org.dspace.content.dao.CollectionDAO;
import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.CollectionService;
@@ -1217,4 +1219,41 @@ public int countCollectionsAdministeredByEntityType(String query, String entityT
discoverQuery, query, entityType).getTotalSearchResults();
}
+ @Override
+ @SuppressWarnings("rawtypes")
+ public List findAllCollectionsByEntityType(Context context, String entityType)
+ throws SearchServiceException {
+ List collectionList = new ArrayList<>();
+
+ DiscoverQuery discoverQuery = new DiscoverQuery();
+ discoverQuery.setDSpaceObjectFilter(IndexableCollection.TYPE);
+ discoverQuery.addFilterQueries("dspace.entity.type:" + entityType);
+
+ DiscoverResult discoverResult = searchService.search(context, discoverQuery);
+ List solrIndexableObjects = discoverResult.getIndexableObjects();
+
+ for (IndexableObject solrCollection : solrIndexableObjects) {
+ Collection c = ((IndexableCollection) solrCollection).getIndexedObject();
+ collectionList.add(c);
+ }
+ return collectionList;
+ }
+
+ /**
+ * Returns total collection archived items
+ *
+ * @param collection Collection
+ * @return total collection archived items
+ * @throws ItemCountException
+ */
+ @Override
+ public int countArchivedItems(Collection collection) throws ItemCountException {
+ return ItemCounter.getInstance().getCount(collection);
+ }
+
+ @Override
+ public boolean exists(Context context, UUID id) throws SQLException {
+ return this.collectionDAO.exists(context, Collection.class, id);
+ }
+
}
diff --git a/dspace-api/src/main/java/org/dspace/content/Community.java b/dspace-api/src/main/java/org/dspace/content/Community.java
index fa99da33091..dd6d978936d 100644
--- a/dspace-api/src/main/java/org/dspace/content/Community.java
+++ b/dspace-api/src/main/java/org/dspace/content/Community.java
@@ -25,6 +25,7 @@
import javax.persistence.Transient;
import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.dspace.browse.ItemCountException;
import org.dspace.content.comparator.NameAscendingComparator;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CommunityService;
@@ -264,4 +265,16 @@ private CommunityService getCommunityService() {
return communityService;
}
+ /**
+ * return count of the community items
+ *
+ * @return int
+ */
+ public int countArchivedItems() {
+ try {
+ return communityService.countArchivedItems(this);
+ } catch (ItemCountException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/dspace-api/src/main/java/org/dspace/content/CommunityServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/CommunityServiceImpl.java
index b4053a724f3..b74aa0aaa33 100644
--- a/dspace-api/src/main/java/org/dspace/content/CommunityServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/CommunityServiceImpl.java
@@ -25,6 +25,8 @@
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.AuthorizeService;
+import org.dspace.browse.ItemCountException;
+import org.dspace.browse.ItemCounter;
import org.dspace.content.dao.CommunityDAO;
import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.CollectionService;
@@ -82,7 +84,6 @@ public class CommunityServiceImpl extends DSpaceObjectServiceImpl imp
protected CommunityServiceImpl() {
super();
-
}
@Override
@@ -712,4 +713,22 @@ public Community findByLegacyId(Context context, int id) throws SQLException {
public int countTotal(Context context) throws SQLException {
return communityDAO.countRows(context);
}
+
+ /**
+ * Returns total community archived items
+ *
+ * @param community Community
+ * @return total community archived items
+ * @throws ItemCountException
+ */
+ @Override
+ public int countArchivedItems(Community community) throws ItemCountException {
+ return ItemCounter.getInstance().getCount(community);
+ }
+
+ @Override
+ public boolean exists(Context context, UUID id) throws SQLException {
+ return this.communityDAO.exists(context, Community.class, id);
+ }
+
}
diff --git a/dspace-api/src/main/java/org/dspace/content/InstallItemServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/InstallItemServiceImpl.java
index 213bbcbaa0c..df24075d548 100644
--- a/dspace-api/src/main/java/org/dspace/content/InstallItemServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/InstallItemServiceImpl.java
@@ -94,7 +94,7 @@ public Item installItem(Context c, InProgressSubmission is,
// As this is a BRAND NEW item, as a final step we need to remove the
// submitter item policies created during deposit and replace them with
// the default policies from the collection.
- itemService.inheritCollectionDefaultPolicies(c, item, collection);
+ itemService.inheritCollectionDefaultPolicies(c, item, collection, false);
return item;
}
@@ -273,4 +273,28 @@ public String getBitstreamProvenanceMessage(Context context, Item myitem)
return myMessage.toString();
}
+
+ @Override
+ public String getSubmittedByProvenanceMessage(Context context, Item item) throws SQLException {
+ // get date
+ DCDate now = DCDate.getCurrent();
+
+ // Create provenance description
+ StringBuffer provmessage = new StringBuffer();
+
+ if (item.getSubmitter() != null) {
+ provmessage.append("Submitted by ").append(item.getSubmitter().getFullName())
+ .append(" (").append(item.getSubmitter().getEmail()).append(") on ")
+ .append(now.toString());
+ } else {
+ // else, null submitter
+ provmessage.append("Submitted by unknown (probably automated) on")
+ .append(now.toString());
+ }
+ provmessage.append("\n");
+
+ // add sizes and checksums of bitstreams
+ provmessage.append(getBitstreamProvenanceMessage(context, item));
+ return provmessage.toString();
+ }
}
diff --git a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java
index 3ad03377cb2..6b3ef003edc 100644
--- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java
@@ -79,7 +79,9 @@
import org.dspace.event.Event;
import org.dspace.harvest.HarvestedItem;
import org.dspace.harvest.service.HarvestedItemService;
+import org.dspace.identifier.DOI;
import org.dspace.identifier.IdentifierException;
+import org.dspace.identifier.service.DOIService;
import org.dspace.identifier.service.IdentifierService;
import org.dspace.layout.CrisLayoutBox;
import org.dspace.layout.CrisLayoutField;
@@ -146,6 +148,8 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl- implements It
@Autowired(required = true)
protected IdentifierService identifierService;
@Autowired(required = true)
+ protected DOIService doiService;
+ @Autowired(required = true)
protected VersioningService versioningService;
@Autowired(required = true)
protected HarvestedItemService harvestedItemService;
@@ -298,9 +302,7 @@ private List getThumbnailFields(List crisLayoutT
* @param context
* @param item
* @param bundle
- * @param metadata
* @param value
- * @param requireOriginal
* @throws SQLException
* @return Bitstream
*/
@@ -962,6 +964,16 @@ protected void rawDelete(Context context, Item item) throws AuthorizeException,
// Remove any Handle
handleService.unbindHandle(context, item);
+ // Delete a DOI if linked to the item.
+ // If no DOI consumer or provider is configured, but a DOI remains linked to this item's uuid,
+ // hibernate will throw a foreign constraint exception.
+ // Here we use the DOI service directly as it is able to manage DOIs even without any configured
+ // consumer or provider.
+ DOI doi = doiService.findDOIByDSpaceObject(context, item);
+ if (doi != null) {
+ doi.setDSpaceObject(null);
+ }
+
// remove version attached to the item
removeVersion(context, item);
@@ -1185,9 +1197,17 @@ public void removeGroupPolicies(Context context, Item item, Group group) throws
@Override
public void inheritCollectionDefaultPolicies(Context context, Item item, Collection collection)
- throws SQLException, AuthorizeException {
- adjustItemPolicies(context, item, collection);
- adjustBundleBitstreamPolicies(context, item, collection);
+ throws SQLException, AuthorizeException {
+ inheritCollectionDefaultPolicies(context, item, collection, true);
+ }
+
+ @Override
+ public void inheritCollectionDefaultPolicies(Context context, Item item, Collection collection,
+ boolean replaceReadRPWithCollectionRP)
+ throws SQLException, AuthorizeException {
+
+ adjustItemPolicies(context, item, collection, replaceReadRPWithCollectionRP);
+ adjustBundleBitstreamPolicies(context, item, collection, replaceReadRPWithCollectionRP);
log.debug(LogHelper.getHeader(context, "item_inheritCollectionDefaultPolicies",
"item_id=" + item.getID()));
@@ -1195,46 +1215,120 @@ public void inheritCollectionDefaultPolicies(Context context, Item item, Collect
@Override
public void adjustBundleBitstreamPolicies(Context context, Item item, Collection collection)
- throws SQLException, AuthorizeException {
- List defaultCollectionPolicies = authorizeService
- .getPoliciesActionFilter(context, collection, Constants.DEFAULT_BITSTREAM_READ);
+ throws SQLException, AuthorizeException {
+ adjustBundleBitstreamPolicies(context, item, collection, true);
+ }
+
+ @Override
+ public void adjustBundleBitstreamPolicies(Context context, Item item, Collection collection,
+ boolean replaceReadRPWithCollectionRP)
+ throws SQLException, AuthorizeException {
+ // Bundles should inherit from DEFAULT_ITEM_READ so that if the item is readable, the files
+ // can be listed (even if they are themselves not readable as per DEFAULT_BITSTREAM_READ or other
+ // policies or embargos applied
+ List defaultCollectionBundlePolicies = authorizeService
+ .getPoliciesActionFilter(context, collection, Constants.DEFAULT_ITEM_READ);
+ // Bitstreams should inherit from DEFAULT_BITSTREAM_READ
+ List defaultCollectionBitstreamPolicies = authorizeService
+ .getPoliciesActionFilter(context, collection, Constants.DEFAULT_BITSTREAM_READ);
List defaultItemPolicies = authorizeService.findPoliciesByDSOAndType(context, item,
ResourcePolicy.TYPE_CUSTOM);
- if (defaultCollectionPolicies.size() < 1) {
+ if (defaultCollectionBitstreamPolicies.size() < 1) {
throw new SQLException("Collection " + collection.getID()
+ " (" + collection.getHandle() + ")"
+ " has no default bitstream READ policies");
}
+ // TODO: should we also throw an exception if no DEFAULT_ITEM_READ?
+ // TODO: should we also throw an exception if no DEFAULT_ITEM_READ?
+
+ boolean removeCurrentReadRPBitstream =
+ replaceReadRPWithCollectionRP && defaultCollectionBitstreamPolicies.size() > 0;
+ boolean removeCurrentReadRPBundle =
+ replaceReadRPWithCollectionRP && defaultCollectionBundlePolicies.size() > 0;
// remove all policies from bundles, add new ones
// Remove bundles
List bunds = item.getBundles();
for (Bundle mybundle : bunds) {
+ // If collection has default READ policies, remove the bundle's READ policies.
+ if (removeCurrentReadRPBundle) {
+ authorizeService.removePoliciesActionFilter(context, mybundle, Constants.READ);
+ }
// if come from InstallItem: remove all submission/workflow policies
authorizeService.removeAllPoliciesByDSOAndType(context, mybundle, ResourcePolicy.TYPE_SUBMISSION);
authorizeService.removeAllPoliciesByDSOAndType(context, mybundle, ResourcePolicy.TYPE_WORKFLOW);
addCustomPoliciesNotInPlace(context, mybundle, defaultItemPolicies);
- addDefaultPoliciesNotInPlace(context, mybundle, defaultCollectionPolicies);
+ addDefaultPoliciesNotInPlace(context, mybundle, defaultCollectionBundlePolicies);
for (Bitstream bitstream : mybundle.getBitstreams()) {
+ // If collection has default READ policies, remove the bundle's READ policies.
+ if (removeCurrentReadRPBitstream) {
+ authorizeService.removePoliciesActionFilter(context, bitstream, Constants.READ);
+ }
+
// if come from InstallItem: remove all submission/workflow policies
- authorizeService.removeAllPoliciesByDSOAndType(context, bitstream, ResourcePolicy.TYPE_SUBMISSION);
- authorizeService.removeAllPoliciesByDSOAndType(context, bitstream, ResourcePolicy.TYPE_WORKFLOW);
- addCustomPoliciesNotInPlace(context, bitstream, defaultItemPolicies);
- addDefaultPoliciesNotInPlace(context, bitstream, defaultCollectionPolicies);
+ removeAllPoliciesAndAddDefault(context, bitstream, defaultItemPolicies,
+ defaultCollectionBitstreamPolicies);
}
}
}
+ @Override
+ public void adjustBitstreamPolicies(Context context, Item item, Collection collection, Bitstream bitstream)
+ throws SQLException, AuthorizeException {
+ adjustBitstreamPolicies(context, item, collection, bitstream, true);
+ }
+
+ @Override
+ public void adjustBitstreamPolicies(Context context, Item item, Collection collection , Bitstream bitstream,
+ boolean replaceReadRPWithCollectionRP)
+ throws SQLException, AuthorizeException {
+ List defaultCollectionPolicies = authorizeService
+ .getPoliciesActionFilter(context, collection, Constants.DEFAULT_BITSTREAM_READ);
+
+ List defaultItemPolicies = authorizeService.findPoliciesByDSOAndType(context, item,
+ ResourcePolicy.TYPE_CUSTOM);
+ if (defaultCollectionPolicies.size() < 1) {
+ throw new SQLException("Collection " + collection.getID()
+ + " (" + collection.getHandle() + ")"
+ + " has no default bitstream READ policies");
+ }
+
+ // remove all policies from bitstream, add new ones
+ removeAllPoliciesAndAddDefault(context, bitstream, defaultItemPolicies, defaultCollectionPolicies);
+ }
+
+ private void removeAllPoliciesAndAddDefault(Context context, Bitstream bitstream,
+ List defaultItemPolicies,
+ List defaultCollectionPolicies)
+ throws SQLException, AuthorizeException {
+ authorizeService.removeAllPoliciesByDSOAndType(context, bitstream, ResourcePolicy.TYPE_SUBMISSION);
+ authorizeService.removeAllPoliciesByDSOAndType(context, bitstream, ResourcePolicy.TYPE_WORKFLOW);
+ addCustomPoliciesNotInPlace(context, bitstream, defaultItemPolicies);
+ addDefaultPoliciesNotInPlace(context, bitstream, defaultCollectionPolicies);
+ }
+
@Override
public void adjustItemPolicies(Context context, Item item, Collection collection)
- throws SQLException, AuthorizeException {
+ throws SQLException, AuthorizeException {
+ adjustItemPolicies(context, item, collection, true);
+ }
+
+ @Override
+ public void adjustItemPolicies(Context context, Item item, Collection collection,
+ boolean replaceReadRPWithCollectionRP)
+ throws SQLException, AuthorizeException {
// read collection's default READ policies
List defaultCollectionPolicies = authorizeService
.getPoliciesActionFilter(context, collection, Constants.DEFAULT_ITEM_READ);
+ // If collection has defaultREAD policies, remove the item's READ policies.
+ if (replaceReadRPWithCollectionRP && defaultCollectionPolicies.size() > 0) {
+ authorizeService.removePoliciesActionFilter(context, item, Constants.READ);
+ }
+
// MUST have default policies
if (defaultCollectionPolicies.size() < 1) {
throw new SQLException("Collection " + collection.getID()
@@ -1444,9 +1538,18 @@ public boolean isInProgressSubmission(Context context, Item item) throws SQLExce
*/
- @Override
+ /**
+ * Add the default policies, which have not been already added to the given DSpace object
+ *
+ * @param context The relevant DSpace Context.
+ * @param dso The DSpace Object to add policies to
+ * @param defaultCollectionPolicies list of policies
+ * @throws SQLException An exception that provides information on a database access error or other errors.
+ * @throws AuthorizeException Exception indicating the current user of the context does not have permission
+ * to perform a particular action.
+ */
public void addDefaultPoliciesNotInPlace(Context context, DSpaceObject dso,
- List defaultCollectionPolicies) throws SQLException, AuthorizeException {
+ List defaultCollectionPolicies) throws SQLException, AuthorizeException {
boolean appendMode = configurationService
.getBooleanProperty("core.authorization.installitem.inheritance-read.append-mode", false);
for (ResourcePolicy defaultPolicy : defaultCollectionPolicies) {
@@ -1741,7 +1844,7 @@ public boolean isItemListedForUser(Context context, Item item) {
@Override
public Iterator
- findByIds(Context context, List ids) throws SQLException {
return itemDAO.findByIds(context,
- ids.stream().map(uuid -> UUID.fromString(uuid)).collect(Collectors.toList()));
+ ids.stream().map(uuid -> UUID.fromString(uuid)).distinct().collect(Collectors.toList()));
}
@Override
@@ -2138,4 +2241,16 @@ public boolean isLatestVersion(Context context, Item item) throws SQLException {
}
+ @Override
+ public void addResourcePolicy(Context context, Item item, int actionID, EPerson eperson)
+ throws SQLException, AuthorizeException {
+ ResourcePolicy resourcePolicy =
+ this.authorizeService.createResourcePolicy(context, item, null, eperson, actionID, null);
+ item.getResourcePolicies().add(resourcePolicy);
+ }
+
+ public boolean exists(Context context, UUID id) throws SQLException {
+ return this.itemDAO.exists(context, Item.class, id);
+ }
+
}
diff --git a/dspace-api/src/main/java/org/dspace/content/MetadataSchemaEnum.java b/dspace-api/src/main/java/org/dspace/content/MetadataSchemaEnum.java
index fa45ed15e00..7babfce3145 100644
--- a/dspace-api/src/main/java/org/dspace/content/MetadataSchemaEnum.java
+++ b/dspace-api/src/main/java/org/dspace/content/MetadataSchemaEnum.java
@@ -18,7 +18,8 @@ public enum MetadataSchemaEnum {
EPERSON("eperson"),
RELATION("relation"),
CRIS("cris"),
- OAIRECERIF("oairecerif");
+ OAIRECERIF("oairecerif"),
+ PERSON("person");
/**
* The String representation of the MetadataSchemaEnum
diff --git a/dspace-api/src/main/java/org/dspace/content/MetadataValue.java b/dspace-api/src/main/java/org/dspace/content/MetadataValue.java
index 639cec0e0c3..923b5575fa4 100644
--- a/dspace-api/src/main/java/org/dspace/content/MetadataValue.java
+++ b/dspace-api/src/main/java/org/dspace/content/MetadataValue.java
@@ -61,7 +61,7 @@ public class MetadataValue implements ReloadableEntity {
* The value of the field
*/
@Lob
- @Type(type = "org.dspace.storage.rdbms.hibernate.DatabaseAwareLobType")
+ @Type(type = "org.hibernate.type.TextType")
@Column(name = "text_value")
private String value;
diff --git a/dspace-api/src/main/java/org/dspace/content/RelationshipMetadataServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/RelationshipMetadataServiceImpl.java
index daf9a34378a..9e0c72258e5 100644
--- a/dspace-api/src/main/java/org/dspace/content/RelationshipMetadataServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/RelationshipMetadataServiceImpl.java
@@ -110,7 +110,8 @@ protected List findLatestForDiscoveryMetadataValues(
// on the left item as a storage/performance improvement.
// As a consequence, when searching for related items (using discovery)
// on the pages of the right items you won't be able to find the left item.
- if (relationshipType.getTilted() != RIGHT && itemEntityType.equals(relationshipType.getLeftType())) {
+ if (relationshipType.getTilted() != RIGHT
+ && Objects.equals(relationshipType.getLeftType(), itemEntityType)) {
String element = relationshipType.getLeftwardType();
List data = relationshipService
.findByLatestItemAndRelationshipType(context, item, relationshipType, true);
diff --git a/dspace-api/src/main/java/org/dspace/content/RelationshipPlacesIndexingServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/RelationshipPlacesIndexingServiceImpl.java
index 1ed14b4fbe1..f29e209d779 100644
--- a/dspace-api/src/main/java/org/dspace/content/RelationshipPlacesIndexingServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/RelationshipPlacesIndexingServiceImpl.java
@@ -55,7 +55,9 @@ public void updateRelationReferences(final Context context, final Relationship r
if (singleDirectionRelationship("right", relationship.getRelationshipType())) {
times = relation.getLeftPlace() - relation.getRightPlace();
}
- rightItemsIdsToAdd.addAll(Collections.nCopies(times, relation.getRightItem().getID().toString()));
+ if (times > 0) {
+ rightItemsIdsToAdd.addAll(Collections.nCopies(times, relation.getRightItem().getID().toString()));
+ }
}
if (!rightItemsIdsToAdd.isEmpty()) {
@@ -79,7 +81,9 @@ public void updateRelationReferences(final Context context, final Relationship r
if (singleDirectionRelationship("left", relationship.getRelationshipType())) {
times = relation.getRightPlace() - relation.getLeftPlace();
}
- leftItemsIdsToAdd.addAll(Collections.nCopies(times, relation.getLeftItem().getID().toString()));
+ if (times > 0) {
+ leftItemsIdsToAdd.addAll(Collections.nCopies(times, relation.getLeftItem().getID().toString()));
+ }
}
if (!leftItemsIdsToAdd.isEmpty()) {
@@ -102,7 +106,9 @@ private void addRightItemsReferences(final Context context, final Relationship r
if (singleDirectionRelationship("right", relationship.getRelationshipType())) {
times = leftItemRelation.getLeftPlace() - leftItemRelation.getRightPlace();
}
- rightItemsToAdd.addAll(Collections.nCopies(times, leftItemRelation.getRightItem().getID().toString()));
+ if (times > 0) {
+ rightItemsToAdd.addAll(Collections.nCopies(times, leftItemRelation.getRightItem().getID().toString()));
+ }
}
if (!rightItemsToAdd.isEmpty()) {
indexingService.updateRelationForItem(leftItem.getID().toString(),
@@ -122,7 +128,9 @@ private void addLeftItemsReferences(final Context context, final Relationship re
if (singleDirectionRelationship("left", relationship.getRelationshipType())) {
times = leftItemRelation.getRightPlace() - leftItemRelation.getLeftPlace();
}
- rightItemsToAdd.addAll(Collections.nCopies(times, leftItemRelation.getLeftItem().getID().toString()));
+ if (times > 0) {
+ rightItemsToAdd.addAll(Collections.nCopies(times, leftItemRelation.getLeftItem().getID().toString()));
+ }
}
if (!rightItemsToAdd.isEmpty()) {
indexingService.updateRelationForItem(rightItem.getID().toString(),
diff --git a/dspace-api/src/main/java/org/dspace/content/SiteServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/SiteServiceImpl.java
index 2f53ed0928a..2b7aa368a5f 100644
--- a/dspace-api/src/main/java/org/dspace/content/SiteServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/SiteServiceImpl.java
@@ -105,4 +105,8 @@ public void delete(Context context, Site dso) throws SQLException, AuthorizeExce
public int getSupportsTypeConstant() {
return Constants.SITE;
}
+
+ public boolean exists(Context context, UUID id) throws SQLException {
+ return this.siteDAO.exists(context, Site.class, id);
+ }
}
diff --git a/dspace-api/src/main/java/org/dspace/content/authority/AuthorityServiceUtils.java b/dspace-api/src/main/java/org/dspace/content/authority/AuthorityServiceUtils.java
index cdb2c324ad9..db4dd6496f0 100644
--- a/dspace-api/src/main/java/org/dspace/content/authority/AuthorityServiceUtils.java
+++ b/dspace-api/src/main/java/org/dspace/content/authority/AuthorityServiceUtils.java
@@ -14,6 +14,7 @@
import org.dspace.content.Collection;
import org.dspace.core.Constants;
import org.dspace.submit.model.UploadConfigurationService;
+import org.dspace.submit.service.SubmissionConfigService;
import org.springframework.beans.factory.annotation.Autowired;
/**
@@ -54,4 +55,32 @@ public String getSubmissionOrFormName(SubmissionConfigReader configReader, int d
return null;
}
}
+
+ /**
+ *
+ * @param submissionConfigService the Submission Config service
+ * @param dsoType the type of dspace object (ITEM or BITSTREAM) for all the
+ * other object
null
is returned
+ * @param collection the collection where the object stays
+ * @return the name of the submission form (if ITEM) or the name of the metadata
+ * form (BITSTREAM)
+ */
+ public String getSubmissionOrFormName(SubmissionConfigService submissionConfigService, int dsoType,
+ Collection collection) {
+ switch (dsoType) {
+ case Constants.ITEM:
+ return submissionConfigService.getSubmissionConfigByCollection(collection).getSubmissionName();
+ case Constants.BITSTREAM:
+ SubmissionConfig subCfg = submissionConfigService.getSubmissionConfigByCollection(collection);
+ for (int i = 0; i < subCfg.getNumberOfSteps(); i++) {
+ SubmissionStepConfig step = subCfg.getStep(i);
+ if (SubmissionStepConfig.UPLOAD_STEP_NAME.equalsIgnoreCase(step.getType())) {
+ return uploadConfigurationService.getMap().get(step.getId()).getMetadata();
+ }
+ }
+ return null;
+ default:
+ return null;
+ }
+ }
}
diff --git a/dspace-api/src/main/java/org/dspace/content/authority/ChoiceAuthorityServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/authority/ChoiceAuthorityServiceImpl.java
index cbc92c3be5f..0c545d1592e 100644
--- a/dspace-api/src/main/java/org/dspace/content/authority/ChoiceAuthorityServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/authority/ChoiceAuthorityServiceImpl.java
@@ -7,7 +7,6 @@
*/
package org.dspace.content.authority;
-import static java.lang.Integer.MAX_VALUE;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.ArrayList;
@@ -22,14 +21,12 @@
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
-import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.app.util.DCInput;
import org.dspace.app.util.DCInputSet;
import org.dspace.app.util.DCInputsReader;
import org.dspace.app.util.DCInputsReaderException;
import org.dspace.app.util.SubmissionConfig;
-import org.dspace.app.util.SubmissionConfigReader;
import org.dspace.app.util.SubmissionConfigReaderException;
import org.dspace.content.Collection;
import org.dspace.content.Item;
@@ -39,9 +36,13 @@
import org.dspace.core.Constants;
import org.dspace.core.Utils;
import org.dspace.core.service.PluginService;
+import org.dspace.discovery.configuration.DiscoveryConfigurationService;
+import org.dspace.discovery.configuration.DiscoverySearchFilterFacet;
import org.dspace.services.ConfigurationService;
+import org.dspace.submit.factory.SubmissionServiceFactory;
import org.dspace.submit.model.UploadConfiguration;
import org.dspace.submit.model.UploadConfigurationService;
+import org.dspace.submit.service.SubmissionConfigService;
import org.springframework.beans.factory.annotation.Autowired;
/**
@@ -66,7 +67,7 @@
* @see ChoiceAuthority
*/
public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService {
- private Logger log = LogManager.getLogger(ChoiceAuthorityServiceImpl.class);
+ private Logger log = org.apache.logging.log4j.LogManager.getLogger(ChoiceAuthorityServiceImpl.class);
// map of field key to authority plugin
protected Map controller = new HashMap();
@@ -95,8 +96,11 @@ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService
protected Map>> authoritiesFormDefinitions =
new HashMap>>();
+ // Map of vocabulary authorities to and their index info equivalent
+ protected Map vocabularyIndexMap = new HashMap<>();
+
// the item submission reader
- private SubmissionConfigReader itemSubmissionConfigReader;
+ private SubmissionConfigService submissionConfigService;
@Autowired(required = true)
protected ConfigurationService configurationService;
@@ -108,6 +112,8 @@ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService
protected AuthorityServiceUtils authorityServiceUtils;
@Autowired(required = true)
protected ItemService itemService;
+ @Autowired
+ private DiscoveryConfigurationService searchConfigurationService;
final static String CHOICES_PLUGIN_PREFIX = "choices.plugin.";
final static String CHOICES_PRESENTATION_PREFIX = "choices.presentation.";
@@ -154,7 +160,7 @@ public Set getChoiceAuthoritiesNames() {
private synchronized void init() {
if (!initialized) {
try {
- itemSubmissionConfigReader = new SubmissionConfigReader();
+ submissionConfigService = SubmissionServiceFactory.getInstance().getSubmissionConfigService();
} catch (SubmissionConfigReaderException e) {
// the system is in an illegal state as the submission definition is not valid
throw new IllegalStateException("Error reading the item submission configuration: " + e.getMessage(),
@@ -244,7 +250,7 @@ public String getChoiceAuthorityName(String schema, String element, String quali
// check if it is the requested collection
Map> controllerFormDefTypes = controllerFormDefinitions.get(fieldKey);
Map controllerFormDef = controllerFormDefTypes.get(dsoType);
- SubmissionConfig submissionConfig = itemSubmissionConfigReader.getSubmissionConfigByCollection(collection);
+ SubmissionConfig submissionConfig = submissionConfigService.getSubmissionConfigByCollection(collection);
String submissionName = submissionConfig.getSubmissionName();
// check if the requested collection has a submission definition that use an authority for the metadata
if (controllerFormDef.containsKey(submissionName)) {
@@ -288,14 +294,14 @@ protected String makeFieldKey(String schema, String element, String qualifier) {
}
@Override
- public void clearCache() {
+ public void clearCache() throws SubmissionConfigReaderException {
controller.clear();
authorities.clear();
presentation.clear();
closed.clear();
controllerFormDefinitions.clear();
authoritiesFormDefinitions.clear();
- itemSubmissionConfigReader = null;
+ submissionConfigService.reload();
initialized = false;
}
@@ -345,7 +351,8 @@ private void loadChoiceAuthorityConfigurations() {
*/
private void autoRegisterChoiceAuthorityFromInputReader() {
try {
- List submissionConfigs = itemSubmissionConfigReader.getAllSubmissionConfigs(MAX_VALUE, 0);
+ List submissionConfigs = submissionConfigService
+ .getAllSubmissionConfigs(Integer.MAX_VALUE, 0);
DCInputsReader dcInputsReader = new DCInputsReader();
// loop over all the defined item submission configuration
@@ -563,19 +570,27 @@ public ChoiceAuthority getAuthorityByFieldKeyCollection(String fieldKey, int dso
init();
ChoiceAuthority ma = getAuthorityByFieldAndCollection(fieldKey, collection);
if (ma == null && collection != null) {
- String submissionName = authorityServiceUtils.getSubmissionOrFormName(itemSubmissionConfigReader,
- dsoType, collection);
- if (submissionName == null) {
- log.warn("No submission name was found for object type " + dsoType + " in collection "
- + collection.getHandle());
- return null;
- }
- Map> mapType2SubAuth = controllerFormDefinitions.get(fieldKey);
- if (mapType2SubAuth != null) {
- Map mapSubAuth = mapType2SubAuth.get(dsoType);
- if (mapSubAuth != null) {
- ma = mapSubAuth.get(submissionName);
+ SubmissionConfigService configReaderService;
+ try {
+ configReaderService = SubmissionServiceFactory.getInstance().getSubmissionConfigService();
+ SubmissionConfig submissionName = configReaderService
+ .getSubmissionConfigByCollection(collection);
+ if (submissionName == null) {
+ log.warn("No submission name was found for object type " + dsoType + " in collection "
+ + collection.getHandle());
+ return null;
}
+ Map> mapType2SubAuth = controllerFormDefinitions.get(fieldKey);
+ if (mapType2SubAuth != null) {
+ Map mapSubAuth = mapType2SubAuth.get(dsoType);
+ if (mapSubAuth != null) {
+ ma = mapSubAuth.get(submissionName.getSubmissionName());
+ }
+ }
+ } catch (SubmissionConfigReaderException e) {
+ // the system is in an illegal state as the submission definition is not valid
+ throw new IllegalStateException("Error reading the item submission configuration: " + e.getMessage(),
+ e);
}
}
return ma;
@@ -587,7 +602,7 @@ private String getCollectionFormName(String fieldKey, Collection collection) {
return "";
}
- String submissionName = authorityServiceUtils.getSubmissionOrFormName(itemSubmissionConfigReader,
+ String submissionName = authorityServiceUtils.getSubmissionOrFormName(submissionConfigService,
Constants.ITEM, collection);
return submissionName;
@@ -701,4 +716,50 @@ private boolean isLinkableToAnEntityWithEntityType(ChoiceAuthority choiceAuthori
return choiceAuthority instanceof LinkableEntityAuthority
&& entityType.equals(((LinkableEntityAuthority) choiceAuthority).getLinkedEntityType());
}
+
+ @Override
+ public DSpaceControlledVocabularyIndex getVocabularyIndex(String nameVocab) {
+ if (this.vocabularyIndexMap.containsKey(nameVocab)) {
+ return this.vocabularyIndexMap.get(nameVocab);
+ } else {
+ init();
+ ChoiceAuthority source = this.getChoiceAuthorityByAuthorityName(nameVocab);
+ if (source != null && source instanceof DSpaceControlledVocabulary) {
+ Set metadataFields = new HashSet<>();
+ Map> formsToFields = this.authoritiesFormDefinitions.get(nameVocab);
+ for (Map.Entry> formToField : formsToFields.entrySet()) {
+ metadataFields.addAll(formToField.getValue().stream().map(value ->
+ StringUtils.replace(value, "_", "."))
+ .collect(Collectors.toList()));
+ }
+ DiscoverySearchFilterFacet matchingFacet = null;
+ for (DiscoverySearchFilterFacet facetConfig : searchConfigurationService.getAllFacetsConfig()) {
+ boolean coversAllFieldsFromVocab = true;
+ for (String fieldFromVocab: metadataFields) {
+ boolean coversFieldFromVocab = false;
+ for (String facetMdField: facetConfig.getMetadataFields()) {
+ if (facetMdField.startsWith(fieldFromVocab)) {
+ coversFieldFromVocab = true;
+ break;
+ }
+ }
+ if (!coversFieldFromVocab) {
+ coversAllFieldsFromVocab = false;
+ break;
+ }
+ }
+ if (coversAllFieldsFromVocab) {
+ matchingFacet = facetConfig;
+ break;
+ }
+ }
+ DSpaceControlledVocabularyIndex vocabularyIndex =
+ new DSpaceControlledVocabularyIndex((DSpaceControlledVocabulary) source, metadataFields,
+ matchingFacet);
+ this.vocabularyIndexMap.put(nameVocab, vocabularyIndex);
+ return vocabularyIndex;
+ }
+ return null;
+ }
+ }
}
diff --git a/dspace-api/src/main/java/org/dspace/content/authority/DSpaceControlledVocabularyIndex.java b/dspace-api/src/main/java/org/dspace/content/authority/DSpaceControlledVocabularyIndex.java
new file mode 100644
index 00000000000..bf8194dbd53
--- /dev/null
+++ b/dspace-api/src/main/java/org/dspace/content/authority/DSpaceControlledVocabularyIndex.java
@@ -0,0 +1,47 @@
+/**
+ * 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.content.authority;
+
+import java.util.Set;
+
+import org.dspace.browse.BrowseIndex;
+import org.dspace.discovery.configuration.DiscoverySearchFilterFacet;
+
+/**
+ * Helper class to transform a {@link org.dspace.content.authority.DSpaceControlledVocabulary} into a
+ * {@code BrowseIndexRest}
+ * cached by {@link org.dspace.content.authority.service.ChoiceAuthorityService#getVocabularyIndex(String)}
+ *
+ * @author Marie Verdonck (Atmire) on 04/05/2023
+ */
+public class DSpaceControlledVocabularyIndex extends BrowseIndex {
+
+ protected DSpaceControlledVocabulary vocabulary;
+ protected Set metadataFields;
+ protected DiscoverySearchFilterFacet facetConfig;
+
+ public DSpaceControlledVocabularyIndex(DSpaceControlledVocabulary controlledVocabulary, Set metadataFields,
+ DiscoverySearchFilterFacet facetConfig) {
+ super(controlledVocabulary.vocabularyName);
+ this.vocabulary = controlledVocabulary;
+ this.metadataFields = metadataFields;
+ this.facetConfig = facetConfig;
+ }
+
+ public DSpaceControlledVocabulary getVocabulary() {
+ return vocabulary;
+ }
+
+ public Set getMetadataFields() {
+ return this.metadataFields;
+ }
+
+ public DiscoverySearchFilterFacet getFacetConfig() {
+ return this.facetConfig;
+ }
+}
diff --git a/dspace-api/src/main/java/org/dspace/content/authority/OrcidAuthority.java b/dspace-api/src/main/java/org/dspace/content/authority/OrcidAuthority.java
index 4dfe09cdec6..02ca7be701d 100644
--- a/dspace-api/src/main/java/org/dspace/content/authority/OrcidAuthority.java
+++ b/dspace-api/src/main/java/org/dspace/content/authority/OrcidAuthority.java
@@ -43,9 +43,11 @@ public class OrcidAuthority extends ItemAuthority {
private static final Logger LOGGER = LoggerFactory.getLogger(OrcidAuthority.class);
- public static final String ORCID_EXTRA = "data-person_identifier_orcid";
+ private static final String IS_LATIN_REGEX = "\\p{IsLatin}+";
- public static final String INSTITUTION_EXTRA = "institution-affiliation-name";
+ public static final String DEFAULT_ORCID_KEY = "person_identifier_orcid";
+
+ public static final String DEFAULT_INSTITUTION_KEY = "institution-affiliation-name";
private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
@@ -118,9 +120,13 @@ private String getTitle(ExpandedResult result) {
String givenName = result.getGivenNames();
String familyName = result.getFamilyNames();
- String title = isNotBlank(givenName) ? capitalizeFully(givenName) : "";
- title += isNotBlank(familyName) ? " " + capitalizeFully(familyName) : "";
-
+ String capitalizedFamilyName = capitalizeFully(familyName);
+ String capitalizedGivenName = capitalizeFully(givenName);
+ String title = capitalizedFamilyName + ", " + capitalizedGivenName;
+ if (!givenName.matches(IS_LATIN_REGEX) || !familyName.matches(IS_LATIN_REGEX)) {
+ title = isNotBlank(familyName) ? capitalizeFully(familyName) : "";
+ title += isNotBlank(givenName) ? " " + capitalizeFully(givenName) : "";
+ }
return title.trim();
}
@@ -131,11 +137,24 @@ private String composeAuthorityValue(String orcid) {
private Map composeExtras(ExpandedResult result) {
Map extras = new HashMap<>();
- extras.put(ORCID_EXTRA, result.getOrcidId());
-
+ String orcidIdKey = getOrcidIdKey();
+ String orcidId = result.getOrcidId();
+ if (StringUtils.isNotBlank(orcidId)) {
+ if (useOrcidIDAsData()) {
+ extras.put("data-" + orcidIdKey, orcidId);
+ }
+ if (useOrcidIDForDisplaying()) {
+ extras.put(orcidIdKey, orcidId);
+ }
+ }
+ String institutionKey = getInstitutionKey();
String[] institutionNames = result.getInstitutionNames();
- if (ArrayUtils.isNotEmpty(institutionNames)) {
- extras.put(INSTITUTION_EXTRA, String.join(", ", institutionNames));
+
+ if (ArrayUtils.isNotEmpty(institutionNames) && useInstitutionAsData()) {
+ extras.put("data-" + institutionKey, String.join(", ", institutionNames));
+ }
+ if (ArrayUtils.isNotEmpty(institutionNames) && useInstitutionForDisplaying()) {
+ extras.put(institutionKey, String.join(", ", institutionNames));
}
return extras;
@@ -165,4 +184,41 @@ public static void setAccessToken(String accessToken) {
OrcidAuthority.accessToken = accessToken;
}
+ public String getOrcidIdKey() {
+ return configurationService.getProperty("cris.OrcidAuthority."
+ + getPluginInstanceName() + ".orcid-id.key",
+ DEFAULT_ORCID_KEY);
+ }
+
+ public String getInstitutionKey() {
+ return configurationService.getProperty("cris.OrcidAuthority."
+ + getPluginInstanceName() + ".institution.key",
+ DEFAULT_INSTITUTION_KEY);
+ }
+
+ public boolean useInstitutionAsData() {
+ return configurationService
+ .getBooleanProperty("cris.OrcidAuthority."
+ + getPluginInstanceName() + ".institution.as-data", true);
+ }
+
+ public boolean useInstitutionForDisplaying() {
+ return configurationService
+ .getBooleanProperty("cris.OrcidAuthority."
+ + getPluginInstanceName() + ".institution.display", true);
+ }
+
+ public boolean useOrcidIDAsData() {
+ return configurationService
+ .getBooleanProperty("cris.OrcidAuthority."
+ + getPluginInstanceName() + ".orcid-id.as-data", true);
+ }
+
+ public boolean useOrcidIDForDisplaying() {
+ return configurationService
+ .getBooleanProperty("cris.OrcidAuthority."
+ + getPluginInstanceName() + ".orcid-id.display", true);
+
+ }
+
}
diff --git a/dspace-api/src/main/java/org/dspace/content/authority/ReciprocalItemAuthorityConsumer.java b/dspace-api/src/main/java/org/dspace/content/authority/ReciprocalItemAuthorityConsumer.java
index a78430fb574..05f4e8aea3f 100644
--- a/dspace-api/src/main/java/org/dspace/content/authority/ReciprocalItemAuthorityConsumer.java
+++ b/dspace-api/src/main/java/org/dspace/content/authority/ReciprocalItemAuthorityConsumer.java
@@ -23,9 +23,12 @@
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.ItemService;
import org.dspace.core.Context;
+import org.dspace.discovery.IndexingService;
+import org.dspace.discovery.indexobject.IndexableItem;
import org.dspace.event.Consumer;
import org.dspace.event.Event;
import org.dspace.services.ConfigurationService;
+import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.utils.DSpace;
/**
@@ -38,26 +41,18 @@
public class ReciprocalItemAuthorityConsumer implements Consumer {
private static final Logger log = LogManager.getLogger(ReciprocalItemAuthorityConsumer.class);
- private final Map reciprocalMetadata = new ConcurrentHashMap<>();
+ private final ConfigurationService configurationService = new DSpace().getConfigurationService();
+ private final ItemService itemService = ContentServiceFactory.getInstance().getItemService();
+ private final Map reciprocalMetadataMap = new ConcurrentHashMap<>();
private final transient Set processedHandles = new HashSet<>();
- private final ItemService itemService;
-
- public ReciprocalItemAuthorityConsumer() {
- ConfigurationService confService = new DSpace().getConfigurationService();
- itemService = ContentServiceFactory.getInstance().getItemService();
- for (String conf : confService.getPropertyKeys("ItemAuthority.reciprocalMetadata")) {
- reciprocalMetadata.put(conf.substring("ItemAuthority.reciprocalMetadata.".length()),
- confService.getProperty(conf));
- reciprocalMetadata.put(confService.getProperty(conf),
- conf.substring("ItemAuthority.reciprocalMetadata.".length()));
- }
- }
+ private final IndexingService indexer = DSpaceServicesFactory.getInstance().getServiceManager()
+ .getServiceByName(IndexingService.class.getName(), IndexingService.class);
@Override
public void initialize() throws Exception {
- // nothing
+ iniReciprocalMetadata();
}
@Override
@@ -73,11 +68,11 @@ public void consume(Context ctx, Event event) throws Exception {
} else {
processedHandles.add(item.getID());
}
- if (!reciprocalMetadata.isEmpty()) {
- for (String k : reciprocalMetadata.keySet()) {
+ if (!reciprocalMetadataMap.isEmpty()) {
+ for (String k : reciprocalMetadataMap.keySet()) {
String entityType = k.split("\\.", 2)[0];
String metadata = k.split("\\.", 2)[1];
- checkItemRefs(ctx, item, entityType, metadata, reciprocalMetadata.get(k));
+ checkItemRefs(ctx, item, entityType, metadata, reciprocalMetadataMap.get(k));
}
}
} finally {
@@ -127,6 +122,34 @@ private void assureReciprocalLink(Context ctx, Item target, String mdString, Str
itemService.addMetadata(ctx, target, mdSplit[0], mdSplit[1], mdSplit.length > 2 ? mdSplit[2] : null, null,
name, sourceUuid, Choices.CF_ACCEPTED);
+ reindexItem(ctx, target);
+ }
+
+ private void reindexItem(Context ctx, Item target) throws SQLException {
+ IndexableItem item = new IndexableItem(target);
+ item.setIndexedObject(ctx.reloadEntity(item.getIndexedObject()));
+ String uniqueIndexID = item.getUniqueIndexID();
+ if (uniqueIndexID != null) {
+ try {
+ indexer.indexContent(ctx, item, true, false, false);
+ log.debug("Indexed "
+ + item.getTypeText()
+ + ", id=" + item.getID()
+ + ", unique_id=" + uniqueIndexID);
+ } catch (Exception e) {
+ log.error("Failed while indexing object: ", e);
+ }
+ }
+ }
+
+ private void iniReciprocalMetadata() {
+ List properties = configurationService.getPropertyKeys("ItemAuthority.reciprocalMetadata");
+ for (String conf : properties) {
+ reciprocalMetadataMap.put(conf.substring("ItemAuthority.reciprocalMetadata.".length()),
+ configurationService.getProperty(conf));
+ reciprocalMetadataMap.put(configurationService.getProperty(conf),
+ conf.substring("ItemAuthority.reciprocalMetadata.".length()));
+ }
}
@Override
diff --git a/dspace-api/src/main/java/org/dspace/content/authority/RorOrgUnitAuthority.java b/dspace-api/src/main/java/org/dspace/content/authority/RorOrgUnitAuthority.java
index 09f7330b62f..de227190181 100644
--- a/dspace-api/src/main/java/org/dspace/content/authority/RorOrgUnitAuthority.java
+++ b/dspace-api/src/main/java/org/dspace/content/authority/RorOrgUnitAuthority.java
@@ -8,21 +8,27 @@
package org.dspace.content.authority;
-import java.util.HashMap;
+import java.util.Collection;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
import org.dspace.content.authority.factory.ItemAuthorityServiceFactory;
-import org.dspace.ror.ROROrgUnitDTO;
-import org.dspace.ror.service.RORApiService;
-import org.dspace.ror.service.RORApiServiceImpl;
+import org.dspace.importer.external.datamodel.ImportRecord;
+import org.dspace.importer.external.exception.MetadataSourceException;
+import org.dspace.importer.external.metadatamapping.MetadatumDTO;
+import org.dspace.importer.external.ror.service.RorImportMetadataSourceServiceImpl;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
+import org.dspace.utils.DSpace;
public class RorOrgUnitAuthority extends ItemAuthority {
- private final RORApiService rorApiService = dspace.getSingletonService(RORApiServiceImpl.class);
+ private final RorImportMetadataSourceServiceImpl rorImportMetadataSource = new DSpace().getServiceManager()
+ .getServicesByType(RorImportMetadataSourceServiceImpl.class).get(0);
+
private final ItemAuthorityServiceFactory itemAuthorityServiceFactory =
dspace.getServiceManager().getServiceByName("itemAuthorityServiceFactory", ItemAuthorityServiceFactory.class);
private final ConfigurationService configurationService =
@@ -32,18 +38,20 @@ public class RorOrgUnitAuthority extends ItemAuthority {
@Override
public Choices getMatches(String text, int start, int limit, String locale) {
+
super.setPluginInstanceName(authorityName);
Choices solrChoices = super.getMatches(text, start, limit, locale);
- return solrChoices.values.length == 0 ? getRORApiMatches(text, start, limit) : solrChoices;
+ try {
+ return solrChoices.values.length == 0 ? getRORApiMatches(text, start, limit) : solrChoices;
+ } catch (MetadataSourceException e) {
+ throw new RuntimeException(e);
+ }
}
- private Choices getRORApiMatches(String text, int start, int limit) {
- Choice[] rorApiChoices = getChoiceFromRORQueryResults(
- rorApiService.getOrgUnits(text).stream()
- .filter(ou -> "active".equals(ou.getStatus()))
- .collect(Collectors.toList())
- ).toArray(new Choice[0]);
+ private Choices getRORApiMatches(String text, int start, int limit) throws MetadataSourceException {
+ Choice[] rorApiChoices = getChoiceFromRORQueryResults(rorImportMetadataSource.getRecords(text, 0, 0))
+ .toArray(new Choice[0]);
int confidenceValue = itemAuthorityServiceFactory.getInstance(authorityName)
.getConfidenceForChoices(rorApiChoices);
@@ -52,16 +60,74 @@ private Choices getRORApiMatches(String text, int start, int limit) {
rorApiChoices.length > (start + limit), 0);
}
- private List getChoiceFromRORQueryResults(List orgUnits) {
+ private List getChoiceFromRORQueryResults(Collection orgUnits) {
return orgUnits
.stream()
- .map(orgUnit -> new Choice(composeAuthorityValue(orgUnit.getIdentifier()), orgUnit.getName(),
- orgUnit.getName(), buildExtras(orgUnit)))
+ .map(orgUnit -> new Choice(composeAuthorityValue(getIdentifier(orgUnit)), getName(orgUnit),
+ getName(orgUnit), buildExtras(orgUnit)))
.collect(Collectors.toList());
}
- private Map buildExtras(ROROrgUnitDTO orgUnit) {
- return new HashMap<>();
+ private String getIdentifier(ImportRecord orgUnit) {
+ return orgUnit.getValue("organization", "identifier", "ror").stream()
+ .findFirst()
+ .map(metadata -> metadata.getValue())
+ .orElse(null);
+ }
+
+ private String getName(ImportRecord orgUnit) {
+ return orgUnit.getValue("dc", "title", null).stream()
+ .findFirst()
+ .map(metadata -> metadata.getValue())
+ .orElse(null);
+ }
+
+ private Map buildExtras(ImportRecord orgUnit) {
+
+ Map extras = new LinkedHashMap();
+
+ addExtra(extras, getIdentifier(orgUnit), "id");
+
+ orgUnit.getSingleValue("dc", "type", null)
+ .ifPresent(type -> addExtra(extras, type, "type"));
+
+ String acronym = orgUnit.getValue("oairecerif", "acronym", null).stream()
+ .map(MetadatumDTO::getValue)
+ .collect(Collectors.joining(", "));
+
+ if (StringUtils.isNotBlank(acronym)) {
+ addExtra(extras, acronym, "acronym");
+ }
+
+ return extras;
+ }
+
+ private void addExtra(Map extras, String value, String extraType) {
+
+ String key = getKey(extraType);
+
+ if (useAsData(extraType)) {
+ extras.put("data-" + key, value);
+ }
+ if (useForDisplaying(extraType)) {
+ extras.put(key, value);
+ }
+
+ }
+
+ private boolean useForDisplaying(String extraType) {
+ return configurationService.getBooleanProperty("cris.OrcidAuthority."
+ + getPluginInstanceName() + "." + extraType + ".display", true);
+ }
+
+ private boolean useAsData(String extraType) {
+ return configurationService.getBooleanProperty("cris.OrcidAuthority."
+ + getPluginInstanceName() + "." + extraType + ".as-data", true);
+ }
+
+ private String getKey(String extraType) {
+ return configurationService.getProperty("cris.OrcidAuthority."
+ + getPluginInstanceName() + "." + extraType + ".key", "ror_orgunit_" + extraType);
}
private String composeAuthorityValue(String rorId) {
diff --git a/dspace-api/src/main/java/org/dspace/content/authority/SherpaAuthority.java b/dspace-api/src/main/java/org/dspace/content/authority/SherpaAuthority.java
index 44bd406ce43..54d8f3325ce 100644
--- a/dspace-api/src/main/java/org/dspace/content/authority/SherpaAuthority.java
+++ b/dspace-api/src/main/java/org/dspace/content/authority/SherpaAuthority.java
@@ -173,9 +173,4 @@ private boolean isLocalItemChoicesEnabled() {
return configurationService.getBooleanProperty("cris." + this.authorityName + ".local-item-choices-enabled");
}
- @Override
- public Map getExternalSource() {
- return Map.of();
- }
-
}
\ No newline at end of file
diff --git a/dspace-api/src/main/java/org/dspace/content/authority/SolrAuthority.java b/dspace-api/src/main/java/org/dspace/content/authority/SolrAuthority.java
index 497fa08f2fa..123626cd096 100644
--- a/dspace-api/src/main/java/org/dspace/content/authority/SolrAuthority.java
+++ b/dspace-api/src/main/java/org/dspace/content/authority/SolrAuthority.java
@@ -200,8 +200,8 @@ protected void addExternalResults(String text, ArrayList choices, List findDuplicateInternalIdentifier(Context context, Bitstrea
@Override
public List findBitstreamsWithNoRecentChecksum(Context context) throws SQLException {
- Query query = createQuery(context,
- "select b from Bitstream b where b not in (select c.bitstream from " +
- "MostRecentChecksum c)");
+ Query query = createQuery(context, "SELECT b FROM MostRecentChecksum c RIGHT JOIN Bitstream b " +
+ "ON c.bitstream = b WHERE c IS NULL" );
+
return query.getResultList();
}
@Override
public Iterator findByCommunity(Context context, Community community) throws SQLException {
- Query query = createQuery(context, "select b from Bitstream b " +
+ Query query = createQuery(context, "select b.id from Bitstream b " +
"join b.bundles bitBundles " +
"join bitBundles.items item " +
"join item.collections itemColl " +
@@ -85,40 +86,43 @@ public Iterator findByCommunity(Context context, Community community)
"WHERE :community IN community");
query.setParameter("community", community);
-
- return iterate(query);
+ @SuppressWarnings("unchecked")
+ List uuids = query.getResultList();
+ return new UUIDIterator(context, uuids, Bitstream.class, this);
}
@Override
public Iterator findByCollection(Context context, Collection collection) throws SQLException {
- Query query = createQuery(context, "select b from Bitstream b " +
+ Query query = createQuery(context, "select b.id from Bitstream b " +
"join b.bundles bitBundles " +
"join bitBundles.items item " +
"join item.collections c " +
"WHERE :collection IN c");
query.setParameter("collection", collection);
-
- return iterate(query);
+ @SuppressWarnings("unchecked")
+ List uuids = query.getResultList();
+ return new UUIDIterator(context, uuids, Bitstream.class, this);
}
@Override
public Iterator findByItem(Context context, Item item) throws SQLException {
- Query query = createQuery(context, "select b from Bitstream b " +
+ Query query = createQuery(context, "select b.id from Bitstream b " +
"join b.bundles bitBundles " +
"join bitBundles.items item " +
"WHERE :item IN item");
query.setParameter("item", item);
-
- return iterate(query);
+ @SuppressWarnings("unchecked")
+ List uuids = query.getResultList();
+ return new UUIDIterator(context, uuids, Bitstream.class, this);
}
@Override
public Iterator findShowableByItem(Context context, UUID itemId, String bundleName) throws SQLException {
Query query = createQuery(
context,
- "select b from Bitstream b " +
+ "select b.id from Bitstream b " +
"join b.bundles bitBundle " +
"join bitBundle.items item " +
"WHERE item.id = :itemId " +
@@ -150,15 +154,18 @@ public Iterator findShowableByItem(Context context, UUID itemId, Stri
query.setParameter("itemId", itemId);
query.setParameter("bundleName", bundleName);
-
- return iterate(query);
+ @SuppressWarnings("unchecked")
+ List uuids = query.getResultList();
+ return new UUIDIterator(context, uuids, Bitstream.class, this);
}
@Override
public Iterator findByStoreNumber(Context context, Integer storeNumber) throws SQLException {
- Query query = createQuery(context, "select b from Bitstream b where b.storeNumber = :storeNumber");
+ Query query = createQuery(context, "select b.id from Bitstream b where b.storeNumber = :storeNumber");
query.setParameter("storeNumber", storeNumber);
- return iterate(query);
+ @SuppressWarnings("unchecked")
+ List uuids = query.getResultList();
+ return new UUIDIterator(context, uuids, Bitstream.class, this);
}
@Override
diff --git a/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java b/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java
index 378084ee8c4..3b12c68dced 100644
--- a/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java
@@ -29,6 +29,7 @@
import org.dspace.content.dao.ItemDAO;
import org.dspace.core.AbstractHibernateDSODAO;
import org.dspace.core.Context;
+import org.dspace.core.UUIDIterator;
import org.dspace.eperson.EPerson;
import org.hibernate.Criteria;
import org.hibernate.criterion.Criterion;
@@ -56,28 +57,34 @@ protected ItemDAOImpl() {
@Override
public Iterator- findAll(Context context, boolean archived) throws SQLException {
- Query query = createQuery(context, "FROM Item WHERE inArchive=:in_archive ORDER BY id");
+ Query query = createQuery(context, "SELECT i.id FROM Item i WHERE inArchive=:in_archive ORDER BY id");
query.setParameter("in_archive", archived);
- return iterate(query);
+ @SuppressWarnings("unchecked")
+ List uuids = query.getResultList();
+ return new UUIDIterator