From cf1257f20e3d9672c6d96eec4b1b949cf330e2b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Jun 2023 16:35:40 +0000 Subject: [PATCH 001/168] Bump netty-handler from 4.1.68.Final to 4.1.94.Final in /dspace-api Bumps [netty-handler](https://github.com/netty/netty) from 4.1.68.Final to 4.1.94.Final. - [Commits](https://github.com/netty/netty/compare/netty-4.1.68.Final...netty-4.1.94.Final) --- updated-dependencies: - dependency-name: io.netty:netty-handler dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index d41c51638c..b21a76f352 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -876,7 +876,7 @@ io.netty netty-handler - 4.1.68.Final + 4.1.94.Final io.netty From b52a4fb50dcda128b30b2aed55dfff180c0e9160 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 20 Jun 2023 13:23:49 -0500 Subject: [PATCH 002/168] Update all Netty dependencies to 4.1.94.Final Also had to add netty-transport-native-unix-common so that Solr doesn't throw dependency convergence issues. --- dspace-api/pom.xml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index b21a76f352..c4c0b2182a 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -856,22 +856,27 @@ - io.netty netty-buffer - 4.1.68.Final + 4.1.94.Final io.netty netty-transport - 4.1.68.Final + 4.1.94.Final + + io.netty + netty-transport-native-unix-common + 4.1.94.Final + io.netty netty-common - 4.1.68.Final + 4.1.94.Final io.netty @@ -881,7 +886,7 @@ io.netty netty-codec - 4.1.68.Final + 4.1.94.Final org.apache.velocity From a533704a27ed97f16125590c4569589991119356 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 23 Jun 2023 12:10:56 -0500 Subject: [PATCH 003/168] [maven-release-plugin] prepare for next development iteration --- dspace-api/pom.xml | 2 +- dspace-iiif/pom.xml | 2 +- dspace-oai/pom.xml | 2 +- dspace-rdf/pom.xml | 2 +- dspace-rest/pom.xml | 4 ++-- dspace-server-webapp/pom.xml | 2 +- dspace-services/pom.xml | 2 +- dspace-sword/pom.xml | 2 +- dspace-swordv2/pom.xml | 2 +- dspace/modules/additions/pom.xml | 2 +- dspace/modules/pom.xml | 2 +- dspace/modules/rest/pom.xml | 2 +- dspace/modules/server/pom.xml | 2 +- dspace/pom.xml | 2 +- pom.xml | 32 ++++++++++++++++---------------- 15 files changed, 31 insertions(+), 31 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index d0d2bf8608..0e70fc52e0 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.6 + 7.6.1-SNAPSHOT .. diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 8ffa26e967..30e20489b8 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6 + 7.6.1-SNAPSHOT .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 8b6a3ee414..08e732d457 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - 7.6 + 7.6.1-SNAPSHOT .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index 80c0614781..09c3e704de 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6 + 7.6.1-SNAPSHOT .. diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index dcc47b1753..320567f3c1 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -3,7 +3,7 @@ org.dspace dspace-rest war - 7.6 + 7.6.1-SNAPSHOT DSpace (Deprecated) REST Webapp DSpace RESTful Web Services API. NOTE: this REST API is DEPRECATED. Please consider using the REST API in the dspace-server-webapp instead! @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.6 + 7.6.1-SNAPSHOT .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 6f4b2871df..2221f9ca0e 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6 + 7.6.1-SNAPSHOT .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 488fa05c96..fe7c6ab8b6 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6 + 7.6.1-SNAPSHOT diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 02f293dbbb..777041775d 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6 + 7.6.1-SNAPSHOT .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index 3bc838d3ea..c3fbdecb79 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - 7.6 + 7.6.1-SNAPSHOT .. diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index 100561849d..dd6dbcb1ff 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - 7.6 + 7.6.1-SNAPSHOT .. diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index c12b13a38d..e66622ce6b 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - 7.6 + 7.6.1-SNAPSHOT ../../pom.xml diff --git a/dspace/modules/rest/pom.xml b/dspace/modules/rest/pom.xml index 4f3e7cc1c6..14c6ff93f7 100644 --- a/dspace/modules/rest/pom.xml +++ b/dspace/modules/rest/pom.xml @@ -13,7 +13,7 @@ org.dspace modules - 7.6 + 7.6.1-SNAPSHOT .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index c3921bfa27..bebb6d183d 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -13,7 +13,7 @@ just adding new jar in the classloader modules org.dspace - 7.6 + 7.6.1-SNAPSHOT .. diff --git a/dspace/pom.xml b/dspace/pom.xml index 75f16a4205..bfa21f5d66 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - 7.6 + 7.6.1-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 432a928b5b..4c9799243f 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - 7.6 + 7.6.1-SNAPSHOT DSpace Parent Project DSpace open source software is a turnkey institutional repository application. @@ -872,14 +872,14 @@ org.dspace dspace-rest - 7.6 + 7.6.1-SNAPSHOT jar classes org.dspace dspace-rest - 7.6 + 7.6.1-SNAPSHOT war @@ -1030,69 +1030,69 @@ org.dspace dspace-api - 7.6 + 7.6.1-SNAPSHOT org.dspace dspace-api test-jar - 7.6 + 7.6.1-SNAPSHOT test org.dspace.modules additions - 7.6 + 7.6.1-SNAPSHOT org.dspace dspace-sword - 7.6 + 7.6.1-SNAPSHOT org.dspace dspace-swordv2 - 7.6 + 7.6.1-SNAPSHOT org.dspace dspace-oai - 7.6 + 7.6.1-SNAPSHOT org.dspace dspace-services - 7.6 + 7.6.1-SNAPSHOT org.dspace dspace-server-webapp test-jar - 7.6 + 7.6.1-SNAPSHOT test org.dspace dspace-rdf - 7.6 + 7.6.1-SNAPSHOT org.dspace dspace-iiif - 7.6 + 7.6.1-SNAPSHOT org.dspace dspace-server-webapp - 7.6 + 7.6.1-SNAPSHOT jar classes org.dspace dspace-server-webapp - 7.6 + 7.6.1-SNAPSHOT war @@ -1927,7 +1927,7 @@ scm:git:git@github.com:DSpace/DSpace.git scm:git:git@github.com:DSpace/DSpace.git git@github.com:DSpace/DSpace.git - dspace-7.6 + HEAD From 6f2d5cab0517c0c4096db79c6745118647069c0b Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 6 Jun 2023 15:46:52 -0400 Subject: [PATCH 004/168] Replace Joda Time classes with java.time. --- .../status/AccessStatusServiceImpl.java | 8 ++- .../org/dspace/authority/AuthorityValue.java | 65 ++++++++++++------- .../CrossRefDateMetadataProcessor.java | 27 ++++---- .../status/DefaultAccessStatusHelperTest.java | 28 ++++++-- .../dspace/app/util/GoogleMetadataTest.java | 22 ++++--- .../dspace/authority/AuthorityValueTest.java | 44 +++++++++++++ .../builder/AbstractDSpaceObjectBuilder.java | 55 +++++++++------- .../org/dspace/builder/BitstreamBuilder.java | 3 +- .../java/org/dspace/builder/ItemBuilder.java | 3 +- .../CrossRefDateMetadataProcessorTest.java | 32 +++++++++ .../app/rest/BitstreamRestControllerIT.java | 9 +-- .../app/rest/BitstreamRestRepositoryIT.java | 21 +++--- .../app/rest/BrowsesResourceControllerIT.java | 6 +- .../app/rest/DiscoveryRestControllerIT.java | 10 ++- .../dspace/app/rest/ItemRestRepositoryIT.java | 48 +++++++------- 15 files changed, 260 insertions(+), 121 deletions(-) create mode 100644 dspace-api/src/test/java/org/dspace/authority/AuthorityValueTest.java create mode 100644 dspace-api/src/test/java/org/dspace/importer/external/crossref/CrossRefDateMetadataProcessorTest.java diff --git a/dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java b/dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java index 544dc99cb4..1ce3673452 100644 --- a/dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java @@ -8,6 +8,8 @@ package org.dspace.access.status; import java.sql.SQLException; +import java.time.LocalDate; +import java.time.ZoneId; import java.util.Date; import org.dspace.access.status.service.AccessStatusService; @@ -15,7 +17,6 @@ import org.dspace.content.Item; import org.dspace.core.Context; import org.dspace.core.service.PluginService; import org.dspace.services.ConfigurationService; -import org.joda.time.LocalDate; import org.springframework.beans.factory.annotation.Autowired; /** @@ -55,7 +56,10 @@ public class AccessStatusServiceImpl implements AccessStatusService { int month = configurationService.getIntProperty("access.status.embargo.forever.month"); int day = configurationService.getIntProperty("access.status.embargo.forever.day"); - forever_date = new LocalDate(year, month, day).toDate(); + forever_date = Date.from(LocalDate.of(year, month, day) + .atStartOfDay() + .atZone(ZoneId.systemDefault()) + .toInstant()); } } diff --git a/dspace-api/src/main/java/org/dspace/authority/AuthorityValue.java b/dspace-api/src/main/java/org/dspace/authority/AuthorityValue.java index 10a608bb76..6ca0292fdb 100644 --- a/dspace-api/src/main/java/org/dspace/authority/AuthorityValue.java +++ b/dspace-api/src/main/java/org/dspace/authority/AuthorityValue.java @@ -9,6 +9,10 @@ package org.dspace.authority; import java.sql.SQLException; import java.text.DateFormat; +import java.time.DateTimeException; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -16,6 +20,7 @@ import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrInputDocument; @@ -25,9 +30,6 @@ import org.dspace.content.MetadataValue; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.core.Context; import org.dspace.util.SolrUtils; -import org.joda.time.DateTime; -import org.joda.time.format.DateTimeFormatter; -import org.joda.time.format.ISODateTimeFormat; /** * @author Antoine Snyers (antoine at atmire.com) @@ -192,7 +194,7 @@ public class AuthorityValue { } /** - * Information that can be used the choice ui + * Information that can be used the choice ui. * * @return map */ @@ -200,42 +202,51 @@ public class AuthorityValue { return new HashMap<>(); } - - public List getDateFormatters() { - List list = new ArrayList<>(); - list.add(ISODateTimeFormat.dateTime()); - list.add(ISODateTimeFormat.dateTimeNoMillis()); + /** + * Build a list of ISO date formatters to parse various forms. + * + *

Note: any formatter which does not parse a zone or + * offset must have a default zone set. See {@link stringToDate}. + * + * @return the formatters. + */ + static private List getDateFormatters() { + List list = new ArrayList<>(); + list.add(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS]X")); + list.add(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME + .withZone(ZoneId.systemDefault().normalized())); return list; } - public Date stringToDate(String date) { + /** + * Convert a date string to internal form, trying several parsers. + * + * @param date serialized date to be converted. + * @return converted date, or null if no parser accepted the input. + */ + static public Date stringToDate(String date) { Date result = null; if (StringUtils.isNotBlank(date)) { - List dateFormatters = getDateFormatters(); - boolean converted = false; - int formatter = 0; - while (!converted) { + for (DateTimeFormatter formatter : getDateFormatters()) { try { - DateTimeFormatter dateTimeFormatter = dateFormatters.get(formatter); - DateTime dateTime = dateTimeFormatter.parseDateTime(date); - result = dateTime.toDate(); - converted = true; - } catch (IllegalArgumentException e) { - formatter++; - if (formatter > dateFormatters.size()) { - converted = true; - } - log.error("Could not find a valid date format for: \"" + date + "\"", e); + ZonedDateTime dateTime = ZonedDateTime.parse(date, formatter); + result = Date.from(dateTime.toInstant()); + break; + } catch (DateTimeException e) { + log.debug("Input '{}' did not match {}", date, formatter); } } } + if (null == result) { + log.error("Could not find a valid date format for: \"{}\"", date); + } return result; } /** * log4j logger */ - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(AuthorityValue.class); + private static Logger log = LogManager.getLogger(); @Override public String toString() { @@ -272,6 +283,10 @@ public class AuthorityValue { return new AuthorityValue(); } + /** + * Get the type of authority which created this value. + * @return type name. + */ public String getAuthorityType() { return "internal"; } diff --git a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefDateMetadataProcessor.java b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefDateMetadataProcessor.java index dec0b050f3..c83abbf2b2 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefDateMetadataProcessor.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefDateMetadataProcessor.java @@ -7,7 +7,8 @@ */ package org.dspace.importer.external.crossref; -import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; @@ -18,12 +19,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.importer.external.metadatamapping.contributor.JsonPathMetadataProcessor; -import org.joda.time.LocalDate; /** * This class is used for CrossRef's Live-Import to extract * issued attribute. - * Beans are configured in the crossref-integration.xml file. + * Beans are configured in the {@code crossref-integration.xml} file. * * @author Francesco Pio Scognamiglio (francescopio.scognamiglio at 4science.com) */ @@ -41,22 +41,25 @@ public class CrossRefDateMetadataProcessor implements JsonPathMetadataProcessor while (dates.hasNext()) { JsonNode date = dates.next(); LocalDate issuedDate = null; - SimpleDateFormat issuedDateFormat = null; + DateTimeFormatter issuedDateFormat = null; if (date.has(0) && date.has(1) && date.has(2)) { - issuedDate = new LocalDate( + issuedDate = LocalDate.of( date.get(0).numberValue().intValue(), date.get(1).numberValue().intValue(), date.get(2).numberValue().intValue()); - issuedDateFormat = new SimpleDateFormat("yyyy-MM-dd"); + issuedDateFormat = DateTimeFormatter.ISO_LOCAL_DATE; } else if (date.has(0) && date.has(1)) { - issuedDate = new LocalDate().withYear(date.get(0).numberValue().intValue()) - .withMonthOfYear(date.get(1).numberValue().intValue()); - issuedDateFormat = new SimpleDateFormat("yyyy-MM"); + issuedDate = LocalDate.of(date.get(0).numberValue().intValue(), + date.get(1).numberValue().intValue(), + 1); + issuedDateFormat = DateTimeFormatter.ofPattern("yyyy-MM"); } else if (date.has(0)) { - issuedDate = new LocalDate().withYear(date.get(0).numberValue().intValue()); - issuedDateFormat = new SimpleDateFormat("yyyy"); + issuedDate = LocalDate.of(date.get(0).numberValue().intValue(), + 1, + 1); + issuedDateFormat = DateTimeFormatter.ofPattern("yyyy"); } - values.add(issuedDateFormat.format(issuedDate.toDate())); + values.add(issuedDate.format(issuedDateFormat)); } return values; } diff --git a/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java b/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java index a41e985deb..b9bca5e913 100644 --- a/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java +++ b/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java @@ -14,6 +14,8 @@ import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; import java.sql.SQLException; +import java.time.LocalDate; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -42,7 +44,6 @@ import org.dspace.core.Constants; import org.dspace.eperson.Group; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.GroupService; -import org.joda.time.LocalDate; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -129,7 +130,7 @@ public class DefaultAccessStatusHelperTest extends AbstractUnitTest { fail("SQL Error in init: " + ex.getMessage()); } helper = new DefaultAccessStatusHelper(); - threshold = new LocalDate(10000, 1, 1).toDate(); + threshold = dateFrom(10000, 1, 1); } /** @@ -266,7 +267,7 @@ public class DefaultAccessStatusHelperTest extends AbstractUnitTest { Group group = groupService.findByName(context, Group.ANONYMOUS); policy.setGroup(group); policy.setAction(Constants.READ); - policy.setStartDate(new LocalDate(9999, 12, 31).toDate()); + policy.setStartDate(dateFrom(9999, 12, 31)); policies.add(policy); authorizeService.removeAllPolicies(context, bitstream); authorizeService.addPolicies(context, policies, bitstream); @@ -293,7 +294,7 @@ public class DefaultAccessStatusHelperTest extends AbstractUnitTest { Group group = groupService.findByName(context, Group.ANONYMOUS); policy.setGroup(group); policy.setAction(Constants.READ); - policy.setStartDate(new LocalDate(10000, 1, 1).toDate()); + policy.setStartDate(dateFrom(10000, 1, 1)); policies.add(policy); authorizeService.removeAllPolicies(context, bitstream); authorizeService.addPolicies(context, policies, bitstream); @@ -383,7 +384,7 @@ public class DefaultAccessStatusHelperTest extends AbstractUnitTest { Group group = groupService.findByName(context, Group.ANONYMOUS); policy.setGroup(group); policy.setAction(Constants.READ); - policy.setStartDate(new LocalDate(9999, 12, 31).toDate()); + policy.setStartDate(dateFrom(9999, 12, 31)); policies.add(policy); authorizeService.removeAllPolicies(context, primaryBitstream); authorizeService.addPolicies(context, policies, primaryBitstream); @@ -412,7 +413,7 @@ public class DefaultAccessStatusHelperTest extends AbstractUnitTest { Group group = groupService.findByName(context, Group.ANONYMOUS); policy.setGroup(group); policy.setAction(Constants.READ); - policy.setStartDate(new LocalDate(9999, 12, 31).toDate()); + policy.setStartDate(dateFrom(9999, 12, 31)); policies.add(policy); authorizeService.removeAllPolicies(context, anotherBitstream); authorizeService.addPolicies(context, policies, anotherBitstream); @@ -420,4 +421,19 @@ public class DefaultAccessStatusHelperTest extends AbstractUnitTest { String status = helper.getAccessStatusFromItem(context, itemWithoutPrimaryAndMultipleBitstreams, threshold); assertThat("testWithNoPrimaryAndMultipleBitstreams 0", status, equalTo(DefaultAccessStatusHelper.OPEN_ACCESS)); } + + /** + * Create a Date from local year, month, day. + * + * @param year the year. + * @param month the month. + * @param day the day. + * @return the assembled date. + */ + private Date dateFrom(int year, int month, int day) { + return Date.from(LocalDate.of(year, month, day) + .atStartOfDay() + .atZone(ZoneId.systemDefault()) + .toInstant()); + } } diff --git a/dspace-api/src/test/java/org/dspace/app/util/GoogleMetadataTest.java b/dspace-api/src/test/java/org/dspace/app/util/GoogleMetadataTest.java index ee6723480e..c2543ca17b 100644 --- a/dspace-api/src/test/java/org/dspace/app/util/GoogleMetadataTest.java +++ b/dspace-api/src/test/java/org/dspace/app/util/GoogleMetadataTest.java @@ -16,11 +16,15 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.sql.SQLException; +import java.time.Period; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.Date; import java.util.List; import java.util.Map; import com.google.common.base.Splitter; +import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.AbstractUnitTest; import org.dspace.authorize.AuthorizeException; @@ -41,10 +45,6 @@ import org.dspace.core.Constants; import org.dspace.eperson.Group; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.GroupService; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.MutablePeriod; -import org.joda.time.format.PeriodFormat; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -54,7 +54,7 @@ public class GoogleMetadataTest extends AbstractUnitTest { /** * log4j category */ - private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(GoogleMetadataTest.class); + private static final Logger log = LogManager.getLogger(); /** * Item instance for the tests @@ -319,6 +319,7 @@ public class GoogleMetadataTest extends AbstractUnitTest { /** * Test empty bitstreams + * @throws java.lang.Exception passed through. */ @Test public void testGetPDFURLWithEmptyBitstreams() throws Exception { @@ -348,8 +349,9 @@ public class GoogleMetadataTest extends AbstractUnitTest { } /** - * Verify there is no mapping for {@link GoogleMetadata#PDF} if there are only embargoed (non-publically accessible - * bitstream) files + * Verify there is no mapping for {@link GoogleMetadata#PDF} if there are + * only embargoed (non-publicly accessible bitstream) files. + * @throws java.lang.Exception passed through. */ @Test public void testGetPdfUrlOfEmbargoed() throws Exception { @@ -363,8 +365,10 @@ public class GoogleMetadataTest extends AbstractUnitTest { b.getFormat(context).setMIMEType("unknown"); bundleService.addBitstream(context, bundle, b); // Set 3 month embargo on pdf - MutablePeriod period = PeriodFormat.getDefault().parseMutablePeriod("3 months"); - Date embargoDate = DateTime.now(DateTimeZone.UTC).plus(period).toDate(); + Period period = Period.ofMonths(3); + Date embargoDate = Date.from(ZonedDateTime.now(ZoneOffset.UTC) + .plus(period) + .toInstant()); Group anonGroup = groupService.findByName(context, Group.ANONYMOUS); authorizeService.removeAllPolicies(context, b); resourcePolicyService.removeAllPolicies(context, b); diff --git a/dspace-api/src/test/java/org/dspace/authority/AuthorityValueTest.java b/dspace-api/src/test/java/org/dspace/authority/AuthorityValueTest.java new file mode 100644 index 0000000000..74b1e77766 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/authority/AuthorityValueTest.java @@ -0,0 +1,44 @@ +package org.dspace.authority; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import static org.junit.Assert.assertNull; + +import java.util.Date; + +import static org.junit.Assert.assertEquals; +import org.junit.Test; + +/** + * + * @author mwood + */ +public class AuthorityValueTest { + /** + * Test of stringToDate method, of class AuthorityValue. + */ + @Test + public void testStringToDate() { + Date expected; + Date actual; + + // Test an invalid date. + actual = AuthorityValue.stringToDate("not a date"); + assertNull("Unparseable date should return null", actual); + + // Test a date-time without zone or offset. + expected = Date.from(LocalDateTime.of(1957, 01, 27, 01, 23, 45) + .atZone(ZoneOffset.of("-05")) + .toInstant()); + actual = AuthorityValue.stringToDate("1957-01-27T01:23:45"); + assertEquals("Local date-time should convert", expected, actual); + + // Test a date-time with milliseconds and offset from UTC. + expected = Date.from(LocalDateTime.of(1957, 01, 27, 01, 23, 45, 678_000_000) + .atZone(ZoneOffset.of("-05")) + .toInstant()); + actual = AuthorityValue.stringToDate("1957-01-27T01:23:45.678-05"); + assertEquals("Zoned date-time with milliseconds should convert", + expected, actual); + } +} diff --git a/dspace-api/src/test/java/org/dspace/builder/AbstractDSpaceObjectBuilder.java b/dspace-api/src/test/java/org/dspace/builder/AbstractDSpaceObjectBuilder.java index b20515017a..4455668146 100644 --- a/dspace-api/src/test/java/org/dspace/builder/AbstractDSpaceObjectBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/AbstractDSpaceObjectBuilder.java @@ -8,6 +8,8 @@ package org.dspace.builder; import java.sql.SQLException; +import java.time.Instant; +import java.time.Period; import java.util.Date; import org.apache.logging.log4j.Logger; @@ -20,11 +22,6 @@ import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.MutablePeriod; -import org.joda.time.format.PeriodFormat; -import org.joda.time.format.PeriodFormatter; /** * Abstract builder to construct DSpace Objects @@ -112,21 +109,22 @@ public abstract class AbstractDSpaceObjectBuilder } /** - * Support method to grant the {@link Constants#READ} permission over an object only to the {@link Group#ANONYMOUS} - * after the specified embargoPeriod. Any other READ permissions will be removed + * Support method to grant the {@link Constants#READ} permission over an + * object only to the {@link Group#ANONYMOUS} after the specified + * embargoPeriod. Any other READ permissions will be removed. * + * @param type of this Builder. * @param embargoPeriod - * the embargo period after which the READ permission will be active. It is parsed using the - * {@link PeriodFormatter#parseMutablePeriod(String)} method of the joda library - * @param dso - * the DSpaceObject on which grant the permission - * @return the builder properly configured to retain read permission on the object only for the specified group + * the embargo period after which the READ permission will be + * active. + * @param dso the DSpaceObject on which to grant the permission. + * @return the builder properly configured to retain read permission on the + * object only for the specified group. */ - protected > B setEmbargo(String embargoPeriod, DSpaceObject dso) { + protected > B setEmbargo(Period embargoPeriod, DSpaceObject dso) { // add policy just for anonymous try { - MutablePeriod period = PeriodFormat.getDefault().parseMutablePeriod(embargoPeriod); - Date embargoDate = DateTime.now(DateTimeZone.UTC).plus(period).toDate(); + Date embargoDate = Date.from(Instant.now().plus(embargoPeriod)); return setOnlyReadPermission(dso, groupService.findByName(context, Group.ANONYMOUS), embargoDate); } catch (Exception e) { @@ -135,14 +133,19 @@ public abstract class AbstractDSpaceObjectBuilder } /** - * Support method to grant the {@link Constants#READ} permission over an object only to a specific group. Any other - * READ permissions will be removed + * Support method to grant the {@link Constants#READ} permission over an + * object only to a specific group.Any other READ permissions will be + * removed. * + * @param type of this Builder. * @param dso * the DSpaceObject on which grant the permission * @param group * the EPersonGroup that will be granted of the permission - * @return the builder properly configured to retain read permission on the object only for the specified group + * @param startDate + * the date on which access begins. + * @return the builder properly configured to retain read permission on the + * object only for the specified group. */ protected > B setOnlyReadPermission(DSpaceObject dso, Group group, Date startDate) { @@ -161,15 +164,20 @@ public abstract class AbstractDSpaceObjectBuilder } return (B) this; } + /** - * Support method to grant the {@link Constants#ADMIN} permission over an object only to a specific eperson. - * If another ADMIN policy is in place for an eperson it will be replaced + * Support method to grant the {@link Constants#READ} permission over an + * object only to a specific EPerson. Any other READ permissions will be + * removed. * + * @param type of this Builder. * @param dso * the DSpaceObject on which grant the permission * @param eperson - * the eperson that will be granted of the permission - * @return the builder properly configured to build the object with the additional admin permission + * the EPerson that will be granted of the permission + * @param startDate the date on which access begins. + * @return the builder properly configured to build the object with the + * additional admin permission. */ protected > B setAdminPermission(DSpaceObject dso, EPerson eperson, Date startDate) { @@ -191,6 +199,7 @@ public abstract class AbstractDSpaceObjectBuilder /** * Support method to grant {@link Constants#REMOVE} permission to a specific eperson * + * @param type of this Builder. * @param dso * the DSpaceObject on which grant the permission * @param eperson @@ -220,6 +229,7 @@ public abstract class AbstractDSpaceObjectBuilder /** * Support method to grant {@link Constants#ADD} permission to a specific eperson * + * @param type of this Builder. * @param dso * the DSpaceObject on which grant the permission * @param eperson @@ -249,6 +259,7 @@ public abstract class AbstractDSpaceObjectBuilder /** * Support method to grant {@link Constants#WRITE} permission to a specific eperson * + * @param type of this Builder. * @param dso * the DSpaceObject on which grant the permission * @param eperson diff --git a/dspace-api/src/test/java/org/dspace/builder/BitstreamBuilder.java b/dspace-api/src/test/java/org/dspace/builder/BitstreamBuilder.java index 424833e5cc..2822d3624e 100644 --- a/dspace-api/src/test/java/org/dspace/builder/BitstreamBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/BitstreamBuilder.java @@ -10,6 +10,7 @@ package org.dspace.builder; import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; +import java.time.Period; import java.util.List; import org.dspace.authorize.AuthorizeException; @@ -171,7 +172,7 @@ public class BitstreamBuilder extends AbstractDSpaceObjectBuilder { return targetBundle; } - public BitstreamBuilder withEmbargoPeriod(String embargoPeriod) { + public BitstreamBuilder withEmbargoPeriod(Period embargoPeriod) { return setEmbargo(embargoPeriod, bitstream); } diff --git a/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java b/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java index 3e5ab0f38f..f6190c5751 100644 --- a/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java @@ -13,6 +13,7 @@ import static org.dspace.content.authority.Choices.CF_ACCEPTED; import java.io.IOException; import java.sql.SQLException; +import java.time.Period; import java.util.UUID; import org.dspace.authorize.AuthorizeException; @@ -291,7 +292,7 @@ public class ItemBuilder extends AbstractDSpaceObjectBuilder { return this; } - public ItemBuilder withEmbargoPeriod(String embargoPeriod) { + public ItemBuilder withEmbargoPeriod(Period embargoPeriod) { return setEmbargo(embargoPeriod, item); } diff --git a/dspace-api/src/test/java/org/dspace/importer/external/crossref/CrossRefDateMetadataProcessorTest.java b/dspace-api/src/test/java/org/dspace/importer/external/crossref/CrossRefDateMetadataProcessorTest.java new file mode 100644 index 0000000000..89b860d36f --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/importer/external/crossref/CrossRefDateMetadataProcessorTest.java @@ -0,0 +1,32 @@ +package org.dspace.importer.external.crossref; + +import java.util.Collection; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author mwood + */ +public class CrossRefDateMetadataProcessorTest { + /** + * Test of processMetadata method, of class CrossRefDateMetadataProcessor. + */ + @Test + public void testProcessMetadata() { + CrossRefDateMetadataProcessor unit = new CrossRefDateMetadataProcessor(); + unit.setPathToArray("/dates"); + Collection metadata = unit.processMetadata("{\"dates\": [" + + "[1957, 1, 27]," + + "[1957, 1]," + + "[1957]" + + "]}"); + String[] metadataValues = (String[]) metadata.toArray(new String[3]); + assertEquals("[yyyy, MM, dd] should parse", "1957-01-27", metadataValues[0]); + assertEquals("[yyyy, MM] should parse", "1957-01", metadataValues[1]); + assertEquals("[yyyy] should parse", "1957", metadataValues[2]); + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java index 4813cc6596..5fbf669bae 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java @@ -49,6 +49,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.io.Writer; +import java.time.Period; import java.util.UUID; import org.apache.commons.io.IOUtils; @@ -393,7 +394,7 @@ public class BitstreamRestControllerIT extends AbstractControllerIntegrationTest .withName("Test Embargoed Bitstream") .withDescription("This bitstream is embargoed") .withMimeType("text/plain") - .withEmbargoPeriod("6 months") + .withEmbargoPeriod(Period.ofMonths(6)) .build(); } context.restoreAuthSystemState(); @@ -437,7 +438,7 @@ public class BitstreamRestControllerIT extends AbstractControllerIntegrationTest .withName("Test Embargoed Bitstream") .withDescription("This bitstream is embargoed") .withMimeType("text/plain") - .withEmbargoPeriod("3 months") + .withEmbargoPeriod(Period.ofMonths(3)) .build(); } context.restoreAuthSystemState(); @@ -480,7 +481,7 @@ public class BitstreamRestControllerIT extends AbstractControllerIntegrationTest .withName("Test Embargoed Bitstream") .withDescription("This bitstream is embargoed") .withMimeType("text/plain") - .withEmbargoPeriod("-3 months") + .withEmbargoPeriod(Period.ofMonths(-3)) .build(); } context.restoreAuthSystemState(); @@ -558,7 +559,7 @@ public class BitstreamRestControllerIT extends AbstractControllerIntegrationTest .withName("Bitstream") .withDescription("Description") .withMimeType("text/plain") - .withEmbargoPeriod("2 week") + .withEmbargoPeriod(Period.ofWeeks(2)) .build(); } context.restoreAuthSystemState(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java index 8b34edb938..bc276557df 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java @@ -25,6 +25,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.io.InputStream; +import java.time.Period; import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -310,7 +311,7 @@ public class BitstreamRestRepositoryIT extends AbstractControllerIntegrationTest .withName("Test Embargoed Bitstream") .withDescription("This bitstream is embargoed") .withMimeType("text/plain") - .withEmbargoPeriod("3 months") + .withEmbargoPeriod(Period.ofMonths(3)) .build(); } context.restoreAuthSystemState(); @@ -363,7 +364,7 @@ public class BitstreamRestRepositoryIT extends AbstractControllerIntegrationTest .withName("Test Embargoed Bitstream") .withDescription("This bitstream is embargoed") .withMimeType(bitstreamFormat.getMIMEType()) - .withEmbargoPeriod("3 months") + .withEmbargoPeriod(Period.ofMonths(3)) .build(); } context.restoreAuthSystemState(); @@ -517,7 +518,7 @@ public class BitstreamRestRepositoryIT extends AbstractControllerIntegrationTest .withName("Test Embargoed Bitstream") .withDescription("This bitstream is embargoed") .withMimeType("text/plain") - .withEmbargoPeriod("3 months") + .withEmbargoPeriod(Period.ofMonths(3)) .build(); } @@ -577,7 +578,7 @@ public class BitstreamRestRepositoryIT extends AbstractControllerIntegrationTest .withName("Test Embargoed Bitstream") .withDescription("This bitstream is embargoed") .withMimeType(bitstreamFormat.getMIMEType()) - .withEmbargoPeriod("3 months") + .withEmbargoPeriod(Period.ofMonths(3)) .build(); } @@ -638,7 +639,7 @@ public class BitstreamRestRepositoryIT extends AbstractControllerIntegrationTest .withName("Test Embargoed Bitstream") .withDescription("This bitstream is embargoed") .withMimeType("text/plain") - .withEmbargoPeriod("3 months") + .withEmbargoPeriod(Period.ofMonths(3)) .build(); } @@ -701,7 +702,7 @@ public class BitstreamRestRepositoryIT extends AbstractControllerIntegrationTest .withName("Test Embargoed Bitstream") .withDescription("This bitstream is embargoed") .withMimeType(bitstreamFormat.getMIMEType()) - .withEmbargoPeriod("3 months") + .withEmbargoPeriod(Period.ofMonths(3)) .build(); } @@ -768,7 +769,7 @@ public class BitstreamRestRepositoryIT extends AbstractControllerIntegrationTest .withName("Test Embargoed Bitstream") .withDescription("This bitstream is embargoed") .withMimeType("text/plain") - .withEmbargoPeriod("3 months") + .withEmbargoPeriod(Period.ofMonths(3)) .build(); } @@ -826,7 +827,7 @@ public class BitstreamRestRepositoryIT extends AbstractControllerIntegrationTest .withName("Test Embargoed Bitstream") .withDescription("This bitstream is embargoed") .withMimeType("text/plain") - .withEmbargoPeriod("3 months") + .withEmbargoPeriod(Period.ofMonths(3)) .build(); } @@ -1899,7 +1900,7 @@ public class BitstreamRestRepositoryIT extends AbstractControllerIntegrationTest .withTitle("Test") .withIssueDate("2010-10-17") .withAuthor("Smith, Donald") - .withEmbargoPeriod("6 months") + .withEmbargoPeriod(Period.ofMonths(6)) .build(); String bitstreamContent = "This is an archived bitstream"; @@ -2372,7 +2373,7 @@ public class BitstreamRestRepositoryIT extends AbstractControllerIntegrationTest .withName("Test Embargoed Bitstream") .withDescription("This bitstream is embargoed") .withMimeType("text/plain") - .withEmbargoPeriod("3 months") + .withEmbargoPeriod(Period.ofMonths(3)) .build(); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BrowsesResourceControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BrowsesResourceControllerIT.java index d1791ab872..75df0feb34 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BrowsesResourceControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BrowsesResourceControllerIT.java @@ -21,6 +21,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import java.time.Period; + import org.dspace.app.rest.matcher.BrowseEntryResourceMatcher; import org.dspace.app.rest.matcher.BrowseIndexMatcher; import org.dspace.app.rest.matcher.ItemMatcher; @@ -776,7 +778,7 @@ public class BrowsesResourceControllerIT extends AbstractControllerIntegrationTe .withIssueDate("2017-08-10") .withAuthor("Mouse, Mickey") .withSubject("Cartoons").withSubject("Mice") - .withEmbargoPeriod("12 months") + .withEmbargoPeriod(Period.ofMonths(12)) .build(); //5. An item that is only readable for an internal groups @@ -909,7 +911,7 @@ public class BrowsesResourceControllerIT extends AbstractControllerIntegrationTe .withIssueDate("2017-08-10") .withAuthor("Mouse, Mickey") .withSubject("Cartoons").withSubject("Mice") - .withEmbargoPeriod("12 months") + .withEmbargoPeriod(Period.ofMonths(12)) .build(); //5. An item that is only readable for an internal groups diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/DiscoveryRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/DiscoveryRestControllerIT.java index a115c8aa2f..6095c20c9e 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/DiscoveryRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/DiscoveryRestControllerIT.java @@ -26,6 +26,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.io.InputStream; +import java.time.Period; import java.util.UUID; import com.jayway.jsonpath.matchers.JsonPathMatchers; @@ -2413,7 +2414,7 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest .withAuthor("test2, test2").withAuthor("Maybe, Maybe") .withSubject("AnotherTest").withSubject("TestingForMore") .withSubject("ExtraEntry") - .withEmbargoPeriod("12 months") + .withEmbargoPeriod(Period.ofMonths(12)) .build(); //Turn on the authorization again @@ -2714,7 +2715,9 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest /** * This test verifies that * {@link org.dspace.discovery.indexobject.InprogressSubmissionIndexFactoryImpl#storeInprogressItemFields} - * indexes the owning collection of workspace items + * indexes the owning collection of workspace items. + * + * @throws java.lang.Exception passed through. */ @Test public void discoverSearchObjectsTestForWorkspaceItemInCollectionScope() throws Exception { @@ -2765,7 +2768,8 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest /** * This test verifies that * {@link org.dspace.discovery.indexobject.InprogressSubmissionIndexFactoryImpl#storeInprogressItemFields} - * indexes the owning collection of workflow items + * indexes the owning collection of workflow items. + * @throws java.lang.Exception passed through. */ @Test public void discoverSearchObjectsTestForWorkflowItemInCollectionScope() throws Exception { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java index 801976be9f..08463fb222 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java @@ -36,6 +36,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import java.io.InputStream; import java.sql.SQLException; +import java.time.Period; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; @@ -313,7 +314,6 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { .andExpect(jsonPath("$.page.totalPages", is(2))) .andExpect(jsonPath("$.page.number", is(0))) .andExpect(jsonPath("$.page.totalElements", is(3))); - ; getClient(token).perform(get("/api/core/items") .param("size", "2") @@ -596,7 +596,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { // is used in the provenance note. String token = getAuthToken(admin.getEmail(), password); - List ops = new ArrayList(); + List ops = new ArrayList<>(); ReplaceOperation replaceOperation = new ReplaceOperation("/withdrawn", true); ops.add(replaceOperation); String patchBody = getPatchContent(ops); @@ -651,7 +651,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { .build(); context.restoreAuthSystemState(); - List ops = new ArrayList(); + List ops = new ArrayList<>(); ReplaceOperation replaceOperation = new ReplaceOperation("/withdrawn", true); ops.add(replaceOperation); String patchBody = getPatchContent(ops); @@ -700,7 +700,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { // try to use an unauthorized user String token = getAuthToken(eperson.getEmail(), password); - List ops = new ArrayList(); + List ops = new ArrayList<>(); ReplaceOperation replaceOperation = new ReplaceOperation("/withdrawn", true); ops.add(replaceOperation); String patchBody = getPatchContent(ops); @@ -760,7 +760,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); - List ops = new ArrayList(); + List ops = new ArrayList<>(); ReplaceOperation replaceOperation = new ReplaceOperation("/withdrawn", null); ops.add(replaceOperation); String patchBody = getPatchContent(ops); @@ -822,7 +822,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { // is used in the provenance note. String token = getAuthToken(admin.getEmail(), password); - List ops = new ArrayList(); + List ops = new ArrayList<>(); ReplaceOperation replaceOperation = new ReplaceOperation("/withdrawn", false); ops.add(replaceOperation); String patchBody = getPatchContent(ops); @@ -882,7 +882,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { context.restoreAuthSystemState(); String tokenAdmin = getAuthToken(admin.getEmail(), password); - List ops = new ArrayList(); + List ops = new ArrayList<>(); ReplaceOperation replaceOperation = new ReplaceOperation("/withdrawn", false); ops.add(replaceOperation); String patchBody = getPatchContent(ops); @@ -932,7 +932,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { String token = getAuthToken(eperson.getEmail(), password); String tokenAdmin = getAuthToken(admin.getEmail(), password); - List ops = new ArrayList(); + List ops = new ArrayList<>(); ReplaceOperation replaceOperation = new ReplaceOperation("/withdrawn", false); ops.add(replaceOperation); String patchBody = getPatchContent(ops); @@ -977,7 +977,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); - List ops = new ArrayList(); + List ops = new ArrayList<>(); ReplaceOperation replaceOperation = new ReplaceOperation("/discoverable", true); ops.add(replaceOperation); String patchBody = getPatchContent(ops); @@ -1031,7 +1031,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); - List ops = new ArrayList(); + List ops = new ArrayList<>(); ReplaceOperation replaceOperation = new ReplaceOperation("/discoverable", true); ops.add(replaceOperation); String patchBody = getPatchContent(ops); @@ -1076,7 +1076,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { String token = getAuthToken(eperson.getEmail(), password); String tokenAdmin = getAuthToken(admin.getEmail(), password); - List ops = new ArrayList(); + List ops = new ArrayList<>(); ReplaceOperation replaceOperation = new ReplaceOperation("/discoverable", true); ops.add(replaceOperation); String patchBody = getPatchContent(ops); @@ -1119,7 +1119,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); - List ops = new ArrayList(); + List ops = new ArrayList<>(); ReplaceOperation replaceOperation = new ReplaceOperation("/discoverable", false); ops.add(replaceOperation); String patchBody = getPatchContent(ops); @@ -1165,7 +1165,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); - List ops = new ArrayList(); + List ops = new ArrayList<>(); // String value should work. ReplaceOperation replaceOperation = new ReplaceOperation("/discoverable", "false"); ops.add(replaceOperation); @@ -1212,7 +1212,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); - List ops = new ArrayList(); + List ops = new ArrayList<>(); ReplaceOperation replaceOperation = new ReplaceOperation("/discoverable", false); ops.add(replaceOperation); String patchBody = getPatchContent(ops); @@ -1257,7 +1257,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { String token = getAuthToken(eperson.getEmail(), password); String tokenAdmin = getAuthToken(admin.getEmail(), password); - List ops = new ArrayList(); + List ops = new ArrayList<>(); ReplaceOperation replaceOperation = new ReplaceOperation("/discoverable", false); ops.add(replaceOperation); String patchBody = getPatchContent(ops); @@ -1311,7 +1311,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); - List ops = new ArrayList(); + List ops = new ArrayList<>(); ReplaceOperation replaceOperation = new ReplaceOperation("/discoverable", null); ops.add(replaceOperation); String patchBody = getPatchContent(ops); @@ -1638,7 +1638,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { .withIssueDate("2017-12-18") .withAuthor("Smith, Donald").withAuthor("Doe, John") .withSubject("ExtraEntry") - .withEmbargoPeriod("6 months") + .withEmbargoPeriod(Period.ofMonths(6)) .build(); //3. a public item @@ -1729,7 +1729,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { .withIssueDate("2015-10-21") .withAuthor("Smith, Donald") .withSubject("ExtraEntry") - .withEmbargoPeriod("1 week") + .withEmbargoPeriod(Period.ofWeeks(1)) .build(); context.restoreAuthSystemState(); @@ -1778,7 +1778,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { .withTitle("embargoed item 1") .withIssueDate("2017-11-18") .withAuthor("Smith, Donald") - .withEmbargoPeriod("-2 week") + .withEmbargoPeriod(Period.ofWeeks(-2)) .build(); context.restoreAuthSystemState(); @@ -2069,7 +2069,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { context.restoreAuthSystemState(); UUID idRef = null; - AtomicReference idRefNoEmbeds = new AtomicReference(); + AtomicReference idRefNoEmbeds = new AtomicReference<>(); try { ObjectMapper mapper = new ObjectMapper(); ItemRest itemRest = new ItemRest(); @@ -3895,7 +3895,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { createOrcidQueue(context, firstProfile, publication).build(); createOrcidQueue(context, secondProfile, publication).build(); - List historyRecords = new ArrayList(); + List historyRecords = new ArrayList<>(); historyRecords.add(createOrcidHistory(context, firstProfile, publication).build()); historyRecords.add(createOrcidHistory(context, firstProfile, publication).withPutCode("12345").build()); historyRecords.add(createOrcidHistory(context, secondProfile, publication).build()); @@ -3982,7 +3982,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { createOrcidQueue(context, firstProfile, funding).build(); createOrcidQueue(context, secondProfile, funding).build(); - List historyRecords = new ArrayList(); + List historyRecords = new ArrayList<>(); historyRecords.add(createOrcidHistory(context, firstProfile, funding).build()); historyRecords.add(createOrcidHistory(context, firstProfile, funding).withPutCode("12345").build()); historyRecords.add(createOrcidHistory(context, secondProfile, funding).build()); @@ -4081,7 +4081,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { String tokenAdmin = getAuthToken(admin.getEmail(), password); String tokenEperson = getAuthToken(eperson.getEmail(), password); - List ops = new ArrayList(); + List ops = new ArrayList<>(); ReplaceOperation replaceOperation = new ReplaceOperation("/withdrawn", true); ops.add(replaceOperation); String patchBody = getPatchContent(ops); @@ -4131,7 +4131,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { .param("projection", "full")) .andExpect(status().isOk()) .andExpect(jsonPath("$", CollectionMatcher.matchCollectionEntryFullProjection( - col1.getName(), col1.getID(), col1.getHandle())));; + col1.getName(), col1.getID(), col1.getHandle()))); // try to spoof information as a logged in eperson using embedding, verify that no embedds are included getClient(tokenEperson).perform(get("/api/core/items/" + item.getID()) From 3c8409522e38c75ac59093f827b5cc035802e595 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 6 Jun 2023 16:30:14 -0400 Subject: [PATCH 005/168] Remove unused dependency on Joda Time --- dspace-api/pom.xml | 4 ---- dspace-server-webapp/pom.xml | 4 ---- 2 files changed, 8 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 0e70fc52e0..3a4b673f0b 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -703,10 +703,6 @@ annotations - - joda-time - joda-time - javax.inject javax.inject diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 2221f9ca0e..b65e3bc238 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -433,10 +433,6 @@ commons-validator commons-validator - - joda-time - joda-time - com.fasterxml.jackson.core jackson-databind From d52eb41383078f278b04482661cb85a295f0437e Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 6 Jun 2023 16:47:47 -0400 Subject: [PATCH 006/168] Expected time should be in the local zone, not EST. --- .../test/java/org/dspace/authority/AuthorityValueTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/test/java/org/dspace/authority/AuthorityValueTest.java b/dspace-api/src/test/java/org/dspace/authority/AuthorityValueTest.java index 74b1e77766..81873d5cdb 100644 --- a/dspace-api/src/test/java/org/dspace/authority/AuthorityValueTest.java +++ b/dspace-api/src/test/java/org/dspace/authority/AuthorityValueTest.java @@ -1,7 +1,10 @@ package org.dspace.authority; +import java.time.Instant; import java.time.LocalDateTime; +import java.time.ZoneId; import java.time.ZoneOffset; +import java.time.ZonedDateTime; import static org.junit.Assert.assertNull; import java.util.Date; @@ -28,7 +31,7 @@ public class AuthorityValueTest { // Test a date-time without zone or offset. expected = Date.from(LocalDateTime.of(1957, 01, 27, 01, 23, 45) - .atZone(ZoneOffset.of("-05")) + .atZone(ZoneId.systemDefault()) .toInstant()); actual = AuthorityValue.stringToDate("1957-01-27T01:23:45"); assertEquals("Local date-time should convert", expected, actual); From 48b21728e7f1d42b966a823e9c05b3bff00c5c42 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 6 Jun 2023 17:14:41 -0400 Subject: [PATCH 007/168] I forgot the license headers on new tests. --- .../test/java/org/dspace/authority/AuthorityValueTest.java | 7 +++++++ .../crossref/CrossRefDateMetadataProcessorTest.java | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/dspace-api/src/test/java/org/dspace/authority/AuthorityValueTest.java b/dspace-api/src/test/java/org/dspace/authority/AuthorityValueTest.java index 81873d5cdb..ac0d7880e1 100644 --- a/dspace-api/src/test/java/org/dspace/authority/AuthorityValueTest.java +++ b/dspace-api/src/test/java/org/dspace/authority/AuthorityValueTest.java @@ -1,3 +1,10 @@ +/** + * 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.authority; import java.time.Instant; diff --git a/dspace-api/src/test/java/org/dspace/importer/external/crossref/CrossRefDateMetadataProcessorTest.java b/dspace-api/src/test/java/org/dspace/importer/external/crossref/CrossRefDateMetadataProcessorTest.java index 89b860d36f..fa9322d72d 100644 --- a/dspace-api/src/test/java/org/dspace/importer/external/crossref/CrossRefDateMetadataProcessorTest.java +++ b/dspace-api/src/test/java/org/dspace/importer/external/crossref/CrossRefDateMetadataProcessorTest.java @@ -1,3 +1,10 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ package org.dspace.importer.external.crossref; import java.util.Collection; From 05c349f0d5d745688a1021f2eb5143b3f67f4053 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 6 Jun 2023 17:30:44 -0400 Subject: [PATCH 008/168] Clean up mess left by fixing errors. --- .../java/org/dspace/authority/AuthorityValueTest.java | 8 +++----- .../crossref/CrossRefDateMetadataProcessorTest.java | 7 +++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/authority/AuthorityValueTest.java b/dspace-api/src/test/java/org/dspace/authority/AuthorityValueTest.java index ac0d7880e1..07c4b65f40 100644 --- a/dspace-api/src/test/java/org/dspace/authority/AuthorityValueTest.java +++ b/dspace-api/src/test/java/org/dspace/authority/AuthorityValueTest.java @@ -7,16 +7,14 @@ */ package org.dspace.authority; -import java.time.Instant; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import static org.junit.Assert.assertNull; - import java.util.Date; -import static org.junit.Assert.assertEquals; import org.junit.Test; /** diff --git a/dspace-api/src/test/java/org/dspace/importer/external/crossref/CrossRefDateMetadataProcessorTest.java b/dspace-api/src/test/java/org/dspace/importer/external/crossref/CrossRefDateMetadataProcessorTest.java index fa9322d72d..323856cd0a 100644 --- a/dspace-api/src/test/java/org/dspace/importer/external/crossref/CrossRefDateMetadataProcessorTest.java +++ b/dspace-api/src/test/java/org/dspace/importer/external/crossref/CrossRefDateMetadataProcessorTest.java @@ -7,12 +7,11 @@ */ package org.dspace.importer.external.crossref; +import static org.junit.Assert.assertEquals; + import java.util.Collection; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; + import org.junit.Test; -import static org.junit.Assert.*; /** * From 0cd92cd3b4d786cd32595b5593adc53a72fae6b3 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Wed, 7 Jun 2023 10:39:59 -0400 Subject: [PATCH 009/168] Instant refuses to be adjusted by a Period. --- .../dspace/builder/AbstractDSpaceObjectBuilder.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/test/java/org/dspace/builder/AbstractDSpaceObjectBuilder.java b/dspace-api/src/test/java/org/dspace/builder/AbstractDSpaceObjectBuilder.java index 4455668146..e7ebd8768e 100644 --- a/dspace-api/src/test/java/org/dspace/builder/AbstractDSpaceObjectBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/AbstractDSpaceObjectBuilder.java @@ -9,7 +9,9 @@ package org.dspace.builder; import java.sql.SQLException; import java.time.Instant; +import java.time.LocalDate; import java.time.Period; +import java.time.ZoneId; import java.util.Date; import org.apache.logging.log4j.Logger; @@ -28,6 +30,7 @@ import org.dspace.eperson.Group; * * @author Tom Desair (tom dot desair at atmire dot com) * @author Raf Ponsaerts (raf dot ponsaerts at atmire dot com) + * @param concrete type of DSpaceObject */ public abstract class AbstractDSpaceObjectBuilder extends AbstractBuilder { @@ -124,7 +127,12 @@ public abstract class AbstractDSpaceObjectBuilder protected > B setEmbargo(Period embargoPeriod, DSpaceObject dso) { // add policy just for anonymous try { - Date embargoDate = Date.from(Instant.now().plus(embargoPeriod)); + Instant embargoInstant = LocalDate.now() + .plus(embargoPeriod) + .atStartOfDay() + .atZone(ZoneId.systemDefault()) + .toInstant(); + Date embargoDate = Date.from(embargoInstant); return setOnlyReadPermission(dso, groupService.findByName(context, Group.ANONYMOUS), embargoDate); } catch (Exception e) { From 289221622adc628c67e004b048326b507acadbd9 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Mon, 12 Jun 2023 09:46:02 -0400 Subject: [PATCH 010/168] Fix Joda-style Period calculation in a newly-added class. --- .../src/test/java/org/dspace/builder/ItemBuilder.java | 10 ++++++++-- .../controller/LinksetRestControllerIT.java | 7 ++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java b/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java index f6190c5751..f4f504e60f 100644 --- a/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java @@ -282,8 +282,8 @@ public class ItemBuilder extends AbstractDSpaceObjectBuilder { } /** - * Withdrawn the item under build. Please note that an user need to be loggedin the context to avoid NPE during the - * creation of the provenance metadata + * Withdraw the item under build. Please note that the Context must be + * logged in to avoid NPE during the creation of the provenance metadata. * * @return the ItemBuilder */ @@ -292,6 +292,12 @@ public class ItemBuilder extends AbstractDSpaceObjectBuilder { return this; } + /** + * Set an embargo to end after some time from "now". + * + * @param embargoPeriod embargo starting "now", for this long. + * @return the ItemBuilder. + */ public ItemBuilder withEmbargoPeriod(Period embargoPeriod) { return setEmbargo(embargoPeriod, item); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java index 6d1d242cad..0bd80a3bf1 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java @@ -19,6 +19,7 @@ import java.text.DateFormat; import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.Date; +import java.time.Period; import org.apache.commons.codec.CharEncoding; import org.apache.commons.io.IOUtils; @@ -466,7 +467,7 @@ public class LinksetRestControllerIT extends AbstractControllerIntegrationTest { .withTitle("Withdrawn Item") .withMetadata("dc", "identifier", "doi", doi) .withIssueDate("2017-11-18") - .withEmbargoPeriod("2 week") + .withEmbargoPeriod(Period.ofWeeks(2)) .build(); context.restoreAuthSystemState(); @@ -837,7 +838,7 @@ public class LinksetRestControllerIT extends AbstractControllerIntegrationTest { .withName("Bitstream") .withDescription("description") .withMimeType(bitstreamMimeType) - .withEmbargoPeriod("6 months") + .withEmbargoPeriod(Period.ofMonths(6)) .build(); } context.restoreAuthSystemState(); @@ -949,7 +950,7 @@ public class LinksetRestControllerIT extends AbstractControllerIntegrationTest { .withTitle("Withdrawn Item") .withMetadata("dc", "identifier", "doi", doi) .withIssueDate("2017-11-18") - .withEmbargoPeriod("2 week") + .withEmbargoPeriod(Period.ofWeeks(2)) .build(); context.restoreAuthSystemState(); From 17c410ec2748181964bc4888086b1ec47114ef36 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Mon, 26 Jun 2023 11:16:13 -0400 Subject: [PATCH 011/168] Rebase messed up the order of imports. --- .../rest/signposting/controller/LinksetRestControllerIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java index 0bd80a3bf1..b363e4885e 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java @@ -18,8 +18,8 @@ import java.io.InputStream; import java.text.DateFormat; import java.text.MessageFormat; import java.text.SimpleDateFormat; -import java.util.Date; import java.time.Period; +import java.util.Date; import org.apache.commons.codec.CharEncoding; import org.apache.commons.io.IOUtils; From c4159cff0d31e4fb99d73914f6dd382abb6716e5 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 27 Jun 2023 17:01:32 -0400 Subject: [PATCH 012/168] More and better(?) documentation. --- .../src/main/java/org/dspace/core/Email.java | 116 +++++++++++++----- 1 file changed, 84 insertions(+), 32 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/core/Email.java b/dspace-api/src/main/java/org/dspace/core/Email.java index 998d934c95..a95407876e 100644 --- a/dspace-api/src/main/java/org/dspace/core/Email.java +++ b/dspace-api/src/main/java/org/dspace/core/Email.java @@ -57,26 +57,40 @@ import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; /** - * Class representing an e-mail message, also used to send e-mails. + * Class representing an e-mail message. The {@link send} method causes the + * assembled message to be formatted and sent. *

* Typical use: - *

+ *
+ * Email email = Email.getEmail(path);
+ * email.addRecipient("foo@bar.com");
+ * email.addArgument("John");
+ * email.addArgument("On the Testing of DSpace");
+ * email.send();
+ * 
+ * {@code path} is the filesystem path of an email template, typically in + * {@code ${dspace.dir}/config/emails/} and can include the subject -- see + * below. Templates are processed by + * Apache Velocity. They may contain VTL directives and property + * placeholders. *

- * Email email = new Email();
- * email.addRecipient("foo@bar.com");
- * email.addArgument("John");
- * email.addArgument("On the Testing of DSpace");
- * email.send();
- *

+ * {@link addArgument(string)} adds a property to the {@code params} array + * in the Velocity context, which can be used to replace placeholder tokens + * in the message. These arguments are indexed by number in the order they were + * added to the message. *

- * name is the name of an email template in - * dspace-dir/config/emails/ (which also includes the subject.) - * arg0 and arg1 are arguments to fill out the - * message with. - *

- * Emails are formatted using Apache Velocity. Headers such as Subject may be - * supplied by the template, by defining them using #set(). Example: - *

+ * The DSpace configuration properties are also available to templates as the + * array {@code config}, indexed by name. Example: {@code ${config.get('dspace.name')}} + *

+ * Recipients and attachments may be added as needed. See {@link addRecipient}, + * {@link addAttachment(File, String)}, and + * {@link addAttachment(InputStream, String, String)}. + *

+ * Headers such as Subject may be supplied by the template, by defining them + * using the VTL directive {@code #set()}. Only headers named in the DSpace + * configuration array property {@code mail.message.headers} will be added. + *

+ * Example: * *

  *
@@ -91,12 +105,14 @@ import org.dspace.services.factory.DSpaceServicesFactory;
  *
  *     Thank you for sending us your submission "${params[1]}".
  *
+ *     --
+ *     The ${config.get('dspace.name')} Team
+ *
  * 
* *

* If the example code above was used to send this mail, the resulting mail * would have the subject Example e-mail and the body would be: - *

* *
  *
@@ -105,7 +121,16 @@ import org.dspace.services.factory.DSpaceServicesFactory;
  *
  *     Thank you for sending us your submission "On the Testing of DSpace".
  *
+ *     --
+ *     The DSpace Team
+ *
  * 
+ *

+ * There are two ways to load a message body. One can create an instance of + * {@link Email} and call {@link setContent} on it, passing the body as a String. Or + * one can use the static factory method {@link getEmail} to load a file by its + * complete filesystem path. In either case the text will be loaded into a + * Velocity template. * * @author Robert Tansley * @author Jim Downing - added attachment handling code @@ -182,7 +207,7 @@ public class Email { } /** - * Add a recipient + * Add a recipient. * * @param email the recipient's email address */ @@ -205,7 +230,7 @@ public class Email { } /** - * Set the subject of the message + * Set the subject of the message. * * @param s the subject of the message */ @@ -214,7 +239,7 @@ public class Email { } /** - * Set the reply-to email address + * Set the reply-to email address. * * @param email the reply-to email address */ @@ -223,7 +248,7 @@ public class Email { } /** - * Fill out the next argument in the template + * Fill out the next argument in the template. * * @param arg the value for the next argument */ @@ -231,6 +256,13 @@ public class Email { arguments.add(arg); } + /** + * Add an attachment bodypart to the message from an external file. + * + * @param f reference to a file to be attached. + * @param name a name for the resulting bodypart in the message's MIME + * structure. + */ public void addAttachment(File f, String name) { attachments.add(new FileAttachment(f, name)); } @@ -238,6 +270,17 @@ public class Email { /** When given a bad MIME type for an attachment, use this instead. */ private static final String DEFAULT_ATTACHMENT_TYPE = "application/octet-stream"; + /** + * Add an attachment bodypart to the message from a byte stream. + * + * @param is the content of this stream will become the content of the + * bodypart. + * @param name a name for the resulting bodypart in the message's MIME + * structure. + * @param mimetype the MIME type of the resulting bodypart, such as + * "text/pdf". If {@code null} it will default to + * "application/octet-stream", which is MIME for "unknown format". + */ public void addAttachment(InputStream is, String name, String mimetype) { if (null == mimetype) { LOG.error("Null MIME type replaced with '" + DEFAULT_ATTACHMENT_TYPE @@ -257,6 +300,11 @@ public class Email { moreAttachments.add(new InputStreamAttachment(is, name, mimetype)); } + /** + * Set the character set of the message. + * + * @param cs the name of a character set, such as "UTF-8" or "EUC-JP". + */ public void setCharset(String cs) { charset = cs; } @@ -447,6 +495,9 @@ public class Email { /** * Get the VTL template for an email message. The message is suitable * for inserting values using Apache Velocity. + *

+ * Note that everything is stored here, so that only send() throws a + * MessagingException. * * @param emailFile * full name for the email template, for example "/dspace/config/emails/register". @@ -484,15 +535,6 @@ public class Email { } return email; } - /* - * Implementation note: It might be necessary to add a quick utility method - * like "send(to, subject, message)". We'll see how far we get without it - - * having all emails as templates in the config allows customisation and - * internationalisation. - * - * Note that everything is stored and the run in send() so that only send() - * throws a MessagingException. - */ /** * Test method to send an email to check email server settings @@ -547,7 +589,7 @@ public class Email { } /** - * Utility struct class for handling file attachments. + * Utility record class for handling file attachments. * * @author ojd20 */ @@ -563,7 +605,7 @@ public class Email { } /** - * Utility struct class for handling file attachments. + * Utility record class for handling file attachments. * * @author Adán Román Ruiz at arvo.es */ @@ -580,6 +622,8 @@ public class Email { } /** + * Wrap an {@link InputStream} in a {@link DataSource}. + * * @author arnaldo */ public static class InputStreamDataSource implements DataSource { @@ -587,6 +631,14 @@ public class Email { private final String contentType; private final ByteArrayOutputStream baos; + /** + * Consume the content of an InputStream and store it in a local buffer. + * + * @param name give the DataSource a name. + * @param contentType the DataSource contains this type of data. + * @param inputStream content to be buffered in the DataSource. + * @throws IOException if the stream cannot be read. + */ InputStreamDataSource(String name, String contentType, InputStream inputStream) throws IOException { this.name = name; this.contentType = contentType; From a30454ca5a523c532e353a63270bb5a32fbd3e70 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 27 Jun 2023 17:05:21 -0400 Subject: [PATCH 013/168] Don't clear the list of accepted embedded message header properties. Why was this cleared? --- dspace-api/src/main/java/org/dspace/core/Email.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/core/Email.java b/dspace-api/src/main/java/org/dspace/core/Email.java index a95407876e..a64a85a073 100644 --- a/dspace-api/src/main/java/org/dspace/core/Email.java +++ b/dspace-api/src/main/java/org/dspace/core/Email.java @@ -378,13 +378,12 @@ public class Email { // No template and no content -- PANIC!!! throw new MessagingException("Email has no body"); } - // No template, so use a String of content. + // No existing template, so use a String of content. StringResourceRepository repo = (StringResourceRepository) templateEngine.getApplicationAttribute(RESOURCE_REPOSITORY_NAME); repo.putStringResource(contentName, content); // Turn content into a template. template = templateEngine.getTemplate(contentName); - templateHeaders = new String[] {}; } StringWriter writer = new StringWriter(); From d939786a46ed7bc141ea240a86c8a509161ca4b5 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 27 Jun 2023 17:23:26 -0400 Subject: [PATCH 014/168] Simplify and modernize the code. Store content directly in the template. --- .../src/main/java/org/dspace/core/Email.java | 58 ++++++++----------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/core/Email.java b/dspace-api/src/main/java/org/dspace/core/Email.java index a64a85a073..f6df740a53 100644 --- a/dspace-api/src/main/java/org/dspace/core/Email.java +++ b/dspace-api/src/main/java/org/dspace/core/Email.java @@ -21,7 +21,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.Enumeration; -import java.util.Iterator; import java.util.List; import java.util.Properties; import javax.activation.DataHandler; @@ -41,7 +40,6 @@ import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; import javax.mail.internet.ParseException; -import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.velocity.Template; @@ -140,7 +138,6 @@ public class Email { /** * The content of the message */ - private String content; private String contentName; /** @@ -201,7 +198,6 @@ public class Email { moreAttachments = new ArrayList<>(10); subject = ""; template = null; - content = ""; replyTo = null; charset = null; } @@ -221,12 +217,20 @@ public class Email { * "Subject:" line must be stripped. * * @param name a name for this message body - * @param cnt the content of the message + * @param content the content of the message */ - public void setContent(String name, String cnt) { - content = cnt; + public void setContent(String name, String content) { contentName = name; arguments.clear(); + + VelocityEngine templateEngine = new VelocityEngine(); + templateEngine.init(VELOCITY_PROPERTIES); + + StringResourceRepository repo = (StringResourceRepository) + templateEngine.getApplicationAttribute(RESOURCE_REPOSITORY_NAME); + repo.putStringResource(contentName, content); + // Turn content into a template. + template = templateEngine.getTemplate(contentName); } /** @@ -328,15 +332,20 @@ public class Email { * {@code mail.message.headers} then that name and its value will be added * to the message's headers. * - *

"subject" is treated specially: if {@link setSubject()} has not been called, - * the value of any "subject" property will be used as if setSubject had - * been called with that value. Thus a template may define its subject, but - * the caller may override it. + *

"subject" is treated specially: if {@link setSubject()} has not been + * called, the value of any "subject" property will be used as if setSubject + * had been called with that value. Thus a template may define its subject, + * but the caller may override it. * * @throws MessagingException if there was a problem sending the mail. * @throws IOException if IO error */ public void send() throws MessagingException, IOException { + if (null == template) { + // No template -- no content -- PANIC!!! + throw new MessagingException("Email has no body"); + } + ConfigurationService config = DSpaceServicesFactory.getInstance().getConfigurationService(); @@ -356,36 +365,18 @@ public class Email { MimeMessage message = new MimeMessage(session); // Set the recipients of the message - Iterator i = recipients.iterator(); - - while (i.hasNext()) { - message.addRecipient(Message.RecipientType.TO, new InternetAddress( - i.next())); + for (String recipient : recipients) { + message.addRecipient(Message.RecipientType.TO, + new InternetAddress(recipient)); } // Get headers defined by the template. String[] templateHeaders = config.getArrayProperty("mail.message.headers"); // Format the mail message body - VelocityEngine templateEngine = new VelocityEngine(); - templateEngine.init(VELOCITY_PROPERTIES); - VelocityContext vctx = new VelocityContext(); vctx.put("config", new UnmodifiableConfigurationService(config)); vctx.put("params", Collections.unmodifiableList(arguments)); - if (null == template) { - if (StringUtils.isBlank(content)) { - // No template and no content -- PANIC!!! - throw new MessagingException("Email has no body"); - } - // No existing template, so use a String of content. - StringResourceRepository repo = (StringResourceRepository) - templateEngine.getApplicationAttribute(RESOURCE_REPOSITORY_NAME); - repo.putStringResource(contentName, content); - // Turn content into a template. - template = templateEngine.getTemplate(contentName); - } - StringWriter writer = new StringWriter(); try { template.merge(vctx, writer); @@ -452,7 +443,8 @@ public class Email { // add the stream messageBodyPart = new MimeBodyPart(); messageBodyPart.setDataHandler(new DataHandler( - new InputStreamDataSource(attachment.name,attachment.mimetype,attachment.is))); + new InputStreamDataSource(attachment.name, + attachment.mimetype, attachment.is))); messageBodyPart.setFileName(attachment.name); multipart.addBodyPart(messageBodyPart); } From f66ca33b0627c1b0789c9c3ce407463f5dc3356e Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Wed, 28 Jun 2023 10:36:30 -0400 Subject: [PATCH 015/168] Rename test suites that are really integration testing. --- ...sswordValidatorTest.java => RegexPasswordValidatorIT.java} | 2 +- ...ningTest.java => RelationshipServiceImplVersioningIT.java} | 2 +- ...ationshipsTest.java => VersioningWithRelationshipsIT.java} | 2 +- ...elationshipDAOImplTest.java => RelationshipDAOImplIT.java} | 4 ++-- ...hipTypeDAOImplTest.java => RelationshipTypeDAOImplIT.java} | 4 ++-- .../service/{ItemServiceTest.java => ItemServiceIT.java} | 4 ++-- ...iderTest.java => VersionedHandleIdentifierProviderIT.java} | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) rename dspace-api/src/test/java/org/dspace/authorize/{RegexPasswordValidatorTest.java => RegexPasswordValidatorIT.java} (97%) rename dspace-api/src/test/java/org/dspace/content/{RelationshipServiceImplVersioningTest.java => RelationshipServiceImplVersioningIT.java} (99%) rename dspace-api/src/test/java/org/dspace/content/{VersioningWithRelationshipsTest.java => VersioningWithRelationshipsIT.java} (99%) rename dspace-api/src/test/java/org/dspace/content/dao/{RelationshipDAOImplTest.java => RelationshipDAOImplIT.java} (98%) rename dspace-api/src/test/java/org/dspace/content/dao/{RelationshipTypeDAOImplTest.java => RelationshipTypeDAOImplIT.java} (98%) rename dspace-api/src/test/java/org/dspace/content/service/{ItemServiceTest.java => ItemServiceIT.java} (99%) rename dspace-api/src/test/java/org/dspace/identifier/{VersionedHandleIdentifierProviderTest.java => VersionedHandleIdentifierProviderIT.java} (97%) diff --git a/dspace-api/src/test/java/org/dspace/authorize/RegexPasswordValidatorTest.java b/dspace-api/src/test/java/org/dspace/authorize/RegexPasswordValidatorIT.java similarity index 97% rename from dspace-api/src/test/java/org/dspace/authorize/RegexPasswordValidatorTest.java rename to dspace-api/src/test/java/org/dspace/authorize/RegexPasswordValidatorIT.java index df333fa500..7286fb8e83 100644 --- a/dspace-api/src/test/java/org/dspace/authorize/RegexPasswordValidatorTest.java +++ b/dspace-api/src/test/java/org/dspace/authorize/RegexPasswordValidatorIT.java @@ -26,7 +26,7 @@ import org.mockito.junit.MockitoJUnitRunner; * @author Luca Giamminonni (luca.giamminonni at 4science.it) */ @RunWith(MockitoJUnitRunner.class) -public class RegexPasswordValidatorTest extends AbstractIntegrationTest { +public class RegexPasswordValidatorIT extends AbstractIntegrationTest { @Mock private ConfigurationService configurationService; diff --git a/dspace-api/src/test/java/org/dspace/content/RelationshipServiceImplVersioningTest.java b/dspace-api/src/test/java/org/dspace/content/RelationshipServiceImplVersioningIT.java similarity index 99% rename from dspace-api/src/test/java/org/dspace/content/RelationshipServiceImplVersioningTest.java rename to dspace-api/src/test/java/org/dspace/content/RelationshipServiceImplVersioningIT.java index d42213da2c..1b6f23032d 100644 --- a/dspace-api/src/test/java/org/dspace/content/RelationshipServiceImplVersioningTest.java +++ b/dspace-api/src/test/java/org/dspace/content/RelationshipServiceImplVersioningIT.java @@ -26,7 +26,7 @@ import org.dspace.services.factory.DSpaceServicesFactory; import org.junit.Before; import org.junit.Test; -public class RelationshipServiceImplVersioningTest extends AbstractIntegrationTestWithDatabase { +public class RelationshipServiceImplVersioningIT extends AbstractIntegrationTestWithDatabase { private RelationshipService relationshipService; private RelationshipDAO relationshipDAO; diff --git a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsTest.java b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java similarity index 99% rename from dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsTest.java rename to dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java index 528568c4e5..44653300e0 100644 --- a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsTest.java +++ b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java @@ -70,7 +70,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; -public class VersioningWithRelationshipsTest extends AbstractIntegrationTestWithDatabase { +public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDatabase { private final RelationshipService relationshipService = ContentServiceFactory.getInstance().getRelationshipService(); diff --git a/dspace-api/src/test/java/org/dspace/content/dao/RelationshipDAOImplTest.java b/dspace-api/src/test/java/org/dspace/content/dao/RelationshipDAOImplIT.java similarity index 98% rename from dspace-api/src/test/java/org/dspace/content/dao/RelationshipDAOImplTest.java rename to dspace-api/src/test/java/org/dspace/content/dao/RelationshipDAOImplIT.java index b6f5da6be0..2d08223b2e 100644 --- a/dspace-api/src/test/java/org/dspace/content/dao/RelationshipDAOImplTest.java +++ b/dspace-api/src/test/java/org/dspace/content/dao/RelationshipDAOImplIT.java @@ -39,9 +39,9 @@ import org.junit.Test; * Created by: Andrew Wood * Date: 20 Sep 2019 */ -public class RelationshipDAOImplTest extends AbstractIntegrationTest { +public class RelationshipDAOImplIT extends AbstractIntegrationTest { - private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(RelationshipDAOImplTest.class); + private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(RelationshipDAOImplIT.class); private Relationship relationship; diff --git a/dspace-api/src/test/java/org/dspace/content/dao/RelationshipTypeDAOImplTest.java b/dspace-api/src/test/java/org/dspace/content/dao/RelationshipTypeDAOImplIT.java similarity index 98% rename from dspace-api/src/test/java/org/dspace/content/dao/RelationshipTypeDAOImplTest.java rename to dspace-api/src/test/java/org/dspace/content/dao/RelationshipTypeDAOImplIT.java index 3fff6fec47..ff7d03b49f 100644 --- a/dspace-api/src/test/java/org/dspace/content/dao/RelationshipTypeDAOImplTest.java +++ b/dspace-api/src/test/java/org/dspace/content/dao/RelationshipTypeDAOImplIT.java @@ -35,9 +35,9 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -public class RelationshipTypeDAOImplTest extends AbstractIntegrationTest { +public class RelationshipTypeDAOImplIT extends AbstractIntegrationTest { - private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(RelationshipTypeDAOImplTest.class); + private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(RelationshipTypeDAOImplIT.class); private Relationship relationship; diff --git a/dspace-api/src/test/java/org/dspace/content/service/ItemServiceTest.java b/dspace-api/src/test/java/org/dspace/content/service/ItemServiceIT.java similarity index 99% rename from dspace-api/src/test/java/org/dspace/content/service/ItemServiceTest.java rename to dspace-api/src/test/java/org/dspace/content/service/ItemServiceIT.java index 50b4d3f3b4..e40577ef36 100644 --- a/dspace-api/src/test/java/org/dspace/content/service/ItemServiceTest.java +++ b/dspace-api/src/test/java/org/dspace/content/service/ItemServiceIT.java @@ -54,8 +54,8 @@ import org.dspace.versioning.service.VersioningService; import org.junit.Before; import org.junit.Test; -public class ItemServiceTest extends AbstractIntegrationTestWithDatabase { - private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(ItemServiceTest.class); +public class ItemServiceIT extends AbstractIntegrationTestWithDatabase { + private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(ItemServiceIT.class); protected RelationshipService relationshipService = ContentServiceFactory.getInstance().getRelationshipService(); protected RelationshipTypeService relationshipTypeService = ContentServiceFactory.getInstance() diff --git a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderTest.java b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java similarity index 97% rename from dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderTest.java rename to dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java index 1bc6bf1408..7e549f6cae 100644 --- a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderTest.java +++ b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java @@ -27,7 +27,7 @@ import org.dspace.services.factory.DSpaceServicesFactory; import org.junit.Before; import org.junit.Test; -public class VersionedHandleIdentifierProviderTest extends AbstractIntegrationTestWithDatabase { +public class VersionedHandleIdentifierProviderIT extends AbstractIntegrationTestWithDatabase { private ServiceManager serviceManager; private IdentifierServiceImpl identifierService; From db81d758a947a9bdbb63fea9e872bc9b52a377ff Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Wed, 28 Jun 2023 19:10:37 +0100 Subject: [PATCH 016/168] OAI: add support to extract embargo from bitstreams and expose it in OAI metadata --- .../java/org/dspace/xoai/util/ItemUtils.java | 32 +++++++++++++++++++ .../oai/metadataFormats/uketd_dc.xsl | 5 +++ 2 files changed, 37 insertions(+) diff --git a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java index 955c3a78c3..f15e82ce52 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java @@ -11,6 +11,8 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.List; import com.lyncode.xoai.dataprovider.xml.xoai.Element; @@ -21,6 +23,9 @@ import org.apache.logging.log4j.Logger; import org.dspace.app.util.factory.UtilServiceFactory; import org.dspace.app.util.service.MetadataExposureService; import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.ResourcePolicy; +import org.dspace.authorize.factory.AuthorizeServiceFactory; +import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.Bitstream; import org.dspace.content.Bundle; import org.dspace.content.Item; @@ -34,6 +39,9 @@ import org.dspace.content.service.RelationshipService; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.core.Utils; +import org.dspace.eperson.Group; +import org.dspace.eperson.factory.EPersonServiceFactory; +import org.dspace.eperson.service.GroupService; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.xoai.data.DSpaceItem; @@ -57,6 +65,9 @@ public class ItemUtils { private static final BitstreamService bitstreamService = ContentServiceFactory.getInstance().getBitstreamService(); + private static final AuthorizeService authorizeService = + AuthorizeServiceFactory.getInstance().getAuthorizeService(); + private static final ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); /** @@ -136,6 +147,9 @@ public class ItemUtils { if (description != null) { bitstream.getField().add(createValue("description", description)); } + // Add bitstream embargo information (READ policy present, for Anonymous group with a start date) + addEmbargoField(context, bit, bitstream); + bitstream.getField().add(createValue("format", bit.getFormat(context).getMIMEType())); bitstream.getField().add(createValue("size", "" + bit.getSizeBytes())); bitstream.getField().add(createValue("url", url)); @@ -148,6 +162,24 @@ public class ItemUtils { return bundles; } + private static void addEmbargoField(Context context, Bitstream bit, Element bitstream) throws SQLException { + GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); + Group anonymousGroup = groupService.findByName(context, Group.ANONYMOUS); + List policies = authorizeService.findPoliciesByDSOAndType(context, bit, ResourcePolicy.TYPE_CUSTOM); + + for (ResourcePolicy policy : policies) { + if (policy.getGroup() == anonymousGroup && policy.getAction() == Constants.READ) { + Date startDate = policies.get(0).getStartDate(); + + if (startDate != null && startDate.after(new Date())) { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); + bitstream.getField().add( + createValue("embargo", formatter.format(startDate))); + } + } + } + } + private static Element createLicenseElement(Context context, Item item) throws SQLException, AuthorizeException, IOException { Element license = create("license"); diff --git a/dspace/config/crosswalks/oai/metadataFormats/uketd_dc.xsl b/dspace/config/crosswalks/oai/metadataFormats/uketd_dc.xsl index a3a4e66670..b9d81aef5d 100644 --- a/dspace/config/crosswalks/oai/metadataFormats/uketd_dc.xsl +++ b/dspace/config/crosswalks/oai/metadataFormats/uketd_dc.xsl @@ -123,6 +123,11 @@ + + + + From 51e60fbcf92ea731c4e355c9cf080d251ffbf68f Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Wed, 28 Jun 2023 19:27:35 +0100 Subject: [PATCH 017/168] ItemUtils.java: added method doc --- .../main/java/org/dspace/xoai/util/ItemUtils.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java index f15e82ce52..b1b949770d 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java @@ -162,10 +162,17 @@ public class ItemUtils { return bundles; } - private static void addEmbargoField(Context context, Bitstream bit, Element bitstream) throws SQLException { + /** + * This method will add embargo metadata for all bitstreams with an active embargo + * @param context + * @param bitstream the bitstream object + * @param bitstreamEl the bitstream metadata object to add embargo value to + * @throws SQLException + */ + private static void addEmbargoField(Context context, Bitstream bitstream, Element bitstreamEl) throws SQLException { GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); Group anonymousGroup = groupService.findByName(context, Group.ANONYMOUS); - List policies = authorizeService.findPoliciesByDSOAndType(context, bit, ResourcePolicy.TYPE_CUSTOM); + List policies = authorizeService.findPoliciesByDSOAndType(context, bitstream, ResourcePolicy.TYPE_CUSTOM); for (ResourcePolicy policy : policies) { if (policy.getGroup() == anonymousGroup && policy.getAction() == Constants.READ) { @@ -173,7 +180,7 @@ public class ItemUtils { if (startDate != null && startDate.after(new Date())) { SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); - bitstream.getField().add( + bitstreamEl.getField().add( createValue("embargo", formatter.format(startDate))); } } From 538be7f09ba790a4ab7099e7027e1e8f6a9c62ea Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Thu, 29 Jun 2023 09:06:08 +0100 Subject: [PATCH 018/168] ItemUtils.java: improved method to account for multiple embargo policies and select the longest embargo --- .../java/org/dspace/xoai/util/ItemUtils.java | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java index b1b949770d..107454ecd0 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java @@ -14,6 +14,8 @@ import java.sql.SQLException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; +import java.util.ArrayList; +import java.util.Collections; import com.lyncode.xoai.dataprovider.xml.xoai.Element; import com.lyncode.xoai.dataprovider.xml.xoai.Metadata; @@ -163,7 +165,8 @@ public class ItemUtils { } /** - * This method will add embargo metadata for all bitstreams with an active embargo + * This method will add embargo metadata for a give bitstream with an active embargo. + * It will parse of relevant policies and select the longest active embargo * @param context * @param bitstream the bitstream object * @param bitstreamEl the bitstream metadata object to add embargo value to @@ -174,17 +177,23 @@ public class ItemUtils { Group anonymousGroup = groupService.findByName(context, Group.ANONYMOUS); List policies = authorizeService.findPoliciesByDSOAndType(context, bitstream, ResourcePolicy.TYPE_CUSTOM); + List embargoDates = new ArrayList<>(); + // Account for cases where there could be more than one embargo policy for (ResourcePolicy policy : policies) { if (policy.getGroup() == anonymousGroup && policy.getAction() == Constants.READ) { - Date startDate = policies.get(0).getStartDate(); - + Date startDate = policy.getStartDate(); if (startDate != null && startDate.after(new Date())) { - SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); - bitstreamEl.getField().add( - createValue("embargo", formatter.format(startDate))); + embargoDates.add(startDate); } } } + if (embargoDates.size() >= 1) { + // Sort array of dates to extract the longest embargo + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); + Collections.sort(embargoDates, Date::compareTo); + bitstreamEl.getField().add( + createValue("embargo", formatter.format(embargoDates.get(embargoDates.size() - 1)))); + } } private static Element createLicenseElement(Context context, Item item) From 2b5c4a5f4a80fb8a311437676bc351dc75d257f0 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Thu, 29 Jun 2023 13:00:59 +0200 Subject: [PATCH 019/168] Improve performance of automatic discovery re-index after database changes --- .../src/main/java/org/dspace/storage/rdbms/DatabaseUtils.java | 1 + 1 file changed, 1 insertion(+) diff --git a/dspace-api/src/main/java/org/dspace/storage/rdbms/DatabaseUtils.java b/dspace-api/src/main/java/org/dspace/storage/rdbms/DatabaseUtils.java index 89010a7308..0732eea2a0 100644 --- a/dspace-api/src/main/java/org/dspace/storage/rdbms/DatabaseUtils.java +++ b/dspace-api/src/main/java/org/dspace/storage/rdbms/DatabaseUtils.java @@ -1465,6 +1465,7 @@ public class DatabaseUtils { Context context = null; try { context = new Context(); + context.setMode(Context.Mode.READ_ONLY); context.turnOffAuthorisationSystem(); log.info( "Post database migration, reindexing all content in Discovery search and browse engine"); From af55090245baf3313407ae1c103b2db53b97b9d4 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Thu, 29 Jun 2023 14:31:39 +0200 Subject: [PATCH 020/168] Ignore vocabulary indexes without discovery facet to avoid NPE --- .../content/authority/ChoiceAuthorityServiceImpl.java | 6 ++++++ 1 file changed, 6 insertions(+) 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 ec8f8769be..4cac1da314 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 @@ -585,6 +585,12 @@ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService break; } } + + // If there is no matching facet, return null to ignore this vocabulary index + if (matchingFacet == null) { + return null; + } + DSpaceControlledVocabularyIndex vocabularyIndex = new DSpaceControlledVocabularyIndex((DSpaceControlledVocabulary) source, metadataFields, matchingFacet); From ea4565bd6016d795b2cb89834c65acc8e0e0c977 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 30 Jun 2023 14:37:06 -0500 Subject: [PATCH 021/168] [maven-release-plugin] prepare branch dspace-7_x --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4c9799243f..3873fa372c 100644 --- a/pom.xml +++ b/pom.xml @@ -1927,7 +1927,7 @@ scm:git:git@github.com:DSpace/DSpace.git scm:git:git@github.com:DSpace/DSpace.git git@github.com:DSpace/DSpace.git - HEAD + dspace-7_x From 2b42811e971853c85caf8487411de79515e2bcf3 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 30 Jun 2023 14:37:09 -0500 Subject: [PATCH 022/168] [maven-release-plugin] prepare for next development iteration --- dspace-api/pom.xml | 2 +- dspace-iiif/pom.xml | 2 +- dspace-oai/pom.xml | 2 +- dspace-rdf/pom.xml | 2 +- dspace-rest/pom.xml | 4 ++-- dspace-server-webapp/pom.xml | 2 +- dspace-services/pom.xml | 2 +- dspace-sword/pom.xml | 2 +- dspace-swordv2/pom.xml | 2 +- dspace/modules/additions/pom.xml | 2 +- dspace/modules/pom.xml | 2 +- dspace/modules/rest/pom.xml | 2 +- dspace/modules/server/pom.xml | 2 +- dspace/pom.xml | 2 +- pom.xml | 32 ++++++++++++++++---------------- 15 files changed, 31 insertions(+), 31 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index c05546d569..6379572ba4 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT .. diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 30e20489b8..6e93a88e13 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 08e732d457..808940eb7b 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index 09c3e704de..f21381eb4e 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT .. diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index 320567f3c1..d7daf92aba 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -3,7 +3,7 @@ org.dspace dspace-rest war - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT DSpace (Deprecated) REST Webapp DSpace RESTful Web Services API. NOTE: this REST API is DEPRECATED. Please consider using the REST API in the dspace-server-webapp instead! @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 2221f9ca0e..6e07365499 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index fe7c6ab8b6..e3db95abf5 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 777041775d..349ac3abd0 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index c3fbdecb79..740351d5db 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT .. diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index dd6dbcb1ff..922e0f0fe5 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT .. diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index e66622ce6b..3a9e5caa8a 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT ../../pom.xml diff --git a/dspace/modules/rest/pom.xml b/dspace/modules/rest/pom.xml index 14c6ff93f7..d3713454f6 100644 --- a/dspace/modules/rest/pom.xml +++ b/dspace/modules/rest/pom.xml @@ -13,7 +13,7 @@ org.dspace modules - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index bebb6d183d..d0a25332aa 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -13,7 +13,7 @@ just adding new jar in the classloader modules org.dspace - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT .. diff --git a/dspace/pom.xml b/dspace/pom.xml index bfa21f5d66..ac7af682da 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 3873fa372c..a082bd0524 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT DSpace Parent Project DSpace open source software is a turnkey institutional repository application. @@ -872,14 +872,14 @@ org.dspace dspace-rest - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT jar classes org.dspace dspace-rest - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT war @@ -1030,69 +1030,69 @@ org.dspace dspace-api - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT org.dspace dspace-api test-jar - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT test org.dspace.modules additions - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT org.dspace dspace-sword - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT org.dspace dspace-swordv2 - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT org.dspace dspace-oai - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT org.dspace dspace-services - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT org.dspace dspace-server-webapp test-jar - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT test org.dspace dspace-rdf - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT org.dspace dspace-iiif - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT org.dspace dspace-server-webapp - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT jar classes org.dspace dspace-server-webapp - 7.6.1-SNAPSHOT + 8.0-SNAPSHOT war @@ -1927,7 +1927,7 @@ scm:git:git@github.com:DSpace/DSpace.git scm:git:git@github.com:DSpace/DSpace.git git@github.com:DSpace/DSpace.git - dspace-7_x + HEAD From 8633799b654e0c9e56a3f11411488d5fa3f9c267 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 31 May 2023 16:45:54 +0000 Subject: [PATCH 023/168] Update dependency com.flipkart.zjsonpatch:zjsonpatch to v0.4.14 --- dspace-server-webapp/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 6e07365499..6cec67baa3 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -288,7 +288,7 @@ com.flipkart.zjsonpatch zjsonpatch - 0.4.6 + 0.4.14 From 3613320e2bc0316f46af91b491c79a4e4b715a35 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 10:08:29 +0000 Subject: [PATCH 024/168] Update dependency de.digitalcollections.iiif:iiif-apis to v0.3.10 --- dspace-iiif/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 6e93a88e13..2f34671139 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -93,7 +93,7 @@ de.digitalcollections.iiif iiif-apis - 0.3.9 + 0.3.10 org.javassist From ad05c6a230b01f2a609a33a1f9a9607715fea2d2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 09:58:13 +0000 Subject: [PATCH 025/168] Update dependency com.h2database:h2 to v2.1.214 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a082bd0524..65e54dc61a 100644 --- a/pom.xml +++ b/pom.xml @@ -1694,7 +1694,7 @@ com.h2database h2 - 2.1.210 + 2.1.214 test From 2cc37373805436398586666c267bf06f1a77ccc7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 10:08:34 +0000 Subject: [PATCH 026/168] Update dependency dnsjava:dnsjava to v2.1.9 --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 6379572ba4..ea284c956c 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -632,7 +632,7 @@ dnsjava dnsjava - 2.1.7 + 2.1.9 From 8d65ad4f3e2832647b9eb29469f863e8a1497bab Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Fri, 2 Jun 2023 19:36:59 +0300 Subject: [PATCH 027/168] pom.xml: bump com.google.code.findbugs:jsr305 Closes: https://github.com/alanorth/DSpace/pull/12 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 65e54dc61a..be5b6e2fb0 100644 --- a/pom.xml +++ b/pom.xml @@ -1738,7 +1738,7 @@ com.google.code.findbugs jsr305 - 3.0.1 + 3.0.2 provided From 193fdd5118da8e3cb367b983143d545ae0d327b0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 18:40:03 +0000 Subject: [PATCH 028/168] Update dependency javax.cache:cache-api to v1.1.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index be5b6e2fb0..d6a9d8c4b2 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ 1.3.2 2.3.1 2.3.1 - 1.1.0 + 1.1.1 9.4.51.v20230217 2.20.0 From d8a4694210d2ce95c08cad227d362e64d938aeea Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 16:54:30 +0000 Subject: [PATCH 029/168] Update dependency net.handle:handle to v9.3.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d6a9d8c4b2..4097404e00 100644 --- a/pom.xml +++ b/pom.xml @@ -1358,7 +1358,7 @@ net.handle handle - 9.3.0 + 9.3.1 From aedf6e154691b967189ea457dee745204c5beba1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 16:57:30 +0000 Subject: [PATCH 030/168] Update dependency org.apache.ant:ant to v1.10.13 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4097404e00..9c2021cb2d 100644 --- a/pom.xml +++ b/pom.xml @@ -1336,7 +1336,7 @@ org.apache.ant ant - 1.10.11 + 1.10.13 org.apache.jena From 0f6c3e7b81aa890bf1d5ac76722a2ea0ce34f7a6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 15:00:31 +0000 Subject: [PATCH 031/168] Update dependency junit:junit to v4.13.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9c2021cb2d..c2411d20c0 100644 --- a/pom.xml +++ b/pom.xml @@ -1669,7 +1669,7 @@ junit junit - 4.13.1 + 4.13.2 test From 25718ae351ad2b02115b44ec71c5bbe01e558c96 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Fri, 2 Jun 2023 22:25:29 +0300 Subject: [PATCH 032/168] pom.xml: bump org.apache.httpcomponents Closes: https://github.com/alanorth/DSpace/pull/21 Closes: https://github.com/alanorth/DSpace/pull/22 Closes: https://github.com/alanorth/DSpace/pull/23 --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index c2411d20c0..9737ca1680 100644 --- a/pom.xml +++ b/pom.xml @@ -1626,17 +1626,17 @@ org.apache.httpcomponents httpcore - 4.4.15 + 4.4.16 org.apache.httpcomponents httpclient - 4.5.13 + 4.5.14 org.apache.httpcomponents httpmime - 4.5.13 + 4.5.14 org.slf4j From b8308ef04924c5fad903949ee995b1d407103b59 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 19:52:57 +0000 Subject: [PATCH 033/168] Update dependency org.ehcache:ehcache to v3.10.8 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9737ca1680..e64b994ce8 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ 42.6.0 8.11.2 - 3.4.0 + 3.10.8 2.10.0 2.13.4 From 5b7ab0b0044228204bc8993ba75f98ec69ab9dea Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 19:37:49 +0000 Subject: [PATCH 034/168] Update dependency org.flywaydb:flyway-core to v8.5.13 --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index ea284c956c..0ea86b6e10 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -668,7 +668,7 @@ org.flywaydb flyway-core - 8.4.4 + 8.5.13 From bdd9866cd3851390a00b0664203c605d1b943a2d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 20:33:06 +0000 Subject: [PATCH 035/168] Update dependency com.opencsv:opencsv to v5.7.1 --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 0ea86b6e10..aab892ee04 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -791,7 +791,7 @@ com.opencsv opencsv - 5.6 + 5.7.1 From 3cd5acc02744da9ba1108423b0949be6157f914d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 20:32:55 +0000 Subject: [PATCH 036/168] Update dependency org.glassfish.jaxb:jaxb-runtime to v2.3.8 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e64b994ce8..191179a4a7 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ 2.13.4.2 1.3.2 2.3.1 - 2.3.1 + 2.3.8 1.1.1 9.4.51.v20230217 From 706bf06a6e688ec91baa8912d4204eb83227d4d2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 19:32:15 +0000 Subject: [PATCH 037/168] Update dependency org.apache.james:apache-mime4j-core to v0.8.9 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 191179a4a7..bdf7caad51 100644 --- a/pom.xml +++ b/pom.xml @@ -1302,7 +1302,7 @@ org.apache.james apache-mime4j-core - 0.8.4 + 0.8.9 From 08a5c74848820223f6e9a2a8e7c10d44eae36f89 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Jun 2023 14:47:00 +0000 Subject: [PATCH 038/168] Update dependency commons-io:commons-io to v2.12.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bdf7caad51..dde2af21aa 100644 --- a/pom.xml +++ b/pom.xml @@ -1489,7 +1489,7 @@ commons-io commons-io - 2.7 + 2.12.0 org.apache.commons From 021a39771c8508cb9b27878f0dfab51991dba0f7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Jun 2023 14:55:45 +0000 Subject: [PATCH 039/168] Update dependency commons-validator:commons-validator to v1.7 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dde2af21aa..f7fd25bc64 100644 --- a/pom.xml +++ b/pom.xml @@ -1516,7 +1516,7 @@ commons-validator commons-validator - 1.5.0 + 1.7 joda-time From 6f18a6b2f1f912474c1bd80f14ed9d01f32a7fe0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Jun 2023 14:55:40 +0000 Subject: [PATCH 040/168] Update dependency commons-codec:commons-codec to v1.15 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f7fd25bc64..f45ccb3c18 100644 --- a/pom.xml +++ b/pom.xml @@ -1456,7 +1456,7 @@ commons-codec commons-codec - 1.10 + 1.15 org.apache.commons From 34ea02f29f5da493d797080bab39b1536bc64e64 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Jun 2023 15:27:30 +0000 Subject: [PATCH 041/168] Update dependency commons-cli:commons-cli to v1.5.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f45ccb3c18..a226dd6c57 100644 --- a/pom.xml +++ b/pom.xml @@ -1451,7 +1451,7 @@ commons-cli commons-cli - 1.4 + 1.5.0 commons-codec From 7627fe0223f085d510354a2ef63e972241c2dc12 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Jun 2023 19:33:38 +0000 Subject: [PATCH 042/168] Update dependency joda-time:joda-time to v2.12.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a226dd6c57..08e6ceede7 100644 --- a/pom.xml +++ b/pom.xml @@ -1521,7 +1521,7 @@ joda-time joda-time - 2.9.2 + 2.12.5 com.sun.mail From 2225edd40fc97e2d52d09453fd3db79b66e01945 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Sun, 4 Jun 2023 22:25:40 +0300 Subject: [PATCH 043/168] pom.xml: bump Jersey Bump jersey due to jersey-media-json-jackson pulling in a conflicting jakarta.xml.bind-api via transitive dependency in dspace-rest, which is the legacy DSpace 6 REST API. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 08e6ceede7..f29dd3c306 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ https://jena.apache.org/documentation/migrate_jena2_jena3.html --> 2.13.0 - 2.35 + 2.39.1 UTF-8 From 60886490037365f60621f3f5f5a898c832617814 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Sun, 4 Jun 2023 22:26:54 +0300 Subject: [PATCH 044/168] dspace-api/pom.xml: add exclusion for javassist Add an exclusion for org.javassist:javassist due to a dependency convergence error caused by eu.openaire:funders-model pulling in a version conflicting with Jersey's transitive dependency. --- dspace-api/pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index aab892ee04..a32f3ddfc2 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -818,6 +818,13 @@ eu.openaire funders-model 2.0.0 + + + + org.javassist + javassist + + From b1715b9b48dd500761c27c77b5e93d2b4cb3ead4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 06:03:24 +0000 Subject: [PATCH 045/168] Update dependency org.webjars.bowergithub.medialize:uri.js to v1.19.11 --- dspace-server-webapp/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 6cec67baa3..f4d47e88c2 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -322,7 +322,7 @@ org.webjars.bowergithub.medialize uri.js - 1.19.10 + 1.19.11 From ecd3604302e67a1df3f44fb0eeb704739ad6fdcb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 06:01:11 +0000 Subject: [PATCH 046/168] Update dependency com.fasterxml:classmate to v1.5.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f29dd3c306..d3f30a8d1b 100644 --- a/pom.xml +++ b/pom.xml @@ -1752,7 +1752,7 @@ com.fasterxml classmate - 1.3.0 + 1.5.1 com.fasterxml.jackson.core From 9188c838255c9dc75f581e9ff69e8443a2a4908e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 06:03:29 +0000 Subject: [PATCH 047/168] Update dependency org.webjars.bowergithub.jquery:jquery-dist to v3.7.0 --- dspace-server-webapp/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index f4d47e88c2..d7a8782b73 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -308,7 +308,7 @@ org.webjars.bowergithub.jquery jquery-dist - 3.6.0 + 3.7.0 From f00c15f449b1e3ebc81ec644f22879046a94a15b Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Mon, 5 Jun 2023 10:33:46 +0300 Subject: [PATCH 048/168] Bump xom:xom dependency No breaking changes, but some bug fixes, performance improvements, and compatibility fixes with Java 17+. See: https://xom.nu/history.html --- dspace-sword/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 349ac3abd0..3ddf4d1a83 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -104,7 +104,7 @@ xom xom - 1.3.7 + 1.3.9 commons-io diff --git a/pom.xml b/pom.xml index d3f30a8d1b..757c522aa3 100644 --- a/pom.xml +++ b/pom.xml @@ -1784,7 +1784,7 @@ xom xom - 1.2.5 + 1.3.9 From 171cd41f0fd060d0a31cbce0d1fddd12aedcd797 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Mon, 5 Jun 2023 10:36:44 +0300 Subject: [PATCH 049/168] Bump jaxen:jaxen dependency to 2.0.0 Should be mostly drop-in API compatible with Jaxen 1.1.x, but more importantly it makes the xom dependency optional so we can remove the exclusions in our various pom.xml files. See: http://cafeconleche.org/jaxen/releases.html --- dspace-api/pom.xml | 6 ------ pom.xml | 8 +------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index a32f3ddfc2..1946eac9a5 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -492,12 +492,6 @@ jaxen jaxen - - - xom - xom - - org.jdom diff --git a/pom.xml b/pom.xml index 757c522aa3..930532b8b1 100644 --- a/pom.xml +++ b/pom.xml @@ -1538,13 +1538,7 @@ jaxen jaxen - 1.1.6 - - - xom - xom - - + 2.0.0 org.jdom From 7a74990894068ec8dad43c04925c79ddf3c72654 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 25 Jun 2023 16:37:20 +0000 Subject: [PATCH 050/168] Update dependency org.apache.bcel:bcel to v6.7.0 --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 1946eac9a5..4252a44398 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -803,7 +803,7 @@ org.apache.bcel bcel - 6.6.0 + 6.7.0 test From 8185cd3ebdb7cd4b0d9f16b244dcff4ed7554d1b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 04:40:30 +0000 Subject: [PATCH 051/168] Update dependency org.scala-lang:scala-library to v2.13.11 --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 4252a44398..ee8c21cb64 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -923,7 +923,7 @@ org.scala-lang scala-library - 2.13.9 + 2.13.11 test From e3f7f7f30f21df2025647ac119a62d7e1c9138c9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 04:53:45 +0000 Subject: [PATCH 052/168] Update dependency commons-io:commons-io to v2.13.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 930532b8b1..4d01bca240 100644 --- a/pom.xml +++ b/pom.xml @@ -1489,7 +1489,7 @@ commons-io commons-io - 2.12.0 + 2.13.0 org.apache.commons From 67298a290e1e95f22416964ce97690c6f25abd37 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 04:56:57 +0000 Subject: [PATCH 053/168] Update dependency org.exparity:hamcrest-date to v2.0.8 --- dspace-server-webapp/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index d7a8782b73..42ed115d91 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -541,7 +541,7 @@ org.exparity hamcrest-date - 2.0.7 + 2.0.8 test From c9197418e02c661ef24ac1f4e3e965c167a52917 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 27 Jun 2023 10:49:50 +0000 Subject: [PATCH 054/168] Update dependency commons-codec:commons-codec to v1.16.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4d01bca240..5853f19246 100644 --- a/pom.xml +++ b/pom.xml @@ -1456,7 +1456,7 @@ commons-codec commons-codec - 1.15 + 1.16.0 org.apache.commons From 8ea07264cf2015fda9d52a2a18268af9ea839a56 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Fri, 30 Jun 2023 21:38:32 +0300 Subject: [PATCH 055/168] pom.xml: update spring boot to v2.7.13 Minor update. Also bump the spring security version to 5.7.9 as is used by spring boot. See: https://github.com/spring-projects/spring-boot/releases/tag/v2.7.13 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5853f19246..c43dc6c597 100644 --- a/pom.xml +++ b/pom.xml @@ -20,8 +20,8 @@ 11 5.3.27 - 2.7.12 - 5.7.8 + 2.7.13 + 5.7.9 5.6.15.Final 6.2.5.Final 42.6.0 From cf87cbea8fc61037d647bf2d4aee1e99ec19f95f Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Fri, 30 Jun 2023 21:41:41 +0300 Subject: [PATCH 056/168] pom.xml: bump spring core version to v5.3.28 Minor version bump with some bug fixes. See: https://github.com/spring-projects/spring-framework/releases/tag/v5.3.28 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c43dc6c597..00e1306d59 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 11 - 5.3.27 + 5.3.28 2.7.13 5.7.9 5.6.15.Final From 8006329514d1818268518d6deab57ecb80b237e2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 2 Jul 2023 15:18:28 +0000 Subject: [PATCH 057/168] Update pdfbox-version to v2.0.29 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 00e1306d59..5dc53194b3 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ 9.4.51.v20230217 2.20.0 - 2.0.28 + 2.0.29 1.19.0 1.7.36 2.5.0 From b846c53baaeae1e19fbbafa3dc7ca724bcaf32c1 Mon Sep 17 00:00:00 2001 From: "David P. Steelman" Date: Mon, 3 Jul 2023 14:09:15 -0400 Subject: [PATCH 058/168] DS-8935. webui.browse.link CrossLinks - Fix for multiple exact matches Fixes #8935 when multiple exact match "webui.browse.link" configuration entries are present that point to different indexes. Modified the code to return the index associated with the given metadata (which is used as the key in the hash map), instead of the key from the keySet (which may not actually be the metadata value being searched for). https://github.com/DSpace/DSpace/issues/8935 --- .../java/org/dspace/browse/CrossLinks.java | 2 +- .../org/dspace/browse/CrossLinksTest.java | 103 ++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 dspace-api/src/test/java/org/dspace/browse/CrossLinksTest.java 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 1ce2e55886..ec4cb199ea 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 class CrossLinks { } 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/test/java/org/dspace/browse/CrossLinksTest.java b/dspace-api/src/test/java/org/dspace/browse/CrossLinksTest.java new file mode 100644 index 0000000000..83aab72d90 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/browse/CrossLinksTest.java @@ -0,0 +1,103 @@ +/** + * 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.browse; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.dspace.AbstractDSpaceTest; +import org.dspace.services.ConfigurationService; +import org.dspace.utils.DSpace; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for {@link CrossLinks} + */ +public class CrossLinksTest extends AbstractDSpaceTest { + protected ConfigurationService configurationService; + + + @Before + public void setUp() { + configurationService = new DSpace().getConfigurationService(); + } + + @Test + public void testFindLinkType_Null() throws Exception { + CrossLinks crossLinks = new CrossLinks(); + assertNull(crossLinks.findLinkType(null)); + } + + @Test + public void testFindLinkType_NoMatch() throws Exception { + CrossLinks crossLinks = new CrossLinks(); + String metadataField = "foo.bar.baz.does.not.exist"; + assertNull(crossLinks.findLinkType(metadataField)); + } + + @Test + public void testFindLinkType_WildcardMatch() throws Exception { + configurationService.setProperty("webui.browse.link.1", "author:dc.contributor.*"); + CrossLinks crossLinks = new CrossLinks(); + + String metadataField = "dc.contributor.author"; + assertEquals("author",crossLinks.findLinkType(metadataField)); + } + + @Test + public void testFindLinkType_SingleExactMatch_Author() throws Exception { + configurationService.setProperty("webui.browse.link.1", "author:dc.contributor.author"); + CrossLinks crossLinks = new CrossLinks(); + + assertEquals("type",crossLinks.findLinkType("dc.genre")); + assertEquals("author",crossLinks.findLinkType("dc.contributor.author")); + } + + @Test + public void testFindLinkType_SingleExactMatch_Type() throws Exception { + configurationService.setProperty("webui.browse.link.1", "type:dc.genre"); + CrossLinks crossLinks = new CrossLinks(); + + assertEquals("type",crossLinks.findLinkType("dc.genre")); + } + + @Test + public void testFindLinkType_MultipleExactMatches_DifferentIndexes() throws Exception { + configurationService.setProperty("webui.browse.link.1", "author:dc.contributor.author"); + configurationService.setProperty("webui.browse.link.2", "type:dc.genre"); + CrossLinks crossLinks = new CrossLinks(); + + assertEquals("author",crossLinks.findLinkType("dc.contributor.author")); + assertEquals("type",crossLinks.findLinkType("dc.genre")); + } + + @Test + public void testFindLinkType_MultipleWildcardMatches_DifferentIndexes() throws Exception { + configurationService.setProperty("webui.browse.link.1", "author:dc.contributor.*"); + configurationService.setProperty("webui.browse.link.2", "subject:dc.subject.*"); + CrossLinks crossLinks = new CrossLinks(); + + assertEquals("author",crossLinks.findLinkType("dc.contributor.author")); + assertEquals("subject",crossLinks.findLinkType("dc.subject.lcsh")); + } + + @Test + public void testFindLinkType_MultiplExactAndWildcardMatches_DifferentIndexes() throws Exception { + configurationService.setProperty("webui.browse.link.1", "author:dc.contributor.*"); + configurationService.setProperty("webui.browse.link.2", "subject:dc.subject.*"); + configurationService.setProperty("webui.browse.link.3", "type:dc.genre"); + configurationService.setProperty("webui.browse.link.4", "dateissued:dc.date.issued"); + CrossLinks crossLinks = new CrossLinks(); + + assertEquals("author",crossLinks.findLinkType("dc.contributor.author")); + assertEquals("subject",crossLinks.findLinkType("dc.subject.lcsh")); + assertEquals("type",crossLinks.findLinkType("dc.genre")); + assertEquals("dateissued",crossLinks.findLinkType("dc.date.issued")); + } +} From c72facbd74481af2656f1dd6a719b88da75e7419 Mon Sep 17 00:00:00 2001 From: "max.nuding" Date: Thu, 6 Jul 2023 09:17:59 +0200 Subject: [PATCH 059/168] Fix #8933: Only add the base statistic core if it hasn't already been added --- .../java/org/dspace/statistics/SolrLoggerServiceImpl.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java b/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java index 7853c3e11a..9f34a42047 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java @@ -1691,6 +1691,14 @@ public class SolrLoggerServiceImpl implements SolrLoggerService, InitializingBea statisticYearCores .add(baseSolrUrl.replace("http://", "").replace("https://", "") + statCoreName); } + var baseCore = ((HttpSolrClient) solr) + .getBaseURL() + .replace("http://", "") + .replace("https://", ""); + if (!statisticYearCores.contains(baseCore)) { + //Also add the core containing the current year, if it hasn't been added already + statisticYearCores.add(baseCore); + } //Also add the core containing the current year ! statisticYearCores.add(((HttpSolrClient) solr) .getBaseURL() From 1b94aecb26059b35b762ff5285beecddd2e0d08f Mon Sep 17 00:00:00 2001 From: Nona Luypaert Date: Thu, 6 Jul 2023 16:48:10 +0200 Subject: [PATCH 060/168] 103837: Only send GA4 events for ORIGINAL bitstreams + ITs --- .../google/GoogleAsyncEventListener.java | 32 +++++- .../google/GoogleAsyncEventListenerIT.java | 105 +++++++++++++++++- dspace/config/dspace.cfg | 7 +- 3 files changed, 133 insertions(+), 11 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/google/GoogleAsyncEventListener.java b/dspace-api/src/main/java/org/dspace/google/GoogleAsyncEventListener.java index c169e4712f..e84d9f8591 100644 --- a/dspace-api/src/main/java/org/dspace/google/GoogleAsyncEventListener.java +++ b/dspace-api/src/main/java/org/dspace/google/GoogleAsyncEventListener.java @@ -22,6 +22,7 @@ import org.apache.commons.collections.buffer.CircularFifoBuffer; import org.apache.commons.lang.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.content.Bitstream; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.core.Constants; import org.dspace.core.Context; @@ -77,7 +78,7 @@ public class GoogleAsyncEventListener extends AbstractUsageEventListener { UsageEvent usageEvent = (UsageEvent) event; LOGGER.debug("Usage event received " + event.getName()); - if (isNotBitstreamViewEvent(usageEvent)) { + if (!isContentBitstream(usageEvent)) { return; } @@ -171,9 +172,32 @@ public class GoogleAsyncEventListener extends AbstractUsageEventListener { return documentPath; } - private boolean isNotBitstreamViewEvent(UsageEvent usageEvent) { - return usageEvent.getAction() != UsageEvent.Action.VIEW - || usageEvent.getObject().getType() != Constants.BITSTREAM; + /** + * Verifies if the usage event is a content bitstream view event, by checking if:

    + *
  • the usage event is a view event
  • + *
  • the object of the usage event is a bitstream
  • + *
  • the bitstream belongs to the ORIGINAL bundle
+ * This last one can be skipped if 'google-analytics.exclude-non-content-bitstreams' is set to false. + * This will make it so the bundle name is completely ignored when sending events. + */ + private boolean isContentBitstream(UsageEvent usageEvent) { + // check if event is a VIEW event and object is a Bitstream + if (usageEvent.getAction() == UsageEvent.Action.VIEW + || usageEvent.getObject().getType() == Constants.BITSTREAM) { + // check if config is set to true + if (configurationService.getBooleanProperty("google-analytics.exclude-non-content-bitstreams")) { + try { + // check if bitstream belongs to the ORIGINAL bundle + return ((Bitstream) usageEvent.getObject()) + .getBundles().stream() + .anyMatch(bundle -> bundle.getName().equals(Constants.CONTENT_BUNDLE_NAME)); + } catch (SQLException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + return true; + } + return false; } private boolean isGoogleAnalyticsKeyNotConfigured() { diff --git a/dspace-server-webapp/src/test/java/org/dspace/google/GoogleAsyncEventListenerIT.java b/dspace-server-webapp/src/test/java/org/dspace/google/GoogleAsyncEventListenerIT.java index 866d0fafed..e43e9fd820 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/google/GoogleAsyncEventListenerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/google/GoogleAsyncEventListenerIT.java @@ -29,12 +29,16 @@ import java.util.ArrayList; import java.util.List; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.builder.BitstreamBuilder; +import org.dspace.builder.BundleBuilder; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.ItemBuilder; import org.dspace.content.Bitstream; +import org.dspace.content.Bundle; import org.dspace.content.Collection; import org.dspace.content.Item; +import org.dspace.core.Constants; import org.dspace.google.client.GoogleAnalyticsClient; import org.dspace.services.ConfigurationService; import org.junit.After; @@ -61,6 +65,8 @@ public class GoogleAsyncEventListenerIT extends AbstractControllerIntegrationTes private Bitstream bitstream; + private Item item; + private List originalGoogleAnalyticsClients; private GoogleAnalyticsClient firstGaClientMock = mock(GoogleAnalyticsClient.class); @@ -80,7 +86,7 @@ public class GoogleAsyncEventListenerIT extends AbstractControllerIntegrationTes .withName("Test collection") .build(); - Item item = ItemBuilder.createItem(context, collection) + item = ItemBuilder.createItem(context, collection) .withTitle("Test item") .build(); @@ -238,6 +244,88 @@ public class GoogleAsyncEventListenerIT extends AbstractControllerIntegrationTes } + @Test + public void testOnBitstreamContentDownloadExcludeNonContentBitstreams() throws Exception { + configurationService.setProperty("google-analytics.exclude-non-content-bitstreams", true); + + context.turnOffAuthorisationSystem(); + Bundle licenseBundle = BundleBuilder.createBundle(context, item) + .withName(Constants.LICENSE_BUNDLE_NAME).build(); + Bitstream license = BitstreamBuilder.createBitstream(context, licenseBundle, + toInputStream("License", defaultCharset())).build(); + Bundle thumbnailBundle = BundleBuilder.createBundle(context, item).withName("THUMBNAIL").build(); + Bitstream thumbnail = BitstreamBuilder.createBitstream(context, thumbnailBundle, + toInputStream("Thumbnail", defaultCharset())).build(); + context.restoreAuthSystemState(); + + assertThat(getStoredEventsAsList(), empty()); + + String bitstreamUrl = "/api/core/bitstreams/" + bitstream.getID() + "/content"; + + downloadBitstreamContent("Postman", "123456", "REF"); + downloadContent("Chrome", "ABCDEFG", "REF-1", license); + downloadContent("Chrome", "987654", "REF-2", thumbnail); + + assertThat(getStoredEventsAsList(), hasSize(1)); + + List storedEvents = getStoredEventsAsList(); + + assertThat(storedEvents, contains( + event("123456", "127.0.0.1", "Postman", "REF", bitstreamUrl, "Test item"))); + + googleAsyncEventListener.sendCollectedEvents(); + + assertThat(getStoredEventsAsList(), empty()); + + verify(firstGaClientMock).isAnalyticsKeySupported(ANALYTICS_KEY); + verify(secondGaClientMock).isAnalyticsKeySupported(ANALYTICS_KEY); + verify(secondGaClientMock).sendEvents(ANALYTICS_KEY, storedEvents); + verifyNoMoreInteractions(firstGaClientMock, secondGaClientMock); + } + + @Test + public void testOnBitstreamContentDownloadIncludeNonContentBitstreams() throws Exception { + configurationService.setProperty("google-analytics.exclude-non-content-bitstreams", false); + + context.turnOffAuthorisationSystem(); + Bundle licenseBundle = BundleBuilder.createBundle(context, item) + .withName(Constants.LICENSE_BUNDLE_NAME).build(); + Bitstream license = BitstreamBuilder.createBitstream(context, licenseBundle, + toInputStream("License", defaultCharset())).build(); + Bundle thumbnailBundle = BundleBuilder.createBundle(context, item).withName("THUMBNAIL").build(); + Bitstream thumbnail = BitstreamBuilder.createBitstream(context, thumbnailBundle, + toInputStream("Thumbnail", defaultCharset())).build(); + context.restoreAuthSystemState(); + + assertThat(getStoredEventsAsList(), empty()); + + String bitstreamUrl = "/api/core/bitstreams/" + bitstream.getID() + "/content"; + String licenseUrl = "/api/core/bitstreams/" + license.getID() + "/content"; + String thumbnailUrl = "/api/core/bitstreams/" + thumbnail.getID() + "/content"; + + downloadBitstreamContent("Postman", "123456", "REF"); + downloadContent("Chrome", "ABCDEFG", "REF-1", license); + downloadContent("Chrome", "987654", "REF-2", thumbnail); + + assertThat(getStoredEventsAsList(), hasSize(3)); + + List storedEvents = getStoredEventsAsList(); + + assertThat(storedEvents, contains( + event("123456", "127.0.0.1", "Postman", "REF", bitstreamUrl, "Test item"), + event("ABCDEFG", "127.0.0.1", "Chrome", "REF-1", licenseUrl, "Test item"), + event("987654", "127.0.0.1", "Chrome", "REF-2", thumbnailUrl, "Test item"))); + + googleAsyncEventListener.sendCollectedEvents(); + + assertThat(getStoredEventsAsList(), empty()); + + verify(firstGaClientMock).isAnalyticsKeySupported(ANALYTICS_KEY); + verify(secondGaClientMock).isAnalyticsKeySupported(ANALYTICS_KEY); + verify(secondGaClientMock).sendEvents(ANALYTICS_KEY, storedEvents); + verifyNoMoreInteractions(firstGaClientMock, secondGaClientMock); + } + @SuppressWarnings("unchecked") private List getStoredEventsAsList() { List events = new ArrayList<>(); @@ -248,13 +336,18 @@ public class GoogleAsyncEventListenerIT extends AbstractControllerIntegrationTes return events; } - private void downloadBitstreamContent(String userAgent, String correlationId, String referrer) throws Exception { + private void downloadContent(String userAgent, String correlationId, String referrer, Bitstream bit) + throws Exception { getClient(getAuthToken(admin.getEmail(), password)) - .perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content") - .header("USER-AGENT", userAgent) - .header("X-CORRELATION-ID", correlationId) - .header("X-REFERRER", referrer)) + .perform(get("/api/core/bitstreams/" + bit.getID() + "/content") + .header("USER-AGENT", userAgent) + .header("X-CORRELATION-ID", correlationId) + .header("X-REFERRER", referrer)) .andExpect(status().isOk()); } + private void downloadBitstreamContent(String userAgent, String correlationId, String referrer) throws Exception { + downloadContent(userAgent, correlationId, referrer, bitstream); + } + } diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 8e532310c1..89c8da9255 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1535,7 +1535,12 @@ log.report.dir = ${dspace.dir}/log # Defines a Measurement Protocol API Secret to be used to track interactions which occur outside of the user's browser. # For example , this is required to track downloads of bitstreams. This setting is only used by Google Analytics 4. # For more details see https://developers.google.com/analytics/devguides/collection/protocol/ga4 -# google.analytics.api-secret = +# google.analytics.api-secret = + +# Ensures only views of bitstreams in the 'ORIGINAL' bundle result in a GA4 event. +# Setting this to false may cause inflated bitstream view numbers, since requesting +# bitstreams in the 'THUMBNAIL' and 'LICENSE' bundles, will also result in GA4 events. +google-analytics.exclude-non-content-bitstreams=true #################################################################### #---------------------------------------------------------------# From 1ca4f59bb273bd1a9f861dc5486c11ba775ed20c Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 6 Jul 2023 15:44:02 -0500 Subject: [PATCH 061/168] Enable Pull Request Opened action to assign PRs to their creator --- .../pull_request_opened.yml | 26 ------------------- .github/workflows/pull_request_opened.yml | 24 +++++++++++++++++ 2 files changed, 24 insertions(+), 26 deletions(-) delete mode 100644 .github/disabled-workflows/pull_request_opened.yml create mode 100644 .github/workflows/pull_request_opened.yml diff --git a/.github/disabled-workflows/pull_request_opened.yml b/.github/disabled-workflows/pull_request_opened.yml deleted file mode 100644 index 0dc718c0b9..0000000000 --- a/.github/disabled-workflows/pull_request_opened.yml +++ /dev/null @@ -1,26 +0,0 @@ -# This workflow runs whenever a new pull request is created -# TEMPORARILY DISABLED. Unfortunately this doesn't work for PRs created from forked repositories (which is how we tend to create PRs). -# There is no known workaround yet. See https://github.community/t/how-to-use-github-token-for-prs-from-forks/16818 -name: Pull Request opened - -# Only run for newly opened PRs against the "main" branch -on: - pull_request: - types: [opened] - branches: - - main - -jobs: - automation: - runs-on: ubuntu-latest - steps: - # Assign the PR to whomever created it. This is useful for visualizing assignments on project boards - # See https://github.com/marketplace/actions/pull-request-assigner - - name: Assign PR to creator - uses: thomaseizinger/assign-pr-creator-action@v1.0.0 - # Note, this authentication token is created automatically - # See: https://docs.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - # Ignore errors. It is possible the PR was created by someone who cannot be assigned - continue-on-error: true diff --git a/.github/workflows/pull_request_opened.yml b/.github/workflows/pull_request_opened.yml new file mode 100644 index 0000000000..9b61af72d1 --- /dev/null +++ b/.github/workflows/pull_request_opened.yml @@ -0,0 +1,24 @@ +# This workflow runs whenever a new pull request is created +name: Pull Request opened + +# Only run for newly opened PRs against the "main" or maintenance branches +# We allow this to run for `pull_request_target` so that github secrets are available +# (This is required to assign a PR back to the creator when the PR comes from a forked repo) +on: + pull_request_target: + types: [ opened ] + branches: + - main + - 'dspace-**' + +permissions: + pull-requests: write + +jobs: + automation: + runs-on: ubuntu-latest + steps: + # Assign the PR to whomever created it. This is useful for visualizing assignments on project boards + # See https://github.com/toshimaru/auto-author-assign + - name: Assign PR to creator + uses: toshimaru/auto-author-assign@v1.6.2 From 3ac66031baffbab3347694bcb35074153a397e2b Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 6 Jul 2023 15:44:36 -0500 Subject: [PATCH 062/168] Ensure codescan and label_merge_conflicts run on maintenance branches --- .github/workflows/codescan.yml | 10 +++++++--- .github/workflows/label_merge_conflicts.yml | 7 ++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/codescan.yml b/.github/workflows/codescan.yml index 7580b4ba3d..9e6dcc0b23 100644 --- a/.github/workflows/codescan.yml +++ b/.github/workflows/codescan.yml @@ -5,12 +5,16 @@ # because CodeQL requires a fresh build with all tests *disabled*. name: "Code Scanning" -# Run this code scan for all pushes / PRs to main branch. Also run once a week. +# Run this code scan for all pushes / PRs to main or maintenance branches. Also run once a week. on: push: - branches: [ main ] + branches: + - main + - 'dspace-**' pull_request: - branches: [ main ] + branches: + - main + - 'dspace-**' # Don't run if PR is only updating static documentation paths-ignore: - '**/*.md' diff --git a/.github/workflows/label_merge_conflicts.yml b/.github/workflows/label_merge_conflicts.yml index cc0c7099f4..0c3b1a0f2a 100644 --- a/.github/workflows/label_merge_conflicts.yml +++ b/.github/workflows/label_merge_conflicts.yml @@ -1,11 +1,12 @@ # This workflow checks open PRs for merge conflicts and labels them when conflicts are found name: Check for merge conflicts -# Run whenever the "main" branch is updated -# NOTE: This means merge conflicts are only checked for when a PR is merged to main. +# Run this for all pushes (i.e. merges) to 'main' or maintenance branches on: push: - branches: [ main ] + branches: + - main + - 'dspace-**' # So that the `conflict_label_name` is removed if conflicts are resolved, # we allow this to run for `pull_request_target` so that github secrets are available. pull_request_target: From dea45355818176017802a0b7337b07ae435c5437 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 6 Jul 2023 16:32:16 -0500 Subject: [PATCH 063/168] Split docker image builds into separate jobs to allow them to run in parallel. --- .github/workflows/docker.yml | 267 ++++++++++++++++++++++++++++------- 1 file changed, 219 insertions(+), 48 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 971954a5e1..9ec6b85735 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -15,30 +15,36 @@ on: permissions: contents: read # to fetch code (actions/checkout) +# Define shared environment variables for all jobs below +env: + # Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action) + # For a new commit on default branch (main), use the literal tag 'dspace-7_x' on Docker image. + # For a new commit on other branches, use the branch name as the tag for Docker image. + # For a new tag, copy that tag name as the tag for Docker image. + IMAGE_TAGS: | + type=raw,value=dspace-7_x,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} + type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }} + type=ref,event=tag + # Define default tag "flavor" for docker/metadata-action per + # https://github.com/docker/metadata-action#flavor-input + # We turn off 'latest' tag by default. + TAGS_FLAVOR: | + latest=false + # Architectures / Platforms for which we will build Docker images + # If this is a PR, we ONLY build for AMD64. For PRs we only do a sanity check test to ensure Docker builds work. + # If this is NOT a PR (e.g. a tag or merge commit), also build for ARM64. NOTE: The ARM64 build takes MUCH + # longer (around 45mins or so) which is why we only run it when pushing a new Docker image. + PLATFORMS: linux/amd64${{ github.event_name != 'pull_request' && ', linux/arm64' || '' }} + jobs: - docker: + #################################################### + # Build/Push the 'dspace/dspace-dependencies' image. + # This image is used by all other jobs. + #################################################### + dspace-dependencies: # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace' if: github.repository == 'dspace/dspace' runs-on: ubuntu-latest - env: - # Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action) - # For a new commit on default branch (main), use the literal tag 'dspace-7_x' on Docker image. - # For a new commit on other branches, use the branch name as the tag for Docker image. - # For a new tag, copy that tag name as the tag for Docker image. - IMAGE_TAGS: | - type=raw,value=dspace-7_x,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} - type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }} - type=ref,event=tag - # Define default tag "flavor" for docker/metadata-action per - # https://github.com/docker/metadata-action#flavor-input - # We turn off 'latest' tag by default. - TAGS_FLAVOR: | - latest=false - # Architectures / Platforms for which we will build Docker images - # If this is a PR, we ONLY build for AMD64. For PRs we only do a sanity check test to ensure Docker builds work. - # If this is NOT a PR (e.g. a tag or merge commit), also build for ARM64. NOTE: The ARM64 build takes MUCH - # longer (around 45mins or so) which is why we only run it when pushing a new Docker image. - PLATFORMS: linux/amd64${{ github.event_name != 'pull_request' && ', linux/arm64' || '' }} steps: # https://github.com/actions/checkout @@ -62,9 +68,6 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_ACCESS_TOKEN }} - #################################################### - # Build/Push the 'dspace/dspace-dependencies' image - #################################################### # https://github.com/docker/metadata-action # Get Metadata for docker_build_deps step below - name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-dependencies' image @@ -78,7 +81,7 @@ jobs: # https://github.com/docker/build-push-action - name: Build and push 'dspace-dependencies' image id: docker_build_deps - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: context: . file: ./Dockerfile.dependencies @@ -90,9 +93,38 @@ jobs: tags: ${{ steps.meta_build_deps.outputs.tags }} labels: ${{ steps.meta_build_deps.outputs.labels }} - ####################################### - # Build/Push the 'dspace/dspace' image - ####################################### + ####################################### + # Build/Push the 'dspace/dspace' image + ####################################### + dspace: + # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace' + if: github.repository == 'dspace/dspace' + # Must run after 'dspace-dependencies' job above + needs: dspace-dependencies + runs-on: ubuntu-latest + + steps: + # https://github.com/actions/checkout + - name: Checkout codebase + uses: actions/checkout@v3 + + # https://github.com/docker/setup-buildx-action + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v2 + + # https://github.com/docker/setup-qemu-action + - name: Set up QEMU emulation to build for multiple architectures + uses: docker/setup-qemu-action@v2 + + # https://github.com/docker/login-action + - name: Login to DockerHub + # Only login if not a PR, as PRs only trigger a Docker build and not a push + if: github.event_name != 'pull_request' + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + # Get Metadata for docker_build step below - name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace' image id: meta_build @@ -104,7 +136,7 @@ jobs: - name: Build and push 'dspace' image id: docker_build - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: context: . file: ./Dockerfile @@ -116,9 +148,38 @@ jobs: tags: ${{ steps.meta_build.outputs.tags }} labels: ${{ steps.meta_build.outputs.labels }} - ##################################################### - # Build/Push the 'dspace/dspace' image ('-test' tag) - ##################################################### + ############################################################# + # Build/Push the 'dspace/dspace' image ('-test' tag) + ############################################################# + dspace-test: + # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace' + if: github.repository == 'dspace/dspace' + # Must run after 'dspace-dependencies' job above + needs: dspace-dependencies + runs-on: ubuntu-latest + + steps: + # https://github.com/actions/checkout + - name: Checkout codebase + uses: actions/checkout@v3 + + # https://github.com/docker/setup-buildx-action + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v2 + + # https://github.com/docker/setup-qemu-action + - name: Set up QEMU emulation to build for multiple architectures + uses: docker/setup-qemu-action@v2 + + # https://github.com/docker/login-action + - name: Login to DockerHub + # Only login if not a PR, as PRs only trigger a Docker build and not a push + if: github.event_name != 'pull_request' + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + # Get Metadata for docker_build_test step below - name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-test' image id: meta_build_test @@ -133,7 +194,7 @@ jobs: - name: Build and push 'dspace-test' image id: docker_build_test - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: context: . file: ./Dockerfile.test @@ -145,9 +206,38 @@ jobs: tags: ${{ steps.meta_build_test.outputs.tags }} labels: ${{ steps.meta_build_test.outputs.labels }} - ########################################### - # Build/Push the 'dspace/dspace-cli' image - ########################################### + ########################################### + # Build/Push the 'dspace/dspace-cli' image + ########################################### + dspace-cli: + # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace' + if: github.repository == 'dspace/dspace' + # Must run after 'dspace-dependencies' job above + needs: dspace-dependencies + runs-on: ubuntu-latest + + steps: + # https://github.com/actions/checkout + - name: Checkout codebase + uses: actions/checkout@v3 + + # https://github.com/docker/setup-buildx-action + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v2 + + # https://github.com/docker/setup-qemu-action + - name: Set up QEMU emulation to build for multiple architectures + uses: docker/setup-qemu-action@v2 + + # https://github.com/docker/login-action + - name: Login to DockerHub + # Only login if not a PR, as PRs only trigger a Docker build and not a push + if: github.event_name != 'pull_request' + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + # Get Metadata for docker_build_test step below - name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-cli' image id: meta_build_cli @@ -159,7 +249,7 @@ jobs: - name: Build and push 'dspace-cli' image id: docker_build_cli - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: context: . file: ./Dockerfile.cli @@ -171,9 +261,36 @@ jobs: tags: ${{ steps.meta_build_cli.outputs.tags }} labels: ${{ steps.meta_build_cli.outputs.labels }} - ########################################### - # Build/Push the 'dspace/dspace-solr' image - ########################################### + ########################################### + # Build/Push the 'dspace/dspace-solr' image + ########################################### + dspace-solr: + # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace' + if: github.repository == 'dspace/dspace' + runs-on: ubuntu-latest + + steps: + # https://github.com/actions/checkout + - name: Checkout codebase + uses: actions/checkout@v3 + + # https://github.com/docker/setup-buildx-action + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v2 + + # https://github.com/docker/setup-qemu-action + - name: Set up QEMU emulation to build for multiple architectures + uses: docker/setup-qemu-action@v2 + + # https://github.com/docker/login-action + - name: Login to DockerHub + # Only login if not a PR, as PRs only trigger a Docker build and not a push + if: github.event_name != 'pull_request' + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + # Get Metadata for docker_build_solr step below - name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-solr' image id: meta_build_solr @@ -185,7 +302,7 @@ jobs: - name: Build and push 'dspace-solr' image id: docker_build_solr - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: context: . file: ./dspace/src/main/docker/dspace-solr/Dockerfile @@ -197,9 +314,36 @@ jobs: tags: ${{ steps.meta_build_solr.outputs.tags }} labels: ${{ steps.meta_build_solr.outputs.labels }} - ########################################################### - # Build/Push the 'dspace/dspace-postgres-pgcrypto' image - ########################################################### + ########################################################### + # Build/Push the 'dspace/dspace-postgres-pgcrypto' image + ########################################################### + dspace-postgres-pgcrypto: + # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace' + if: github.repository == 'dspace/dspace' + runs-on: ubuntu-latest + + steps: + # https://github.com/actions/checkout + - name: Checkout codebase + uses: actions/checkout@v3 + + # https://github.com/docker/setup-buildx-action + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v2 + + # https://github.com/docker/setup-qemu-action + - name: Set up QEMU emulation to build for multiple architectures + uses: docker/setup-qemu-action@v2 + + # https://github.com/docker/login-action + - name: Login to DockerHub + # Only login if not a PR, as PRs only trigger a Docker build and not a push + if: github.event_name != 'pull_request' + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + # Get Metadata for docker_build_postgres step below - name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-postgres-pgcrypto' image id: meta_build_postgres @@ -211,7 +355,7 @@ jobs: - name: Build and push 'dspace-postgres-pgcrypto' image id: docker_build_postgres - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: # Must build out of subdirectory to have access to install script for pgcrypto context: ./dspace/src/main/docker/dspace-postgres-pgcrypto/ @@ -224,9 +368,36 @@ jobs: tags: ${{ steps.meta_build_postgres.outputs.tags }} labels: ${{ steps.meta_build_postgres.outputs.labels }} - ########################################################### - # Build/Push the 'dspace/dspace-postgres-pgcrypto' image ('-loadsql' tag) - ########################################################### + ######################################################################## + # Build/Push the 'dspace/dspace-postgres-pgcrypto' image (-loadsql tag) + ######################################################################## + dspace-postgres-pgcrypto-loadsql: + # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace' + if: github.repository == 'dspace/dspace' + runs-on: ubuntu-latest + + steps: + # https://github.com/actions/checkout + - name: Checkout codebase + uses: actions/checkout@v3 + + # https://github.com/docker/setup-buildx-action + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v2 + + # https://github.com/docker/setup-qemu-action + - name: Set up QEMU emulation to build for multiple architectures + uses: docker/setup-qemu-action@v2 + + # https://github.com/docker/login-action + - name: Login to DockerHub + # Only login if not a PR, as PRs only trigger a Docker build and not a push + if: github.event_name != 'pull_request' + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + # Get Metadata for docker_build_postgres_loadsql step below - name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-postgres-pgcrypto-loadsql' image id: meta_build_postgres_loadsql @@ -241,7 +412,7 @@ jobs: - name: Build and push 'dspace-postgres-pgcrypto-loadsql' image id: docker_build_postgres_loadsql - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: # Must build out of subdirectory to have access to install script for pgcrypto context: ./dspace/src/main/docker/dspace-postgres-pgcrypto-curl/ From d1e1900b331f5ac1507048b0ebb7b72c7f5047e7 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 7 Jul 2023 11:47:09 -0500 Subject: [PATCH 064/168] Ensure 'main' code is tagged as 'latest' in DockerHub --- .github/workflows/docker.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 9ec6b85735..f1ae184fd5 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -18,16 +18,16 @@ permissions: # Define shared environment variables for all jobs below env: # Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action) - # For a new commit on default branch (main), use the literal tag 'dspace-7_x' on Docker image. + # For a new commit on default branch (main), use the literal tag 'latest' on Docker image. # For a new commit on other branches, use the branch name as the tag for Docker image. # For a new tag, copy that tag name as the tag for Docker image. IMAGE_TAGS: | - type=raw,value=dspace-7_x,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} + type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }} type=ref,event=tag # Define default tag "flavor" for docker/metadata-action per # https://github.com/docker/metadata-action#flavor-input - # We turn off 'latest' tag by default. + # We manage the 'latest' tag ourselves to the 'main' branch (see settings above) TAGS_FLAVOR: | latest=false # Architectures / Platforms for which we will build Docker images From 4aea2a99a91d49c1b7dab36149b179a9156db892 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Mon, 10 Jul 2023 13:43:27 +0200 Subject: [PATCH 065/168] Add flag Pattern.UNICODE_CHARACTER_CLASS to pattern compilation to recognize unicode characters --- .../org/dspace/discovery/indexobject/ItemIndexFactoryImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/indexobject/ItemIndexFactoryImpl.java b/dspace-api/src/main/java/org/dspace/discovery/indexobject/ItemIndexFactoryImpl.java index 005f9b4247..e1136a3f59 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/indexobject/ItemIndexFactoryImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/indexobject/ItemIndexFactoryImpl.java @@ -845,7 +845,7 @@ public class ItemIndexFactoryImpl extends DSpaceObjectIndexFactoryImpl Date: Tue, 11 Jul 2023 16:52:20 +0200 Subject: [PATCH 066/168] remove obsolete code fragments --- .../dspace/discovery/indexobject/ItemIndexFactoryImpl.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/indexobject/ItemIndexFactoryImpl.java b/dspace-api/src/main/java/org/dspace/discovery/indexobject/ItemIndexFactoryImpl.java index 005f9b4247..39947146dd 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/indexobject/ItemIndexFactoryImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/indexobject/ItemIndexFactoryImpl.java @@ -172,13 +172,6 @@ public class ItemIndexFactoryImpl extends DSpaceObjectIndexFactoryImpl Date: Fri, 14 Jul 2023 11:06:35 +0100 Subject: [PATCH 067/168] Update FullTextContentStreams.java Fix NPE if bitstream is null --- .../dspace/discovery/FullTextContentStreams.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/FullTextContentStreams.java b/dspace-api/src/main/java/org/dspace/discovery/FullTextContentStreams.java index ee220e5a4f..6d0c57c628 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/FullTextContentStreams.java +++ b/dspace-api/src/main/java/org/dspace/discovery/FullTextContentStreams.java @@ -77,13 +77,19 @@ public class FullTextContentStreams extends ContentStreamBase { // a-ha! grab the text out of the bitstreams List bitstreams = myBundle.getBitstreams(); + log.debug("Processing full-text bitstreams. Item handle: " + sourceInfo); + for (Bitstream fulltextBitstream : emptyIfNull(bitstreams)) { fullTextStreams.add(new FullTextBitstream(sourceInfo, fulltextBitstream)); - log.debug("Added BitStream: " - + fulltextBitstream.getStoreNumber() + " " - + fulltextBitstream.getSequenceID() + " " - + fulltextBitstream.getName()); + if (fulltextBitstream != null) { + log.debug("Added BitStream: " + + fulltextBitstream.getStoreNumber() + " " + + fulltextBitstream.getSequenceID() + " " + + fulltextBitstream.getName()); + } else { + log.error("Found a NULL bitstream when processing full-text files: item handle:" + sourceInfo); + } } } } From e645d0fa25195096ca9d60ecfbd2b141967556a2 Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Fri, 14 Jul 2023 16:17:22 +0100 Subject: [PATCH 068/168] Update FullTextContentStreams.java Add additional NPE checks --- .../java/org/dspace/discovery/FullTextContentStreams.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/FullTextContentStreams.java b/dspace-api/src/main/java/org/dspace/discovery/FullTextContentStreams.java index 6d0c57c628..21468def68 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/FullTextContentStreams.java +++ b/dspace-api/src/main/java/org/dspace/discovery/FullTextContentStreams.java @@ -76,7 +76,6 @@ public class FullTextContentStreams extends ContentStreamBase { if (StringUtils.equals(FULLTEXT_BUNDLE, myBundle.getName())) { // a-ha! grab the text out of the bitstreams List bitstreams = myBundle.getBitstreams(); - log.debug("Processing full-text bitstreams. Item handle: " + sourceInfo); for (Bitstream fulltextBitstream : emptyIfNull(bitstreams)) { @@ -164,16 +163,16 @@ public class FullTextContentStreams extends ContentStreamBase { } public String getContentType(final Context context) throws SQLException { - BitstreamFormat format = bitstream.getFormat(context); + BitstreamFormat format = bitstream != null ? bitstream.getFormat(context) : null; return format == null ? null : StringUtils.trimToEmpty(format.getMIMEType()); } public String getFileName() { - return StringUtils.trimToEmpty(bitstream.getName()); + return bitstream != null ? StringUtils.trimToEmpty(bitstream.getName()) : null; } public long getSize() { - return bitstream.getSizeBytes(); + return bitstream != null ? bitstream.getSizeBytes() : -1; } public InputStream getInputStream() throws SQLException, IOException, AuthorizeException { From acf376db346d2f3b979b91c4e007cf39f0ab8d18 Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Sun, 16 Jul 2023 20:42:03 +0100 Subject: [PATCH 069/168] Update ItemUtils.java Prevent npe if bitstream is null --- dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java index 955c3a78c3..35bef8c8d7 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java @@ -103,6 +103,11 @@ public class ItemUtils { bundle.getElement().add(bitstreams); List bits = b.getBitstreams(); for (Bitstream bit : bits) { + // Check if bitstream is null and log the error + if (bit == null) { + log.error("Null bitstream found, check item uuid: " + item.getID()); + break; + } Element bitstream = create("bitstream"); bitstreams.getElement().add(bitstream); String url = ""; From aa35a47add5565a9302d276da2ceb22b8dbc320f Mon Sep 17 00:00:00 2001 From: corrado lombardi Date: Wed, 19 Jul 2023 12:58:36 +0200 Subject: [PATCH 070/168] [DURACOM-179] replaced 'null' value with exception actual value in sendErrorResponse method calls having 'null' --- .../exception/DSpaceApiExceptionControllerAdvice.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java index 4ad1e47934..a65ea13bc2 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java @@ -132,7 +132,7 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH Exception ex) throws IOException { //422 is not defined in HttpServletResponse. Its meaning is "Unprocessable Entity". //Using the value from HttpStatus. - sendErrorResponse(request, response, null, + sendErrorResponse(request, response, ex, "Unprocessable or invalid entity", HttpStatus.UNPROCESSABLE_ENTITY.value()); } @@ -140,7 +140,7 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH @ExceptionHandler( {InvalidSearchRequestException.class}) protected void handleInvalidSearchRequestException(HttpServletRequest request, HttpServletResponse response, Exception ex) throws IOException { - sendErrorResponse(request, response, null, + sendErrorResponse(request, response, ex, "Invalid search request", HttpStatus.UNPROCESSABLE_ENTITY.value()); } @@ -180,7 +180,7 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH TranslatableException ex) throws IOException { Context context = ContextUtil.obtainContext(request); sendErrorResponse( - request, response, null, ex.getLocalizedMessage(context), HttpStatus.UNPROCESSABLE_ENTITY.value() + request, response, (Exception) ex, ex.getLocalizedMessage(context), HttpStatus.UNPROCESSABLE_ENTITY.value() ); } @@ -188,7 +188,7 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH protected void ParameterConversionException(HttpServletRequest request, HttpServletResponse response, Exception ex) throws IOException { // we want the 400 status for missing parameters, see https://jira.lyrasis.org/browse/DS-4428 - sendErrorResponse(request, response, null, + sendErrorResponse(request, response, ex, "A required parameter is invalid", HttpStatus.BAD_REQUEST.value()); } @@ -197,7 +197,7 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH protected void MissingParameterException(HttpServletRequest request, HttpServletResponse response, Exception ex) throws IOException { // we want the 400 status for missing parameters, see https://jira.lyrasis.org/browse/DS-4428 - sendErrorResponse(request, response, null, + sendErrorResponse(request, response, ex, "A required parameter is missing", HttpStatus.BAD_REQUEST.value()); } From f9681bb76bf405b5b9ba5d8ca90b7d4ff6432c4c Mon Sep 17 00:00:00 2001 From: damian Date: Fri, 21 Jul 2023 12:02:12 +0200 Subject: [PATCH 071/168] Reading localized license file. --- .../org/dspace/core/LicenseServiceImpl.java | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/core/LicenseServiceImpl.java b/dspace-api/src/main/java/org/dspace/core/LicenseServiceImpl.java index 8324105a30..f99b3c31e5 100644 --- a/dspace-api/src/main/java/org/dspace/core/LicenseServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/core/LicenseServiceImpl.java @@ -17,9 +17,12 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; +import javax.servlet.http.HttpServletRequest; import org.dspace.core.service.LicenseService; import org.dspace.services.factory.DSpaceServicesFactory; +import org.dspace.services.model.Request; +import org.dspace.web.ContextUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -101,13 +104,14 @@ public class LicenseServiceImpl implements LicenseService { /** * Get the site-wide default license that submitters need to grant * + * Localized license requires: default_{{locale}}.license file. + * Locale also must be listed in webui.supported.locales setting. + * * @return the default license */ @Override public String getDefaultSubmissionLicense() { - if (null == license) { - init(); - } + init(); return license; } @@ -115,9 +119,8 @@ public class LicenseServiceImpl implements LicenseService { * Load in the default license. */ protected void init() { - File licenseFile = new File( - DSpaceServicesFactory.getInstance().getConfigurationService().getProperty("dspace.dir") - + File.separator + "config" + File.separator + "default.license"); + Context context = obtainContext(); + File licenseFile = new File(I18nUtil.getDefaultLicense(context)); FileInputStream fir = null; InputStreamReader ir = null; @@ -169,4 +172,14 @@ public class LicenseServiceImpl implements LicenseService { } } } + + private Context obtainContext() { + Request currentRequest = DSpaceServicesFactory.getInstance().getRequestService().getCurrentRequest(); + if (currentRequest != null) { + HttpServletRequest request = currentRequest.getHttpServletRequest(); + return ContextUtil.obtainContext(request); + } else { + return new Context(); + } + } } From 0df490d4af2ef2fbb4b540b9a399147301fee65a Mon Sep 17 00:00:00 2001 From: damian Date: Fri, 21 Jul 2023 12:02:12 +0200 Subject: [PATCH 072/168] Reading localized license file. --- .../org/dspace/core/LicenseServiceImpl.java | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/core/LicenseServiceImpl.java b/dspace-api/src/main/java/org/dspace/core/LicenseServiceImpl.java index 8324105a30..5e42b04e71 100644 --- a/dspace-api/src/main/java/org/dspace/core/LicenseServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/core/LicenseServiceImpl.java @@ -17,9 +17,12 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; +import javax.servlet.http.HttpServletRequest; import org.dspace.core.service.LicenseService; import org.dspace.services.factory.DSpaceServicesFactory; +import org.dspace.services.model.Request; +import org.dspace.web.ContextUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -101,13 +104,14 @@ public class LicenseServiceImpl implements LicenseService { /** * Get the site-wide default license that submitters need to grant * + * Localized license requires: default_{{locale}}.license file. + * Locale also must be listed in webui.supported.locales setting. + * * @return the default license */ @Override public String getDefaultSubmissionLicense() { - if (null == license) { - init(); - } + init(); return license; } @@ -115,9 +119,8 @@ public class LicenseServiceImpl implements LicenseService { * Load in the default license. */ protected void init() { - File licenseFile = new File( - DSpaceServicesFactory.getInstance().getConfigurationService().getProperty("dspace.dir") - + File.separator + "config" + File.separator + "default.license"); + Context context = obtainContext(); + File licenseFile = new File(I18nUtil.getDefaultLicense(context)); FileInputStream fir = null; InputStreamReader ir = null; @@ -169,4 +172,17 @@ public class LicenseServiceImpl implements LicenseService { } } } + + /** + * Obtaining current request context + */ + private Context obtainContext() { + Request currentRequest = DSpaceServicesFactory.getInstance().getRequestService().getCurrentRequest(); + if (currentRequest != null) { + HttpServletRequest request = currentRequest.getHttpServletRequest(); + return ContextUtil.obtainContext(request); + } else { + return new Context(); + } + } } From c004a33c9dd1224d23de3ca23b371e5d8e258a22 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 24 Jul 2023 11:08:15 -0500 Subject: [PATCH 073/168] Replace all old docker "dspace-7_x" tags with "latest" --- Dockerfile | 5 ++-- Dockerfile.cli | 5 ++-- Dockerfile.test | 5 ++-- docker-compose-cli.yml | 2 +- docker-compose.yml | 6 ++--- dspace/src/main/docker-compose/README.md | 4 +-- .../src/main/docker-compose/db.entities.yml | 2 +- dspace/src/main/docker-compose/db.restore.yml | 2 +- .../docker-compose/docker-compose-angular.yml | 2 +- dspace/src/main/docker/README.md | 26 +++++++++---------- 10 files changed, 31 insertions(+), 28 deletions(-) diff --git a/Dockerfile b/Dockerfile index f1ff6adf5a..664cba89fa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,15 @@ # This image will be published as dspace/dspace # See https://github.com/DSpace/DSpace/tree/main/dspace/src/main/docker for usage details # -# - note: default tag for branch: dspace/dspace: dspace/dspace:dspace-7_x +# - note: default tag for branch: dspace/dspace: dspace/dspace:latest # This Dockerfile uses JDK11 by default, but has also been tested with JDK17. # To build with JDK17, use "--build-arg JDK_VERSION=17" ARG JDK_VERSION=11 +ARG DSPACE_VERSION=latest # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:dspace-7_x as build +FROM dspace/dspace-dependencies:${DSPACE_VERSION} as build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install diff --git a/Dockerfile.cli b/Dockerfile.cli index 62e83b79ef..d54978375e 100644 --- a/Dockerfile.cli +++ b/Dockerfile.cli @@ -1,14 +1,15 @@ # This image will be published as dspace/dspace-cli # See https://github.com/DSpace/DSpace/tree/main/dspace/src/main/docker for usage details # -# - note: default tag for branch: dspace/dspace-cli: dspace/dspace-cli:dspace-7_x +# - note: default tag for branch: dspace/dspace-cli: dspace/dspace-cli:latest # This Dockerfile uses JDK11 by default, but has also been tested with JDK17. # To build with JDK17, use "--build-arg JDK_VERSION=17" ARG JDK_VERSION=11 +ARG DSPACE_VERSION=latest # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:dspace-7_x as build +FROM dspace/dspace-dependencies:${DSPACE_VERSION} as build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install diff --git a/Dockerfile.test b/Dockerfile.test index 4e9b2b5b43..16a04d0002 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -1,16 +1,17 @@ # This image will be published as dspace/dspace # See https://github.com/DSpace/DSpace/tree/main/dspace/src/main/docker for usage details # -# - note: default tag for branch: dspace/dspace: dspace/dspace:dspace-7_x-test +# - note: default tag for branch: dspace/dspace: dspace/dspace:latest-test # # This image is meant for TESTING/DEVELOPMENT ONLY as it deploys the old v6 REST API under HTTP (not HTTPS) # This Dockerfile uses JDK11 by default, but has also been tested with JDK17. # To build with JDK17, use "--build-arg JDK_VERSION=17" ARG JDK_VERSION=11 +ARG DSPACE_VERSION=latest # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:dspace-7_x as build +FROM dspace/dspace-dependencies:${DSPACE_VERSION} as build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install diff --git a/docker-compose-cli.yml b/docker-compose-cli.yml index 9c66fed683..7dbdde3703 100644 --- a/docker-compose-cli.yml +++ b/docker-compose-cli.yml @@ -2,7 +2,7 @@ version: "3.7" services: dspace-cli: - image: "${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-dspace-7_x}" + image: "${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-latest}" container_name: dspace-cli build: context: . diff --git a/docker-compose.yml b/docker-compose.yml index 36ba6af2c9..e623d96079 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,7 +28,7 @@ services: # proxies.trusted.ipranges: This setting is required for a REST API running in Docker to trust requests # from the host machine. This IP range MUST correspond to the 'dspacenet' subnet defined above. proxies__P__trusted__P__ipranges: '172.23.0' - image: "${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-dspace-7_x-test}" + image: "${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-latest-test}" build: context: . dockerfile: Dockerfile.test @@ -66,7 +66,7 @@ services: dspacedb: container_name: dspacedb # Uses a custom Postgres image with pgcrypto installed - image: "${DOCKER_OWNER:-dspace}/dspace-postgres-pgcrypto:${DSPACE_VER:-dspace-7_x}" + image: "${DOCKER_OWNER:-dspace}/dspace-postgres-pgcrypto:${DSPACE_VER:-latest}" build: # Must build out of subdirectory to have access to install script for pgcrypto context: ./dspace/src/main/docker/dspace-postgres-pgcrypto/ @@ -86,7 +86,7 @@ services: # DSpace Solr container dspacesolr: container_name: dspacesolr - image: "${DOCKER_OWNER:-dspace}/dspace-solr:${DSPACE_VER:-dspace-7_x}" + image: "${DOCKER_OWNER:-dspace}/dspace-solr:${DSPACE_VER:-latest}" build: context: . dockerfile: ./dspace/src/main/docker/dspace-solr/Dockerfile diff --git a/dspace/src/main/docker-compose/README.md b/dspace/src/main/docker-compose/README.md index 35a6e60554..8660a9796c 100644 --- a/dspace/src/main/docker-compose/README.md +++ b/dspace/src/main/docker-compose/README.md @@ -268,8 +268,8 @@ Here's how to fix those issues by migrating your old Postgres data to the new ve * Pull down an older version of the image from Dockerhub (using a tag) * Or, temporarily rebuild your local image with the old version of Postgres. For example: ``` - # This command will rebuild using PostgreSQL v11 & tag it locally as "dspace-7_x" - docker build --build-arg POSTGRES_VERSION=11 -t dspace/dspace-postgres-pgcrypto:dspace-7_x ./dspace/src/main/docker/dspace-postgres-pgcrypto/ + # This command will rebuild using PostgreSQL v11 & tag it locally as "latest" + docker build --build-arg POSTGRES_VERSION=11 -t dspace/dspace-postgres-pgcrypto:latest ./dspace/src/main/docker/dspace-postgres-pgcrypto/ # Then restart container with that image docker-compose -p d7 up -d ``` diff --git a/dspace/src/main/docker-compose/db.entities.yml b/dspace/src/main/docker-compose/db.entities.yml index 32c54a5d0b..943f0732c6 100644 --- a/dspace/src/main/docker-compose/db.entities.yml +++ b/dspace/src/main/docker-compose/db.entities.yml @@ -10,7 +10,7 @@ version: "3.7" services: dspacedb: - image: dspace/dspace-postgres-pgcrypto:dspace-7_x-loadsql + image: dspace/dspace-postgres-pgcrypto:${DSPACE_VER:-latest}-loadsql environment: # This SQL is available from https://github.com/DSpace-Labs/AIP-Files/releases/tag/demo-entities-data - LOADSQL=https://github.com/DSpace-Labs/AIP-Files/releases/download/demo-entities-data/dspace7-entities-data.sql diff --git a/dspace/src/main/docker-compose/db.restore.yml b/dspace/src/main/docker-compose/db.restore.yml index fc2f30b9d8..5646370a91 100644 --- a/dspace/src/main/docker-compose/db.restore.yml +++ b/dspace/src/main/docker-compose/db.restore.yml @@ -14,7 +14,7 @@ version: "3.7" # This can be used to restore a "dspacedb" container from a pg_dump, or during upgrade to a new version of PostgreSQL. services: dspacedb: - image: dspace/dspace-postgres-pgcrypto:dspace-7_x-loadsql + image: dspace/dspace-postgres-pgcrypto:${DSPACE_VER:-latest}-loadsql environment: # Location where the dump SQL file will be available on the running container - LOCALSQL=/tmp/pgdump.sql diff --git a/dspace/src/main/docker-compose/docker-compose-angular.yml b/dspace/src/main/docker-compose/docker-compose-angular.yml index 00dde2e831..6690fb8bc5 100644 --- a/dspace/src/main/docker-compose/docker-compose-angular.yml +++ b/dspace/src/main/docker-compose/docker-compose-angular.yml @@ -23,7 +23,7 @@ services: DSPACE_REST_HOST: localhost DSPACE_REST_PORT: 8080 DSPACE_REST_NAMESPACE: /server - image: dspace/dspace-angular:dspace-7_x + image: dspace/dspace-angular:${DSPACE_VER:-latest} networks: dspacenet: ports: diff --git a/dspace/src/main/docker/README.md b/dspace/src/main/docker/README.md index ac1b4cb923..bee59ad876 100644 --- a/dspace/src/main/docker/README.md +++ b/dspace/src/main/docker/README.md @@ -16,7 +16,7 @@ Caching these Maven dependencies provides a speed increase to all later builds b are only downloaded once. ``` -docker build -t dspace/dspace-dependencies:dspace-7_x -f Dockerfile.dependencies . +docker build -t dspace/dspace-dependencies:latest -f Dockerfile.dependencies . ``` This image is built *automatically* after each commit is made to the `main` branch. @@ -25,7 +25,7 @@ A corresponding image exists for DSpace 4-6. Admins to our DockerHub repo can manually publish with the following command. ``` -docker push dspace/dspace-dependencies:dspace-7_x +docker push dspace/dspace-dependencies:latest ``` ## Dockerfile.test (in root folder) @@ -37,7 +37,7 @@ This image deploys two DSpace webapps to Tomcat running in Docker: This image also sets up debugging in Tomcat for development. ``` -docker build -t dspace/dspace:dspace-7_x-test -f Dockerfile.test . +docker build -t dspace/dspace:latest-test -f Dockerfile.test . ``` This image is built *automatically* after each commit is made to the `main` branch. @@ -46,7 +46,7 @@ A corresponding image exists for DSpace 4-6. Admins to our DockerHub repo can manually publish with the following command. ``` -docker push dspace/dspace:dspace-7_x-test +docker push dspace/dspace:latest-test ``` ## Dockerfile (in root folder) @@ -56,7 +56,7 @@ This image deploys one DSpace webapp to Tomcat running in Docker: 1. The DSpace 7 REST API (at `http://localhost:8080/server`) ``` -docker build -t dspace/dspace:dspace-7_x -f Dockerfile . +docker build -t dspace/dspace:latest -f Dockerfile . ``` This image is built *automatically* after each commit is made to the `main` branch. @@ -65,14 +65,14 @@ A corresponding image exists for DSpace 4-6. Admins to our DockerHub repo can publish with the following command. ``` -docker push dspace/dspace:dspace-7_x +docker push dspace/dspace:latest ``` ## Dockerfile.cli (in root folder) This Dockerfile builds a DSpace 7 CLI (command line interface) image, which can be used to run DSpace's commandline tools via Docker. ``` -docker build -t dspace/dspace-cli:dspace-7_x -f Dockerfile.cli . +docker build -t dspace/dspace-cli:latest -f Dockerfile.cli . ``` This image is built *automatically* after each commit is made to the `main` branch. @@ -81,7 +81,7 @@ A corresponding image exists for DSpace 6. Admins to our DockerHub repo can publish with the following command. ``` -docker push dspace/dspace-cli:dspace-7_x +docker push dspace/dspace-cli:latest ``` ## ./dspace-postgres-pgcrypto/Dockerfile @@ -92,20 +92,20 @@ This image is built *automatically* after each commit is made to the `main` bran How to build manually: ``` cd dspace/src/main/docker/dspace-postgres-pgcrypto -docker build -t dspace/dspace-postgres-pgcrypto:dspace-7_x . +docker build -t dspace/dspace-postgres-pgcrypto:latest . ``` It is also possible to change the version of PostgreSQL or the PostgreSQL user's password during the build: ``` cd dspace/src/main/docker/dspace-postgres-pgcrypto -docker build -t dspace/dspace-postgres-pgcrypto:dspace-7_x --build-arg POSTGRES_VERSION=11 --build-arg POSTGRES_PASSWORD=mypass . +docker build -t dspace/dspace-postgres-pgcrypto:latest --build-arg POSTGRES_VERSION=11 --build-arg POSTGRES_PASSWORD=mypass . ``` A copy of this file exists in the DSpace 6 branch. A specialized version of this file exists for DSpace 4 in DSpace-Docker-Images. Admins to our DockerHub repo can (manually) publish with the following command. ``` -docker push dspace/dspace-postgres-pgcrypto:dspace-7_x +docker push dspace/dspace-postgres-pgcrypto:latest ``` ## ./dspace-postgres-pgcrypto-curl/Dockerfile @@ -118,7 +118,7 @@ This image is built *automatically* after each commit is made to the `main` bran How to build manually: ``` cd dspace/src/main/docker/dspace-postgres-pgcrypto-curl -docker build -t dspace/dspace-postgres-pgcrypto:dspace-7_x-loadsql . +docker build -t dspace/dspace-postgres-pgcrypto:latest-loadsql . ``` Similar to `dspace-postgres-pgcrypto` above, you can also modify the version of PostgreSQL or the PostgreSQL user's password. @@ -128,7 +128,7 @@ A copy of this file exists in the DSpace 6 branch. Admins to our DockerHub repo can (manually) publish with the following command. ``` -docker push dspace/dspace-postgres-pgcrypto:dspace-7_x-loadsql +docker push dspace/dspace-postgres-pgcrypto:latest-loadsql ``` ## ./dspace-shibboleth/Dockerfile From f6a898c3d13360286c416b2588ab0447d9e3d81b Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 27 Jul 2023 16:55:42 -0500 Subject: [PATCH 074/168] Add action to automatically create a port PR when specified --- .../workflows/port_merged_pull_request.yml | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/port_merged_pull_request.yml diff --git a/.github/workflows/port_merged_pull_request.yml b/.github/workflows/port_merged_pull_request.yml new file mode 100644 index 0000000000..418498fa44 --- /dev/null +++ b/.github/workflows/port_merged_pull_request.yml @@ -0,0 +1,38 @@ +# This workflow will attempt to port a merged pull request to +# the branch specified in a "port to" label (if exists) +name: Port merged Pull Request + +# Only run for merged PRs against the "main" or maintenance branches +# We allow this to run for `pull_request_target` so that github secrets are available +# (This is required when the PR comes from a forked repo) +on: + pull_request_target: + types: [ closed ] + branches: + - main + - 'dspace-**' + +permissions: + contents: write # so action can add comments + pull-requests: write # so action can create pull requests + +jobs: + port_pr: + runs-on: ubuntu-latest + # Don't run on closed *unmerged* pull requests + if: github.event.pull_request.merged + steps: + # Checkout code + - uses: actions/checkout@v3 + # Port PR to other branch (ONLY if labeled with "port to") + # See https://github.com/korthout/backport-action + - name: Create backport pull requests + uses: korthout/backport-action@v1 + with: + # Trigger based on a "port to [branch]" label on PR + # (This label must specify the branch name to port to) + label_pattern: '^port to ([^ ]+)$' + # Title to add to the (newly created) port PR + pull_title: '[Port ${target_branch}] ${pull_title}' + # Description to add to the (newly created) port PR + pull_description: 'Port of #${pull_number} by @${pull_author} to `${target_branch}`.' \ No newline at end of file From 16c46c49797cf4a15b8ef51efdd365610bdf73ab Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Wed, 26 Jul 2023 21:26:04 +0300 Subject: [PATCH 075/168] dspace.cfg: remove old webui.itemlist properties These properties are no longer used in DSpace 7: webui.itemlist.widths webui.itemlist.*.widths webui.itemlist.tablewidth --- dspace/config/dspace.cfg | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index cafd37931f..c37cfea0b1 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1472,11 +1472,6 @@ log.report.dir = ${dspace.dir}/log # # webui.itemlist.columns = thumbnail, dc.date.issued(date), dc.title, dc.contributor.* # -# You can customise the width of each column with the following line - you can have numbers (pixels) -# or percentages. For the 'thumbnail' column, a setting of '*' will use the max width specified -# for browse thumbnails (webui.browse.thumbnail.maxwidth, thumbnail.maxwidth) -# webui.itemlist.widths = *, 130, 60%, 40% - # Additionally, you can override the DC fields used on the listing page for # a given browse index and/or sort option. As a sort option or index may be defined # on a field that isn't normally included in the list, this allows you to display @@ -1502,14 +1497,6 @@ log.report.dir = ${dspace.dir}/log # and thumbnails in the item list # # webui.itemlist.dateaccessioned.columns = thumbnail, dc.date.accessioned(date), dc.title, dc.contributor.* -# -# As above, you can customise the width of the columns for each configured column list, substituting '.widths' for -# '.columns' in the property name. See the setting for webui.itemlist.widths for more details -# webui.itemlist.dateaccessioned.widths = *, 130, 60%, 40% - -# You can also set the overall size of the item list table with the following setting. It can lead to faster -# table rendering when used with the column widths above, but not generally recommended. -# webui.itemlist.tablewidth = 100% ##### SFX Server (OpenURL) ##### From 213a546486073f09e73d91d014c489ed300bf59d Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Wed, 26 Jul 2023 21:42:16 +0300 Subject: [PATCH 076/168] dspace.cfg: remove old webui.browse.thumbnail.show property The webui.browse.thumbnail.show property is no longer used as of DSpace 7. Also remove subsequent references to adding thumbnails to item view columns since these are from legacy DSpace. --- dspace/config/dspace.cfg | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index c37cfea0b1..c7f1cb80a9 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1460,9 +1460,6 @@ log.report.dir = ${dspace.dir}/log # fields at least the date and title fields as specified by the # webui.browse.index.* configuration options below. # -# If you have enabled thumbnails (webui.browse.thumbnail.show), you must also -# include a 'thumbnail' entry in your columns - this is where the thumbnail will be displayed -# # If you want to mark each item include a 'mark_[value]' (without the brackets - replace the word 'value' with anything that # has a meaning for your mark) entry in your columns - this is where the icon will be displayed. # Do not forget to add a Spring bean with id = "org.dspace.app.itemmarking.ItemMarkingExtractor.[value]" @@ -1470,7 +1467,7 @@ log.report.dir = ${dspace.dir}/log # You can add more than one 'mark_[value]' options (with different value) in case you need to mark items more than one time for # different purposes. Remember to add the respective beans in file 'config/spring/api/item-marking.xml'. # -# webui.itemlist.columns = thumbnail, dc.date.issued(date), dc.title, dc.contributor.* +# webui.itemlist.columns = dc.date.issued(date), dc.title, dc.contributor.* # # Additionally, you can override the DC fields used on the listing page for # a given browse index and/or sort option. As a sort option or index may be defined @@ -1489,14 +1486,6 @@ log.report.dir = ${dspace.dir}/log # In the last case, a sort option name will always take precedence over a browse # index name. Note also, that for any additional columns you list, you will need to # ensure there is an itemlist. entry in the messages file. -# -# The following example would display the date of accession in place of the issue date -# whenever the dateaccessioned browse index or sort option is selected. -# -# Just like webui.itemlist.columns, you will need to include a 'thumbnail' entry to display -# and thumbnails in the item list -# -# webui.itemlist.dateaccessioned.columns = thumbnail, dc.date.accessioned(date), dc.title, dc.contributor.* ##### SFX Server (OpenURL) ##### From 35f72bc9d0c1a01aa0b9313216bbbe63e6960a81 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Wed, 26 Jul 2023 21:45:42 +0300 Subject: [PATCH 077/168] dspace.cfg: remove old webui.itemlist.browse.* property The webui.itemlist.browse.* properties are no longer used in DSpace 7. --- dspace/config/dspace.cfg | 6 ------ 1 file changed, 6 deletions(-) diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index c7f1cb80a9..1feaf05cda 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1478,14 +1478,8 @@ log.report.dir = ${dspace.dir}/log # they are listed below is the priority in which they will be used (so a combination # of an index name and sort name will take precedence over just the browse name). # -# webui.itemlist.browse..sort..columns # webui.itemlist.sort..columns -# webui.itemlist.browse..columns # webui.itemlist..columns -# -# In the last case, a sort option name will always take precedence over a browse -# index name. Note also, that for any additional columns you list, you will need to -# ensure there is an itemlist. entry in the messages file. ##### SFX Server (OpenURL) ##### From 22974e982c99b7faa9d287ddc5bef4715f19849a Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Fri, 28 Jul 2023 10:50:07 -0400 Subject: [PATCH 078/168] On failure log the name of the assetstore file and trace causes of exception. --- .../mediafilter/MediaFilterServiceImpl.java | 12 +++--- .../java/org/dspace/util/ThrowableUtils.java | 41 +++++++++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/util/ThrowableUtils.java diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java index e2c6c9c5db..1a8c2ddd3e 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java @@ -40,6 +40,7 @@ import org.dspace.eperson.Group; import org.dspace.eperson.service.GroupService; import org.dspace.scripts.handler.DSpaceRunnableHandler; import org.dspace.services.ConfigurationService; +import org.dspace.util.ThrowableUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; @@ -240,8 +241,9 @@ public class MediaFilterServiceImpl implements MediaFilterService, InitializingB sb.append("\tFile Size: ").append(size); sb.append("\tChecksum: ").append(checksum); sb.append("\tAsset Store: ").append(assetstore); + sb.append("\tInternal ID: ").append(myBitstream.getInternalId()); logError(sb.toString()); - logError(e.getMessage(), e); + logError(ThrowableUtils.formatCauseChain(e)); } } else if (filterClass instanceof SelfRegisterInputFormats) { // Filter implements self registration, so check to see if it should be applied @@ -319,10 +321,10 @@ public class MediaFilterServiceImpl implements MediaFilterService, InitializingB // check if destination bitstream exists Bundle existingBundle = null; - List existingBitstreams = new ArrayList(); + List existingBitstreams = new ArrayList<>(); List bundles = itemService.getBundles(item, formatFilter.getBundleName()); - if (bundles.size() > 0) { + if (!bundles.isEmpty()) { // only finds the last matching bundle and all matching bitstreams in the proper bundle(s) for (Bundle bundle : bundles) { List bitstreams = bundle.getBitstreams(); @@ -337,7 +339,7 @@ public class MediaFilterServiceImpl implements MediaFilterService, InitializingB } // if exists and overwrite = false, exit - if (!overWrite && (existingBitstreams.size() > 0)) { + if (!overWrite && (!existingBitstreams.isEmpty())) { if (!isQuiet) { logInfo("SKIPPED: bitstream " + source.getID() + " (item: " + item.getHandle() + ") because '" + newName + "' already exists"); @@ -370,7 +372,7 @@ public class MediaFilterServiceImpl implements MediaFilterService, InitializingB } Bundle targetBundle; // bundle we're modifying - if (bundles.size() < 1) { + if (bundles.isEmpty()) { // create new bundle if needed targetBundle = bundleService.create(context, item, formatFilter.getBundleName()); } else { diff --git a/dspace-api/src/main/java/org/dspace/util/ThrowableUtils.java b/dspace-api/src/main/java/org/dspace/util/ThrowableUtils.java new file mode 100644 index 0000000000..7809e2048a --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/util/ThrowableUtils.java @@ -0,0 +1,41 @@ +/** + * 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.util; + +/** + * Things you wish {@link Throwable} or some logging package would do for you. + * + * @author mwood + */ +public class ThrowableUtils { + /** + * Utility class: do not instantiate. + */ + private ThrowableUtils() { } + + /** + * Trace a chain of {@code Throwable}s showing only causes. + * Less voluminous than a stack trace. Useful if you just want to know + * what caused third-party code to return an uninformative exception + * message. + * + * @param throwable the exception or whatever. + * @return list of messages from each {@code Throwable} in the chain, + * separated by '\n'. + */ + static public String formatCauseChain(Throwable throwable) { + StringBuilder trace = new StringBuilder(); + trace.append(throwable.getMessage()); + Throwable cause = throwable.getCause(); + while (null != cause) { + trace.append("\nCaused by: ").append(cause.getMessage()); + cause = cause.getCause(); + } + return trace.toString(); + } +} From d6b612fc5cf84fe6b7226649451b7b927ded8997 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Fri, 28 Jul 2023 11:23:20 -0400 Subject: [PATCH 079/168] Report Throwable's type too. --- dspace-api/src/main/java/org/dspace/util/ThrowableUtils.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/util/ThrowableUtils.java b/dspace-api/src/main/java/org/dspace/util/ThrowableUtils.java index 7809e2048a..e1502e89b5 100644 --- a/dspace-api/src/main/java/org/dspace/util/ThrowableUtils.java +++ b/dspace-api/src/main/java/org/dspace/util/ThrowableUtils.java @@ -33,7 +33,9 @@ public class ThrowableUtils { trace.append(throwable.getMessage()); Throwable cause = throwable.getCause(); while (null != cause) { - trace.append("\nCaused by: ").append(cause.getMessage()); + trace.append("\nCaused by: ") + .append(cause.getClass().getCanonicalName()).append(' ') + .append(cause.getMessage()); cause = cause.getCause(); } return trace.toString(); From 2dc7c90e83867247df9004c54d27b5c551136283 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 28 Jul 2023 11:15:08 -0500 Subject: [PATCH 080/168] Run PR Port action as 'dspace-bot' to allow new PRs to trigger CI checks --- .github/workflows/port_merged_pull_request.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/port_merged_pull_request.yml b/.github/workflows/port_merged_pull_request.yml index 418498fa44..6c491e41c2 100644 --- a/.github/workflows/port_merged_pull_request.yml +++ b/.github/workflows/port_merged_pull_request.yml @@ -35,4 +35,10 @@ jobs: # Title to add to the (newly created) port PR pull_title: '[Port ${target_branch}] ${pull_title}' # Description to add to the (newly created) port PR - pull_description: 'Port of #${pull_number} by @${pull_author} to `${target_branch}`.' \ No newline at end of file + pull_description: 'Port of #${pull_number} by @${pull_author} to `${target_branch}`.' + # Copy all labels from original PR to (newly created) port PR + # NOTE: The labels matching 'label_pattern' are automatically excluded + copy_labels_pattern: '*' + # Use a personal access token (PAT) to create PR as 'dspace-bot' user. + # A PAT is required in order for the new PR to trigger its own actions (for CI checks) + github_token: ${{ secrets.PR_PORT_TOKEN }} \ No newline at end of file From 5bff43356fca0aa9ee78782f000c0d1a25a6cfbb Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 28 Jul 2023 11:15:42 -0500 Subject: [PATCH 081/168] Minor update to label_merge_conflicts to ignore any errors (seem random at this time) --- .github/workflows/label_merge_conflicts.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/label_merge_conflicts.yml b/.github/workflows/label_merge_conflicts.yml index 0c3b1a0f2a..a023f4eef2 100644 --- a/.github/workflows/label_merge_conflicts.yml +++ b/.github/workflows/label_merge_conflicts.yml @@ -25,6 +25,8 @@ jobs: # See: https://github.com/prince-chrismc/label-merge-conflicts-action - name: Auto-label PRs with merge conflicts uses: prince-chrismc/label-merge-conflicts-action@v3 + # Ignore any failures -- may occur (randomly?) for older, outdated PRs. + continue-on-error: true # Add "merge conflict" label if a merge conflict is detected. Remove it when resolved. # Note, the authentication token is created automatically # See: https://docs.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token From 799528963e3c0391852ecbaf82ef21ec7d477342 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 28 Jul 2023 11:48:30 -0500 Subject: [PATCH 082/168] Fix typo. Config must be a valid regex --- .github/workflows/port_merged_pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/port_merged_pull_request.yml b/.github/workflows/port_merged_pull_request.yml index 6c491e41c2..50faf3f886 100644 --- a/.github/workflows/port_merged_pull_request.yml +++ b/.github/workflows/port_merged_pull_request.yml @@ -38,7 +38,7 @@ jobs: pull_description: 'Port of #${pull_number} by @${pull_author} to `${target_branch}`.' # Copy all labels from original PR to (newly created) port PR # NOTE: The labels matching 'label_pattern' are automatically excluded - copy_labels_pattern: '*' + copy_labels_pattern: '.*' # Use a personal access token (PAT) to create PR as 'dspace-bot' user. # A PAT is required in order for the new PR to trigger its own actions (for CI checks) github_token: ${{ secrets.PR_PORT_TOKEN }} \ No newline at end of file From 71cfe184a5b831f1cbc1d487f6ef32e04eb127b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Jul 2023 17:38:04 +0000 Subject: [PATCH 083/168] Bump h2 from 2.1.214 to 2.2.220 Bumps [h2](https://github.com/h2database/h2database) from 2.1.214 to 2.2.220. - [Release notes](https://github.com/h2database/h2database/releases) - [Commits](https://github.com/h2database/h2database/compare/version-2.1.214...version-2.2.220) --- updated-dependencies: - dependency-name: com.h2database:h2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5dc53194b3..7822d43109 100644 --- a/pom.xml +++ b/pom.xml @@ -1688,7 +1688,7 @@ com.h2database h2 - 2.1.214 + 2.2.220 test From bbe5df3f7dd4a33423fdf47702e23f3eb9ef821f Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Mon, 31 Jul 2023 09:55:09 -0400 Subject: [PATCH 084/168] More description on OutOfMemoryError too. --- .../mediafilter/MediaFilterServiceImpl.java | 50 +++++++++++++------ 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java index 1a8c2ddd3e..b50fb22355 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java @@ -10,6 +10,7 @@ package org.dspace.app.mediafilter; import java.io.InputStream; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -226,23 +227,8 @@ public class MediaFilterServiceImpl implements MediaFilterService, InitializingB filtered = true; } } catch (Exception e) { - String handle = myItem.getHandle(); - List bundles = myBitstream.getBundles(); - long size = myBitstream.getSizeBytes(); - String checksum = myBitstream.getChecksum() + " (" + myBitstream.getChecksumAlgorithm() + ")"; - int assetstore = myBitstream.getStoreNumber(); - // Printout helpful information to find the errored bitstream. - StringBuilder sb = new StringBuilder("ERROR filtering, skipping bitstream:\n"); - sb.append("\tItem Handle: ").append(handle); - for (Bundle bundle : bundles) { - sb.append("\tBundle Name: ").append(bundle.getName()); - } - sb.append("\tFile Size: ").append(size); - sb.append("\tChecksum: ").append(checksum); - sb.append("\tAsset Store: ").append(assetstore); - sb.append("\tInternal ID: ").append(myBitstream.getInternalId()); - logError(sb.toString()); + logError(formatBitstreamDetails(myItem.getHandle(), myBitstream)); logError(ThrowableUtils.formatCauseChain(e)); } } else if (filterClass instanceof SelfRegisterInputFormats) { @@ -401,6 +387,7 @@ public class MediaFilterServiceImpl implements MediaFilterService, InitializingB } catch (OutOfMemoryError oome) { logError("!!! OutOfMemoryError !!!"); + logError(formatBitstreamDetails(item.getHandle(), source)); } // we are overwriting, so remove old bitstream @@ -498,6 +485,37 @@ public class MediaFilterServiceImpl implements MediaFilterService, InitializingB } } + /** + * Describe a Bitstream in detail. Format a single line of text with + * information such as Bitstore index, backing file ID, size, checksum, + * enclosing Item and Bundles. + * + * @param itemHandle Handle of the Item by which we found the Bitstream. + * @param bitstream the Bitstream to be described. + * @return Bitstream details. + */ + private String formatBitstreamDetails(String itemHandle, + Bitstream bitstream) { + List 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); From a76af35a0cd4f0c0e8737c736578b17bcc349691 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 1 Aug 2023 17:13:07 -0400 Subject: [PATCH 085/168] Make workflow curation tasks actually work. When curation runs, there was no "current user" and no claimed task, so the code broke when trying to find people to notify about curation failures. --- .../curate/XmlWorkflowCuratorServiceImpl.java | 29 ++++++++++++++----- .../dspace/eperson/EPersonServiceImpl.java | 18 ++++++++++++ .../eperson/service/EPersonService.java | 12 ++++++++ 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java b/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java index 05c7a8d999..dd6c8d5e15 100644 --- a/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java @@ -13,6 +13,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Collection; @@ -47,14 +48,17 @@ import org.springframework.stereotype.Service; * Manage interactions between curation and workflow. A curation task can be * attached to a workflow step, to be executed during the step. * + *

+ * NOTE: when run in workflow, curation tasks run with + * authorization disabled. + * * @see CurationTaskConfig * @author mwood */ @Service public class XmlWorkflowCuratorServiceImpl implements XmlWorkflowCuratorService { - private static final Logger LOG - = org.apache.logging.log4j.LogManager.getLogger(); + private static final Logger LOG = LogManager.getLogger(); @Autowired(required = true) protected XmlWorkflowFactory workflowFactory; @@ -97,7 +101,13 @@ public class XmlWorkflowCuratorServiceImpl throws AuthorizeException, IOException, SQLException { Curator curator = new Curator(); curator.setReporter(reporter); - return curate(curator, c, wfi); + c.turnOffAuthorisationSystem(); + if (null == c.getCurrentUser()) { // We need someone to email + c.setCurrentUser(ePersonService.findAnAdministrator(c)); + } + boolean failedP = curate(curator, c, wfi); + c.restoreAuthSystemState(); + return failedP; } @Override @@ -123,7 +133,7 @@ public class XmlWorkflowCuratorServiceImpl item.setOwningCollection(wfi.getCollection()); for (Task task : step.tasks) { curator.addTask(task.name); - curator.curate(item); + curator.curate(c, item); int status = curator.getStatus(task.name); String result = curator.getResult(task.name); String action = "none"; @@ -223,8 +233,12 @@ public class XmlWorkflowCuratorServiceImpl String status, String action, String message) throws AuthorizeException, IOException, SQLException { List epa = resolveContacts(c, task.getContacts(status), wfi); - if (epa.size() > 0) { + if (!epa.isEmpty()) { workflowService.notifyOfCuration(c, wfi, epa, task.name, action, message); + } else { + LOG.warn("No contacts were found for workflow item {}: " + + "task {} returned action {} with message {}", + wfi.getID(), task.name, action, message); } } @@ -247,8 +261,7 @@ public class XmlWorkflowCuratorServiceImpl // decode contacts if ("$flowgroup".equals(contact)) { // special literal for current flowgoup - ClaimedTask claimedTask = claimedTaskService.findByWorkflowIdAndEPerson(c, wfi, c.getCurrentUser()); - String stepID = claimedTask.getStepID(); + String stepID = getFlowStep(c, wfi).step; Step step; try { Workflow workflow = workflowFactory.getWorkflow(wfi.getCollection()); @@ -266,11 +279,13 @@ public class XmlWorkflowCuratorServiceImpl epList.addAll(group.getMembers()); } } else if ("$colladmin".equals(contact)) { + // special literal for collection administrators Group adGroup = wfi.getCollection().getAdministrators(); if (adGroup != null) { epList.addAll(groupService.allMembers(c, adGroup)); } } else if ("$siteadmin".equals(contact)) { + // special literal for site administrator EPerson siteEp = ePersonService.findByEmail(c, configurationService.getProperty("mail.admin")); if (siteEp != null) { diff --git a/dspace-api/src/main/java/org/dspace/eperson/EPersonServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/EPersonServiceImpl.java index 61477995c7..e3b743c4a7 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/EPersonServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/EPersonServiceImpl.java @@ -47,6 +47,7 @@ import org.dspace.eperson.service.GroupService; import org.dspace.eperson.service.SubscribeService; import org.dspace.event.Event; import org.dspace.orcid.service.OrcidTokenService; +import org.dspace.services.ConfigurationService; import org.dspace.util.UUIDUtils; import org.dspace.versioning.Version; import org.dspace.versioning.VersionHistory; @@ -101,6 +102,8 @@ public class EPersonServiceImpl extends DSpaceObjectServiceImpl impleme protected VersionDAO versionDAO; @Autowired(required = true) protected ClaimedTaskService claimedTaskService; + @Autowired(required = true) + protected ConfigurationService configurationService; @Autowired protected OrcidTokenService orcidTokenService; @@ -113,6 +116,21 @@ public class EPersonServiceImpl extends DSpaceObjectServiceImpl impleme return ePersonDAO.findByID(context, EPerson.class, id); } + @Override + public EPerson findAnAdministrator(Context c) + throws SQLException { + List contacts = groupService.findByName(c, Group.ADMIN).getMembers(); + EPerson currentUser; + if (contacts.isEmpty()) { + log.warn("Administrators group is empty"); + currentUser = findByEmail(c, configurationService.getProperty("mail.admin")); + // Null if no such EPerson + } else { + currentUser = contacts.get(0); + } + return currentUser; + } + @Override public EPerson findByIdOrLegacyId(Context context, String id) throws SQLException { if (StringUtils.isNumeric(id)) { diff --git a/dspace-api/src/main/java/org/dspace/eperson/service/EPersonService.java b/dspace-api/src/main/java/org/dspace/eperson/service/EPersonService.java index c5c9801c16..c3def01a82 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/service/EPersonService.java +++ b/dspace-api/src/main/java/org/dspace/eperson/service/EPersonService.java @@ -157,6 +157,18 @@ public interface EPersonService extends DSpaceObjectService, DSpaceObje public List findAll(Context context, int sortField, int pageSize, int offset) throws SQLException; + /** + * Try very hard to find an administrator's account. Might return a member + * of the Administrators group, or an account with a configured email + * address. + * + * @param context current DSpace session. + * @return a presumed administrator account, or null if none could be found. + * @throws SQLException + */ + public EPerson findAnAdministrator(Context context) + throws SQLException; + /** * Create a new eperson * From 895926f021a355181faef47b5c41e78031700475 Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Wed, 2 Aug 2023 15:24:29 +0100 Subject: [PATCH 086/168] Refactored access-status to include embargo date based on the DefaultAccessStatusHelper logic (look at primary or first bitstream) --- .../access/status/AccessStatusHelper.java | 10 +++ .../status/AccessStatusServiceImpl.java | 5 ++ .../status/DefaultAccessStatusHelper.java | 89 ++++++++++++++++++- .../status/service/AccessStatusService.java | 12 +++ .../status/DefaultAccessStatusHelperTest.java | 7 ++ .../AccessStatusElementItemCompilePlugin.java | 14 +++ .../oai/metadataFormats/uketd_dc.xsl | 11 +-- 7 files changed, 139 insertions(+), 9 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/access/status/AccessStatusHelper.java b/dspace-api/src/main/java/org/dspace/access/status/AccessStatusHelper.java index 1cacbf6aed..d847e907b4 100644 --- a/dspace-api/src/main/java/org/dspace/access/status/AccessStatusHelper.java +++ b/dspace-api/src/main/java/org/dspace/access/status/AccessStatusHelper.java @@ -27,4 +27,14 @@ public interface AccessStatusHelper { */ public String getAccessStatusFromItem(Context context, Item item, Date threshold) throws SQLException; + + /** + * Retrieve embargo information for the item + * + * @param context the DSpace context + * @param item the item to check for embargo information + * @return an embargo date + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + public String getEmbargoFromItem(Context context, Item item) throws SQLException; } diff --git a/dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java b/dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java index 544dc99cb4..f0f68b22a1 100644 --- a/dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java @@ -63,4 +63,9 @@ public class AccessStatusServiceImpl implements AccessStatusService { public String getAccessStatus(Context context, Item item) throws SQLException { return helper.getAccessStatusFromItem(context, item, forever_date); } + + @Override + public String getEmbargoFromItem(Context context, Item item) throws SQLException { + return helper.getEmbargoFromItem(context, item); + } } diff --git a/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java b/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java index a67fa67af3..e7055181aa 100644 --- a/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java +++ b/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java @@ -26,6 +26,7 @@ import org.dspace.content.service.ItemService; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.eperson.Group; +import org.joda.time.LocalDate; /** * Default plugin implementation of the access status helper. @@ -33,6 +34,11 @@ import org.dspace.eperson.Group; * calculate the access status of an item based on the policies of * the primary or the first bitstream in the original bundle. * Users can override this method for enhanced functionality. + * + * The getEmbargoInformationFromItem method provides a simple logic to + * * retrieve embargo information of bitstreams from an item based on the policies of + * * the primary or the first bitstream in the original bundle. + * * Users can override this method for enhanced functionality. */ public class DefaultAccessStatusHelper implements AccessStatusHelper { public static final String EMBARGO = "embargo"; @@ -54,12 +60,12 @@ public class DefaultAccessStatusHelper implements AccessStatusHelper { /** * Look at the item's policies to determine an access status value. - * It is also considering a date threshold for embargos and restrictions. + * It is also considering a date threshold for embargoes and restrictions. * * If the item is null, simply returns the "unknown" value. * * @param context the DSpace context - * @param item the item to embargo + * @param item the item to check for embargoes * @param threshold the embargo threshold date * @return an access status value */ @@ -86,7 +92,7 @@ public class DefaultAccessStatusHelper implements AccessStatusHelper { .findFirst() .orElse(null); } - return caculateAccessStatusForDso(context, bitstream, threshold); + return calculateAccessStatusForDso(context, bitstream, threshold); } /** @@ -104,7 +110,7 @@ public class DefaultAccessStatusHelper implements AccessStatusHelper { * @param threshold the embargo threshold date * @return an access status value */ - private String caculateAccessStatusForDso(Context context, DSpaceObject dso, Date threshold) + private String calculateAccessStatusForDso(Context context, DSpaceObject dso, Date threshold) throws SQLException { if (dso == null) { return METADATA_ONLY; @@ -156,4 +162,79 @@ public class DefaultAccessStatusHelper implements AccessStatusHelper { } return RESTRICTED; } + + /** + * Look at the policies of the primary (or first) bitstream of the item to retrieve its embargo. + * + * If the item is null, simply returns an empty map with no embargo information. + * + * @param context the DSpace context + * @param item the item to embargo + * @return an access status value + */ + @Override + public String getEmbargoFromItem(Context context, Item item) + throws SQLException { + Date embargoDate; + + if (item == null) { + return null; + } + // Consider only the original bundles. + List bundles = item.getBundles(Constants.DEFAULT_BUNDLE_NAME); + // Check for primary bitstreams first. + Bitstream bitstream = bundles.stream() + .map(bundle -> bundle.getPrimaryBitstream()) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); + if (bitstream == null) { + // If there is no primary bitstream, + // take the first bitstream in the bundles. + bitstream = bundles.stream() + .map(bundle -> bundle.getBitstreams()) + .flatMap(List::stream) + .findFirst() + .orElse(null); + } + + embargoDate = this.retrieveLongestEmbargo(context, bitstream); + + return embargoDate != null ? embargoDate.toString() : null; + } + + /** + * + */ + private Date retrieveLongestEmbargo(Context context, Bitstream bitstream) throws SQLException { + Date embargoDate = null; + // Only consider read policies. + List policies = authorizeService + .getPoliciesActionFilter(context, bitstream, Constants.READ); + + // Looks at all read policies. + for (ResourcePolicy policy : policies) { + boolean isValid = resourcePolicyService.isDateValid(policy); + Group group = policy.getGroup(); + + if (group != null && StringUtils.equals(group.getName(), Group.ANONYMOUS)) { + // Only calculate the status for the anonymous group. + if (!isValid) { + // If the policy is not valid there is an active embargo + Date startDate = policy.getStartDate(); + + if (startDate != null && !startDate.before(LocalDate.now().toDate())) { + // There is an active embargo: aim to take the longest embargo + if (embargoDate == null) { + embargoDate = startDate; + } else { + embargoDate = startDate.after(embargoDate) ? startDate : embargoDate; + } + } + } + } + } + + return embargoDate; + } } diff --git a/dspace-api/src/main/java/org/dspace/access/status/service/AccessStatusService.java b/dspace-api/src/main/java/org/dspace/access/status/service/AccessStatusService.java index 43de5e3c47..937cb02692 100644 --- a/dspace-api/src/main/java/org/dspace/access/status/service/AccessStatusService.java +++ b/dspace-api/src/main/java/org/dspace/access/status/service/AccessStatusService.java @@ -8,6 +8,7 @@ package org.dspace.access.status.service; import java.sql.SQLException; +import java.util.Date; import org.dspace.content.Item; import org.dspace.core.Context; @@ -40,7 +41,18 @@ public interface AccessStatusService { * * @param context the DSpace context * @param item the item + * @return an access status value * @throws SQLException An exception that provides information on a database access error or other errors. */ public String getAccessStatus(Context context, Item item) throws SQLException; + + /** + * Retrieve embargo information for the item + * + * @param context the DSpace context + * @param item the item to check for embargo information + * @return an embargo date + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + public String getEmbargoFromItem(Context context, Item item) throws SQLException; } diff --git a/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java b/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java index a41e985deb..9d90452bee 100644 --- a/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java +++ b/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java @@ -8,6 +8,7 @@ package org.dspace.access.status; import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; @@ -273,6 +274,8 @@ public class DefaultAccessStatusHelperTest extends AbstractUnitTest { context.restoreAuthSystemState(); String status = helper.getAccessStatusFromItem(context, itemWithEmbargo, threshold); assertThat("testWithEmbargo 0", status, equalTo(DefaultAccessStatusHelper.EMBARGO)); + String embargoDate = helper.getEmbargoFromItem(context, itemWithEmbargo); + assertThat("testWithEmbargo 1", embargoDate, equalTo(policy.getStartDate().toString())); } /** @@ -390,6 +393,8 @@ public class DefaultAccessStatusHelperTest extends AbstractUnitTest { context.restoreAuthSystemState(); String status = helper.getAccessStatusFromItem(context, itemWithPrimaryAndMultipleBitstreams, threshold); assertThat("testWithPrimaryAndMultipleBitstreams 0", status, equalTo(DefaultAccessStatusHelper.EMBARGO)); + String embargoDate = helper.getEmbargoFromItem(context, itemWithPrimaryAndMultipleBitstreams); + assertThat("testWithPrimaryAndMultipleBitstreams 1", embargoDate, equalTo(policy.getStartDate().toString())); } /** @@ -419,5 +424,7 @@ public class DefaultAccessStatusHelperTest extends AbstractUnitTest { context.restoreAuthSystemState(); String status = helper.getAccessStatusFromItem(context, itemWithoutPrimaryAndMultipleBitstreams, threshold); assertThat("testWithNoPrimaryAndMultipleBitstreams 0", status, equalTo(DefaultAccessStatusHelper.OPEN_ACCESS)); + String embargoDate = helper.getEmbargoFromItem(context, itemWithEmbargo); + assertThat("testWithNoPrimaryAndMultipleBitstreams 1", embargoDate, equalTo(null)); } } diff --git a/dspace-oai/src/main/java/org/dspace/xoai/app/plugins/AccessStatusElementItemCompilePlugin.java b/dspace-oai/src/main/java/org/dspace/xoai/app/plugins/AccessStatusElementItemCompilePlugin.java index 6b3c5ded98..3201a02291 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/app/plugins/AccessStatusElementItemCompilePlugin.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/app/plugins/AccessStatusElementItemCompilePlugin.java @@ -12,6 +12,7 @@ import java.util.List; import com.lyncode.xoai.dataprovider.xml.xoai.Element; import com.lyncode.xoai.dataprovider.xml.xoai.Metadata; +import org.apache.commons.lang3.StringUtils; import org.dspace.access.status.factory.AccessStatusServiceFactory; import org.dspace.access.status.service.AccessStatusService; import org.dspace.content.Item; @@ -31,6 +32,13 @@ import org.dspace.xoai.util.ItemUtils; * open.access * * + * OR + * + * + * embargo + * 2024-10-10 + * + * * } * * Returning Values are based on: @@ -46,9 +54,15 @@ public class AccessStatusElementItemCompilePlugin implements XOAIExtensionItemCo String accessStatusType; accessStatusType = accessStatusService.getAccessStatus(context, item); + String embargoFromItem = accessStatusService.getEmbargoFromItem(context, item); + Element accessStatus = ItemUtils.create("access-status"); accessStatus.getField().add(ItemUtils.createValue("value", accessStatusType)); + if (StringUtils.isNotEmpty(embargoFromItem)) { + accessStatus.getField().add(ItemUtils.createValue("embargo", embargoFromItem)); + } + Element others; List elements = metadata.getElement(); if (ItemUtils.getElement(elements, "others") != null) { diff --git a/dspace/config/crosswalks/oai/metadataFormats/uketd_dc.xsl b/dspace/config/crosswalks/oai/metadataFormats/uketd_dc.xsl index b9d81aef5d..a180b49c56 100644 --- a/dspace/config/crosswalks/oai/metadataFormats/uketd_dc.xsl +++ b/dspace/config/crosswalks/oai/metadataFormats/uketd_dc.xsl @@ -115,6 +115,12 @@ + + + + + + @@ -123,11 +129,6 @@ - - - - From 4bd2cfdf0f931aec7a05db42f255423fe806ea77 Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Wed, 2 Aug 2023 16:22:54 +0100 Subject: [PATCH 087/168] Remove unused imports --- .../org/dspace/access/status/service/AccessStatusService.java | 1 - .../org/dspace/access/status/DefaultAccessStatusHelperTest.java | 1 - 2 files changed, 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/access/status/service/AccessStatusService.java b/dspace-api/src/main/java/org/dspace/access/status/service/AccessStatusService.java index 937cb02692..2ed47bde4c 100644 --- a/dspace-api/src/main/java/org/dspace/access/status/service/AccessStatusService.java +++ b/dspace-api/src/main/java/org/dspace/access/status/service/AccessStatusService.java @@ -8,7 +8,6 @@ package org.dspace.access.status.service; import java.sql.SQLException; -import java.util.Date; import org.dspace.content.Item; import org.dspace.core.Context; diff --git a/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java b/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java index 9d90452bee..f450f72e6a 100644 --- a/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java +++ b/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java @@ -8,7 +8,6 @@ package org.dspace.access.status; import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; From 724a4ffb0ed9ffefb2866930655767590b462bb5 Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Wed, 2 Aug 2023 18:01:07 +0100 Subject: [PATCH 088/168] Fix style issues --- .../src/main/java/org/dspace/xoai/util/ItemUtils.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java index 2d252ff476..2af526c560 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java @@ -13,9 +13,9 @@ import java.io.InputStream; import java.sql.SQLException; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.List; import java.util.ArrayList; import java.util.Collections; +import java.util.List; import com.lyncode.xoai.dataprovider.xml.xoai.Element; import com.lyncode.xoai.dataprovider.xml.xoai.Metadata; @@ -180,7 +180,9 @@ public class ItemUtils { private static void addEmbargoField(Context context, Bitstream bitstream, Element bitstreamEl) throws SQLException { GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); Group anonymousGroup = groupService.findByName(context, Group.ANONYMOUS); - List policies = authorizeService.findPoliciesByDSOAndType(context, bitstream, ResourcePolicy.TYPE_CUSTOM); + List policies = authorizeService.findPoliciesByDSOAndType(context, + bitstream, + ResourcePolicy.TYPE_CUSTOM); List embargoDates = new ArrayList<>(); // Account for cases where there could be more than one embargo policy From 6e2c8a4ae0068d844d0fc796001c170c8849babf Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Wed, 2 Aug 2023 18:56:05 +0100 Subject: [PATCH 089/168] Fix style issues --- dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java index 2af526c560..6a0808259e 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java @@ -12,9 +12,9 @@ import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; import java.text.SimpleDateFormat; -import java.util.Date; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.List; import com.lyncode.xoai.dataprovider.xml.xoai.Element; From bb9e88d1bb452d0865f4827134baf907e6d34044 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Wed, 2 Aug 2023 16:25:46 -0400 Subject: [PATCH 090/168] Community request: fake EPerson from configuration. --- .../curate/XmlWorkflowCuratorServiceImpl.java | 7 ++++- .../dspace/eperson/EPersonServiceImpl.java | 29 ++++++++++++------- .../eperson/service/EPersonService.java | 12 ++++---- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java b/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java index dd6c8d5e15..97537befd2 100644 --- a/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java @@ -102,10 +102,15 @@ public class XmlWorkflowCuratorServiceImpl Curator curator = new Curator(); curator.setReporter(reporter); c.turnOffAuthorisationSystem(); + boolean wasAnonymous = false; if (null == c.getCurrentUser()) { // We need someone to email - c.setCurrentUser(ePersonService.findAnAdministrator(c)); + wasAnonymous = true; + c.setCurrentUser(ePersonService.getSystemEPerson(c)); } boolean failedP = curate(curator, c, wfi); + if (wasAnonymous) { + c.setCurrentUser(null); + } c.restoreAuthSystemState(); return failedP; } diff --git a/dspace-api/src/main/java/org/dspace/eperson/EPersonServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/EPersonServiceImpl.java index e3b743c4a7..2d0574a630 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/EPersonServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/EPersonServiceImpl.java @@ -116,19 +116,28 @@ public class EPersonServiceImpl extends DSpaceObjectServiceImpl impleme return ePersonDAO.findByID(context, EPerson.class, id); } + /** + * Create a fake EPerson which can receive email. Its address will be the + * value of "mail.admin", or "postmaster" if all else fails. + * @param c + * @return + * @throws SQLException + */ @Override - public EPerson findAnAdministrator(Context c) + public EPerson getSystemEPerson(Context c) throws SQLException { - List contacts = groupService.findByName(c, Group.ADMIN).getMembers(); - EPerson currentUser; - if (contacts.isEmpty()) { - log.warn("Administrators group is empty"); - currentUser = findByEmail(c, configurationService.getProperty("mail.admin")); - // Null if no such EPerson - } else { - currentUser = contacts.get(0); + String adminEmail = configurationService.getProperty("mail.admin"); + if (null == adminEmail) { + adminEmail = "postmaster"; // Last-ditch attempt to send *somewhere* } - return currentUser; + EPerson systemEPerson = findByEmail(c, adminEmail); + + if (null == systemEPerson) { + systemEPerson = new EPerson(); + systemEPerson.setEmail(adminEmail); + } + + return systemEPerson; } @Override diff --git a/dspace-api/src/main/java/org/dspace/eperson/service/EPersonService.java b/dspace-api/src/main/java/org/dspace/eperson/service/EPersonService.java index c3def01a82..47be942e97 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/service/EPersonService.java +++ b/dspace-api/src/main/java/org/dspace/eperson/service/EPersonService.java @@ -13,6 +13,7 @@ import java.sql.SQLException; import java.util.Date; import java.util.List; import java.util.Set; +import javax.validation.constraints.NotNull; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Item; @@ -158,15 +159,16 @@ public interface EPersonService extends DSpaceObjectService, DSpaceObje throws SQLException; /** - * Try very hard to find an administrator's account. Might return a member - * of the Administrators group, or an account with a configured email - * address. + * The "System EPerson" is a fake account that exists only to receive email. + * It has an email address that should be presumed usable. It does not + * exist in the database and is not complete. * * @param context current DSpace session. - * @return a presumed administrator account, or null if none could be found. + * @return an EPerson that can presumably receive email. * @throws SQLException */ - public EPerson findAnAdministrator(Context context) + @NotNull + public EPerson getSystemEPerson(Context context) throws SQLException; /** From b71eee89c1e1dd7569e800e13eb8878548853ce6 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 2 Aug 2023 15:36:40 -0500 Subject: [PATCH 091/168] Enable entity type to submission form mapping by default --- dspace/config/item-submission.xml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/dspace/config/item-submission.xml b/dspace/config/item-submission.xml index a6cd49bdf1..1060a33031 100644 --- a/dspace/config/item-submission.xml +++ b/dspace/config/item-submission.xml @@ -55,9 +55,7 @@ - - - - - - PLEASE NOTICE THAT YOU WILL HAVE TO RESTART DSPACE - - - - - - - Uncomment if you intend to use them - --> - @@ -65,8 +63,6 @@ - --> - From be22790aad7f627e2ac027773e272b703986f589 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Wed, 2 Aug 2023 17:23:36 -0400 Subject: [PATCH 092/168] Correct some documentation. --- .../curate/service/XmlWorkflowCuratorService.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/curate/service/XmlWorkflowCuratorService.java b/dspace-api/src/main/java/org/dspace/curate/service/XmlWorkflowCuratorService.java index 2ad1eac129..778b779cfe 100644 --- a/dspace-api/src/main/java/org/dspace/curate/service/XmlWorkflowCuratorService.java +++ b/dspace-api/src/main/java/org/dspace/curate/service/XmlWorkflowCuratorService.java @@ -42,9 +42,9 @@ public interface XmlWorkflowCuratorService { * * @param c the context * @param wfi the workflow item - * @return true if curation was completed or not required, + * @return true if curation was completed or not required; * false if tasks were queued for later completion, - * or item was rejected + * or item was rejected. * @throws AuthorizeException if authorization error * @throws IOException if IO error * @throws SQLException if database error @@ -58,7 +58,9 @@ public interface XmlWorkflowCuratorService { * @param curator the curation context * @param c the user context * @param wfId the workflow item's ID - * @return true if curation failed. + * @return true if curation curation was completed or not required; + * false if tasks were queued for later completion, + * or item was rejected. * @throws AuthorizeException if authorization error * @throws IOException if IO error * @throws SQLException if database error @@ -72,7 +74,9 @@ public interface XmlWorkflowCuratorService { * @param curator the curation context * @param c the user context * @param wfi the workflow item - * @return true if curation failed. + * @return true if workflow curation was completed or not required; + * false if tasks were queued for later completion, + * or item was rejected. * @throws AuthorizeException if authorization error * @throws IOException if IO error * @throws SQLException if database error From 0de4c3945ed7f30d41841cda4bf01acf9ffc130f Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Thu, 3 Aug 2023 08:54:01 +0100 Subject: [PATCH 093/168] Add null check --- .../org/dspace/access/status/DefaultAccessStatusHelper.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java b/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java index e7055181aa..9b5227491b 100644 --- a/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java +++ b/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java @@ -198,6 +198,10 @@ public class DefaultAccessStatusHelper implements AccessStatusHelper { .orElse(null); } + if (bitstream == null) { + return null; + } + embargoDate = this.retrieveLongestEmbargo(context, bitstream); return embargoDate != null ? embargoDate.toString() : null; From 291afa765d29836a67727fdd2f82ac0c9f9310c4 Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Thu, 3 Aug 2023 09:54:00 +0100 Subject: [PATCH 094/168] ItemUtils.java: refactored addEmbargoField --- .../java/org/dspace/xoai/util/ItemUtils.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java index 6a0808259e..80eb67a2b9 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java @@ -12,8 +12,6 @@ import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collections; import java.util.Date; import java.util.List; @@ -184,22 +182,28 @@ public class ItemUtils { bitstream, ResourcePolicy.TYPE_CUSTOM); - List embargoDates = new ArrayList<>(); + Date embargoDate = null; + // Account for cases where there could be more than one embargo policy for (ResourcePolicy policy : policies) { if (policy.getGroup() == anonymousGroup && policy.getAction() == Constants.READ) { Date startDate = policy.getStartDate(); if (startDate != null && startDate.after(new Date())) { - embargoDates.add(startDate); + // There is an active embargo: aim to take the longest embargo + if (embargoDate == null) { + embargoDate = startDate; + } else { + embargoDate = startDate.after(embargoDate) ? startDate : embargoDate; + } } } } - if (embargoDates.size() >= 1) { + + if (embargoDate != null) { // Sort array of dates to extract the longest embargo SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); - Collections.sort(embargoDates, Date::compareTo); bitstreamEl.getField().add( - createValue("embargo", formatter.format(embargoDates.get(embargoDates.size() - 1)))); + createValue("embargo", formatter.format(embargoDate))); } } From 29a88d7e2dcfc36d2cd7991de3b84ef5f5623b63 Mon Sep 17 00:00:00 2001 From: Christian Bethge <54576195+ChrisBethgster@users.noreply.github.com> Date: Thu, 3 Aug 2023 13:01:12 +0200 Subject: [PATCH 095/168] #9006 fix referenced configuration file --- .../src/main/java/org/dspace/statistics/GeoIpService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/statistics/GeoIpService.java b/dspace-api/src/main/java/org/dspace/statistics/GeoIpService.java index 7f8a11e5ba..40fea6cf54 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/GeoIpService.java +++ b/dspace-api/src/main/java/org/dspace/statistics/GeoIpService.java @@ -37,7 +37,7 @@ public class GeoIpService { public DatabaseReader getDatabaseReader() throws IllegalStateException { String dbPath = configurationService.getProperty("usage-statistics.dbfile"); if (StringUtils.isBlank(dbPath)) { - throw new IllegalStateException("The required 'dbfile' configuration is missing in solr-statistics.cfg!"); + throw new IllegalStateException("The required 'dbfile' configuration is missing in usage-statistics.cfg!"); } try { From 309b0b355e4bffd6a1be3e6341dd6d17f99892c8 Mon Sep 17 00:00:00 2001 From: Christian Bethge <54576195+ChrisBethgster@users.noreply.github.com> Date: Thu, 3 Aug 2023 13:04:03 +0200 Subject: [PATCH 096/168] #9006 fix referenced configuration file (Test) --- .../src/test/java/org/dspace/app/rest/HealthIndicatorsIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/HealthIndicatorsIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/HealthIndicatorsIT.java index 8c1c534de1..0bb6793398 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/HealthIndicatorsIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/HealthIndicatorsIT.java @@ -67,7 +67,7 @@ public class HealthIndicatorsIT extends AbstractControllerIntegrationTest { match("solrSearchCore", Status.UP, Map.of("status", 0, "detectedPathType", "root")), match("solrStatisticsCore", Status.UP, Map.of("status", 0, "detectedPathType", "root")), match("geoIp", UP_WITH_ISSUES_STATUS, - Map.of("reason", "The required 'dbfile' configuration is missing in solr-statistics.cfg!")) + Map.of("reason", "The required 'dbfile' configuration is missing in usage-statistics.cfg!")) ))); } From 4b40872a6d5a3934c1f79c6babf439a21ce25f66 Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Thu, 3 Aug 2023 14:30:33 +0100 Subject: [PATCH 097/168] uketd_dc.xsl: also expose access-status if embargo or restricted --- dspace/config/crosswalks/oai/metadataFormats/uketd_dc.xsl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dspace/config/crosswalks/oai/metadataFormats/uketd_dc.xsl b/dspace/config/crosswalks/oai/metadataFormats/uketd_dc.xsl index a180b49c56..5c434e49ed 100644 --- a/dspace/config/crosswalks/oai/metadataFormats/uketd_dc.xsl +++ b/dspace/config/crosswalks/oai/metadataFormats/uketd_dc.xsl @@ -115,6 +115,14 @@ + + + + + + + + From d44507d647dd0c96ab80bce6d9a7aeb6edfb5540 Mon Sep 17 00:00:00 2001 From: Max Nuding Date: Fri, 4 Aug 2023 08:39:03 +0200 Subject: [PATCH 098/168] Remove duplicate code --- .../java/org/dspace/statistics/SolrLoggerServiceImpl.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java b/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java index 9f34a42047..19c79af34d 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java @@ -1699,11 +1699,6 @@ public class SolrLoggerServiceImpl implements SolrLoggerService, InitializingBea //Also add the core containing the current year, if it hasn't been added already statisticYearCores.add(baseCore); } - //Also add the core containing the current year ! - statisticYearCores.add(((HttpSolrClient) solr) - .getBaseURL() - .replace("http://", "") - .replace("https://", "")); } catch (IOException | SolrServerException e) { log.error(e.getMessage(), e); } From 8ae5ffbf9b41fe2ad4e1146eceeff47e6ba985a0 Mon Sep 17 00:00:00 2001 From: Christian Bethge Date: Mon, 7 Aug 2023 09:42:07 +0200 Subject: [PATCH 099/168] fix MissingOptionException on help --- .../dspace/app/launcher/ScriptLauncher.java | 14 ++++- .../org/dspace/scripts/DSpaceRunnable.java | 55 +++++++++++++++++-- .../configuration/ScriptConfiguration.java | 15 +++++ .../dspace/app/bulkedit/MetadataExportIT.java | 15 +++-- .../dspace/app/bulkedit/MetadataImportIT.java | 5 +- .../app/csv/CSVMetadataImportReferenceIT.java | 5 +- .../java/org/dspace/curate/CurationIT.java | 10 ++-- 7 files changed, 98 insertions(+), 21 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java b/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java index fcb2098bd0..e6df016613 100644 --- a/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java +++ b/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java @@ -21,6 +21,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.core.Context; import org.dspace.scripts.DSpaceRunnable; +import org.dspace.scripts.DSpaceRunnable.StepResult; import org.dspace.scripts.configuration.ScriptConfiguration; import org.dspace.scripts.factory.ScriptServiceFactory; import org.dspace.scripts.handler.DSpaceRunnableHandler; @@ -145,9 +146,16 @@ public class ScriptLauncher { private static int executeScript(String[] args, DSpaceRunnableHandler dSpaceRunnableHandler, DSpaceRunnable script) { try { - script.initialize(args, dSpaceRunnableHandler, null); - script.run(); - return 0; + StepResult result = script.initialize(args, dSpaceRunnableHandler, null); + + if (StepResult.Continue.equals(result)) { + // only run the script, if the normal initialize is successful + script.run(); + } else { + // otherwise - for example the script is started with the help argument + } + + return 0; } catch (ParseException e) { script.printHelp(); e.printStackTrace(); diff --git a/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java b/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java index 2319aee317..4f64f69731 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java +++ b/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java @@ -35,6 +35,11 @@ public abstract class DSpaceRunnable implements R * The CommandLine object for the script that'll hold the information */ protected CommandLine commandLine; + + /** + * The minimal CommandLine object for the script that'll hold help information + */ + protected CommandLine helpCommandLine; /** * This EPerson identifier variable is the UUID of the EPerson that's running the script @@ -64,27 +69,64 @@ public abstract class DSpaceRunnable implements R * @param args The arguments given to the script * @param dSpaceRunnableHandler The DSpaceRunnableHandler object that defines from where the script was ran * @param currentUser + * @return the result of this step; StepResult.Continue: continue the normal process, initialize is successful; + * otherwise exit the process (the help or version is shown) * @throws ParseException If something goes wrong */ - public void initialize(String[] args, DSpaceRunnableHandler dSpaceRunnableHandler, + public StepResult initialize(String[] args, DSpaceRunnableHandler dSpaceRunnableHandler, EPerson currentUser) throws ParseException { if (currentUser != null) { this.setEpersonIdentifier(currentUser.getID()); } this.setHandler(dSpaceRunnableHandler); - this.parse(args); + + // parse the command line in a first step for the help options + // --> no other option is required + StepResult result = this.parseForHelp(args); + switch (result) { + case Exit: + // arguments of the command line matches the help options, handle this + handleHelpCommandLine(); + break; + + case Continue: + // arguments of the command line matches NOT the help options, parse the args for the normal options + result = this.parse(args); + break; + } + + return result; } - /** + + /** This method handle the help command line. In this easy implementation only the help is printed. + * For more complexity override this method. + */ + private void handleHelpCommandLine() { + printHelp(); + } + + + /** * This method will take the primitive array of String objects that represent the parameters given to the String * and it'll parse these into a CommandLine object that can be used by the script to retrieve the data * @param args The primitive array of Strings representing the parameters * @throws ParseException If something goes wrong */ - private void parse(String[] args) throws ParseException { + private StepResult parse(String[] args) throws ParseException { commandLine = new DefaultParser().parse(getScriptConfiguration().getOptions(), args); setup(); + return StepResult.Continue; } + + private StepResult parseForHelp(String[] args) throws ParseException { + helpCommandLine = new DefaultParser().parse(getScriptConfiguration().getHelpOptions(), args); + if (helpCommandLine.getOptions() != null && helpCommandLine.getOptions().length > 0) { + return StepResult.Exit; + } + + return StepResult.Continue; + } /** * This method has to be included in every script and handles the setup of the script by parsing the CommandLine @@ -158,4 +200,9 @@ public abstract class DSpaceRunnable implements R public void setEpersonIdentifier(UUID epersonIdentifier) { this.epersonIdentifier = epersonIdentifier; } + + public enum StepResult { + Continue, + Exit; + } } diff --git a/dspace-api/src/main/java/org/dspace/scripts/configuration/ScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/scripts/configuration/ScriptConfiguration.java index 642409a924..62f30f99f6 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/configuration/ScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/scripts/configuration/ScriptConfiguration.java @@ -10,6 +10,7 @@ package org.dspace.scripts.configuration; import java.sql.SQLException; import java.util.List; +import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.dspace.authorize.service.AuthorizeService; import org.dspace.core.Context; @@ -104,6 +105,20 @@ public abstract class ScriptConfiguration implements B * @return the options value of this ScriptConfiguration */ public abstract Options getOptions(); + + /** + * The getter for the options of the Script (help informations) + * @return the options value of this ScriptConfiguration for help + */ + public Options getHelpOptions() { + Options options = new Options(); + + options.addOption(Option.builder("h").longOpt("help") + .desc("help") + .hasArg(false).required(false).build()); + + return options; + } @Override public void setBeanName(String beanName) { diff --git a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportIT.java b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportIT.java index f767ba1663..68e5717012 100644 --- a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportIT.java +++ b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportIT.java @@ -99,8 +99,9 @@ public class MetadataExportIT script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration); } if (script != null) { - script.initialize(args, testDSpaceRunnableHandler, null); - script.run(); + if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { + script.run(); + } } } @@ -206,8 +207,9 @@ public class MetadataExportIT script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration); } if (script != null) { - script.initialize(args, testDSpaceRunnableHandler, null); - script.run(); + if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { + script.run(); + } } Exception exceptionDuringTestRun = testDSpaceRunnableHandler.getException(); @@ -235,8 +237,9 @@ public class MetadataExportIT script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration); } if (script != null) { - script.initialize(args, testDSpaceRunnableHandler, null); - script.run(); + if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { + script.run(); + } } Exception exceptionDuringTestRun = testDSpaceRunnableHandler.getException(); diff --git a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java index ac5e1e6ae6..0a0024ced9 100644 --- a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java +++ b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java @@ -144,8 +144,9 @@ public class MetadataImportIT extends AbstractIntegrationTestWithDatabase { script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration); } if (script != null) { - script.initialize(args, testDSpaceRunnableHandler, null); - script.run(); + if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { + script.run(); + } } } diff --git a/dspace-api/src/test/java/org/dspace/app/csv/CSVMetadataImportReferenceIT.java b/dspace-api/src/test/java/org/dspace/app/csv/CSVMetadataImportReferenceIT.java index 5933dff71c..59b9491ccd 100644 --- a/dspace-api/src/test/java/org/dspace/app/csv/CSVMetadataImportReferenceIT.java +++ b/dspace-api/src/test/java/org/dspace/app/csv/CSVMetadataImportReferenceIT.java @@ -702,8 +702,9 @@ public class CSVMetadataImportReferenceIT extends AbstractIntegrationTestWithDat script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration); } if (script != null) { - script.initialize(args, testDSpaceRunnableHandler, null); - script.run(); + if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { + script.run(); + } } if (testDSpaceRunnableHandler.getException() != null) { throw testDSpaceRunnableHandler.getException(); diff --git a/dspace-api/src/test/java/org/dspace/curate/CurationIT.java b/dspace-api/src/test/java/org/dspace/curate/CurationIT.java index 6232793c74..b50d52e963 100644 --- a/dspace-api/src/test/java/org/dspace/curate/CurationIT.java +++ b/dspace-api/src/test/java/org/dspace/curate/CurationIT.java @@ -43,8 +43,9 @@ public class CurationIT extends AbstractIntegrationTestWithDatabase { script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration); } if (script != null) { - script.initialize(args, testDSpaceRunnableHandler, null); - script.run(); + if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { + script.run(); + } } } @@ -69,8 +70,9 @@ public class CurationIT extends AbstractIntegrationTestWithDatabase { script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration); } if (script != null) { - script.initialize(args, testDSpaceRunnableHandler, null); - script.run(); + if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { + script.run(); + } } } } From b1377ca1ef82d80f2ece9b48b8f1571e786c4525 Mon Sep 17 00:00:00 2001 From: Christian Bethge Date: Mon, 7 Aug 2023 11:27:08 +0200 Subject: [PATCH 100/168] fix stylecheck --- .../dspace/app/launcher/ScriptLauncher.java | 10 ++- .../org/dspace/scripts/DSpaceRunnable.java | 70 ++++++++++--------- .../configuration/ScriptConfiguration.java | 11 ++- .../dspace/app/bulkedit/MetadataExportIT.java | 18 ++--- .../dspace/app/bulkedit/MetadataImportIT.java | 6 +- .../app/csv/CSVMetadataImportReferenceIT.java | 7 +- .../java/org/dspace/curate/CurationIT.java | 12 ++-- 7 files changed, 67 insertions(+), 67 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java b/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java index e6df016613..bcb61a48ee 100644 --- a/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java +++ b/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java @@ -147,15 +147,13 @@ public class ScriptLauncher { DSpaceRunnable script) { try { StepResult result = script.initialize(args, dSpaceRunnableHandler, null); - if (StepResult.Continue.equals(result)) { - // only run the script, if the normal initialize is successful - script.run(); + // only run the script, if the normal initialize is successful + script.run(); } else { - // otherwise - for example the script is started with the help argument + // otherwise - for example the script is started with the help argument } - - return 0; + return 0; } catch (ParseException e) { script.printHelp(); e.printStackTrace(); diff --git a/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java b/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java index 4f64f69731..7fb8567f8c 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java +++ b/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java @@ -35,7 +35,7 @@ public abstract class DSpaceRunnable implements R * The CommandLine object for the script that'll hold the information */ protected CommandLine commandLine; - + /** * The minimal CommandLine object for the script that'll hold help information */ @@ -69,8 +69,8 @@ public abstract class DSpaceRunnable implements R * @param args The arguments given to the script * @param dSpaceRunnableHandler The DSpaceRunnableHandler object that defines from where the script was ran * @param currentUser - * @return the result of this step; StepResult.Continue: continue the normal process, initialize is successful; - * otherwise exit the process (the help or version is shown) + * @return the result of this step; StepResult.Continue: continue the normal process, + * initialize is successful; otherwise exit the process (the help or version is shown) * @throws ParseException If something goes wrong */ public StepResult initialize(String[] args, DSpaceRunnableHandler dSpaceRunnableHandler, @@ -79,35 +79,38 @@ public abstract class DSpaceRunnable implements R this.setEpersonIdentifier(currentUser.getID()); } this.setHandler(dSpaceRunnableHandler); - - // parse the command line in a first step for the help options + + // parse the command line in a first step for the help options // --> no other option is required StepResult result = this.parseForHelp(args); switch (result) { - case Exit: - // arguments of the command line matches the help options, handle this - handleHelpCommandLine(); - break; - - case Continue: - // arguments of the command line matches NOT the help options, parse the args for the normal options - result = this.parse(args); - break; - } - + case Exit: + // arguments of the command line matches the help options, handle this + handleHelpCommandLine(); + break; + + case Continue: + // arguments of the command line matches NOT the help options, parse the args for the normal options + result = this.parse(args); + break; + default: + break; + } + return result; } - /** This method handle the help command line. In this easy implementation only the help is printed. - * For more complexity override this method. - */ - private void handleHelpCommandLine() { - printHelp(); - } + /** + * This method handle the help command line. In this easy implementation only the help is printed. For more + * complexity override this method. + */ + private void handleHelpCommandLine() { + printHelp(); + } - /** + /** * This method will take the primitive array of String objects that represent the parameters given to the String * and it'll parse these into a CommandLine object that can be used by the script to retrieve the data * @param args The primitive array of Strings representing the parameters @@ -118,15 +121,15 @@ public abstract class DSpaceRunnable implements R setup(); return StepResult.Continue; } - + private StepResult parseForHelp(String[] args) throws ParseException { - helpCommandLine = new DefaultParser().parse(getScriptConfiguration().getHelpOptions(), args); - if (helpCommandLine.getOptions() != null && helpCommandLine.getOptions().length > 0) { - return StepResult.Exit; - } - - return StepResult.Continue; - } + helpCommandLine = new DefaultParser().parse(getScriptConfiguration().getHelpOptions(), args); + if (helpCommandLine.getOptions() != null && helpCommandLine.getOptions().length > 0) { + return StepResult.Exit; + } + + return StepResult.Continue; + } /** * This method has to be included in every script and handles the setup of the script by parsing the CommandLine @@ -200,9 +203,8 @@ public abstract class DSpaceRunnable implements R public void setEpersonIdentifier(UUID epersonIdentifier) { this.epersonIdentifier = epersonIdentifier; } - + public enum StepResult { - Continue, - Exit; + Continue, Exit; } } diff --git a/dspace-api/src/main/java/org/dspace/scripts/configuration/ScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/scripts/configuration/ScriptConfiguration.java index 62f30f99f6..bbedab04e2 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/configuration/ScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/scripts/configuration/ScriptConfiguration.java @@ -105,17 +105,16 @@ public abstract class ScriptConfiguration implements B * @return the options value of this ScriptConfiguration */ public abstract Options getOptions(); - + /** * The getter for the options of the Script (help informations) + * * @return the options value of this ScriptConfiguration for help */ public Options getHelpOptions() { - Options options = new Options(); - - options.addOption(Option.builder("h").longOpt("help") - .desc("help") - .hasArg(false).required(false).build()); + Options options = new Options(); + + options.addOption(Option.builder("h").longOpt("help").desc("help").hasArg(false).required(false).build()); return options; } diff --git a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportIT.java b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportIT.java index 68e5717012..0b7fd80268 100644 --- a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportIT.java +++ b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportIT.java @@ -99,9 +99,9 @@ public class MetadataExportIT script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration); } if (script != null) { - if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { - script.run(); - } + if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { + script.run(); + } } } @@ -207,9 +207,9 @@ public class MetadataExportIT script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration); } if (script != null) { - if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { - script.run(); - } + if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { + script.run(); + } } Exception exceptionDuringTestRun = testDSpaceRunnableHandler.getException(); @@ -237,9 +237,9 @@ public class MetadataExportIT script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration); } if (script != null) { - if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { - script.run(); - } + if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { + script.run(); + } } Exception exceptionDuringTestRun = testDSpaceRunnableHandler.getException(); diff --git a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java index 0a0024ced9..e50f7913ad 100644 --- a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java +++ b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java @@ -144,9 +144,9 @@ public class MetadataImportIT extends AbstractIntegrationTestWithDatabase { script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration); } if (script != null) { - if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { - script.run(); - } + if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { + script.run(); + } } } diff --git a/dspace-api/src/test/java/org/dspace/app/csv/CSVMetadataImportReferenceIT.java b/dspace-api/src/test/java/org/dspace/app/csv/CSVMetadataImportReferenceIT.java index 59b9491ccd..aee4b4d267 100644 --- a/dspace-api/src/test/java/org/dspace/app/csv/CSVMetadataImportReferenceIT.java +++ b/dspace-api/src/test/java/org/dspace/app/csv/CSVMetadataImportReferenceIT.java @@ -702,9 +702,10 @@ public class CSVMetadataImportReferenceIT extends AbstractIntegrationTestWithDat script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration); } if (script != null) { - if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { - script.run(); - } + if (DSpaceRunnable.StepResult.Continue + .equals(script.initialize(args, testDSpaceRunnableHandler, null))) { + script.run(); + } } if (testDSpaceRunnableHandler.getException() != null) { throw testDSpaceRunnableHandler.getException(); diff --git a/dspace-api/src/test/java/org/dspace/curate/CurationIT.java b/dspace-api/src/test/java/org/dspace/curate/CurationIT.java index b50d52e963..31bfe2550a 100644 --- a/dspace-api/src/test/java/org/dspace/curate/CurationIT.java +++ b/dspace-api/src/test/java/org/dspace/curate/CurationIT.java @@ -43,9 +43,9 @@ public class CurationIT extends AbstractIntegrationTestWithDatabase { script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration); } if (script != null) { - if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { - script.run(); - } + if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { + script.run(); + } } } @@ -70,9 +70,9 @@ public class CurationIT extends AbstractIntegrationTestWithDatabase { script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration); } if (script != null) { - if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { - script.run(); - } + if (DSpaceRunnable.StepResult.Continue.equals(script.initialize(args, testDSpaceRunnableHandler, null))) { + script.run(); + } } } } From 54280e8fe2d0d2e98250ce647c451d588f4c3559 Mon Sep 17 00:00:00 2001 From: Koen Pauwels Date: Fri, 4 Aug 2023 11:39:35 +0200 Subject: [PATCH 101/168] 103818 ItemServiceImpl#inheritCollectionDefaultPolicies now clears item READ policies if new parent collection has a default READ policy --- .../org/dspace/content/ItemServiceImpl.java | 8 +++ .../content/service/ItemServiceTest.java | 54 +++++++++++++++++++ 2 files changed, 62 insertions(+) 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 8d1ba14b2c..3458361f43 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -920,6 +920,14 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl implements It @Override public void inheritCollectionDefaultPolicies(Context context, Item item, Collection collection) throws SQLException, AuthorizeException { + + // If collection has READ policies, remove the item's READ policies. + List defaultCollectionPolicies = authorizeService + .getPoliciesActionFilter(context, collection, Constants.DEFAULT_ITEM_READ); + if (!defaultCollectionPolicies.isEmpty()) { + authorizeService.removePoliciesActionFilter(context, item, Constants.READ); + } + adjustItemPolicies(context, item, collection); adjustBundleBitstreamPolicies(context, item, collection); diff --git a/dspace-api/src/test/java/org/dspace/content/service/ItemServiceTest.java b/dspace-api/src/test/java/org/dspace/content/service/ItemServiceTest.java index 50b4d3f3b4..1847a27c7f 100644 --- a/dspace-api/src/test/java/org/dspace/content/service/ItemServiceTest.java +++ b/dspace-api/src/test/java/org/dspace/content/service/ItemServiceTest.java @@ -26,6 +26,8 @@ import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.app.requestitem.RequestItem; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.ResourcePolicy; +import org.dspace.authorize.factory.AuthorizeServiceFactory; +import org.dspace.authorize.service.AuthorizeService; import org.dspace.builder.BitstreamBuilder; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; @@ -48,6 +50,8 @@ import org.dspace.content.WorkspaceItem; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.core.Constants; import org.dspace.eperson.Group; +import org.dspace.eperson.factory.EPersonServiceFactory; +import org.dspace.eperson.service.GroupService; import org.dspace.versioning.Version; import org.dspace.versioning.factory.VersionServiceFactory; import org.dspace.versioning.service.VersioningService; @@ -68,6 +72,8 @@ public class ItemServiceTest extends AbstractIntegrationTestWithDatabase { protected WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService(); protected MetadataValueService metadataValueService = ContentServiceFactory.getInstance().getMetadataValueService(); protected VersioningService versioningService = VersionServiceFactory.getInstance().getVersionService(); + protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); + protected GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); Community community; Collection collection1; @@ -752,6 +758,54 @@ public class ItemServiceTest extends AbstractIntegrationTestWithDatabase { assertNull(itemService.find(context, item.getID())); } + + @Test + public void testMoveItemToCollectionWithMoreRestrictiveReadPolicy() throws Exception { + /* Verify that, if we move an item from a collection with a permissive default READ policy + * to a collection with a restrictive default READ policy, + * that the item does not retain the original permissive READ policy. + */ + + context.turnOffAuthorisationSystem(); + + Group anonymous = groupService.findByName(context, Group.ANONYMOUS); + Group admin = groupService.findByName(context, Group.ADMIN); + + // Set up the two different collections: one permissive and one restrictive in its default READ policy. + Collection permissive = CollectionBuilder + .createCollection(context, community) + .build(); + Collection restrictive = CollectionBuilder + .createCollection(context, community) + .build(); + authorizeService.removePoliciesActionFilter(context, restrictive, Constants.DEFAULT_ITEM_READ); + authorizeService.addPolicy(context, restrictive, Constants.DEFAULT_ITEM_READ, admin); + + // Add an item to the permissive collection. + Item item = ItemBuilder + .createItem(context, permissive) + .build(); + + // Verify that the item has exactly one READ policy, for the anonymous group. + assertEquals( + List.of(anonymous), + authorizeService.getPoliciesActionFilter(context, item, Constants.READ) + .stream().map(ResourcePolicy::getGroup).collect(Collectors.toList()) + ); + + // Move the item to the restrictive collection, making sure to inherit default policies. + itemService.move(context, item, permissive, restrictive, true); + + // Verify that the item has exactly one READ policy, but now for the admin group. + assertEquals( + List.of(admin), + authorizeService.getPoliciesActionFilter(context, item, Constants.READ) + .stream().map(ResourcePolicy::getGroup).collect(Collectors.toList()) + ); + + context.restoreAuthSystemState(); + } + private void assertMetadataValue(String authorQualifier, String contributorElement, String dcSchema, String value, String authority, int place, MetadataValue metadataValue) { assertThat(metadataValue.getValue(), equalTo(value)); From e4ff24a2d95ebf8e91f70304ee6a7de48382a15a Mon Sep 17 00:00:00 2001 From: Koen Pauwels Date: Fri, 4 Aug 2023 15:34:14 +0200 Subject: [PATCH 102/168] 103818 Add boolean parameter to ItemServiceImpl#inheritCollectionDefaultPolicies to decide whether to override item read policies --- .../content/InstallItemServiceImpl.java | 2 +- .../org/dspace/content/ItemServiceImpl.java | 17 ++++++++++---- .../dspace/content/service/ItemService.java | 22 ++++++++++++++++++- 3 files changed, 35 insertions(+), 6 deletions(-) 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 32c5b92c60..b52043e267 100644 --- a/dspace-api/src/main/java/org/dspace/content/InstallItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/InstallItemServiceImpl.java @@ -93,7 +93,7 @@ public class InstallItemServiceImpl implements InstallItemService { // 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; } 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 3458361f43..663585034d 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -920,12 +920,21 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl implements It @Override public void inheritCollectionDefaultPolicies(Context context, Item item, Collection collection) throws SQLException, AuthorizeException { + inheritCollectionDefaultPolicies(context, item, collection, true); + } + + @Override + public void inheritCollectionDefaultPolicies(Context context, Item item, Collection collection, + boolean overrideItemReadPolicies) + throws SQLException, AuthorizeException { // If collection has READ policies, remove the item's READ policies. - List defaultCollectionPolicies = authorizeService - .getPoliciesActionFilter(context, collection, Constants.DEFAULT_ITEM_READ); - if (!defaultCollectionPolicies.isEmpty()) { - authorizeService.removePoliciesActionFilter(context, item, Constants.READ); + if (overrideItemReadPolicies) { + List defaultCollectionPolicies = authorizeService + .getPoliciesActionFilter(context, collection, Constants.DEFAULT_ITEM_READ); + if (!defaultCollectionPolicies.isEmpty()) { + authorizeService.removePoliciesActionFilter(context, item, Constants.READ); + } } adjustItemPolicies(context, item, collection); diff --git a/dspace-api/src/main/java/org/dspace/content/service/ItemService.java b/dspace-api/src/main/java/org/dspace/content/service/ItemService.java index b6bf7aa5cf..8b93e953eb 100644 --- a/dspace-api/src/main/java/org/dspace/content/service/ItemService.java +++ b/dspace-api/src/main/java/org/dspace/content/service/ItemService.java @@ -473,7 +473,7 @@ public interface ItemService public void removeGroupPolicies(Context context, Item item, Group group) throws SQLException, AuthorizeException; /** - * remove all policies on an item and its contents, and replace them with + * Remove all policies on an item and its contents, and replace them with * the DEFAULT_ITEM_READ and DEFAULT_BITSTREAM_READ policies belonging to * the collection. * @@ -488,6 +488,26 @@ public interface ItemService public void inheritCollectionDefaultPolicies(Context context, Item item, Collection collection) throws java.sql.SQLException, AuthorizeException; + /** + * Remove all submission and workflow policies on an item and its contents, and add + * default collection policies which are not yet already in place. + * If overrideItemReadPolicies is true, then all read policies on the item are replaced (but only if the + * collection has a default read policy). + * + * @param context DSpace context object + * @param item item to reset policies on + * @param collection Collection + * @param overrideItemReadPolicies if true, all read policies on the item are replaced (but only if the + * collection has a default read policy) + * @throws SQLException if database error + * if an SQL error or if no default policies found. It's a bit + * draconian, but default policies must be enforced. + * @throws AuthorizeException if authorization error + */ + public void inheritCollectionDefaultPolicies(Context context, Item item, Collection collection, + boolean overrideItemReadPolicies) + throws java.sql.SQLException, AuthorizeException; + /** * Adjust the Bundle and Bitstream policies to reflect what have been defined * during the submission/workflow. The temporary SUBMISSION and WORKFLOW From d4eb327ce5e139d44768456921298a4fefb9311e Mon Sep 17 00:00:00 2001 From: Koen Pauwels Date: Fri, 4 Aug 2023 16:38:30 +0200 Subject: [PATCH 103/168] 103818 Add boolean parameters to ItemServiceImpl methodds to decide whether to override read policies --- .../org/dspace/content/ItemServiceImpl.java | 47 +++++++--- .../dspace/content/service/ItemService.java | 93 ++++++++++++++++--- 2 files changed, 113 insertions(+), 27 deletions(-) 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 663585034d..ac38f0cca4 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -925,20 +925,11 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl implements It @Override public void inheritCollectionDefaultPolicies(Context context, Item item, Collection collection, - boolean overrideItemReadPolicies) + boolean replaceReadRPWithCollectionRP) throws SQLException, AuthorizeException { - // If collection has READ policies, remove the item's READ policies. - if (overrideItemReadPolicies) { - List defaultCollectionPolicies = authorizeService - .getPoliciesActionFilter(context, collection, Constants.DEFAULT_ITEM_READ); - if (!defaultCollectionPolicies.isEmpty()) { - authorizeService.removePoliciesActionFilter(context, item, Constants.READ); - } - } - - adjustItemPolicies(context, item, collection); - adjustBundleBitstreamPolicies(context, item, collection); + adjustItemPolicies(context, item, collection, replaceReadRPWithCollectionRP); + adjustBundleBitstreamPolicies(context, item, collection, replaceReadRPWithCollectionRP); log.debug(LogHelper.getHeader(context, "item_inheritCollectionDefaultPolicies", "item_id=" + item.getID())); @@ -947,6 +938,13 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl implements It @Override public void adjustBundleBitstreamPolicies(Context context, Item item, Collection collection) 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 @@ -969,6 +967,10 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl implements It // Remove bundles List bunds = item.getBundles(); for (Bundle mybundle : bunds) { + // If collection has default READ policies, remove the bitstream's READ policies. + if (replaceReadRPWithCollectionRP && defaultCollectionBitstreamPolicies.size() > 0) { + authorizeService.removePoliciesActionFilter(context, item, Constants.READ); + } // if come from InstallItem: remove all submission/workflow policies authorizeService.removeAllPoliciesByDSOAndType(context, mybundle, ResourcePolicy.TYPE_SUBMISSION); @@ -985,7 +987,14 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl implements It } @Override - public void adjustBitstreamPolicies(Context context, Item item, Collection collection , Bitstream bitstream) + 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); @@ -1015,10 +1024,22 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl implements It @Override public void adjustItemPolicies(Context context, Item item, Collection collection) 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() diff --git a/dspace-api/src/main/java/org/dspace/content/service/ItemService.java b/dspace-api/src/main/java/org/dspace/content/service/ItemService.java index 8b93e953eb..de7644af83 100644 --- a/dspace-api/src/main/java/org/dspace/content/service/ItemService.java +++ b/dspace-api/src/main/java/org/dspace/content/service/ItemService.java @@ -527,6 +527,28 @@ public interface ItemService public void adjustBundleBitstreamPolicies(Context context, Item item, Collection collection) throws SQLException, AuthorizeException; + /** + * Adjust the Bundle and Bitstream policies to reflect what have been defined + * during the submission/workflow. The temporary SUBMISSION and WORKFLOW + * policies are removed and the policies defined at the item and collection + * level are copied and inherited as appropriate. Custom selected Item policies + * are copied to the bundle/bitstream only if no explicit custom policies were + * already applied to the bundle/bitstream. Collection's policies are inherited + * if there are no other policies defined or if the append mode is defined by + * the configuration via the core.authorization.installitem.inheritance-read.append-mode property + * + * @param context DSpace context object + * @param item Item to adjust policies on + * @param collection Collection + * @param replaceReadRPWithCollectionRP if true, all read policies on the item are replaced (but only if the + * collection has a default read policy) + * @throws SQLException If database error + * @throws AuthorizeException If authorization error + */ + public void adjustBundleBitstreamPolicies(Context context, Item item, Collection collection, + boolean replaceReadRPWithCollectionRP) + throws SQLException, AuthorizeException; + /** * Adjust the Bitstream policies to reflect what have been defined * during the submission/workflow. The temporary SUBMISSION and WORKFLOW @@ -547,6 +569,29 @@ public interface ItemService public void adjustBitstreamPolicies(Context context, Item item, Collection collection, Bitstream bitstream) throws SQLException, AuthorizeException; + /** + * Adjust the Bitstream policies to reflect what have been defined + * during the submission/workflow. The temporary SUBMISSION and WORKFLOW + * policies are removed and the policies defined at the item and collection + * level are copied and inherited as appropriate. Custom selected Item policies + * are copied to the bitstream only if no explicit custom policies were + * already applied to the bitstream. Collection's policies are inherited + * if there are no other policies defined or if the append mode is defined by + * the configuration via the core.authorization.installitem.inheritance-read.append-mode property + * + * @param context DSpace context object + * @param item Item to adjust policies on + * @param collection Collection + * @param bitstream Bitstream to adjust policies on + * @param replaceReadRPWithCollectionRP If true, all read policies on the bitstream are replaced (but only if the + * collection has a default read policy) + * @throws SQLException If database error + * @throws AuthorizeException If authorization error + */ + public void adjustBitstreamPolicies(Context context, Item item, Collection collection, Bitstream bitstream, + boolean replaceReadRPWithCollectionRP) + throws SQLException, AuthorizeException; + /** * Adjust the Item's policies to reflect what have been defined during the @@ -565,6 +610,26 @@ public interface ItemService public void adjustItemPolicies(Context context, Item item, Collection collection) throws SQLException, AuthorizeException; + /** + * Adjust the Item's policies to reflect what have been defined during the + * submission/workflow. The temporary SUBMISSION and WORKFLOW policies are + * removed and the default policies defined at the collection level are + * inherited as appropriate. Collection's policies are inherited if there are no + * other policies defined or if the append mode is defined by the configuration + * via the core.authorization.installitem.inheritance-read.append-mode property + * + * @param context DSpace context object + * @param item Item to adjust policies on + * @param collection Collection + * @param replaceReadRPWithCollectionRP If true, all read policies on the item are replaced (but only if the + * collection has a default read policy) + * @throws SQLException If database error + * @throws AuthorizeException If authorization error + */ + public void adjustItemPolicies(Context context, Item item, Collection collection, + boolean replaceReadRPWithCollectionRP) + throws SQLException, AuthorizeException; + /** * Moves the item from one collection to another one * @@ -810,24 +875,24 @@ public interface ItemService int countWithdrawnItems(Context context) throws SQLException; /** - * finds all items for which the current user has editing rights - * @param context DSpace context object - * @param offset page offset - * @param limit page size limit - * @return list of items for which the current user has editing rights - * @throws SQLException - * @throws SearchServiceException - */ + * finds all items for which the current user has editing rights + * @param context DSpace context object + * @param offset page offset + * @param limit page size limit + * @return list of items for which the current user has editing rights + * @throws SQLException + * @throws SearchServiceException + */ public List findItemsWithEdit(Context context, int offset, int limit) throws SQLException, SearchServiceException; /** - * counts all items for which the current user has editing rights - * @param context DSpace context object - * @return list of items for which the current user has editing rights - * @throws SQLException - * @throws SearchServiceException - */ + * counts all items for which the current user has editing rights + * @param context DSpace context object + * @return list of items for which the current user has editing rights + * @throws SQLException + * @throws SearchServiceException + */ public int countItemsWithEdit(Context context) throws SQLException, SearchServiceException; /** From 82c9b6fc9baee9f4f5d8b4cc967b5d12b63cdd39 Mon Sep 17 00:00:00 2001 From: Christian Bethge Date: Tue, 8 Aug 2023 09:36:16 +0200 Subject: [PATCH 104/168] ingore unrecognized arguments on help --- dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java b/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java index 7fb8567f8c..5e16fea5ae 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java +++ b/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java @@ -123,7 +123,7 @@ public abstract class DSpaceRunnable implements R } private StepResult parseForHelp(String[] args) throws ParseException { - helpCommandLine = new DefaultParser().parse(getScriptConfiguration().getHelpOptions(), args); + helpCommandLine = new DefaultParser().parse(getScriptConfiguration().getHelpOptions(), args, true); if (helpCommandLine.getOptions() != null && helpCommandLine.getOptions().length > 0) { return StepResult.Exit; } From 88749f6c61559b98fe35de3b8f4346f5995d1ef0 Mon Sep 17 00:00:00 2001 From: Koen Pauwels Date: Fri, 4 Aug 2023 17:00:35 +0200 Subject: [PATCH 105/168] 103818 Extend ItemServiceTest#testMoveItemToCollectionWithMoreRestrictiveReadPolicy --- .../org/dspace/content/ItemServiceImpl.java | 16 ++++++++-- .../content/service/ItemServiceTest.java | 30 +++++++++++++++++-- 2 files changed, 41 insertions(+), 5 deletions(-) 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 ac38f0cca4..ebea2aa5b8 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -963,13 +963,18 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl implements It } // 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 bitstream's READ policies. - if (replaceReadRPWithCollectionRP && defaultCollectionBitstreamPolicies.size() > 0) { - authorizeService.removePoliciesActionFilter(context, item, Constants.READ); + // 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 @@ -979,6 +984,11 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl implements It 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 removeAllPoliciesAndAddDefault(context, bitstream, defaultItemPolicies, defaultCollectionBitstreamPolicies); diff --git a/dspace-api/src/test/java/org/dspace/content/service/ItemServiceTest.java b/dspace-api/src/test/java/org/dspace/content/service/ItemServiceTest.java index 1847a27c7f..18e0047599 100644 --- a/dspace-api/src/test/java/org/dspace/content/service/ItemServiceTest.java +++ b/dspace-api/src/test/java/org/dspace/content/service/ItemServiceTest.java @@ -39,6 +39,7 @@ import org.dspace.builder.RelationshipTypeBuilder; import org.dspace.builder.RequestItemBuilder; import org.dspace.builder.ResourcePolicyBuilder; import org.dspace.content.Bitstream; +import org.dspace.content.Bundle; import org.dspace.content.Collection; import org.dspace.content.Community; import org.dspace.content.EntityType; @@ -786,22 +787,47 @@ public class ItemServiceTest extends AbstractIntegrationTestWithDatabase { .createItem(context, permissive) .build(); - // Verify that the item has exactly one READ policy, for the anonymous group. + Bitstream bitstream = BitstreamBuilder.createBitstream(context, item, InputStream.nullInputStream()) + .build(); + + Bundle bundle = item.getBundles("ORIGINAL").get(0); + + // Verify that the item, bundle and bitstream each have exactly one READ policy, for the anonymous group. assertEquals( List.of(anonymous), authorizeService.getPoliciesActionFilter(context, item, Constants.READ) .stream().map(ResourcePolicy::getGroup).collect(Collectors.toList()) ); + assertEquals( + List.of(anonymous), + authorizeService.getPoliciesActionFilter(context, bundle, Constants.READ) + .stream().map(ResourcePolicy::getGroup).collect(Collectors.toList()) + ); + assertEquals( + List.of(anonymous), + authorizeService.getPoliciesActionFilter(context, bitstream, Constants.READ) + .stream().map(ResourcePolicy::getGroup).collect(Collectors.toList()) + ); // Move the item to the restrictive collection, making sure to inherit default policies. itemService.move(context, item, permissive, restrictive, true); - // Verify that the item has exactly one READ policy, but now for the admin group. + // Verify that the item, bundle and bitstream each have exactly one READ policy, but now for the admin group. assertEquals( List.of(admin), authorizeService.getPoliciesActionFilter(context, item, Constants.READ) .stream().map(ResourcePolicy::getGroup).collect(Collectors.toList()) ); + assertEquals( + List.of(anonymous), + authorizeService.getPoliciesActionFilter(context, bundle, Constants.READ) + .stream().map(ResourcePolicy::getGroup).collect(Collectors.toList()) + ); + assertEquals( + List.of(anonymous), + authorizeService.getPoliciesActionFilter(context, bitstream, Constants.READ) + .stream().map(ResourcePolicy::getGroup).collect(Collectors.toList()) + ); context.restoreAuthSystemState(); } From 8c76f491eedf12b3328f8279cb53ba3ac83cb40d Mon Sep 17 00:00:00 2001 From: Koen Pauwels Date: Tue, 8 Aug 2023 11:45:44 +0200 Subject: [PATCH 106/168] 104878 Fix error in ItemServiceTest related to inheriting collection policies upon item move --- .../content/service/ItemServiceTest.java | 85 +++++++++++++++++-- 1 file changed, 80 insertions(+), 5 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/content/service/ItemServiceTest.java b/dspace-api/src/test/java/org/dspace/content/service/ItemServiceTest.java index 18e0047599..16d78a8e3e 100644 --- a/dspace-api/src/test/java/org/dspace/content/service/ItemServiceTest.java +++ b/dspace-api/src/test/java/org/dspace/content/service/ItemServiceTest.java @@ -761,10 +761,11 @@ public class ItemServiceTest extends AbstractIntegrationTestWithDatabase { } @Test - public void testMoveItemToCollectionWithMoreRestrictiveReadPolicy() throws Exception { - /* Verify that, if we move an item from a collection with a permissive default READ policy - * to a collection with a restrictive default READ policy, - * that the item does not retain the original permissive READ policy. + public void testMoveItemToCollectionWithMoreRestrictiveItemReadPolicy() throws Exception { + /* Verify that, if we move an item from a collection with a permissive default item READ policy + * to a collection with a restrictive default item READ policy, + * that the item does not retain the original permissive item READ policy. + * However, its bundles and bitstreams do. */ context.turnOffAuthorisationSystem(); @@ -812,7 +813,7 @@ public class ItemServiceTest extends AbstractIntegrationTestWithDatabase { // Move the item to the restrictive collection, making sure to inherit default policies. itemService.move(context, item, permissive, restrictive, true); - // Verify that the item, bundle and bitstream each have exactly one READ policy, but now for the admin group. + // Verify that the item's read policy now only allows administrators. assertEquals( List.of(admin), authorizeService.getPoliciesActionFilter(context, item, Constants.READ) @@ -832,6 +833,80 @@ public class ItemServiceTest extends AbstractIntegrationTestWithDatabase { context.restoreAuthSystemState(); } + @Test + public void testMoveItemToCollectionWithMoreRestrictiveBitstreamReadPolicy() throws Exception { + /* Verify that, if we move an item from a collection with a permissive default bitstream READ policy + * to a collection with a restrictive default bitstream READ policy, + * that the item's bundles and bitstreams do not retain the original permissive READ policy. + * However, the item itself does retain the original policy. + */ + + context.turnOffAuthorisationSystem(); + + Group anonymous = groupService.findByName(context, Group.ANONYMOUS); + Group admin = groupService.findByName(context, Group.ADMIN); + + // Set up the two different collections: one permissive and one restrictive in its default READ policy. + Collection permissive = CollectionBuilder + .createCollection(context, community) + .build(); + Collection restrictive = CollectionBuilder + .createCollection(context, community) + .build(); + authorizeService.removePoliciesActionFilter(context, restrictive, Constants.DEFAULT_BITSTREAM_READ); + authorizeService.addPolicy(context, restrictive, Constants.DEFAULT_BITSTREAM_READ, admin); + + // Add an item to the permissive collection. + Item item = ItemBuilder + .createItem(context, permissive) + .build(); + + Bitstream bitstream = BitstreamBuilder.createBitstream(context, item, InputStream.nullInputStream()) + .build(); + + Bundle bundle = item.getBundles("ORIGINAL").get(0); + + // Verify that the item, bundle and bitstream each have exactly one READ policy, for the anonymous group. + assertEquals( + List.of(anonymous), + authorizeService.getPoliciesActionFilter(context, item, Constants.READ) + .stream().map(ResourcePolicy::getGroup).collect(Collectors.toList()) + ); + assertEquals( + List.of(anonymous), + authorizeService.getPoliciesActionFilter(context, bundle, Constants.READ) + .stream().map(ResourcePolicy::getGroup).collect(Collectors.toList()) + ); + assertEquals( + List.of(anonymous), + authorizeService.getPoliciesActionFilter(context, bitstream, Constants.READ) + .stream().map(ResourcePolicy::getGroup).collect(Collectors.toList()) + ); + + // Move the item to the restrictive collection, making sure to inherit default policies. + itemService.move(context, item, permissive, restrictive, true); + + // Verify that the bundle and bitstream's read policies now only allows administrators. + assertEquals( + List.of(anonymous), + authorizeService.getPoliciesActionFilter(context, item, Constants.READ) + .stream().map(ResourcePolicy::getGroup).collect(Collectors.toList()) + ); + assertEquals( + List.of(admin), + authorizeService.getPoliciesActionFilter(context, bundle, Constants.READ) + .stream().map(ResourcePolicy::getGroup).collect(Collectors.toList()) + ); + assertEquals( + List.of(admin), + authorizeService.getPoliciesActionFilter(context, bitstream, Constants.READ) + .stream().map(ResourcePolicy::getGroup).collect(Collectors.toList()) + ); + + context.restoreAuthSystemState(); + + } + private void assertMetadataValue(String authorQualifier, String contributorElement, String dcSchema, String value, String authority, int place, MetadataValue metadataValue) { assertThat(metadataValue.getValue(), equalTo(value)); From 0e9b482f784948350509b13df96bbd8737cd218a Mon Sep 17 00:00:00 2001 From: Koen Pauwels Date: Tue, 8 Aug 2023 12:56:10 +0200 Subject: [PATCH 107/168] 104878 Adjust ItemServiceTest to expect correct behavior of bundles when item is migrated --- .../org/dspace/content/service/ItemServiceTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/content/service/ItemServiceTest.java b/dspace-api/src/test/java/org/dspace/content/service/ItemServiceTest.java index 16d78a8e3e..d2f4b6d851 100644 --- a/dspace-api/src/test/java/org/dspace/content/service/ItemServiceTest.java +++ b/dspace-api/src/test/java/org/dspace/content/service/ItemServiceTest.java @@ -764,8 +764,8 @@ public class ItemServiceTest extends AbstractIntegrationTestWithDatabase { public void testMoveItemToCollectionWithMoreRestrictiveItemReadPolicy() throws Exception { /* Verify that, if we move an item from a collection with a permissive default item READ policy * to a collection with a restrictive default item READ policy, - * that the item does not retain the original permissive item READ policy. - * However, its bundles and bitstreams do. + * that the item and its bundles do not retain the original permissive item READ policy. + * However, its bitstreams do. */ context.turnOffAuthorisationSystem(); @@ -820,7 +820,7 @@ public class ItemServiceTest extends AbstractIntegrationTestWithDatabase { .stream().map(ResourcePolicy::getGroup).collect(Collectors.toList()) ); assertEquals( - List.of(anonymous), + List.of(admin), authorizeService.getPoliciesActionFilter(context, bundle, Constants.READ) .stream().map(ResourcePolicy::getGroup).collect(Collectors.toList()) ); @@ -837,8 +837,8 @@ public class ItemServiceTest extends AbstractIntegrationTestWithDatabase { public void testMoveItemToCollectionWithMoreRestrictiveBitstreamReadPolicy() throws Exception { /* Verify that, if we move an item from a collection with a permissive default bitstream READ policy * to a collection with a restrictive default bitstream READ policy, - * that the item's bundles and bitstreams do not retain the original permissive READ policy. - * However, the item itself does retain the original policy. + * that the item's bitstreams do not retain the original permissive READ policy. + * However, the item itself and its bundles do retain the original policy. */ context.turnOffAuthorisationSystem(); @@ -893,7 +893,7 @@ public class ItemServiceTest extends AbstractIntegrationTestWithDatabase { .stream().map(ResourcePolicy::getGroup).collect(Collectors.toList()) ); assertEquals( - List.of(admin), + List.of(anonymous), authorizeService.getPoliciesActionFilter(context, bundle, Constants.READ) .stream().map(ResourcePolicy::getGroup).collect(Collectors.toList()) ); From 51d20fa7fd2960657b77d0fe5fe88fbce6289b60 Mon Sep 17 00:00:00 2001 From: Koen Pauwels Date: Tue, 8 Aug 2023 16:22:49 +0200 Subject: [PATCH 108/168] Fix failing IT in BulkAccessControlIT --- .../org/dspace/app/bulkaccesscontrol/BulkAccessControl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/app/bulkaccesscontrol/BulkAccessControl.java b/dspace-api/src/main/java/org/dspace/app/bulkaccesscontrol/BulkAccessControl.java index 50e1022dbe..7bef232f04 100644 --- a/dspace-api/src/main/java/org/dspace/app/bulkaccesscontrol/BulkAccessControl.java +++ b/dspace-api/src/main/java/org/dspace/app/bulkaccesscontrol/BulkAccessControl.java @@ -464,7 +464,7 @@ public class BulkAccessControl extends DSpaceRunnable createResourcePolicy(item, accessCondition, itemAccessConditions.get(accessCondition.getName()))); - itemService.adjustItemPolicies(context, item, item.getOwningCollection()); + itemService.adjustItemPolicies(context, item, item.getOwningCollection(), false); } /** From 2e62fa3fd1f264aac0bb4a12953b6385211e5656 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 8 Aug 2023 11:04:28 -0400 Subject: [PATCH 109/168] Handle missing role. --- .../curate/XmlWorkflowCuratorServiceImpl.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java b/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java index 97537befd2..70a36f278e 100644 --- a/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java @@ -31,6 +31,7 @@ import org.dspace.workflow.CurationTaskConfig; import org.dspace.workflow.FlowStep; import org.dspace.workflow.Task; import org.dspace.workflow.TaskSet; +import org.dspace.xmlworkflow.Role; import org.dspace.xmlworkflow.RoleMembers; import org.dspace.xmlworkflow.WorkflowConfigurationException; import org.dspace.xmlworkflow.factory.XmlWorkflowFactory; @@ -276,12 +277,17 @@ public class XmlWorkflowCuratorServiceImpl String.valueOf(wfi.getID()), e); return epList; } - RoleMembers roleMembers = step.getRole().getMembers(c, wfi); - for (EPerson ep : roleMembers.getEPersons()) { - epList.add(ep); - } - for (Group group : roleMembers.getGroups()) { - epList.addAll(group.getMembers()); + Role role = step.getRole(); + if (null != role) { + RoleMembers roleMembers = role.getMembers(c, wfi); + for (EPerson ep : roleMembers.getEPersons()) { + epList.add(ep); + } + for (Group group : roleMembers.getGroups()) { + epList.addAll(group.getMembers()); + } + } else { + epList.add(ePersonService.getSystemEPerson(c)); } } else if ("$colladmin".equals(contact)) { // special literal for collection administrators From 1f3ad993cc4d10694112227245be3de1ec7b3762 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 8 Aug 2023 16:43:12 -0500 Subject: [PATCH 110/168] Remove useless log.info --- .../src/main/java/org/dspace/content/BundleServiceImpl.java | 1 - 1 file changed, 1 deletion(-) 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 20c43e4bfc..546d48d430 100644 --- a/dspace-api/src/main/java/org/dspace/content/BundleServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/BundleServiceImpl.java @@ -194,7 +194,6 @@ public class BundleServiceImpl extends DSpaceObjectServiceImpl implement 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()) { From 6fbe4f4c0003dbb257deee79b2f6f57b4dc13b50 Mon Sep 17 00:00:00 2001 From: nwoodward Date: Wed, 9 Aug 2023 11:35:46 -0500 Subject: [PATCH 111/168] fix logical bug when checking if field is controlled authority --- .../src/main/java/org/dspace/app/bulkedit/MetadataImport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImport.java b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImport.java index 4161bbb4d8..9044c723ff 100644 --- a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImport.java +++ b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImport.java @@ -1363,7 +1363,7 @@ public class MetadataImport extends DSpaceRunnable Date: Fri, 11 Aug 2023 08:47:31 +0200 Subject: [PATCH 112/168] 3331 - remove the --optimize feature of 'dspace stats-util' --- .../dspace/statistics/SolrLoggerServiceImpl.java | 16 ---------------- .../statistics/service/SolrLoggerService.java | 6 ------ .../dspace/statistics/util/StatisticsClient.java | 3 --- 3 files changed, 25 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java b/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java index 19c79af34d..97585f5a47 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java @@ -1203,22 +1203,6 @@ public class SolrLoggerServiceImpl implements SolrLoggerService, InitializingBea } - @Override - public void optimizeSOLR() { - try { - long start = System.currentTimeMillis(); - System.out.println("SOLR Optimize -- Process Started:" + start); - solr.optimize(); - long finish = System.currentTimeMillis(); - System.out.println("SOLR Optimize -- Process Finished:" + finish); - System.out.println("SOLR Optimize -- Total time taken:" + (finish - start) + " (ms)."); - } catch (SolrServerException sse) { - System.err.println(sse.getMessage()); - } catch (IOException ioe) { - System.err.println(ioe.getMessage()); - } - } - @Override public void shardSolrIndex() throws IOException, SolrServerException { if (!(solr instanceof HttpSolrClient)) { diff --git a/dspace-api/src/main/java/org/dspace/statistics/service/SolrLoggerService.java b/dspace-api/src/main/java/org/dspace/statistics/service/SolrLoggerService.java index 3728318625..61b2bb6013 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/service/SolrLoggerService.java +++ b/dspace-api/src/main/java/org/dspace/statistics/service/SolrLoggerService.java @@ -266,12 +266,6 @@ public interface SolrLoggerService { */ public String getIgnoreSpiderIPs(); - /** - * Maintenance to keep a SOLR index efficient. - * Note: This might take a long time. - */ - public void optimizeSOLR(); - public void shardSolrIndex() throws IOException, SolrServerException; public void reindexBitstreamHits(boolean removeDeletedBitstreams) throws Exception; diff --git a/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsClient.java b/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsClient.java index e45ce163ed..319fe437d6 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsClient.java +++ b/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsClient.java @@ -67,7 +67,6 @@ public class StatisticsClient { options.addOption("m", "mark-spiders", false, "Update isBot Flag in Solr"); options.addOption("f", "delete-spiders-by-flag", false, "Delete Spiders in Solr By isBot Flag"); options.addOption("i", "delete-spiders-by-ip", false, "Delete Spiders in Solr By IP Address"); - options.addOption("o", "optimize", false, "Run maintenance on the SOLR index"); options.addOption("b", "reindex-bitstreams", false, "Reindex the bitstreams to ensure we have the bundle name"); options.addOption("e", "export", false, "Export SOLR view statistics data to usage-statistics-intermediate-format"); @@ -93,8 +92,6 @@ public class StatisticsClient { solrLoggerService.deleteRobotsByIsBotFlag(); } else if (line.hasOption('i')) { solrLoggerService.deleteRobotsByIP(); - } else if (line.hasOption('o')) { - solrLoggerService.optimizeSOLR(); } else if (line.hasOption('b')) { solrLoggerService.reindexBitstreamHits(line.hasOption('r')); } else if (line.hasOption('e')) { From 22e209322c61bc1c7f940a1b686b87d90b5fd433 Mon Sep 17 00:00:00 2001 From: Nona Luypaert Date: Thu, 17 Aug 2023 16:43:21 +0200 Subject: [PATCH 113/168] 103837: Refactor GA config to list bundles --- .../google/GoogleAsyncEventListener.java | 30 +++++----- .../google/GoogleAsyncEventListenerIT.java | 56 ++++++++++++------- dspace/config/dspace.cfg | 12 ++-- 3 files changed, 60 insertions(+), 38 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/google/GoogleAsyncEventListener.java b/dspace-api/src/main/java/org/dspace/google/GoogleAsyncEventListener.java index e84d9f8591..cb9a120fd0 100644 --- a/dspace-api/src/main/java/org/dspace/google/GoogleAsyncEventListener.java +++ b/dspace-api/src/main/java/org/dspace/google/GoogleAsyncEventListener.java @@ -23,6 +23,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.content.Bitstream; +import org.dspace.content.Bundle; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.core.Constants; import org.dspace.core.Context; @@ -176,26 +177,27 @@ public class GoogleAsyncEventListener extends AbstractUsageEventListener { * Verifies if the usage event is a content bitstream view event, by checking if:

    *
  • the usage event is a view event
  • *
  • the object of the usage event is a bitstream
  • - *
  • the bitstream belongs to the ORIGINAL bundle
- * This last one can be skipped if 'google-analytics.exclude-non-content-bitstreams' is set to false. - * This will make it so the bundle name is completely ignored when sending events. + *
  • the bitstream belongs to one of the configured bundles (fallback: ORIGINAL bundle)
  • */ private boolean isContentBitstream(UsageEvent usageEvent) { // check if event is a VIEW event and object is a Bitstream if (usageEvent.getAction() == UsageEvent.Action.VIEW || usageEvent.getObject().getType() == Constants.BITSTREAM) { - // check if config is set to true - if (configurationService.getBooleanProperty("google-analytics.exclude-non-content-bitstreams")) { - try { - // check if bitstream belongs to the ORIGINAL bundle - return ((Bitstream) usageEvent.getObject()) - .getBundles().stream() - .anyMatch(bundle -> bundle.getName().equals(Constants.CONTENT_BUNDLE_NAME)); - } catch (SQLException e) { - throw new RuntimeException(e.getMessage(), e); - } + // check if bitstream belongs to a configured bundle + List allowedBundles = List.of(configurationService + .getArrayProperty("google-analytics.bundles", new String[]{Constants.CONTENT_BUNDLE_NAME})); + if (allowedBundles.contains("none")) { + // GA events for bitstream views were turned off in config + return false; } - return true; + List bitstreamBundles; + try { + bitstreamBundles = ((Bitstream) usageEvent.getObject()) + .getBundles().stream().map(Bundle::getName).collect(Collectors.toList()); + } catch (SQLException e) { + throw new RuntimeException(e.getMessage(), e); + } + return allowedBundles.stream().anyMatch(bitstreamBundles::contains); } return false; } diff --git a/dspace-server-webapp/src/test/java/org/dspace/google/GoogleAsyncEventListenerIT.java b/dspace-server-webapp/src/test/java/org/dspace/google/GoogleAsyncEventListenerIT.java index e43e9fd820..17df839ebf 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/google/GoogleAsyncEventListenerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/google/GoogleAsyncEventListenerIT.java @@ -245,17 +245,12 @@ public class GoogleAsyncEventListenerIT extends AbstractControllerIntegrationTes } @Test - public void testOnBitstreamContentDownloadExcludeNonContentBitstreams() throws Exception { - configurationService.setProperty("google-analytics.exclude-non-content-bitstreams", true); - + public void testOnBitstreamContentDownloadDefaultBundleConfig() throws Exception { context.turnOffAuthorisationSystem(); Bundle licenseBundle = BundleBuilder.createBundle(context, item) .withName(Constants.LICENSE_BUNDLE_NAME).build(); Bitstream license = BitstreamBuilder.createBitstream(context, licenseBundle, toInputStream("License", defaultCharset())).build(); - Bundle thumbnailBundle = BundleBuilder.createBundle(context, item).withName("THUMBNAIL").build(); - Bitstream thumbnail = BitstreamBuilder.createBitstream(context, thumbnailBundle, - toInputStream("Thumbnail", defaultCharset())).build(); context.restoreAuthSystemState(); assertThat(getStoredEventsAsList(), empty()); @@ -264,14 +259,14 @@ public class GoogleAsyncEventListenerIT extends AbstractControllerIntegrationTes downloadBitstreamContent("Postman", "123456", "REF"); downloadContent("Chrome", "ABCDEFG", "REF-1", license); - downloadContent("Chrome", "987654", "REF-2", thumbnail); assertThat(getStoredEventsAsList(), hasSize(1)); List storedEvents = getStoredEventsAsList(); assertThat(storedEvents, contains( - event("123456", "127.0.0.1", "Postman", "REF", bitstreamUrl, "Test item"))); + event("123456", "127.0.0.1", "Postman", "REF", bitstreamUrl, "Test item")) + ); googleAsyncEventListener.sendCollectedEvents(); @@ -284,14 +279,14 @@ public class GoogleAsyncEventListenerIT extends AbstractControllerIntegrationTes } @Test - public void testOnBitstreamContentDownloadIncludeNonContentBitstreams() throws Exception { - configurationService.setProperty("google-analytics.exclude-non-content-bitstreams", false); + public void testOnBitstreamContentDownloadMultipleBundleConfig() throws Exception { + configurationService.setProperty("google-analytics.bundles", + List.of(Constants.DEFAULT_BUNDLE_NAME, "CONTENT")); context.turnOffAuthorisationSystem(); - Bundle licenseBundle = BundleBuilder.createBundle(context, item) - .withName(Constants.LICENSE_BUNDLE_NAME).build(); - Bitstream license = BitstreamBuilder.createBitstream(context, licenseBundle, - toInputStream("License", defaultCharset())).build(); + Bundle contentBundle = BundleBuilder.createBundle(context, item).withName("CONTENT").build(); + Bitstream content = BitstreamBuilder.createBitstream(context, contentBundle, + toInputStream("Test Content", defaultCharset())).build(); Bundle thumbnailBundle = BundleBuilder.createBundle(context, item).withName("THUMBNAIL").build(); Bitstream thumbnail = BitstreamBuilder.createBitstream(context, thumbnailBundle, toInputStream("Thumbnail", defaultCharset())).build(); @@ -300,21 +295,20 @@ public class GoogleAsyncEventListenerIT extends AbstractControllerIntegrationTes assertThat(getStoredEventsAsList(), empty()); String bitstreamUrl = "/api/core/bitstreams/" + bitstream.getID() + "/content"; - String licenseUrl = "/api/core/bitstreams/" + license.getID() + "/content"; - String thumbnailUrl = "/api/core/bitstreams/" + thumbnail.getID() + "/content"; + String contentUrl = "/api/core/bitstreams/" + content.getID() + "/content"; downloadBitstreamContent("Postman", "123456", "REF"); - downloadContent("Chrome", "ABCDEFG", "REF-1", license); + downloadContent("Chrome", "ABCDEFG", "REF-1", content); downloadContent("Chrome", "987654", "REF-2", thumbnail); - assertThat(getStoredEventsAsList(), hasSize(3)); + assertThat(getStoredEventsAsList(), hasSize(2)); List storedEvents = getStoredEventsAsList(); assertThat(storedEvents, contains( event("123456", "127.0.0.1", "Postman", "REF", bitstreamUrl, "Test item"), - event("ABCDEFG", "127.0.0.1", "Chrome", "REF-1", licenseUrl, "Test item"), - event("987654", "127.0.0.1", "Chrome", "REF-2", thumbnailUrl, "Test item"))); + event("ABCDEFG", "127.0.0.1", "Chrome", "REF-1", contentUrl, "Test item") + )); googleAsyncEventListener.sendCollectedEvents(); @@ -326,6 +320,28 @@ public class GoogleAsyncEventListenerIT extends AbstractControllerIntegrationTes verifyNoMoreInteractions(firstGaClientMock, secondGaClientMock); } + @Test + public void testOnBitstreamContentDownloadNoneBundleConfig() throws Exception { + configurationService.setProperty("google-analytics.bundles", "none"); + + context.turnOffAuthorisationSystem(); + Bundle contentBundle = BundleBuilder.createBundle(context, item).withName("CONTENT").build(); + Bitstream content = BitstreamBuilder.createBitstream(context, contentBundle, + toInputStream("Test Content", defaultCharset())).build(); + Bundle thumbnailBundle = BundleBuilder.createBundle(context, item).withName("THUMBNAIL").build(); + Bitstream thumbnail = BitstreamBuilder.createBitstream(context, thumbnailBundle, + toInputStream("Thumbnail", defaultCharset())).build(); + context.restoreAuthSystemState(); + + assertThat(getStoredEventsAsList(), empty()); + + downloadBitstreamContent("Postman", "123456", "REF"); + downloadContent("Chrome", "ABCDEFG", "REF-1", content); + downloadContent("Chrome", "987654", "REF-2", thumbnail); + + assertThat(getStoredEventsAsList(), empty()); + } + @SuppressWarnings("unchecked") private List getStoredEventsAsList() { List events = new ArrayList<>(); diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 89c8da9255..be11cbf033 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1537,10 +1537,14 @@ log.report.dir = ${dspace.dir}/log # For more details see https://developers.google.com/analytics/devguides/collection/protocol/ga4 # google.analytics.api-secret = -# Ensures only views of bitstreams in the 'ORIGINAL' bundle result in a GA4 event. -# Setting this to false may cause inflated bitstream view numbers, since requesting -# bitstreams in the 'THUMBNAIL' and 'LICENSE' bundles, will also result in GA4 events. -google-analytics.exclude-non-content-bitstreams=true +# Ensures only views of bitstreams in configured bundles result in a GA4 event. +# Config can contain multiple bundles for which the bitstream views will result in GA4 events, eg: +# google-analytics.bundles = ORIGINAL, CONTENT +# If config is not set or empty, the default fallback is Constants#CONTENT_BUNDLE_NAME bundle ('ORIGINAL'). +# If config contains 'LICENSE' or 'THUMBNAIL' bundles, it may cause inflated bitstream view numbers. +# Set config to 'none' to disable GA4 bitstream events, eg: +# google-analytics.bundles = none +google-analytics.bundles = ORIGINAL #################################################################### #---------------------------------------------------------------# From ca8abddff1230e581501b482623966e64016d609 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 21 Aug 2023 23:35:23 +0200 Subject: [PATCH 114/168] README.md: Fix typo --- dspace-server-webapp/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/README.md b/dspace-server-webapp/README.md index 8d3853e8cc..d418124ea1 100644 --- a/dspace-server-webapp/README.md +++ b/dspace-server-webapp/README.md @@ -10,7 +10,7 @@ This webapp uses the following technologies: We don't use Spring Data REST as we haven't a spring data layer and we want to provide clear separation between the persistence representation and the REST representation ## How to contribute -Check the infomation available on the DSpace Official Wiki page for the [DSpace 7 Working Group](https://wiki.duraspace.org/display/DSPACE/DSpace+7+UI+Working+Group) +Check the information available on the DSpace Official Wiki page for the [DSpace 7 Working Group](https://wiki.duraspace.org/display/DSPACE/DSpace+7+UI+Working+Group) [DSpace 7 REST: Coding DSpace Objects](https://wiki.duraspace.org/display/DSPACE/DSpace+7+REST%3A+Coding+DSpace+Objects) From 940183411ea6ea87aa5de9193794c553178637d4 Mon Sep 17 00:00:00 2001 From: Hrafn Malmquist Date: Tue, 22 Aug 2023 21:24:24 +0100 Subject: [PATCH 115/168] Bump up versions of buildnumber-maven-plugin & build-helper-maven-plugin. add configuration for SCM failure (#9016) --- Dockerfile | 2 +- dspace-api/pom.xml | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 664cba89fa..dd633def28 100644 --- a/Dockerfile +++ b/Dockerfile @@ -51,7 +51,7 @@ RUN ant init_installation update_configs update_code update_webapps FROM tomcat:9-jdk${JDK_VERSION} # NOTE: DSPACE_INSTALL must align with the "dspace.dir" default configuration. ENV DSPACE_INSTALL=/dspace -# Copy the /dspace directory from 'ant_build' containger to /dspace in this container +# Copy the /dspace directory from 'ant_build' container to /dspace in this container COPY --from=ant_build /dspace $DSPACE_INSTALL # Expose Tomcat port and AJP port EXPOSE 8080 8009 diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index ee8c21cb64..45aee2227a 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -102,7 +102,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.0.0 + 3.4.0 validate @@ -116,7 +116,10 @@ org.codehaus.mojo buildnumber-maven-plugin - 1.4 + 3.2.0 + + UNKNOWN_REVISION + validate From 86285d78aa0844b9811dcebdefa897ceeb944226 Mon Sep 17 00:00:00 2001 From: Christian Bethge Date: Wed, 23 Aug 2023 10:33:51 +0200 Subject: [PATCH 116/168] add DSpaceSkipUnknownArgumentsParser as parser to ignore/skip unknown arguments in cli by help, fix not necessary ParseException in help --- .../cli/DSpaceSkipUnknownArgumentsParser.java | 77 +++++++++++++++++++ .../org/dspace/scripts/DSpaceRunnable.java | 3 +- 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 dspace-api/src/main/java/org/dspace/cli/DSpaceSkipUnknownArgumentsParser.java 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 0000000000..afd74a588d --- /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/scripts/DSpaceRunnable.java b/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java index 5e16fea5ae..2ea0a52d6e 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java +++ b/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java @@ -18,6 +18,7 @@ import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.lang3.StringUtils; +import org.dspace.cli.DSpaceSkipUnknownArgumentsParser; import org.dspace.eperson.EPerson; import org.dspace.scripts.configuration.ScriptConfiguration; import org.dspace.scripts.handler.DSpaceRunnableHandler; @@ -123,7 +124,7 @@ public abstract class DSpaceRunnable implements R } private StepResult parseForHelp(String[] args) throws ParseException { - helpCommandLine = new DefaultParser().parse(getScriptConfiguration().getHelpOptions(), args, true); + helpCommandLine = new DSpaceSkipUnknownArgumentsParser().parse(getScriptConfiguration().getHelpOptions(), args); if (helpCommandLine.getOptions() != null && helpCommandLine.getOptions().length > 0) { return StepResult.Exit; } From 064e2caa37dfa283c3c08dee0e7321e36073bfa2 Mon Sep 17 00:00:00 2001 From: Christian Bethge Date: Wed, 23 Aug 2023 10:41:44 +0200 Subject: [PATCH 117/168] remove not necessary else --- .../main/java/org/dspace/app/launcher/ScriptLauncher.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java b/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java index bcb61a48ee..89a416bfa8 100644 --- a/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java +++ b/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java @@ -147,11 +147,11 @@ public class ScriptLauncher { DSpaceRunnable script) { try { StepResult result = script.initialize(args, dSpaceRunnableHandler, null); + // check the StepResult, only run the script if the result is Continue; + // otherwise - for example the script is started with the help as argument, nothing is to do if (StepResult.Continue.equals(result)) { - // only run the script, if the normal initialize is successful + // runs the script, the normal initialization is successful script.run(); - } else { - // otherwise - for example the script is started with the help argument } return 0; } catch (ParseException e) { From c23bc8a33867759919fd8fce0c28118a8630427e Mon Sep 17 00:00:00 2001 From: Martin Walk Date: Thu, 3 Aug 2023 13:39:43 +0200 Subject: [PATCH 118/168] Fix #8963: Remove deletion constraint from Groomer (cherry picked from commit e07763b021c009e421b97dc5b6404c284c9de168) --- .../main/java/org/dspace/eperson/Groomer.java | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/eperson/Groomer.java b/dspace-api/src/main/java/org/dspace/eperson/Groomer.java index 2a828cdc12..5485bb1d0c 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/Groomer.java +++ b/dspace-api/src/main/java/org/dspace/eperson/Groomer.java @@ -141,20 +141,10 @@ public class Groomer { System.out.println(); if (delete) { - List whyNot = ePersonService.getDeleteConstraints(myContext, account); - if (!whyNot.isEmpty()) { - System.out.print("\tCannot be deleted; referenced in"); - for (String table : whyNot) { - System.out.print(' '); - System.out.print(table); - } - System.out.println(); - } else { - try { - ePersonService.delete(myContext, account); - } catch (AuthorizeException | IOException ex) { - System.err.println(ex.getMessage()); - } + try { + ePersonService.delete(myContext, account); + } catch (AuthorizeException | IOException ex) { + System.err.println(ex.getMessage()); } } } From b1d3471995ce7687b4ca256b90d6c1487e9719dd Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 23 Aug 2023 16:51:26 -0500 Subject: [PATCH 119/168] Enable new skip merge commit feature --- .github/workflows/port_merged_pull_request.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/port_merged_pull_request.yml b/.github/workflows/port_merged_pull_request.yml index 50faf3f886..109835d14d 100644 --- a/.github/workflows/port_merged_pull_request.yml +++ b/.github/workflows/port_merged_pull_request.yml @@ -39,6 +39,8 @@ jobs: # Copy all labels from original PR to (newly created) port PR # NOTE: The labels matching 'label_pattern' are automatically excluded copy_labels_pattern: '.*' + # Skip any merge commits in the ported PR. This means only non-merge commits are cherry-picked to the new PR + merge_commits: 'skip' # Use a personal access token (PAT) to create PR as 'dspace-bot' user. # A PAT is required in order for the new PR to trigger its own actions (for CI checks) github_token: ${{ secrets.PR_PORT_TOKEN }} \ No newline at end of file From eae4463eaa5916bd9b20f4e4132398aceeba1f02 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Thu, 24 Aug 2023 13:03:26 -0400 Subject: [PATCH 120/168] Avoid double slashes in sitemap paths. --- .../org/dspace/app/sitemap/GenerateSitemaps.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) 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 d65447d311..400b5ecb87 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 @@ -189,7 +189,10 @@ public class GenerateSitemaps { */ 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()) { @@ -212,7 +215,7 @@ public class GenerateSitemaps { List comms = communityService.findAll(c); for (Community comm : comms) { - String url = uiURLStem + "/communities/" + comm.getID(); + String url = uiURLStem + "communities/" + comm.getID(); if (makeHTMLMap) { html.addURL(url, null); @@ -227,7 +230,7 @@ public class GenerateSitemaps { List colls = collectionService.findAll(c); for (Collection coll : colls) { - String url = uiURLStem + "/collections/" + coll.getID(); + String url = uiURLStem + "collections/" + coll.getID(); if (makeHTMLMap) { html.addURL(url, null); @@ -259,11 +262,11 @@ public class GenerateSitemaps { && StringUtils.isNotBlank(discoverResult.getSearchDocument( discoverResult.getIndexableObjects().get(0)).get(0).getSearchFieldValues("entityType").get(0)) ) { - url = uiURLStem + "/entities/" + StringUtils.lowerCase(discoverResult.getSearchDocument( + 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(); + url = uiURLStem + "items/" + i.getID(); } Date lastMod = i.getLastModified(); From 1160341cb2a2c163c8fddc04ddab46de9041e1b8 Mon Sep 17 00:00:00 2001 From: Christian Bethge Date: Tue, 29 Aug 2023 15:28:21 +0200 Subject: [PATCH 121/168] 9043 use Templates for compiled XSLT instead of Transformer - use Templates are thread-safe and NOT Transformer --- dspace-oai/pom.xml | 2 +- .../services/impl/resources/DSpaceResourceResolver.java | 7 +++---- .../dspace/xoai/tests/integration/xoai/PipelineTest.java | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 808940eb7b..59cee28293 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -15,7 +15,7 @@ ${basedir}/.. - 3.3.0 + 3.3.1-SNAPSHOT 5.87.0.RELEASE diff --git a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/DSpaceResourceResolver.java b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/DSpaceResourceResolver.java index e67e9c56bd..83c4486f71 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/DSpaceResourceResolver.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/DSpaceResourceResolver.java @@ -12,7 +12,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import javax.xml.transform.Source; -import javax.xml.transform.Transformer; +import javax.xml.transform.Templates; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamSource; @@ -40,8 +40,7 @@ public class DSpaceResourceResolver implements ResourceResolver { } @Override - public Transformer getTransformer(String path) throws IOException, - TransformerConfigurationException { + public Templates getTemplates(String path) throws IOException, TransformerConfigurationException { // construct a Source that reads from an InputStream Source mySrc = new StreamSource(getResource(path)); // specify a system ID (the path to the XSLT-file on the filesystem) @@ -49,6 +48,6 @@ public class DSpaceResourceResolver implements ResourceResolver { // XSLT-files (like ) String systemId = basePath + "/" + path; mySrc.setSystemId(systemId); - return transformerFactory.newTransformer(mySrc); + return transformerFactory.newTemplates(mySrc); } } diff --git a/dspace-oai/src/test/java/org/dspace/xoai/tests/integration/xoai/PipelineTest.java b/dspace-oai/src/test/java/org/dspace/xoai/tests/integration/xoai/PipelineTest.java index de76c99245..0f48824159 100644 --- a/dspace-oai/src/test/java/org/dspace/xoai/tests/integration/xoai/PipelineTest.java +++ b/dspace-oai/src/test/java/org/dspace/xoai/tests/integration/xoai/PipelineTest.java @@ -29,7 +29,7 @@ public class PipelineTest { InputStream input = PipelineTest.class.getClassLoader().getResourceAsStream("item.xml"); InputStream xslt = PipelineTest.class.getClassLoader().getResourceAsStream("oai_dc.xsl"); String output = FileUtils.readAllText(new XSLPipeline(input, true) - .apply(factory.newTransformer(new StreamSource(xslt))) + .apply(factory.newTemplates(new StreamSource(xslt))) .getTransformed()); assertThat(output, oai_dc().withXPath("/oai_dc:dc/dc:title", equalTo("Teste"))); From 2bcc0b38a9436b0abc4c54e419f0fa6ae194269c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 22:40:56 +0000 Subject: [PATCH 122/168] Bump org.eclipse.jetty:jetty-xml Bumps [org.eclipse.jetty:jetty-xml](https://github.com/eclipse/jetty.project) from 9.4.51.v20230217 to 9.4.52.v20230823. - [Release notes](https://github.com/eclipse/jetty.project/releases) - [Commits](https://github.com/eclipse/jetty.project/compare/jetty-9.4.51.v20230217...jetty-9.4.52.v20230823) --- updated-dependencies: - dependency-name: org.eclipse.jetty:jetty-xml dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7822d43109..aeb15fbefa 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ 2.3.8 1.1.1 - 9.4.51.v20230217 + 9.4.52.v20230823 2.20.0 2.0.29 1.19.0 From 997057f8e4c3b48092e41534dd99535b109b214a Mon Sep 17 00:00:00 2001 From: Nona Luypaert Date: Wed, 6 Sep 2023 19:48:55 +0200 Subject: [PATCH 123/168] 103837: Fix isContentBitstream() in GoogleAsyncEventListener --- .../main/java/org/dspace/google/GoogleAsyncEventListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/google/GoogleAsyncEventListener.java b/dspace-api/src/main/java/org/dspace/google/GoogleAsyncEventListener.java index cb9a120fd0..c1c59acf4a 100644 --- a/dspace-api/src/main/java/org/dspace/google/GoogleAsyncEventListener.java +++ b/dspace-api/src/main/java/org/dspace/google/GoogleAsyncEventListener.java @@ -182,7 +182,7 @@ public class GoogleAsyncEventListener extends AbstractUsageEventListener { private boolean isContentBitstream(UsageEvent usageEvent) { // check if event is a VIEW event and object is a Bitstream if (usageEvent.getAction() == UsageEvent.Action.VIEW - || usageEvent.getObject().getType() == Constants.BITSTREAM) { + && usageEvent.getObject().getType() == Constants.BITSTREAM) { // check if bitstream belongs to a configured bundle List allowedBundles = List.of(configurationService .getArrayProperty("google-analytics.bundles", new String[]{Constants.CONTENT_BUNDLE_NAME})); From 934ebc890ea45e9f34471dd9f27c9d45baaecef3 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Mon, 10 Jul 2023 11:29:33 +0200 Subject: [PATCH 124/168] Add a configuration key to disable hierarchical browse indexes (cherry picked from commit e55bc87c1aee8806befcd9dede575b258dfdfa9c) --- .../content/authority/ChoiceAuthorityServiceImpl.java | 10 ++++++++++ dspace/config/dspace.cfg | 5 +++++ 2 files changed, 15 insertions(+) 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 4cac1da314..f2bc4f0be0 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 @@ -17,6 +17,7 @@ import java.util.Map.Entry; import java.util.Set; import java.util.stream.Collectors; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.app.util.DCInput; @@ -557,6 +558,15 @@ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService init(); ChoiceAuthority source = this.getChoiceAuthorityByAuthorityName(nameVocab); if (source != null && source instanceof DSpaceControlledVocabulary) { + + // First, check if this vocabulary index is disabled + String[] vocabulariesDisabled = configurationService + .getArrayProperty("webui.browse.vocabularies.disabled"); + if (vocabulariesDisabled != null && ArrayUtils.contains(vocabulariesDisabled, nameVocab)) { + // Discard this vocabulary browse index + return null; + } + Set metadataFields = new HashSet<>(); Map> formsToFields = this.authoritiesFormDefinitions.get(nameVocab); for (Map.Entry> formToField : formsToFields.entrySet()) { diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index d9eb1dfa7e..3a3d3cc632 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1158,6 +1158,11 @@ webui.browse.index.4 = subject:metadata:dc.subject.*:text ## example of authority-controlled browse category - see authority control config #webui.browse.index.5 = lcAuthor:metadataAuthority:dc.contributor.author:authority +# By default, browse hierarchical indexes are created based on the used controlled +# vocabularies in the submission forms. These could be disabled adding the name of +# the vocabularies to exclude in this comma-separated property: +# webui.browse.vocabularies.disabled = srsc + # Enable/Disable tag cloud in browsing. # webui.browse.index.tagcloud. = true | false # where n is the index number from the above options From 06f4d8df81e7731cba0496e7387572e229de92cc Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 8 Sep 2023 14:01:25 -0500 Subject: [PATCH 125/168] Add note that rebooting Tomcat required (cherry picked from commit ff393fe72d9b218734e4efb0ac8266f222d8da62) --- dspace/config/dspace.cfg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 3a3d3cc632..61027c5550 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1160,7 +1160,8 @@ webui.browse.index.4 = subject:metadata:dc.subject.*:text # By default, browse hierarchical indexes are created based on the used controlled # vocabularies in the submission forms. These could be disabled adding the name of -# the vocabularies to exclude in this comma-separated property: +# the vocabularies to exclude in this comma-separated property. +# (Requires reboot of servlet container, e.g. Tomcat, to reload) # webui.browse.vocabularies.disabled = srsc # Enable/Disable tag cloud in browsing. From 80b35c9650ec2f40bea3b497b65ce77d0c97bfcf Mon Sep 17 00:00:00 2001 From: Hrafn Malmquist Date: Sat, 9 Sep 2023 00:53:11 +0100 Subject: [PATCH 126/168] Add websvc.opensearch.autolink and websvc.opensearch.shortname to exposed REST configuration properties --- dspace/config/modules/rest.cfg | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dspace/config/modules/rest.cfg b/dspace/config/modules/rest.cfg index 657e02b58d..b08f8d5145 100644 --- a/dspace/config/modules/rest.cfg +++ b/dspace/config/modules/rest.cfg @@ -36,7 +36,6 @@ rest.patch.operations.limit = 1000 # (Requires reboot of servlet container, e.g. Tomcat, to reload) rest.properties.exposed = plugin.named.org.dspace.curate.CurationTask rest.properties.exposed = google.analytics.key -rest.properties.exposed = websvc.opensearch.enable rest.properties.exposed = versioning.item.history.include.submitter rest.properties.exposed = researcher-profile.entity-type rest.properties.exposed = orcid.application-client-id @@ -44,7 +43,10 @@ rest.properties.exposed = orcid.authorize-url rest.properties.exposed = orcid.scope rest.properties.exposed = orcid.disconnection.allowed-users rest.properties.exposed = registration.verification.enabled +rest.properties.exposed = websvc.opensearch.enable rest.properties.exposed = websvc.opensearch.svccontext +rest.properties.exposed = websvc.opensearch.shortname +rest.properties.exposed = websvc.opensearch.autolink rest.properties.exposed = submit.type-bind.field rest.properties.exposed = google.recaptcha.key.site rest.properties.exposed = google.recaptcha.version From b6d20eef71ab40b622cc4808efa5c677e9ecce21 Mon Sep 17 00:00:00 2001 From: Mark Cooper Date: Tue, 12 Sep 2023 18:33:37 -0700 Subject: [PATCH 127/168] Add a "container friendly" log4j2 cfg and output compose dspace log to console (#8828) Co-authored-by: Tim Donohue --- docker-compose.yml | 1 + dspace/config/log4j2-container.xml | 65 ++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 dspace/config/log4j2-container.xml diff --git a/docker-compose.yml b/docker-compose.yml index e623d96079..38007908c6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,6 +28,7 @@ services: # proxies.trusted.ipranges: This setting is required for a REST API running in Docker to trust requests # from the host machine. This IP range MUST correspond to the 'dspacenet' subnet defined above. proxies__P__trusted__P__ipranges: '172.23.0' + LOGGING_CONFIG: /dspace/config/log4j2-container.xml image: "${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-latest-test}" build: context: . diff --git a/dspace/config/log4j2-container.xml b/dspace/config/log4j2-container.xml new file mode 100644 index 0000000000..9fd358c72a --- /dev/null +++ b/dspace/config/log4j2-container.xml @@ -0,0 +1,65 @@ + + + + + + INFO + INFO + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 14223bd712ce91cf97096f2201924baea8456814 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 13 Sep 2023 10:15:07 -0500 Subject: [PATCH 128/168] Remove 'cross join' from count query. Updates "countHandlesByPrefix" to use a query similar to existing "findByPrefix" --- .../main/java/org/dspace/handle/dao/impl/HandleDAOImpl.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/handle/dao/impl/HandleDAOImpl.java b/dspace-api/src/main/java/org/dspace/handle/dao/impl/HandleDAOImpl.java index 3bd702bf80..71bb798ae3 100644 --- a/dspace-api/src/main/java/org/dspace/handle/dao/impl/HandleDAOImpl.java +++ b/dspace-api/src/main/java/org/dspace/handle/dao/impl/HandleDAOImpl.java @@ -90,13 +90,11 @@ public class HandleDAOImpl extends AbstractHibernateDAO implements Handl @Override public long countHandlesByPrefix(Context context, String prefix) throws SQLException { - - CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context); - CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(Long.class); + CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, Handle.class); Root handleRoot = criteriaQuery.from(Handle.class); - criteriaQuery.select(criteriaBuilder.count(criteriaQuery.from(Handle.class))); + criteriaQuery.select(handleRoot); criteriaQuery.where(criteriaBuilder.like(handleRoot.get(Handle_.handle), prefix + "%")); return countLong(context, criteriaQuery, criteriaBuilder, handleRoot); } From 1271374d37a3f7d9cec270e54ec106895aa934bc Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 13 Sep 2023 11:47:55 -0500 Subject: [PATCH 129/168] Fix ClassCastException (Collection cannot be cast to Item) in Handle identifier classes --- .../identifier/HandleIdentifierProvider.java | 3 +- ...dentifierProviderWithCanonicalHandles.java | 63 ++++++++++--------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/HandleIdentifierProvider.java b/dspace-api/src/main/java/org/dspace/identifier/HandleIdentifierProvider.java index 1ded40c8f8..59a1e13a21 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/HandleIdentifierProvider.java +++ b/dspace-api/src/main/java/org/dspace/identifier/HandleIdentifierProvider.java @@ -88,8 +88,7 @@ public class HandleIdentifierProvider extends IdentifierProvider { try { handleService.createHandle(context, dso, identifier); if (dso instanceof Item || dso instanceof Collection || dso instanceof Community) { - Item item = (Item) dso; - populateHandleMetadata(context, item, identifier); + populateHandleMetadata(context, dso, identifier); } } catch (IOException | IllegalStateException | SQLException | AuthorizeException e) { log.error(LogHelper.getHeader(context, diff --git a/dspace-api/src/main/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandles.java b/dspace-api/src/main/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandles.java index 78ad6b7b79..bfa3313199 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandles.java +++ b/dspace-api/src/main/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandles.java @@ -180,45 +180,46 @@ public class VersionedHandleIdentifierProviderWithCanonicalHandles extends Ident @Override public void register(Context context, DSpaceObject dso, String identifier) { try { + if (dso instanceof Item) { + Item item = (Item) dso; + // if for this identifier is already present a record in the Handle table and the corresponding item + // has an history someone is trying to restore the latest version for the item. When + // trying to restore the latest version the identifier in input doesn't have the for 1234/123.latestVersion + // it is the canonical 1234/123 + VersionHistory itemHistory = getHistory(context, identifier); + if (!identifier.matches(".*/.*\\.\\d+") && itemHistory != null) { - Item item = (Item) dso; + int newVersionNumber = versionHistoryService.getLatestVersion(context, itemHistory) + .getVersionNumber() + 1; + String canonical = identifier; + identifier = identifier.concat(".").concat("" + newVersionNumber); + restoreItAsVersion(context, dso, identifier, item, canonical, itemHistory); + } else if (identifier.matches(".*/.*\\.\\d+")) { + // if identifier == 1234.5/100.4 reinstate the version 4 in the version table if absent - // if for this identifier is already present a record in the Handle table and the corresponding item - // has an history someone is trying to restore the latest version for the item. When - // trying to restore the latest version the identifier in input doesn't have the for 1234/123.latestVersion - // it is the canonical 1234/123 - VersionHistory itemHistory = getHistory(context, identifier); - if (!identifier.matches(".*/.*\\.\\d+") && itemHistory != null) { - - int newVersionNumber = versionHistoryService.getLatestVersion(context, itemHistory) - .getVersionNumber() + 1; - String canonical = identifier; - identifier = identifier.concat(".").concat("" + newVersionNumber); - restoreItAsVersion(context, dso, identifier, item, canonical, itemHistory); - } else if (identifier.matches(".*/.*\\.\\d+")) { - // if identifier == 1234.5/100.4 reinstate the version 4 in the version table if absent - - // if it is a version of an item is needed to put back the record - // in the versionitem table - String canonical = getCanonical(identifier); - DSpaceObject canonicalItem = this.resolve(context, canonical); - if (canonicalItem == null) { - restoreItAsCanonical(context, dso, identifier, item, canonical); - } else { - VersionHistory history = versionHistoryService.findByItem(context, (Item) canonicalItem); - if (history == null) { + // if it is a version of an item is needed to put back the record + // in the versionitem table + String canonical = getCanonical(identifier); + DSpaceObject canonicalItem = this.resolve(context, canonical); + if (canonicalItem == null) { restoreItAsCanonical(context, dso, identifier, item, canonical); } else { - restoreItAsVersion(context, dso, identifier, item, canonical, history); + VersionHistory history = versionHistoryService.findByItem(context, (Item) canonicalItem); + if (history == null) { + restoreItAsCanonical(context, dso, identifier, item, canonical); + } else { + restoreItAsVersion(context, dso, identifier, item, canonical, history); + } } - } - } else { - //A regular handle - createNewIdentifier(context, dso, identifier); - if (dso instanceof Item) { + } else { + // A regular handle to create for an Item + createNewIdentifier(context, dso, identifier); modifyHandleMetadata(context, item, getCanonical(identifier)); } + } else { + // Handle being registered for a different type of object (e.g. Collection or Community) + createNewIdentifier(context, dso, identifier); } } catch (IOException | SQLException | AuthorizeException e) { log.error(LogHelper.getHeader(context, From ffa2683c632cfab59b0432c203434ac15a6eb85f Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 13 Sep 2023 12:28:55 -0500 Subject: [PATCH 130/168] Fix checkstyle. Correct grammar of comment while doing so. --- ...ionedHandleIdentifierProviderWithCanonicalHandles.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandles.java b/dspace-api/src/main/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandles.java index bfa3313199..e6a092c472 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandles.java +++ b/dspace-api/src/main/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandles.java @@ -182,10 +182,10 @@ public class VersionedHandleIdentifierProviderWithCanonicalHandles extends Ident try { if (dso instanceof Item) { Item item = (Item) dso; - // if for this identifier is already present a record in the Handle table and the corresponding item - // has an history someone is trying to restore the latest version for the item. When - // trying to restore the latest version the identifier in input doesn't have the for 1234/123.latestVersion - // it is the canonical 1234/123 + // if this identifier is already present in the Handle table and the corresponding item + // has a history, then someone is trying to restore the latest version for the item. When + // trying to restore the latest version, the identifier in input doesn't have the + // 1234/123.latestVersion. Instead, it is the canonical 1234/123 VersionHistory itemHistory = getHistory(context, identifier); if (!identifier.matches(".*/.*\\.\\d+") && itemHistory != null) { From ef7f02fe81bc570353c0bf6a43706c77909e30e3 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 13 Sep 2023 16:56:29 -0500 Subject: [PATCH 131/168] Fix "Site cannot be indexed" error by ignoring ADD/REMOVE events on Site object --- .../main/java/org/dspace/discovery/IndexEventConsumer.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/IndexEventConsumer.java b/dspace-api/src/main/java/org/dspace/discovery/IndexEventConsumer.java index 4ff1f31344..bf1c7da4e1 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/IndexEventConsumer.java +++ b/dspace-api/src/main/java/org/dspace/discovery/IndexEventConsumer.java @@ -154,7 +154,11 @@ public class IndexEventConsumer implements Consumer { case Event.REMOVE: case Event.ADD: - if (object == null) { + // At this time, ADD and REMOVE actions are ignored on SITE object. They are only triggered for + // top-level communities. No action is necessary as Community itself is indexed (or deleted) separately. + if (event.getSubjectType() == Constants.SITE) { + log.debug(event.getEventTypeAsString() + " event triggered for Site object. Skipping it."); + } else if (object == null) { log.warn(event.getEventTypeAsString() + " event, could not get object for " + event.getObjectTypeAsString() + " id=" + event.getObjectID() From d17ef09082aa237cffdc928d9560667487c2c976 Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Thu, 14 Sep 2023 10:02:24 +0100 Subject: [PATCH 132/168] DefaultAccessStatusHelper: fix logic to take shortest embargo --- .../access/status/DefaultAccessStatusHelper.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java b/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java index 9b5227491b..05f0757060 100644 --- a/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java +++ b/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java @@ -175,7 +175,7 @@ public class DefaultAccessStatusHelper implements AccessStatusHelper { @Override public String getEmbargoFromItem(Context context, Item item) throws SQLException { - Date embargoDate; + Date embargoedDate; if (item == null) { return null; @@ -202,15 +202,15 @@ public class DefaultAccessStatusHelper implements AccessStatusHelper { return null; } - embargoDate = this.retrieveLongestEmbargo(context, bitstream); + embargoedDate = this.retrieveShortestEmbargo(context, bitstream); - return embargoDate != null ? embargoDate.toString() : null; + return embargoedDate != null ? embargoedDate.toString() : null; } /** * */ - private Date retrieveLongestEmbargo(Context context, Bitstream bitstream) throws SQLException { + private Date retrieveShortestEmbargo(Context context, Bitstream bitstream) throws SQLException { Date embargoDate = null; // Only consider read policies. List policies = authorizeService @@ -228,11 +228,12 @@ public class DefaultAccessStatusHelper implements AccessStatusHelper { Date startDate = policy.getStartDate(); if (startDate != null && !startDate.before(LocalDate.now().toDate())) { - // There is an active embargo: aim to take the longest embargo + // There is an active embargo: aim to take the shortest embargo (account for rare cases where + // more than one resource policy exists) if (embargoDate == null) { embargoDate = startDate; } else { - embargoDate = startDate.after(embargoDate) ? startDate : embargoDate; + embargoDate = startDate.before(embargoDate) ? startDate : embargoDate; } } } From 490a982e8055991a6b8cbacece22b924466e22df Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Thu, 14 Sep 2023 16:39:39 +0100 Subject: [PATCH 133/168] Remove currently unused customisation of ItemUtils --- .../java/org/dspace/xoai/util/ItemUtils.java | 54 ------------------- 1 file changed, 54 deletions(-) diff --git a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java index 80eb67a2b9..35bef8c8d7 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java @@ -11,8 +11,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; -import java.text.SimpleDateFormat; -import java.util.Date; import java.util.List; import com.lyncode.xoai.dataprovider.xml.xoai.Element; @@ -23,9 +21,6 @@ import org.apache.logging.log4j.Logger; import org.dspace.app.util.factory.UtilServiceFactory; import org.dspace.app.util.service.MetadataExposureService; import org.dspace.authorize.AuthorizeException; -import org.dspace.authorize.ResourcePolicy; -import org.dspace.authorize.factory.AuthorizeServiceFactory; -import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.Bitstream; import org.dspace.content.Bundle; import org.dspace.content.Item; @@ -39,9 +34,6 @@ import org.dspace.content.service.RelationshipService; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.core.Utils; -import org.dspace.eperson.Group; -import org.dspace.eperson.factory.EPersonServiceFactory; -import org.dspace.eperson.service.GroupService; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.xoai.data.DSpaceItem; @@ -65,9 +57,6 @@ public class ItemUtils { private static final BitstreamService bitstreamService = ContentServiceFactory.getInstance().getBitstreamService(); - private static final AuthorizeService authorizeService = - AuthorizeServiceFactory.getInstance().getAuthorizeService(); - private static final ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); /** @@ -152,9 +141,6 @@ public class ItemUtils { if (description != null) { bitstream.getField().add(createValue("description", description)); } - // Add bitstream embargo information (READ policy present, for Anonymous group with a start date) - addEmbargoField(context, bit, bitstream); - bitstream.getField().add(createValue("format", bit.getFormat(context).getMIMEType())); bitstream.getField().add(createValue("size", "" + bit.getSizeBytes())); bitstream.getField().add(createValue("url", url)); @@ -167,46 +153,6 @@ public class ItemUtils { return bundles; } - /** - * This method will add embargo metadata for a give bitstream with an active embargo. - * It will parse of relevant policies and select the longest active embargo - * @param context - * @param bitstream the bitstream object - * @param bitstreamEl the bitstream metadata object to add embargo value to - * @throws SQLException - */ - private static void addEmbargoField(Context context, Bitstream bitstream, Element bitstreamEl) throws SQLException { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - Group anonymousGroup = groupService.findByName(context, Group.ANONYMOUS); - List policies = authorizeService.findPoliciesByDSOAndType(context, - bitstream, - ResourcePolicy.TYPE_CUSTOM); - - Date embargoDate = null; - - // Account for cases where there could be more than one embargo policy - for (ResourcePolicy policy : policies) { - if (policy.getGroup() == anonymousGroup && policy.getAction() == Constants.READ) { - Date startDate = policy.getStartDate(); - if (startDate != null && startDate.after(new Date())) { - // There is an active embargo: aim to take the longest embargo - if (embargoDate == null) { - embargoDate = startDate; - } else { - embargoDate = startDate.after(embargoDate) ? startDate : embargoDate; - } - } - } - } - - if (embargoDate != null) { - // Sort array of dates to extract the longest embargo - SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); - bitstreamEl.getField().add( - createValue("embargo", formatter.format(embargoDate))); - } - } - private static Element createLicenseElement(Context context, Item item) throws SQLException, AuthorizeException, IOException { Element license = create("license"); From 51d8a7d9979b81ab42f712d4a37c1f8abdf9b039 Mon Sep 17 00:00:00 2001 From: nwoodward Date: Tue, 19 Sep 2023 15:08:42 -0500 Subject: [PATCH 134/168] remove optimize option from oai import --- .../src/main/java/org/dspace/xoai/app/XOAI.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java b/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java index e27a3ee947..4f842b8e94 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java @@ -85,7 +85,6 @@ public class XOAI { // needed because the solr query only returns 10 rows by default private final Context context; - private boolean optimize; private final boolean verbose; private boolean clean; @@ -122,9 +121,8 @@ public class XOAI { return formats; } - public XOAI(Context context, boolean optimize, boolean clean, boolean verbose) { + public XOAI(Context context, boolean clean, boolean verbose) { this.context = context; - this.optimize = optimize; this.clean = clean; this.verbose = verbose; @@ -173,12 +171,6 @@ public class XOAI { } solrServerResolver.getServer().commit(); - if (optimize) { - println("Optimizing Index"); - solrServerResolver.getServer().optimize(); - println("Index optimized"); - } - // Set last compilation date xoaiLastCompilationCacheService.put(new Date()); return result; @@ -586,7 +578,6 @@ public class XOAI { CommandLineParser parser = new DefaultParser(); Options options = new Options(); options.addOption("c", "clear", false, "Clear index before indexing"); - options.addOption("o", "optimize", false, "Optimize index at the end"); options.addOption("v", "verbose", false, "Verbose output"); options.addOption("h", "help", false, "Shows some help"); options.addOption("n", "number", true, "FOR DEVELOPMENT MUST DELETE"); @@ -620,7 +611,7 @@ public class XOAI { if (COMMAND_IMPORT.equals(command)) { ctx = new Context(Context.Mode.READ_ONLY); - XOAI indexer = new XOAI(ctx, line.hasOption('o'), line.hasOption('c'), line.hasOption('v')); + XOAI indexer = new XOAI(ctx, line.hasOption('c'), line.hasOption('v')); applicationContext.getAutowireCapableBeanFactory().autowireBean(indexer); @@ -706,7 +697,6 @@ public class XOAI { System.out.println(" " + COMMAND_IMPORT + " - To import DSpace items into OAI index and cache system"); System.out.println(" " + COMMAND_CLEAN_CACHE + " - Cleans the OAI cached responses"); System.out.println("> Parameters:"); - System.out.println(" -o Optimize index after indexing (" + COMMAND_IMPORT + " only)"); System.out.println(" -c Clear index (" + COMMAND_IMPORT + " only)"); System.out.println(" -v Verbose output"); System.out.println(" -h Shows this text"); From e05e73a112ce60bd0689ce68af442382712bd5fc Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Wed, 20 Sep 2023 16:26:14 +0100 Subject: [PATCH 135/168] DefaultAccessStatusHelper: getEmbargoFromItem return null embargo if status than embargo --- .../dspace/access/status/AccessStatusHelper.java | 4 +++- .../access/status/AccessStatusServiceImpl.java | 2 +- .../access/status/DefaultAccessStatusHelper.java | 13 ++++++++----- .../status/DefaultAccessStatusHelperTest.java | 6 +++--- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/access/status/AccessStatusHelper.java b/dspace-api/src/main/java/org/dspace/access/status/AccessStatusHelper.java index d847e907b4..2d782dc3b8 100644 --- a/dspace-api/src/main/java/org/dspace/access/status/AccessStatusHelper.java +++ b/dspace-api/src/main/java/org/dspace/access/status/AccessStatusHelper.java @@ -22,6 +22,7 @@ public interface AccessStatusHelper { * * @param context the DSpace context * @param item the item + * @param threshold the embargo threshold date * @return an access status value * @throws SQLException An exception that provides information on a database access error or other errors. */ @@ -33,8 +34,9 @@ public interface AccessStatusHelper { * * @param context the DSpace context * @param item the item to check for embargo information + * @param threshold the embargo threshold date * @return an embargo date * @throws SQLException An exception that provides information on a database access error or other errors. */ - public String getEmbargoFromItem(Context context, Item item) throws SQLException; + public String getEmbargoFromItem(Context context, Item item, Date threshold) throws SQLException; } diff --git a/dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java b/dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java index 1d4dc6088c..01b3707479 100644 --- a/dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java @@ -70,6 +70,6 @@ public class AccessStatusServiceImpl implements AccessStatusService { @Override public String getEmbargoFromItem(Context context, Item item) throws SQLException { - return helper.getEmbargoFromItem(context, item); + return helper.getEmbargoFromItem(context, item, forever_date); } } diff --git a/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java b/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java index 05f0757060..5f0e6d8b25 100644 --- a/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java +++ b/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java @@ -173,11 +173,14 @@ public class DefaultAccessStatusHelper implements AccessStatusHelper { * @return an access status value */ @Override - public String getEmbargoFromItem(Context context, Item item) + public String getEmbargoFromItem(Context context, Item item, Date threshold) throws SQLException { - Date embargoedDate; + Date embargoDate; - if (item == null) { + // If Item status is not "embargo" then return a null embargo date. + String accessStatus = getAccessStatusFromItem(context, item, threshold); + + if (item == null || !accessStatus.equals(EMBARGO)) { return null; } // Consider only the original bundles. @@ -202,9 +205,9 @@ public class DefaultAccessStatusHelper implements AccessStatusHelper { return null; } - embargoedDate = this.retrieveShortestEmbargo(context, bitstream); + embargoDate = this.retrieveShortestEmbargo(context, bitstream); - return embargoedDate != null ? embargoedDate.toString() : null; + return embargoDate != null ? embargoDate.toString() : null; } /** diff --git a/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java b/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java index c97349ac7c..51291ee985 100644 --- a/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java +++ b/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java @@ -274,7 +274,7 @@ public class DefaultAccessStatusHelperTest extends AbstractUnitTest { context.restoreAuthSystemState(); String status = helper.getAccessStatusFromItem(context, itemWithEmbargo, threshold); assertThat("testWithEmbargo 0", status, equalTo(DefaultAccessStatusHelper.EMBARGO)); - String embargoDate = helper.getEmbargoFromItem(context, itemWithEmbargo); + String embargoDate = helper.getEmbargoFromItem(context, itemWithEmbargo, threshold); assertThat("testWithEmbargo 1", embargoDate, equalTo(policy.getStartDate().toString())); } @@ -393,7 +393,7 @@ public class DefaultAccessStatusHelperTest extends AbstractUnitTest { context.restoreAuthSystemState(); String status = helper.getAccessStatusFromItem(context, itemWithPrimaryAndMultipleBitstreams, threshold); assertThat("testWithPrimaryAndMultipleBitstreams 0", status, equalTo(DefaultAccessStatusHelper.EMBARGO)); - String embargoDate = helper.getEmbargoFromItem(context, itemWithPrimaryAndMultipleBitstreams); + String embargoDate = helper.getEmbargoFromItem(context, itemWithPrimaryAndMultipleBitstreams, threshold); assertThat("testWithPrimaryAndMultipleBitstreams 1", embargoDate, equalTo(policy.getStartDate().toString())); } @@ -424,7 +424,7 @@ public class DefaultAccessStatusHelperTest extends AbstractUnitTest { context.restoreAuthSystemState(); String status = helper.getAccessStatusFromItem(context, itemWithoutPrimaryAndMultipleBitstreams, threshold); assertThat("testWithNoPrimaryAndMultipleBitstreams 0", status, equalTo(DefaultAccessStatusHelper.OPEN_ACCESS)); - String embargoDate = helper.getEmbargoFromItem(context, itemWithEmbargo); + String embargoDate = helper.getEmbargoFromItem(context, itemWithEmbargo, threshold); assertThat("testWithNoPrimaryAndMultipleBitstreams 1", embargoDate, equalTo(null)); } From 68cdb108e9becf35c2435f46f22a0045549684cc Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Mon, 2 Oct 2023 17:44:50 +1300 Subject: [PATCH 136/168] Additional Item class cast fixes in handle providers DSOs were not properly checked if they were instanceof Item before attempting the cast in HandleIdentifierProvider and VersionedHandleIdentifierProviderWithCanonicalHandles --- .../org/dspace/identifier/HandleIdentifierProvider.java | 5 ++--- ...rsionedHandleIdentifierProviderWithCanonicalHandles.java | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/HandleIdentifierProvider.java b/dspace-api/src/main/java/org/dspace/identifier/HandleIdentifierProvider.java index 59a1e13a21..82358362da 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/HandleIdentifierProvider.java +++ b/dspace-api/src/main/java/org/dspace/identifier/HandleIdentifierProvider.java @@ -68,10 +68,9 @@ public class HandleIdentifierProvider extends IdentifierProvider { try { String id = mint(context, dso); - // move canonical to point the latest version + // Populate metadata if (dso instanceof Item || dso instanceof Collection || dso instanceof Community) { - Item item = (Item) dso; - populateHandleMetadata(context, item, id); + populateHandleMetadata(context, dso, id); } return id; diff --git a/dspace-api/src/main/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandles.java b/dspace-api/src/main/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandles.java index e6a092c472..9993f78b4d 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandles.java +++ b/dspace-api/src/main/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandles.java @@ -95,11 +95,11 @@ public class VersionedHandleIdentifierProviderWithCanonicalHandles extends Ident String id = mint(context, dso); // move canonical to point the latest version - if (dso != null && dso.getType() == Constants.ITEM) { + if (dso.getType() == Constants.ITEM && dso instanceof Item) { Item item = (Item) dso; - VersionHistory history = null; + VersionHistory history; try { - history = versionHistoryService.findByItem(context, (Item) dso); + history = versionHistoryService.findByItem(context, item); } catch (SQLException ex) { throw new RuntimeException("A problem with the database connection occured.", ex); } From 5e04edf41e452cd383597680da9c3101211156b8 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 2 Oct 2023 10:55:43 -0500 Subject: [PATCH 137/168] Remove Oracle script that accidentally made it in via #8800 --- .../V7.6_2023.04.19__process_parameters_to_text_type.sql | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.04.19__process_parameters_to_text_type.sql diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.04.19__process_parameters_to_text_type.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.04.19__process_parameters_to_text_type.sql deleted file mode 100644 index 6b2dd705ea..0000000000 --- a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.04.19__process_parameters_to_text_type.sql +++ /dev/null @@ -1,9 +0,0 @@ --- --- The contents of this file are subject to the license and copyright --- detailed in the LICENSE and NOTICE files at the root of the source --- tree and available online at --- --- http://www.dspace.org/license/ --- - -ALTER TABLE process MODIFY (parameters CLOB); From db36d5eeae3e76b61178c2c7ac4243bc2fc20a97 Mon Sep 17 00:00:00 2001 From: aroman-arvo Date: Mon, 2 Oct 2023 18:00:09 +0200 Subject: [PATCH 138/168] 8968 - request-a-copy email: non ASCII characters are encoded as HTML character entity references --- .../dspace/app/rest/repository/RequestItemRepository.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java index 7c0694c52f..8a60867f9e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java @@ -173,11 +173,10 @@ public class RequestItemRepository username = user.getFullName(); } else { // An anonymous session may provide a name. // Escape username to evade nasty XSS attempts - username = StringEscapeUtils.escapeHtml4(rir.getRequestName()); + username = rir.getRequestName(); } - // Requester's message text, escaped to evade nasty XSS attempts - String message = StringEscapeUtils.escapeHtml4(rir.getRequestMessage()); + String message = rir.getRequestMessage(); // Create the request. String token; From bf6e042085140e305d43d61ddce564fbfe819c7f Mon Sep 17 00:00:00 2001 From: aroman-arvo Date: Mon, 2 Oct 2023 18:38:33 +0200 Subject: [PATCH 139/168] unused import --- .../org/dspace/app/rest/repository/RequestItemRepository.java | 1 - 1 file changed, 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java index 8a60867f9e..bc276d73d5 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java @@ -21,7 +21,6 @@ import javax.servlet.http.HttpServletRequest; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.commons.text.StringEscapeUtils; import org.apache.commons.validator.routines.EmailValidator; import org.apache.http.client.utils.URIBuilder; import org.apache.logging.log4j.LogManager; From 48b0b71c6301b6eb46c387c47b71d0729cc2f889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulo=20Gra=C3=A7a?= Date: Tue, 3 Oct 2023 16:52:15 +0100 Subject: [PATCH 140/168] add test and fix --- .../main/java/org/dspace/content/Bundle.java | 2 +- .../java/org/dspace/content/BundleTest.java | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) 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 6c62c3dc91..e5cbdb6ff2 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 class Bundle extends DSpaceObject implements DSpaceObjectLegacySupport { * Unset the primary bitstream ID of the bundle */ public void unsetPrimaryBitstreamID() { - primaryBitstream = null; + setPrimaryBitstreamID(null); } /** diff --git a/dspace-api/src/test/java/org/dspace/content/BundleTest.java b/dspace-api/src/test/java/org/dspace/content/BundleTest.java index 4ff35f5b4d..13b943b4d6 100644 --- a/dspace-api/src/test/java/org/dspace/content/BundleTest.java +++ b/dspace-api/src/test/java/org/dspace/content/BundleTest.java @@ -513,6 +513,38 @@ public class BundleTest extends AbstractDSpaceObjectTest { } + /** + * Test removeBitstream method and also the unsetPrimaryBitstreamID method, of class Bundle. + */ + @Test + public void testRemoveBitstreamAuthAndUnsetPrimaryBitstreamID() throws IOException, SQLException, AuthorizeException { + // Allow Item WRITE permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, item, Constants.WRITE); + // Allow Bundle ADD permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.ADD); + // Allow Bitstream WRITE permissions + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.WRITE)); + // Allow Bitstream DELETE permissions + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.DELETE)); + + + context.turnOffAuthorisationSystem(); + //set a value different than default + File f = new File(testProps.get("test.bitstream").toString()); + Bitstream bs = bitstreamService.create(context, new FileInputStream(f)); + bundleService.addBitstream(context, b, bs); + b.setPrimaryBitstreamID(bs); + context.restoreAuthSystemState(); + + assertThat("testRemoveBitstreamAuthAndUnsetPrimaryBitstreamID 0", b.getPrimaryBitstream(), equalTo(bs)); + //remove bitstream + bundleService.removeBitstream(context, b, bs); + //is -1 when not set + assertThat("testRemoveBitstreamAuthAndUnsetPrimaryBitstreamID 1", b.getPrimaryBitstream(), equalTo(null)); + } + /** * Test of update method, of class Bundle. */ From c15ac0eb4a3d39a0de47adbfa5260a6f3b396837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ad=C3=A1n=20Rom=C3=A1n=20Ruiz?= Date: Fri, 6 Oct 2023 10:04:41 +0200 Subject: [PATCH 141/168] #8585 Add submitter information to provenance metadata --- .../xmlworkflow/XmlWorkflowServiceImpl.java | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java index da7910da29..51292fd477 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java @@ -221,6 +221,8 @@ public class XmlWorkflowServiceImpl implements XmlWorkflowService { //Get our next step, if none is found, archive our item firstStep = wf.getNextStep(context, wfi, firstStep, ActionResult.OUTCOME_COMPLETE); if (firstStep == null) { + // record the submitted provenance message + recordStart(context, wfi.getItem(),null); archive(context, wfi); } else { activateFirstStep(context, wf, firstStep, wfi); @@ -334,7 +336,7 @@ public class XmlWorkflowServiceImpl implements XmlWorkflowService { + "item_id=" + wfi.getItem().getID() + "collection_id=" + wfi.getCollection().getID())); - // record the start of the workflow w/provenance message +// record the start of the workflow w/provenance message recordStart(context, wfi.getItem(), firstActionConfig.getProcessingAction()); //Fire an event ! @@ -1187,25 +1189,30 @@ public class XmlWorkflowServiceImpl implements XmlWorkflowService { DCDate now = DCDate.getCurrent(); // Create provenance description - String provmessage = ""; + StringBuffer provmessage = new StringBuffer(); if (myitem.getSubmitter() != null) { - provmessage = "Submitted by " + myitem.getSubmitter().getFullName() - + " (" + myitem.getSubmitter().getEmail() + ") on " - + now.toString() + " workflow start=" + action.getProvenanceStartId() + "\n"; + provmessage.append("Submitted by ").append(myitem.getSubmitter().getFullName()) + .append(" (").append(myitem.getSubmitter().getEmail()).append(") on ") + .append(now.toString()); } else { // else, null submitter - provmessage = "Submitted by unknown (probably automated) on" - + now.toString() + " workflow start=" + action.getProvenanceStartId() + "\n"; + provmessage.append("Submitted by unknown (probably automated) on") + .append(now.toString()); + } + if (action != null) { + provmessage.append(" workflow start=").append(action.getProvenanceStartId()).append("\n"); + } else { + provmessage.append("\n"); } // add sizes and checksums of bitstreams - provmessage += installItemService.getBitstreamProvenanceMessage(context, myitem); + provmessage.append(installItemService.getBitstreamProvenanceMessage(context, myitem)); // Add message to the DC itemService .addMetadata(context, myitem, MetadataSchemaEnum.DC.getName(), - "description", "provenance", "en", provmessage); + "description", "provenance", "en", provmessage.toString()); itemService.update(context, myitem); } From 103c8ee75771d3d9e58e530b8855d07cc14598c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ad=C3=A1n=20Rom=C3=A1n=20Ruiz?= Date: Fri, 6 Oct 2023 17:27:53 +0200 Subject: [PATCH 142/168] 8968 - added custom StringEscapper --- .../org/dspace/util/StringEscapeUtils.java | 49 +++++++++++++++++++ .../repository/RequestItemRepository.java | 6 ++- 2 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/util/StringEscapeUtils.java diff --git a/dspace-api/src/main/java/org/dspace/util/StringEscapeUtils.java b/dspace-api/src/main/java/org/dspace/util/StringEscapeUtils.java new file mode 100644 index 0000000000..86010a5c19 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/util/StringEscapeUtils.java @@ -0,0 +1,49 @@ +/** + * 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.util; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.text.translate.AggregateTranslator; +import org.apache.commons.text.translate.CharSequenceTranslator; +import org.apache.commons.text.translate.EntityArrays; +import org.apache.commons.text.translate.LookupTranslator; + +public class StringEscapeUtils extends org.apache.commons.text.StringEscapeUtils { + public static final CharSequenceTranslator ESCAPE_MAIL; + static { + final Map escapeMailMap = new HashMap<>(); + escapeMailMap.put("#", "#"); + ESCAPE_MAIL = new AggregateTranslator( + new LookupTranslator(EntityArrays.BASIC_ESCAPE), + new LookupTranslator(EntityArrays.APOS_ESCAPE), + new LookupTranslator(Collections.unmodifiableMap(escapeMailMap)) + ); + } + + /** + * Escapes the characters in a {@code String} using custom rules to avoid XSS attacks. + * + *

    Escapes user-entered text that is sent with mail to avoid possible XSS attacks. + * It escapes double-quote, ampersand, less-than, greater-than, apostrophe, number sign (", &, <, >,',#)

    + * + *

    Example:

    + *
    +     * input string: 
    lá lé lí ló LÚ pingüino & yo #
    !!" + * output string: <div attr="*x" onblur="alert(1)*"> lá lé lí ló LÚ pingüino & yo # </div>!! + *
    + * + * @param input String to escape values in, may be null + * @return String with escaped values, {@code null} if null string input + */ + public static final String escapeMail(final String input) { + return ESCAPE_MAIL.translate(input); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java index bc276d73d5..863a5c4146 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java @@ -42,6 +42,7 @@ import org.dspace.content.service.ItemService; import org.dspace.core.Context; import org.dspace.eperson.EPerson; import org.dspace.services.ConfigurationService; +import org.dspace.util.StringEscapeUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -172,10 +173,11 @@ public class RequestItemRepository username = user.getFullName(); } else { // An anonymous session may provide a name. // Escape username to evade nasty XSS attempts - username = rir.getRequestName(); + username = StringEscapeUtils.escapeMail(rir.getRequestName()); } - String message = rir.getRequestMessage(); + // Requester's message text, escaped to evade nasty XSS attempts + String message = StringEscapeUtils.escapeMail(rir.getRequestMessage()); // Create the request. String token; From 2c2b3b18dc781054539add48ca74e4bf688c400c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ad=C3=A1n=20Rom=C3=A1n=20Ruiz?= Date: Fri, 6 Oct 2023 17:42:14 +0200 Subject: [PATCH 143/168] checkstyle --- .../src/main/java/org/dspace/util/StringEscapeUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/util/StringEscapeUtils.java b/dspace-api/src/main/java/org/dspace/util/StringEscapeUtils.java index 86010a5c19..dfc89ca194 100644 --- a/dspace-api/src/main/java/org/dspace/util/StringEscapeUtils.java +++ b/dspace-api/src/main/java/org/dspace/util/StringEscapeUtils.java @@ -37,7 +37,8 @@ public class StringEscapeUtils extends org.apache.commons.text.StringEscapeUtils *

    Example:

    *
          * input string: 
    lá lé lí ló LÚ pingüino & yo #
    !!" - * output string: <div attr="*x" onblur="alert(1)*"> lá lé lí ló LÚ pingüino & yo # </div>!! + * output string: <div attr="*x" onblur="alert(1)*"> lá lé lí ló LÚ + * pingüino & yo # </div>!! *
    * * @param input String to escape values in, may be null From 090beedb6f692df29d1a61d4c2e6fde09d4b4c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ad=C3=A1n=20Rom=C3=A1n=20Ruiz?= Date: Mon, 9 Oct 2023 18:01:46 +0200 Subject: [PATCH 144/168] 8968 - implementated using HtmlUtils scaping --- .../org/dspace/util/StringEscapeUtils.java | 50 ------------------- .../repository/RequestItemRepository.java | 8 +-- 2 files changed, 4 insertions(+), 54 deletions(-) delete mode 100644 dspace-api/src/main/java/org/dspace/util/StringEscapeUtils.java diff --git a/dspace-api/src/main/java/org/dspace/util/StringEscapeUtils.java b/dspace-api/src/main/java/org/dspace/util/StringEscapeUtils.java deleted file mode 100644 index dfc89ca194..0000000000 --- a/dspace-api/src/main/java/org/dspace/util/StringEscapeUtils.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE and NOTICE files at the root of the source - * tree and available online at - * - * http://www.dspace.org/license/ - */ -package org.dspace.util; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import org.apache.commons.text.translate.AggregateTranslator; -import org.apache.commons.text.translate.CharSequenceTranslator; -import org.apache.commons.text.translate.EntityArrays; -import org.apache.commons.text.translate.LookupTranslator; - -public class StringEscapeUtils extends org.apache.commons.text.StringEscapeUtils { - public static final CharSequenceTranslator ESCAPE_MAIL; - static { - final Map escapeMailMap = new HashMap<>(); - escapeMailMap.put("#", "#"); - ESCAPE_MAIL = new AggregateTranslator( - new LookupTranslator(EntityArrays.BASIC_ESCAPE), - new LookupTranslator(EntityArrays.APOS_ESCAPE), - new LookupTranslator(Collections.unmodifiableMap(escapeMailMap)) - ); - } - - /** - * Escapes the characters in a {@code String} using custom rules to avoid XSS attacks. - * - *

    Escapes user-entered text that is sent with mail to avoid possible XSS attacks. - * It escapes double-quote, ampersand, less-than, greater-than, apostrophe, number sign (", &, <, >,',#)

    - * - *

    Example:

    - *
    -     * input string: 
    lá lé lí ló LÚ pingüino & yo #
    !!" - * output string: <div attr="*x" onblur="alert(1)*"> lá lé lí ló LÚ - * pingüino & yo # </div>!! - *
    - * - * @param input String to escape values in, may be null - * @return String with escaped values, {@code null} if null string input - */ - public static final String escapeMail(final String input) { - return ESCAPE_MAIL.translate(input); - } -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java index 863a5c4146..945afe16e8 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java @@ -42,13 +42,13 @@ import org.dspace.content.service.ItemService; import org.dspace.core.Context; import org.dspace.eperson.EPerson; import org.dspace.services.ConfigurationService; -import org.dspace.util.StringEscapeUtils; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Component; - +import org.springframework.web.util.HtmlUtils; /** * Component to expose item requests. * @@ -173,11 +173,11 @@ public class RequestItemRepository username = user.getFullName(); } else { // An anonymous session may provide a name. // Escape username to evade nasty XSS attempts - username = StringEscapeUtils.escapeMail(rir.getRequestName()); + username = HtmlUtils.htmlEscape(rir.getRequestName(),"UTF-8"); } // Requester's message text, escaped to evade nasty XSS attempts - String message = StringEscapeUtils.escapeMail(rir.getRequestMessage()); + String message = HtmlUtils.htmlEscape(rir.getRequestMessage(),"UTF-8"); // Create the request. String token; From d12fbe2c340e18e42dba4380ee9976bccb4ca421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ad=C3=A1n=20Rom=C3=A1n=20Ruiz?= Date: Mon, 9 Oct 2023 18:18:35 +0200 Subject: [PATCH 145/168] checkstiye --- .../org/dspace/app/rest/repository/RequestItemRepository.java | 1 - 1 file changed, 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java index 945afe16e8..f45dbee66f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java @@ -42,7 +42,6 @@ import org.dspace.content.service.ItemService; import org.dspace.core.Context; import org.dspace.eperson.EPerson; import org.dspace.services.ConfigurationService; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; From a1248074681a7bc4603176fb3e7d989b91edcbcd Mon Sep 17 00:00:00 2001 From: "Gantner, Florian Klaus" Date: Tue, 10 Oct 2023 16:19:11 +0200 Subject: [PATCH 146/168] quote Pattern for thumbnail resolution constructed from bitstream filename --- .../dspace/content/BitstreamServiceImpl.java | 2 +- .../app/rest/BitstreamRestRepositoryIT.java | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) 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 cc89cea33a..7433338ad9 100644 --- a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java @@ -403,7 +403,7 @@ public class BitstreamServiceImpl extends DSpaceObjectServiceImpl imp @Override public Bitstream getThumbnail(Context context, Bitstream bitstream) throws SQLException { - Pattern pattern = Pattern.compile("^" + bitstream.getName() + ".([^.]+)$"); + Pattern pattern = Pattern.compile("^" + Pattern.quote(bitstream.getName()) + ".([^.]+)$"); for (Bundle bundle : bitstream.getBundles()) { for (Item item : bundle.getItems()) { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java index bc276557df..864acf1a56 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java @@ -1695,6 +1695,53 @@ public class BitstreamRestRepositoryIT extends AbstractControllerIntegrationTest .andExpect(jsonPath("$.type", is("bitstream"))); } + @Test + public void thumbnailEndpointTestWithSpecialCharactersInFileName() throws Exception { + // Given an Item + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Collection col1 = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection 1").build(); + + Item item = ItemBuilder.createItem(context, col1) + .withTitle("Test item -- thumbnail") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .build(); + + Bundle originalBundle = BundleBuilder.createBundle(context, item) + .withName(Constants.DEFAULT_BUNDLE_NAME) + .build(); + Bundle thumbnailBundle = BundleBuilder.createBundle(context, item) + .withName("THUMBNAIL") + .build(); + + InputStream is = IOUtils.toInputStream("dummy", "utf-8"); + + // With an ORIGINAL Bitstream & matching THUMBNAIL Bitstream containing special characters in filenames + Bitstream bitstream = BitstreamBuilder.createBitstream(context, originalBundle, is) + .withName("test (2023) file.pdf") + .withMimeType("application/pdf") + .build(); + Bitstream thumbnail = BitstreamBuilder.createBitstream(context, thumbnailBundle, is) + .withName("test (2023) file.pdf.jpg") + .withMimeType("image/jpeg") + .build(); + + context.restoreAuthSystemState(); + + String tokenAdmin = getAuthToken(admin.getEmail(), password); + + getClient(tokenAdmin).perform(get("/api/core/bitstreams/" + bitstream.getID() + "/thumbnail")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.uuid", Matchers.is(thumbnail.getID().toString()))) + .andExpect(jsonPath("$.type", is("bitstream"))); + } + @Test public void thumbnailEndpointMultipleThumbnailsWithPrimaryBitstreamTest() throws Exception { // Given an Item From 94ee9d04033c6f15abdea035a0650177af1a52af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Oct 2023 21:19:14 +0000 Subject: [PATCH 147/168] Bump org.eclipse.jetty:jetty-http Bumps [org.eclipse.jetty:jetty-http](https://github.com/eclipse/jetty.project) from 9.4.52.v20230823 to 9.4.53.v20231009. - [Release notes](https://github.com/eclipse/jetty.project/releases) - [Commits](https://github.com/eclipse/jetty.project/compare/jetty-9.4.52.v20230823...jetty-9.4.53.v20231009) --- updated-dependencies: - dependency-name: org.eclipse.jetty:jetty-http dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index aeb15fbefa..6feb81c884 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ 2.3.8 1.1.1 - 9.4.52.v20230823 + 9.4.53.v20231009 2.20.0 2.0.29 1.19.0 From 669ff343503539aa6fc8b23600989ab958a403b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie-H=C3=A9l=C3=A8ne=20V=C3=A9zina?= Date: Wed, 11 Oct 2023 09:49:35 -0400 Subject: [PATCH 148/168] oai_openaire.xsl : change resourceTypeGeneral for thesis Thesis are "Literature" resource type (resourceTypeGeneral), not "other research product" ref: https://github.com/openaire/guidelines-literature-repositories/issues/43#issuecomment-1318262914 and https://api.openaire.eu/vocabularies/dnet:result_typologies/publication --- .../crosswalks/oai/metadataFormats/oai_openaire.xsl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl index 3a1d75eb56..16c63c9c1a 100644 --- a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl +++ b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl @@ -1432,6 +1432,18 @@ literature + + literature + + + literature + + + literature + + + literature + dataset From a9bcc0c223d0219f464d986d7b7c66b3c4cbc39c Mon Sep 17 00:00:00 2001 From: "Gantner, Florian Klaus" Date: Thu, 12 Oct 2023 17:58:13 +0200 Subject: [PATCH 149/168] check null value of bitstream name before quoting name for regex --- .../main/java/org/dspace/content/BitstreamServiceImpl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 7433338ad9..92acce6765 100644 --- a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java @@ -403,7 +403,9 @@ public class BitstreamServiceImpl extends DSpaceObjectServiceImpl imp @Override public Bitstream getThumbnail(Context context, Bitstream bitstream) throws SQLException { - Pattern pattern = Pattern.compile("^" + Pattern.quote(bitstream.getName()) + ".([^.]+)$"); + Pattern pattern = Pattern.compile("^" + + (bitstream.getName() != null ? Pattern.quote(bitstream.getName()) : bitstream.getName()) + + ".([^.]+)$"); for (Bundle bundle : bitstream.getBundles()) { for (Item item : bundle.getItems()) { From f25b6d479bad15bbd2353c877286a2245c5d6543 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Thu, 12 Oct 2023 16:13:28 -0400 Subject: [PATCH 150/168] Define required _version_ field and its fieldType. --- dspace/solr/authority/conf/schema.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dspace/solr/authority/conf/schema.xml b/dspace/solr/authority/conf/schema.xml index 6c32819302..511dbabd47 100644 --- a/dspace/solr/authority/conf/schema.xml +++ b/dspace/solr/authority/conf/schema.xml @@ -87,9 +87,20 @@ + + + From ea6307dcc68a75c935049a02022145691693cff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ad=C3=A1n=20Rom=C3=A1n=20Ruiz?= Date: Mon, 16 Oct 2023 09:33:54 +0200 Subject: [PATCH 151/168] 8585 - added provenance to metadata-import and itemImport --- .../dspace/app/bulkedit/MetadataImport.java | 4 ++++ .../app/itemimport/ItemImportServiceImpl.java | 4 ++++ .../content/InstallItemServiceImpl.java | 24 +++++++++++++++++++ .../content/service/InstallItemService.java | 11 +++++++++ 4 files changed, 43 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImport.java b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImport.java index 4161bbb4d8..983843a1dc 100644 --- a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImport.java +++ b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImport.java @@ -578,6 +578,10 @@ public class MetadataImport extends DSpaceRunnable Date: Tue, 17 Oct 2023 11:34:04 +0300 Subject: [PATCH 152/168] dspace-api: fix misaligned comment --- .../java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java index 51292fd477..bc91a1fd92 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java @@ -336,7 +336,7 @@ public class XmlWorkflowServiceImpl implements XmlWorkflowService { + "item_id=" + wfi.getItem().getID() + "collection_id=" + wfi.getCollection().getID())); -// record the start of the workflow w/provenance message + // record the start of the workflow w/provenance message recordStart(context, wfi.getItem(), firstActionConfig.getProcessingAction()); //Fire an event ! From 47ca74bc4220249b95de9b8e71186277c9ac31ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulo=20Gra=C3=A7a?= Date: Thu, 19 Oct 2023 08:58:08 +0100 Subject: [PATCH 153/168] unset primary bitstream on bitstream service --- .../main/java/org/dspace/content/BitstreamServiceImpl.java | 4 ++++ 1 file changed, 4 insertions(+) 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 cc89cea33a..77f10880ea 100644 --- a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java @@ -276,6 +276,10 @@ public class BitstreamServiceImpl extends DSpaceObjectServiceImpl imp //Remove our bitstream from all our bundles final List bundles = bitstream.getBundles(); for (Bundle bundle : bundles) { + //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); } From 8a531ad0c7e8fdf09fa9a3870024687e6708a9a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulo=20Gra=C3=A7a?= Date: Thu, 19 Oct 2023 09:38:01 +0100 Subject: [PATCH 154/168] adding sql expression to fix deleted primary bitstreams from bundle --- ....10.12__Fix-deleted-primary-bitstreams.sql | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.10.12__Fix-deleted-primary-bitstreams.sql diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.10.12__Fix-deleted-primary-bitstreams.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.10.12__Fix-deleted-primary-bitstreams.sql new file mode 100644 index 0000000000..b1739dbd96 --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.10.12__Fix-deleted-primary-bitstreams.sql @@ -0,0 +1,26 @@ +BEGIN; + +-- Remove all primary bitstreams that are marked as deleted +UPDATE bundle +SET primary_bitstream_id = NULL +WHERE primary_bitstream_id IN + ( SELECT bs.uuid + FROM bitstream AS bs + INNER JOIN bundle as bl ON bs.uuid = bl.primary_bitstream_id + WHERE bs.deleted IS TRUE ); + +-- Remove all primary bitstreams that don't make part on bundle's bitstreams +UPDATE bundle +SET primary_bitstream_id = NULL +WHERE primary_bitstream_id IN + ( SELECT bl.primary_bitstream_id + FROM bundle as bl + WHERE bl.primary_bitstream_id IS NOT NULL + AND bl.primary_bitstream_id NOT IN + ( SELECT bitstream_id + FROM bundle2bitstream AS b2b + WHERE b2b.bundle_id = bl.uuid + ) + ); + +COMMIT; \ No newline at end of file From 3255e073fa110a3354f1265853bbf531c677f6ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulo=20Gra=C3=A7a?= Date: Thu, 19 Oct 2023 09:58:24 +0100 Subject: [PATCH 155/168] add bundle remove authorization --- .../src/main/java/org/dspace/content/BitstreamServiceImpl.java | 1 + 1 file changed, 1 insertion(+) 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 77f10880ea..5391dcf389 100644 --- a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java @@ -276,6 +276,7 @@ public class BitstreamServiceImpl extends DSpaceObjectServiceImpl imp //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(); From 4a05600194fb9be7e19084f3a9106a0152fd0d80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulo=20Gra=C3=A7a?= Date: Thu, 19 Oct 2023 10:16:38 +0100 Subject: [PATCH 156/168] adding missing bundle REMOVE authorization --- dspace-api/src/test/java/org/dspace/content/BundleTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dspace-api/src/test/java/org/dspace/content/BundleTest.java b/dspace-api/src/test/java/org/dspace/content/BundleTest.java index 13b943b4d6..851d8267ea 100644 --- a/dspace-api/src/test/java/org/dspace/content/BundleTest.java +++ b/dspace-api/src/test/java/org/dspace/content/BundleTest.java @@ -522,6 +522,8 @@ public class BundleTest extends AbstractDSpaceObjectTest { doNothing().when(authorizeServiceSpy).authorizeAction(context, item, Constants.WRITE); // Allow Bundle ADD permissions doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.ADD); + // Allow Bundle REMOVE permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.REMOVE); // Allow Bitstream WRITE permissions doNothing().when(authorizeServiceSpy) .authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.WRITE)); From caba4bbb96f56c103c4dd8ac9f9fa5863b40e04c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulo=20Gra=C3=A7a?= Date: Thu, 19 Oct 2023 11:16:26 +0100 Subject: [PATCH 157/168] add missing head style check --- .../V7.6_2023.10.12__Fix-deleted-primary-bitstreams.sql | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.10.12__Fix-deleted-primary-bitstreams.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.10.12__Fix-deleted-primary-bitstreams.sql index b1739dbd96..c97d224657 100644 --- a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.10.12__Fix-deleted-primary-bitstreams.sql +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.10.12__Fix-deleted-primary-bitstreams.sql @@ -1,3 +1,11 @@ +-- +-- 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/ +-- + BEGIN; -- Remove all primary bitstreams that are marked as deleted From 74605f159af5e53a3e890f578732a858cef12e51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulo=20Gra=C3=A7a?= Date: Thu, 19 Oct 2023 11:42:58 +0100 Subject: [PATCH 158/168] fix style errors --- .../src/main/java/org/dspace/content/BitstreamServiceImpl.java | 2 +- dspace-api/src/test/java/org/dspace/content/BundleTest.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) 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 5391dcf389..2746812c1c 100644 --- a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java @@ -278,7 +278,7 @@ public class BitstreamServiceImpl extends DSpaceObjectServiceImpl imp 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())) { + if (bitstream.equals(bundle.getPrimaryBitstream())) { bundle.unsetPrimaryBitstreamID(); } bundle.removeBitstream(bitstream); diff --git a/dspace-api/src/test/java/org/dspace/content/BundleTest.java b/dspace-api/src/test/java/org/dspace/content/BundleTest.java index 851d8267ea..4af64b81cb 100644 --- a/dspace-api/src/test/java/org/dspace/content/BundleTest.java +++ b/dspace-api/src/test/java/org/dspace/content/BundleTest.java @@ -517,7 +517,8 @@ public class BundleTest extends AbstractDSpaceObjectTest { * Test removeBitstream method and also the unsetPrimaryBitstreamID method, of class Bundle. */ @Test - public void testRemoveBitstreamAuthAndUnsetPrimaryBitstreamID() throws IOException, SQLException, AuthorizeException { + public void testRemoveBitstreamAuthAndUnsetPrimaryBitstreamID() + throws IOException, SQLException, AuthorizeException { // Allow Item WRITE permissions doNothing().when(authorizeServiceSpy).authorizeAction(context, item, Constants.WRITE); // Allow Bundle ADD permissions From c5466c2249c092f6638a7072b57c934d1d3581b5 Mon Sep 17 00:00:00 2001 From: "Gantner, Florian Klaus" Date: Thu, 19 Oct 2023 15:44:03 +0200 Subject: [PATCH 159/168] extract bitstream thumbnail name pattern into own function --- .../java/org/dspace/content/BitstreamServiceImpl.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) 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 92acce6765..1653266056 100644 --- a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java @@ -403,9 +403,7 @@ public class BitstreamServiceImpl extends DSpaceObjectServiceImpl imp @Override public Bitstream getThumbnail(Context context, Bitstream bitstream) throws SQLException { - Pattern pattern = Pattern.compile("^" + - (bitstream.getName() != null ? Pattern.quote(bitstream.getName()) : bitstream.getName()) - + ".([^.]+)$"); + Pattern pattern = getBitstreamNamePattern(bitstream); for (Bundle bundle : bitstream.getBundles()) { for (Item item : bundle.getItems()) { @@ -422,6 +420,13 @@ public class BitstreamServiceImpl extends DSpaceObjectServiceImpl imp 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) { From 11a08f1ac0a9b75bf3f2869d3760b2f0e229aefe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Oct 2023 21:57:32 +0000 Subject: [PATCH 160/168] Bump org.json:json from 20230227 to 20231013 in /dspace-api Bumps [org.json:json](https://github.com/douglascrockford/JSON-java) from 20230227 to 20231013. - [Release notes](https://github.com/douglascrockford/JSON-java/releases) - [Changelog](https://github.com/stleary/JSON-java/blob/master/docs/RELEASES.md) - [Commits](https://github.com/douglascrockford/JSON-java/commits) --- updated-dependencies: - dependency-name: org.json:json dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 546cbf01f3..547be787e4 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -769,7 +769,7 @@ org.json json - 20230227 + 20231013 From bb6498ed5e4696201d3e45bd377faa407dca277f Mon Sep 17 00:00:00 2001 From: wwuck Date: Sat, 28 Oct 2023 00:32:54 +1100 Subject: [PATCH 161/168] Add a null check when assigning ldap groups Prevent NullReferenceException by checking if the group list is null Fixes #8920 --- .../authenticate/LDAPAuthentication.java | 99 ++++++++++++------- 1 file changed, 61 insertions(+), 38 deletions(-) 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 afd82db863..aced16876d 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java @@ -713,8 +713,8 @@ public class LDAPAuthentication 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; @@ -725,52 +725,75 @@ public class LDAPAuthentication String ldapSearchString = t[0]; String dspaceGroupName = t[1]; - // list of strings with dn from LDAP groups - // inner loop - Iterator groupIterator = group.iterator(); - while (groupIterator.hasNext()) { - - // 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 { - cmp = StringUtils.equalsIgnoreCase(currentGroup, ldapSearchString); - } + if (group == null) { + cmp = StringUtils.containsIgnoreCase(dn, ldapSearchString + ","); 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); - } 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)); - } - } 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)); + assignGroup(context, groupmapIndex, dspaceGroupName); + } + } else { + // list of strings with dn from LDAP groups + // inner loop + Iterator groupIterator = group.iterator(); + while (groupIterator.hasNext()) { + + // 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 { + cmp = StringUtils.equalsIgnoreCase(currentGroup, ldapSearchString); + } + + if (cmp) { + assignGroup(context, groupmapIndex, 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 && From 3a9560ee15c5e36afae261c8df1f1d6e890d558a Mon Sep 17 00:00:00 2001 From: Sean Kalynuk Date: Wed, 26 Jul 2023 11:27:32 -0500 Subject: [PATCH 162/168] Fixes #8558 - set Solr UTC timezone Set the timezone of the Solr date formatter to UTC (cherry picked from commit 4c329b43193a3bea151bdf9af27b663affcf7246) --- dspace-api/src/main/java/org/dspace/util/SolrUtils.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/util/SolrUtils.java b/dspace-api/src/main/java/org/dspace/util/SolrUtils.java index f62feba298..7b11d73834 100644 --- a/dspace-api/src/main/java/org/dspace/util/SolrUtils.java +++ b/dspace-api/src/main/java/org/dspace/util/SolrUtils.java @@ -35,6 +35,8 @@ public class SolrUtils { * @return date formatter compatible with Solr. */ public static DateFormat getDateFormatter() { - return new SimpleDateFormat(SolrUtils.SOLR_DATE_FORMAT); + DateFormat formatter = new SimpleDateFormat(SolrUtils.SOLR_DATE_FORMAT); + formatter.setTimeZone(SOLR_TIME_ZONE); + return formatter; } } From e6d108a94e41e58d6d701f3ef0429fda438e6555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulo=20Gra=C3=A7a?= Date: Mon, 30 Oct 2023 11:27:18 +0000 Subject: [PATCH 163/168] new testDeleteBitstreamAndUnsetPrimaryBitstreamID test for primary bitstream verification --- .../org/dspace/content/BitstreamTest.java | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/dspace-api/src/test/java/org/dspace/content/BitstreamTest.java b/dspace-api/src/test/java/org/dspace/content/BitstreamTest.java index 921e4efcc7..30ef5f37fb 100644 --- a/dspace-api/src/test/java/org/dspace/content/BitstreamTest.java +++ b/dspace-api/src/test/java/org/dspace/content/BitstreamTest.java @@ -432,6 +432,55 @@ public class BitstreamTest extends AbstractDSpaceObjectTest { assertThat("testExpunge 0", bitstreamService.find(context, bitstreamId), nullValue()); } + /** + * Test of delete method, of class Bitstream. + */ + @Test + public void testDeleteBitstreamAndUnsetPrimaryBitstreamID() + throws IOException, SQLException, AuthorizeException { + + context.turnOffAuthorisationSystem(); + + Community owningCommunity = communityService.create(null, context); + Collection collection = collectionService.create(context, owningCommunity); + WorkspaceItem workspaceItem = workspaceItemService.create(context, collection, false); + Item item = installItemService.installItem(context, workspaceItem); + Bundle b = bundleService.create(context, item, "TESTBUNDLE"); + + // Allow Item WRITE permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, item, Constants.WRITE); + // Allow Bundle ADD permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.ADD); + // Allow Bundle REMOVE permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.REMOVE); + // Allow Bitstream WRITE permissions + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.WRITE)); + // Allow Bitstream DELETE permissions + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.DELETE)); + + //set a value different than default + File f = new File(testProps.get("test.bitstream").toString()); + + // Create a new bitstream, which we can delete. + Bitstream bs = bitstreamService.create(context, new FileInputStream(f)); + bundleService.addBitstream(context, b, bs); + // set primary bitstream + b.setPrimaryBitstreamID(bs); + context.restoreAuthSystemState(); + + // Test that delete will flag the bitstream as deleted + assertFalse("testDeleteBitstreamAndUnsetPrimaryBitstreamID 0", bs.isDeleted()); + assertThat("testDeleteBitstreamAndUnsetPrimaryBitstreamID 1", b.getPrimaryBitstream(), equalTo(bs)); + // Delete bitstream + bitstreamService.delete(context, bs); + assertTrue("testDeleteBitstreamAndUnsetPrimaryBitstreamID 2", bs.isDeleted()); + + // Now test if the primary bitstream was unset from bundle + assertThat("testDeleteBitstreamAndUnsetPrimaryBitstreamID 3", b.getPrimaryBitstream(), equalTo(null)); + } + /** * Test of retrieve method, of class Bitstream. */ From ad0d22a13a35a2167557efeb5ddea7a3a504424d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulo=20Gra=C3=A7a?= Date: Mon, 30 Oct 2023 11:45:12 +0000 Subject: [PATCH 164/168] new testDeleteBitstreamAndUnsetPrimaryBitstreamID test for primary bitstream verification --- .../java/org/dspace/content/BitstreamTest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/content/BitstreamTest.java b/dspace-api/src/test/java/org/dspace/content/BitstreamTest.java index 30ef5f37fb..eb3de96d2f 100644 --- a/dspace-api/src/test/java/org/dspace/content/BitstreamTest.java +++ b/dspace-api/src/test/java/org/dspace/content/BitstreamTest.java @@ -464,18 +464,18 @@ public class BitstreamTest extends AbstractDSpaceObjectTest { File f = new File(testProps.get("test.bitstream").toString()); // Create a new bitstream, which we can delete. - Bitstream bs = bitstreamService.create(context, new FileInputStream(f)); - bundleService.addBitstream(context, b, bs); + Bitstream delBS = bitstreamService.create(context, new FileInputStream(f)); + bundleService.addBitstream(context, b, delBS); // set primary bitstream - b.setPrimaryBitstreamID(bs); + b.setPrimaryBitstreamID(delBS); context.restoreAuthSystemState(); // Test that delete will flag the bitstream as deleted - assertFalse("testDeleteBitstreamAndUnsetPrimaryBitstreamID 0", bs.isDeleted()); - assertThat("testDeleteBitstreamAndUnsetPrimaryBitstreamID 1", b.getPrimaryBitstream(), equalTo(bs)); + assertFalse("testDeleteBitstreamAndUnsetPrimaryBitstreamID 0", delBS.isDeleted()); + assertThat("testDeleteBitstreamAndUnsetPrimaryBitstreamID 1", b.getPrimaryBitstream(), equalTo(delBS)); // Delete bitstream - bitstreamService.delete(context, bs); - assertTrue("testDeleteBitstreamAndUnsetPrimaryBitstreamID 2", bs.isDeleted()); + bitstreamService.delete(context, delBS); + assertTrue("testDeleteBitstreamAndUnsetPrimaryBitstreamID 2", delBS.isDeleted()); // Now test if the primary bitstream was unset from bundle assertThat("testDeleteBitstreamAndUnsetPrimaryBitstreamID 3", b.getPrimaryBitstream(), equalTo(null)); From a3e506c7f452133e3cc973705d671dba61a469d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulo=20Gra=C3=A7a?= Date: Mon, 30 Oct 2023 13:08:53 +0000 Subject: [PATCH 165/168] new testDeleteBitstreamAndUnsetPrimaryBitstreamID remove unnecessary stubs --- .../src/test/java/org/dspace/content/BitstreamTest.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/content/BitstreamTest.java b/dspace-api/src/test/java/org/dspace/content/BitstreamTest.java index eb3de96d2f..e85a0fc7b7 100644 --- a/dspace-api/src/test/java/org/dspace/content/BitstreamTest.java +++ b/dspace-api/src/test/java/org/dspace/content/BitstreamTest.java @@ -447,10 +447,6 @@ public class BitstreamTest extends AbstractDSpaceObjectTest { Item item = installItemService.installItem(context, workspaceItem); Bundle b = bundleService.create(context, item, "TESTBUNDLE"); - // Allow Item WRITE permissions - doNothing().when(authorizeServiceSpy).authorizeAction(context, item, Constants.WRITE); - // Allow Bundle ADD permissions - doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.ADD); // Allow Bundle REMOVE permissions doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.REMOVE); // Allow Bitstream WRITE permissions From 160ebbd791c0545db6516403da40cb191a2c8b99 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 30 Oct 2023 15:13:39 -0500 Subject: [PATCH 166/168] Update to newly released XOAI 3.4.0 --- dspace-oai/pom.xml | 39 ++++----------------------------------- 1 file changed, 4 insertions(+), 35 deletions(-) diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 59cee28293..b900ebe88d 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -15,7 +15,7 @@ ${basedir}/.. - 3.3.1-SNAPSHOT + 3.4.0 5.87.0.RELEASE @@ -55,41 +55,10 @@ xoai ${xoai.version} + - org.hamcrest - hamcrest-all - - - - org.mockito - mockito-all - - - org.apache.commons - commons-lang3 - - - log4j - log4j - - - org.slf4j - slf4j-log4j12 - - - - org.codehaus.woodstox - wstx-asl - - - - org.dom4j - dom4j - - - - com.lyncode - test-support + com.fasterxml.woodstox + woodstox-core
    From c0bbd9d91f894fbe26f8cf7c4f166da8ba1cefd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulo=20Gra=C3=A7a?= Date: Mon, 30 Oct 2023 22:48:49 +0000 Subject: [PATCH 167/168] make comments more clear to understand --- .../V7.6_2023.10.12__Fix-deleted-primary-bitstreams.sql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.10.12__Fix-deleted-primary-bitstreams.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.10.12__Fix-deleted-primary-bitstreams.sql index c97d224657..7a0bae1582 100644 --- a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.10.12__Fix-deleted-primary-bitstreams.sql +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.10.12__Fix-deleted-primary-bitstreams.sql @@ -8,7 +8,7 @@ BEGIN; --- Remove all primary bitstreams that are marked as deleted +-- Unset any primary bitstream that is marked as deleted UPDATE bundle SET primary_bitstream_id = NULL WHERE primary_bitstream_id IN @@ -17,7 +17,7 @@ WHERE primary_bitstream_id IN INNER JOIN bundle as bl ON bs.uuid = bl.primary_bitstream_id WHERE bs.deleted IS TRUE ); --- Remove all primary bitstreams that don't make part on bundle's bitstreams +-- Unset any primary bitstream that don't belong to bundle's bitstreams list UPDATE bundle SET primary_bitstream_id = NULL WHERE primary_bitstream_id IN @@ -31,4 +31,4 @@ WHERE primary_bitstream_id IN ) ); -COMMIT; \ No newline at end of file +COMMIT; From 74cce86afcc163c52502892556679e6175fa1948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulo=20Gra=C3=A7a?= Date: Mon, 30 Oct 2023 22:49:31 +0000 Subject: [PATCH 168/168] typo --- .../V7.6_2023.10.12__Fix-deleted-primary-bitstreams.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.10.12__Fix-deleted-primary-bitstreams.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.10.12__Fix-deleted-primary-bitstreams.sql index 7a0bae1582..9dd2f54a43 100644 --- a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.10.12__Fix-deleted-primary-bitstreams.sql +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.10.12__Fix-deleted-primary-bitstreams.sql @@ -17,7 +17,7 @@ WHERE primary_bitstream_id IN INNER JOIN bundle as bl ON bs.uuid = bl.primary_bitstream_id WHERE bs.deleted IS TRUE ); --- Unset any primary bitstream that don't belong to bundle's bitstreams list +-- Unset any primary bitstream that don't belong to bundle's bitstream list UPDATE bundle SET primary_bitstream_id = NULL WHERE primary_bitstream_id IN